Skip to content

Commit

Permalink
Split off clipboard into own type
Browse files Browse the repository at this point in the history
  • Loading branch information
Zerthox committed Jan 2, 2025
1 parent 041823d commit 36f6512
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 69 deletions.
4 changes: 2 additions & 2 deletions reffect/src/action/element.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ impl ElementAction {
Self::Cut => {
let child = children.remove(index);
log::debug!("Cut child {index} {}", child.kind.as_ref());
edit.set_clipboard(child);
edit.clipboard.set(child);
}
Self::Copy => {
let child = children[index].clone();
log::debug!("Copy child {index} {}", child.kind.as_ref());
edit.set_clipboard(child);
edit.clipboard.set(child);
}
Self::Duplicate => {
let child = children[index].clone();
Expand Down
70 changes: 70 additions & 0 deletions reffect/src/context/clipboard.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
use crate::elements::{Element, ElementType};
use nexus::imgui::Ui;
use std::{cell::UnsafeCell, fmt};

/// Clipboard state.
#[repr(transparent)]
pub struct Clipboard<T> {
contents: UnsafeCell<Option<T>>, // never give out references to this!
}

impl<T> Clipboard<T> {
unsafe fn get(&self) -> &Option<T> {
self.contents.get().as_ref().unwrap_unchecked()
}

#[allow(clippy::mut_from_ref)]
unsafe fn get_mut(&self) -> &mut Option<T> {
self.contents.get().as_mut().unwrap_unchecked()
}

pub fn has_some(&self) -> bool {
unsafe { self.get() }.is_some()
}

pub fn take(&self) -> Option<T> {
unsafe { self.get_mut() }.take()
}

pub fn set(&self, element: T) {
*unsafe { self.get_mut() } = Some(element);
}
}

impl Clipboard<Element> {
pub fn has_icon(&self) -> bool {
matches!(
unsafe { self.get() },
Some(Element {
kind: ElementType::Icon(_),
..
}),
)
}

pub fn debug(&self, ui: &Ui) {
match unsafe { self.get() } {
Some(element) => ui.text(&element.kind),
None => ui.text_disabled("empty"),
}
}
}

impl<T> Default for Clipboard<T> {
fn default() -> Self {
Self {
contents: UnsafeCell::new(None),
}
}
}

impl<T> fmt::Debug for Clipboard<T>
where
T: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Clipboard")
.field("element", unsafe { self.get() })
.finish()
}
}
66 changes: 7 additions & 59 deletions reffect/src/context/edit_state.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
use super::UiContext;
use crate::{
elements::{Element, ElementType},
id::Id,
};
use super::{Clipboard, UiContext};
use crate::{elements::Element, id::Id};
use nexus::imgui::Ui;
use std::{cell::UnsafeCell, fmt};

#[derive(Debug)]
pub struct EditState {
/// Whether edit mode is allowed in combat.
pub during_combat: bool,
Expand All @@ -23,8 +20,8 @@ pub struct EditState {
// TODO: keep parents sorted?
parents: Vec<Id>,

/// Current clipboard contents.
clipboard: UnsafeCell<Option<Element>>, // we never give out references to this!
/// Clipboard state.
pub clipboard: Clipboard<Element>,
}

impl EditState {
Expand Down Expand Up @@ -93,50 +90,14 @@ impl EditState {
self.allowed = false;
}

// do not expose these references!
unsafe fn get_clipboard(&self) -> &Option<Element> {
self.clipboard.get().as_ref().unwrap_unchecked()
}

// do not expose these references!
#[allow(clippy::mut_from_ref)]
unsafe fn get_clipboard_mut(&self) -> &mut Option<Element> {
self.clipboard.get().as_mut().unwrap_unchecked()
}

pub fn has_clipboard(&self) -> bool {
unsafe { self.get_clipboard() }.is_some()
}

pub fn has_icon_clipboard(&self) -> bool {
matches!(
unsafe { self.get_clipboard() },
Some(Element {
kind: ElementType::Icon(_),
..
}),
)
}

pub fn take_clipboard(&self) -> Option<Element> {
unsafe { self.get_clipboard_mut() }.take()
}

pub fn set_clipboard(&self, element: Element) {
*unsafe { self.get_clipboard_mut() } = Some(element);
}

pub fn debug(&self, ui: &Ui) {
ui.text("Edit allowed:");
ui.same_line();
ui.text(self.is_allowed().to_string());

ui.text("Clipboard:");
ui.same_line();
match unsafe { self.get_clipboard() } {
Some(element) => ui.text(&element.kind),
None => ui.text_disabled("empty"),
}
self.clipboard.debug(ui);

ui.text("Selected element:");
ui.same_line();
Expand All @@ -158,20 +119,7 @@ impl Default for EditState {
allowed: true,
selected: Id::default(),
parents: Vec::new(),
clipboard: UnsafeCell::new(None),
clipboard: Clipboard::default(),
}
}
}

impl fmt::Debug for EditState {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("EditState")
.field("during_combat", &self.during_combat)
.field("show_all", &self.show_all)
.field("allowed", &self.allowed)
.field("selected", &self.selected)
.field("parents", &self.parents)
.field("clipboard", unsafe { self.get_clipboard() })
.finish()
}
}
3 changes: 2 additions & 1 deletion reffect/src/context/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
mod clipboard;
mod edit_state;
mod links;
mod map;
mod player;
mod ui;

pub use self::{edit_state::*, links::*, map::*, player::*, ui::*};
pub use self::{clipboard::*, edit_state::*, links::*, map::*, player::*, ui::*};

use crate::{
internal::{BuffMap, Interface, Internal, Resources, State},
Expand Down
4 changes: 2 additions & 2 deletions reffect/src/elements/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,10 +198,10 @@ impl Common {
})
});
if MenuItem::new("Paste")
.enabled(state.has_clipboard())
.enabled(state.clipboard.has_some())
.build(ui)
{
children.push(state.take_clipboard().expect("paste without clipboard"))
children.push(state.clipboard.take().expect("paste without clipboard"))
}
}
}
Expand Down
5 changes: 3 additions & 2 deletions reffect/src/elements/list/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,16 @@ impl IconAction {
children.remove(index);
}
Self::Cut(index) => {
edit.set_clipboard(children.remove(index).into_element(size));
edit.clipboard
.set(children.remove(index).into_element(size));
}
Self::Paste(index) => {
if let Some(Element {
common,
filter,
kind: ElementType::Icon(element),
..
}) = edit.take_clipboard()
}) = edit.clipboard.take()
{
children.insert(index, ListIcon::from_element(common, element, filter));
} else {
Expand Down
6 changes: 3 additions & 3 deletions reffect/src/elements/list/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ impl RenderOptions for IconList {

item_context_menu("##listiconctx", || {
if MenuItem::new("Paste")
.enabled(ctx.edit.has_icon_clipboard())
.enabled(ctx.edit.clipboard.has_icon())
.build(ui)
{
action = IconAction::Paste(i)
Expand All @@ -121,7 +121,7 @@ impl RenderOptions for IconList {
action = IconAction::Cut(i);
}
if MenuItem::new("Copy").build(ui) {
ctx.edit.set_clipboard(icon.clone().into_element(self.size))
ctx.edit.clipboard.set(icon.clone().into_element(self.size))
}
if MenuItem::new("Duplicate").build(ui) {
action = IconAction::Duplicate(i);
Expand Down Expand Up @@ -172,7 +172,7 @@ impl RenderOptions for IconList {
}
item_context_menu("##addiconctx", || {
if MenuItem::new("Paste")
.enabled(ctx.edit.has_icon_clipboard())
.enabled(ctx.edit.clipboard.has_icon())
.build(ui)
{
action = IconAction::Paste(self.icons.len());
Expand Down

0 comments on commit 36f6512

Please sign in to comment.