Skip to content

Commit 669f7b5

Browse files
Implement copy and paste
1 parent ec26422 commit 669f7b5

File tree

4 files changed

+130
-36
lines changed

4 files changed

+130
-36
lines changed

src/renderer/app_state.rs

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -274,9 +274,9 @@ impl<'a> AppState<'a> {
274274
self.window.set_cursor_icon(CursorIcon::Default);
275275
}
276276
}
277-
(KeyCode::KeyC, ElementState::Pressed) => {
278-
feature_uniform.reset_features();
279-
}
277+
// (KeyCode::KeyC, ElementState::Pressed) => {
278+
// feature_uniform.reset_features();
279+
// }
280280
(KeyCode::KeyB, ElementState::Pressed) => {
281281
feature_uniform.toggle_blur();
282282
}
@@ -336,6 +336,54 @@ impl<'a> AppState<'a> {
336336
self.revision_stack.undo();
337337
}
338338
}
339+
(KeyCode::KeyC, ElementState::Pressed) => {
340+
#[cfg(target_os = "macos")]
341+
if self.modifiers.state().super_key() {
342+
let circle = self.mouse_state.selected_shape().map(|i| {
343+
self.editor_state
344+
.get_element_by_id(i)
345+
.expect("selected id must be valid")
346+
.inner()
347+
.clone()
348+
});
349+
350+
self.mouse_state.set_clipboard_shape(circle);
351+
}
352+
353+
#[cfg(not(target_os = "macos"))]
354+
if self.modifiers.state().control_key() {
355+
let circle = self.mouse_state.selected_shape().map(|i| {
356+
self.editor_state
357+
.get_element_by_id(i)
358+
.expect("selected id must be valid")
359+
.inner()
360+
.clone()
361+
});
362+
363+
self.mouse_state.set_clipboard_shape(circle);
364+
}
365+
}
366+
(KeyCode::KeyV, ElementState::Pressed) => {
367+
let window_dimensions = (self.size.width as f32, self.size.height as f32);
368+
369+
#[cfg(target_os = "macos")]
370+
if self.modifiers.state().super_key() {
371+
if let Some(circle) = self.mouse_state.clipboard_shape() {
372+
let copied_element_id =
373+
self.editor_state.copy_shape(circle, window_dimensions);
374+
self.mouse_state.set_selected_shape(Some(copied_element_id));
375+
}
376+
}
377+
378+
#[cfg(not(target_os = "macos"))]
379+
if self.modifiers.state().control_key() {
380+
if let Some(circle) = self.mouse_state.clipboard_shape() {
381+
let copied_element_id =
382+
self.editor_state.copy_shape(circle, window_dimensions);
383+
self.mouse_state.set_selected_shape(Some(copied_element_id));
384+
}
385+
}
386+
}
339387
_ => return false,
340388
},
341389
_ => return false,

src/renderer/feature_uniform.rs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,6 @@ impl FeatureUniform {
3434
transform: Self::TRANSFORM_IDENTITY,
3535
}
3636
}
37-
38-
pub(crate) const fn reset_features(&mut self) {
39-
self.grayscale = 0;
40-
// self.sepia = 0;
41-
self.invert = 0;
42-
self.blur = 0;
43-
self.sharpen = 0;
44-
self.edge_detect = 0;
45-
self.transform = Self::TRANSFORM_IDENTITY;
46-
}
4737
}
4838

4939
impl FeatureUniform {

src/renderer/mouse_state.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
use crate::renderer::shape::Coordinate;
1+
use crate::renderer::shape::{Circle, Coordinate};
22

33
#[derive(Debug, Default)]
44
pub struct MouseState {
55
pressed: bool,
66
position_x: f32,
77
position_y: f32,
88
start_drag: Option<(f32, f32)>,
9-
selected_shape: Option<usize>, // index to the shape stack
9+
selected_shape: Option<usize>, // index to the shape stack
10+
clipboard_shape: Option<Circle>, // index to the shape stack
1011
dragging_shape: bool,
1112
drag_offset: (f32, f32),
1213
}
@@ -42,8 +43,8 @@ impl MouseState {
4243
self.selected_shape
4344
}
4445

45-
pub(crate) const fn set_selected_shape(&mut self, index: Option<usize>) {
46-
self.selected_shape = index;
46+
pub(crate) const fn set_selected_shape(&mut self, element_id: Option<usize>) {
47+
self.selected_shape = element_id;
4748
}
4849

4950
pub(crate) const fn dragging_shape(&self) -> bool {
@@ -61,4 +62,12 @@ impl MouseState {
6162
pub(crate) const fn set_drag_offset(&mut self, offset: (f32, f32)) {
6263
self.drag_offset = offset;
6364
}
65+
66+
pub(crate) fn clipboard_shape(&self) -> Option<Circle> {
67+
self.clipboard_shape.clone()
68+
}
69+
70+
pub(crate) const fn set_clipboard_shape(&mut self, element_id: Option<Circle>) {
71+
self.clipboard_shape = element_id;
72+
}
6473
}

src/renderer/shape.rs

Lines changed: 66 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,16 @@ impl EditorState {
5858
}
5959

6060
pub fn create_shape(&mut self, circle: Circle) -> usize {
61+
let element_id = self.create_element(circle);
62+
self.revision_stack.push(ElementAction::Draw { element_id });
63+
64+
element_id
65+
}
66+
67+
fn create_element(&mut self, circle: Circle) -> usize {
6168
let _id = self.next_id();
6269

6370
self.element_stack.push(Element { _id, inner: circle });
64-
self.revision_stack
65-
.push(ElementAction::Draw { element_id: _id });
6671

6772
_id
6873
}
@@ -77,13 +82,15 @@ impl EditorState {
7782
element_id,
7883
original_pos,
7984
});
85+
86+
println!("{:#?}", self.revision_stack);
8087
}
8188
}
8289

8390
pub fn translate_shape(&mut self, element_id: usize, new_coord: Coordinate) {
8491
if let Some(i) = self.get_element_index_by_id(element_id) {
8592
let element = unsafe { self.element_stack.get_unchecked_mut(i) };
86-
element.inner.translate(new_coord);
93+
element.inner.set_center(new_coord);
8794
}
8895
}
8996

@@ -94,53 +101,72 @@ impl EditorState {
94101
.find_map(|(i, &Element { _id, .. })| if _id == element_id { Some(i) } else { None })
95102
}
96103

104+
pub fn get_element_by_id(&self, element_id: usize) -> Option<&Element> {
105+
self.get_element_index_by_id(element_id)
106+
.map(|i| &self.element_stack[i])
107+
}
108+
97109
pub fn get_element_by_point(
98110
&self,
99111
point: Coordinate,
100112
window_dimension: (f32, f32),
101113
) -> Option<&Element> {
102114
self.element_stack.iter().rev().find(|e| {
103115
let circle = e.inner();
104-
let circle_in_pixel_coords = circle.convert_pixel_coordinate(window_dimension);
116+
let circle_in_pixel_coords = circle.convert_into_pixel_coordinate(window_dimension);
105117

106118
compute_distance(circle_in_pixel_coords.center(), point)
107119
<= circle_in_pixel_coords.radius()
108120
})
109121
}
110122

111123
pub fn remove_shape_by_id(&mut self, element_id: usize) {
112-
if let Some(i) = self.get_element_index_by_id(element_id) {
113-
self.remove_shape_by_index(i);
114-
}
115-
}
116-
117-
fn remove_shape_by_index(&mut self, index: usize) {
118-
let Element { _id, inner } = self.element_stack.remove(index);
124+
let Element { _id, inner } = self.remove_element(element_id);
119125
self.revision_stack
120126
.push(ElementAction::Delete { element_id: _id });
127+
121128
self.deleted_shapes.insert(_id, inner);
122129
}
123130

131+
fn remove_element(&mut self, element_id: usize) -> Element {
132+
if let Some(i) = self.get_element_index_by_id(element_id) {
133+
return self.element_stack.remove(i);
134+
}
135+
136+
panic!("expected a valid element id");
137+
}
138+
124139
fn find_deleted_shape(&mut self, element_id: usize) -> Option<Circle> {
125140
self.deleted_shapes.remove(&element_id)
126141
}
127142

128143
pub fn undo(&mut self) {
129-
if let Some(last_action) = self.revision_stack.last() {
144+
if let Some(last_action) = self.revision_stack.pop() {
130145
match last_action {
131146
ElementAction::Move {
132147
element_id,
133148
original_pos,
134-
} => self.translate_shape(*element_id, *original_pos),
135-
ElementAction::Draw { element_id } => self.remove_shape_by_id(*element_id),
149+
} => self.translate_shape(element_id, original_pos),
150+
ElementAction::Draw { element_id } => {
151+
let _ = self.remove_element(element_id);
152+
}
136153
ElementAction::Delete { element_id } => {
137-
if let Some(circle) = self.find_deleted_shape(*element_id) {
138-
self.create_shape(circle);
154+
if let Some(circle) = self.find_deleted_shape(element_id) {
155+
self.create_element(circle);
139156
}
140157
}
141158
}
142159
}
143160
}
161+
162+
pub fn copy_shape(&mut self, circle: Circle, window_dimensions: (f32, f32)) -> usize {
163+
let circle = circle
164+
.convert_into_pixel_coordinate(window_dimensions)
165+
.translate((40.0, 40.0))
166+
.convert_into_normalized_coordinate(window_dimensions);
167+
168+
self.create_shape(circle)
169+
}
144170
}
145171

146172
pub fn compute_distance(from: Coordinate, to: Coordinate) -> f32 {
@@ -150,7 +176,7 @@ pub fn compute_distance(from: Coordinate, to: Coordinate) -> f32 {
150176
// Stored as (x, y)
151177
pub type Coordinate = (f32, f32);
152178

153-
#[derive(Debug)]
179+
#[derive(Debug, Clone)]
154180
pub struct Circle {
155181
center: Coordinate,
156182
radius: f32,
@@ -171,7 +197,17 @@ impl Circle {
171197
}
172198
}
173199

174-
pub fn convert_pixel_coordinate(&self, window_dimensions: (f32, f32)) -> Self {
200+
pub fn convert_into_normalized_coordinate(&self, window_dimensions: (f32, f32)) -> Self {
201+
let (w, h) = window_dimensions;
202+
let (cx, cy) = self.center;
203+
204+
Self {
205+
center: (cx / w, cy / h),
206+
radius: self.radius / w.min(h),
207+
}
208+
}
209+
210+
pub fn convert_into_pixel_coordinate(&self, window_dimensions: (f32, f32)) -> Self {
175211
let (w, h) = window_dimensions;
176212
let (cx, cy) = self.center;
177213

@@ -189,7 +225,18 @@ impl Circle {
189225
self.radius
190226
}
191227

192-
pub const fn translate(&mut self, new_center: Coordinate) {
228+
pub const fn set_center(&mut self, new_center: Coordinate) {
193229
self.center = new_center;
194230
}
231+
232+
// note the offsets are in pixel coordinates!
233+
pub const fn translate(&self, offset: Coordinate) -> Self {
234+
let (dx, dy) = offset;
235+
let (x, y) = self.center;
236+
237+
Self {
238+
center: (x + dx, y + dy),
239+
radius: self.radius,
240+
}
241+
}
195242
}

0 commit comments

Comments
 (0)