Skip to main content

egui_charts/drawings/manager/
edit.rs

1//! Editing the drawing collection: add, delete, layer, group, edit, show, hide and lock.
2
3use super::{DrawingManager, DrawingSyncAction, queue_sync, sync_id};
4use crate::drawings::domain::{Drawing, DrawingToolType};
5use crate::drawings::services::{ZOrderService, in_z_order};
6use crate::model::PostContent;
7use egui::Rect;
8use std::collections::HashSet;
9
10impl DrawingManager {
11    // === Drawing CRUD ===
12
13    /// Deletes the drawing with the given ID from the drawing list.
14    ///
15    /// The deleted drawing is pushed to the undo history and a cloud sync
16    /// delete action is queued. If the deleted drawing was selected, the
17    /// selection is cleared. A locked drawing is not deleted.
18    pub fn delete_drawing(&mut self, id: usize) {
19        if self.drawings.iter().any(|d| d.id == id && d.locked) {
20            return;
21        }
22        if let Some(index) = self.drawings.iter().position(|d| d.id == id) {
23            let drawing = self.drawings.remove(index);
24            if drawing.saveable {
25                self.queue(DrawingSyncAction::Delete(sync_id(&drawing)));
26            }
27            self.history.push_delete(drawing, index);
28        }
29        self.selection.deselect(id);
30    }
31
32    /// Stores newly typed text in its placement undo step, or removes an empty placement
33    /// without an undo step. If another operation followed placement, records a normal edit
34    /// or deletion instead.
35    pub(crate) fn finish_new_text(&mut self, id: usize, text: Option<String>) {
36        let Some(index) = self.drawings.iter().position(|d| d.id == id) else {
37            return;
38        };
39        if !self.history.pop_add(id) {
40            self.finished_text_placement = Some(id);
41            match text {
42                Some(text) => self.edit_drawing(id, |d| d.text = Some(text)),
43                None => self.delete_drawing(id),
44            }
45            return;
46        }
47        match text {
48            Some(text) => {
49                self.drawings[index].text = Some(text);
50                self.history.push_add(self.drawings[index].clone());
51                self.queue_update(id);
52            }
53            None => {
54                let drawing = self.drawings.remove(index);
55                if drawing.saveable {
56                    self.queue(DrawingSyncAction::Delete(sync_id(&drawing)));
57                }
58                self.selection.deselect(id);
59            }
60        }
61    }
62
63    /// Deletes the selected drawings, keeping any locked ones.
64    pub fn delete_selected(&mut self) {
65        let before: Vec<_> = self
66            .drawings
67            .iter()
68            .enumerate()
69            .filter(|(_, d)| d.selectable && self.selection.is_selected(d.id) && !d.locked)
70            .map(|(index, d)| (index, d.clone()))
71            .collect();
72        if before.is_empty() {
73            return;
74        }
75        for (_, drawing) in &before {
76            if drawing.saveable {
77                self.queue(DrawingSyncAction::Delete(sync_id(drawing)));
78            }
79        }
80        self.drawings
81            .retain(|d| !before.iter().any(|(_, old)| old.id == d.id));
82        self.selection
83            .prune(&self.drawings.iter().map(|d| d.id).collect());
84        self.history.push_snapshot(before);
85    }
86
87    /// Removes every drawing that is not locked and clears the current drawing and selection.
88    ///
89    /// Queues a `Delete` for each drawing it removes, so the host deletes their stored records.
90    /// To unload drawings without deleting them, clear [`Self::drawings`], then call
91    /// [`Self::cancel_curr_drawing`] and [`Self::deselect`]. A bulk removal is one undo step.
92    pub fn clear_all(&mut self) {
93        self.history.push_snapshot(
94            self.drawings
95                .iter()
96                .enumerate()
97                .filter(|(_, drawing)| !drawing.locked)
98                .map(|(index, drawing)| (index, drawing.clone()))
99                .collect(),
100        );
101        for drawing in self.drawings.iter().filter(|d| !d.locked) {
102            if drawing.saveable {
103                self.queue(DrawingSyncAction::Delete(sync_id(drawing)));
104            }
105        }
106        self.drawings.retain(|d| d.locked);
107        self.cancel_curr_drawing();
108        self.selection.deselect_all();
109    }
110
111    // === Layer ordering ===
112    //
113    // `Drawing::z_order` decides the layering: drawings are drawn in ascending z-order and hit
114    // from the highest down, in list order where it is the same.
115
116    /// Bring a drawing to the front (drawn last, on top), above the highest z-order
117    pub fn bring_to_front(&mut self, id: usize) {
118        if let Some(drawing) = self.drawings.iter().find(|drawing| drawing.id == id) {
119            self.history.push_modify(id, drawing.clone());
120            ZOrderService::bring_to_front(&mut self.drawings, id);
121            self.queue_update(id);
122        }
123    }
124
125    /// Send a drawing to the back (drawn first, behind others), below the lowest z-order
126    pub fn send_to_back(&mut self, id: usize) {
127        if let Some(drawing) = self.drawings.iter().find(|drawing| drawing.id == id) {
128            self.history.push_modify(id, drawing.clone());
129            ZOrderService::send_to_back(&mut self.drawings, id);
130            self.queue_update(id);
131        }
132    }
133
134    /// Apply a layer move as one undo step, saving each changed drawing.
135    pub fn reorder_drawings(&mut self, id: usize, layers: &[(usize, i32)]) {
136        let mut previous = Vec::new();
137        for &(id, layer) in layers {
138            if let Some(drawing) = self.drawings.iter_mut().find(|d| d.id == id)
139                && drawing.z_order != layer
140            {
141                previous.push((id, drawing.z_order));
142                drawing.z_order = layer;
143                self.queue(DrawingSyncAction::UpdateDrawing(id));
144            }
145        }
146        if !previous.is_empty() {
147            self.history.push_reorder(id, previous);
148        }
149    }
150
151    /// Duplicate a drawing three bars to the right, recording its creation for undo and saving.
152    /// Adds nothing for a drawing of a disabled tool or a copy that saving would refuse.
153    pub fn duplicate_drawing(&mut self, id: usize) {
154        let Some(mut copy) = self.drawings.iter().find(|d| d.id == id).cloned() else {
155            return;
156        };
157        copy.stored_id = None;
158        copy.created_at = None;
159        copy.screen_anchor = copy.screen_anchor.map(|[x, y]| [x + 0.02, y]);
160        let first = copy.first_moved_anchor();
161        for point in copy.chart_points.iter_mut().skip(first) {
162            point.bar_idx += 3.0;
163        }
164        if self.validate_created(&copy).is_ok() {
165            self.push_synced(copy);
166        }
167    }
168
169    /// Paste copies three bars to the right with fresh identities, selecting them as one undoable addition.
170    /// The caller maps clipboard anchors into this manager's chart coordinates first. Leaves out a
171    /// copy of a disabled tool or an unregistered custom tool and a copy that saving would refuse;
172    /// when it leaves out every copy, it selects nothing and records no undo step.
173    pub fn paste_drawings(&mut self, drawings: &[Drawing]) {
174        let mut ids = Vec::new();
175        for drawing in in_z_order(drawings) {
176            let mut copy = drawing.clone();
177            copy.stored_id = None;
178            copy.created_at = None;
179            copy.screen_anchor = copy.screen_anchor.map(|[x, y]| [x + 0.02, y]);
180            copy.points.clear();
181            copy.level_ys.clear();
182            let first = copy.first_moved_anchor();
183            for point in copy.chart_points.iter_mut().skip(first) {
184                *point = point.with_shifted_bar_idx(3.0);
185            }
186            if self.validate_created(&copy).is_err() {
187                continue;
188            }
189            copy.id = self.take_id();
190            copy.z_order = self.top_z_order();
191            ids.push(copy.id);
192            let id = copy.id;
193            self.drawings.push(copy);
194            self.queue(DrawingSyncAction::SaveDrawing(id));
195        }
196        if ids.is_empty() {
197            return;
198        }
199        self.select_drawings(ids.iter().copied());
200        self.history
201            .push_command(crate::drawings::services::DrawingCommand::Snapshot {
202                drawings: Vec::new(),
203                ids,
204            });
205    }
206
207    /// Replaces every saveable drawing with `drawings`, restored from storage, as one undo step.
208    /// Each keeps its stored identity (`stored_id`, `created_at`, `notes`) and layer and gets a
209    /// new id; drawings that are never saved stay. Queues a `Delete` for each drawing it removes
210    /// and a save for each it adds.
211    pub(crate) fn replace_saved_drawings(&mut self, drawings: Vec<Drawing>) {
212        let removed: Vec<_> = self
213            .drawings
214            .iter()
215            .enumerate()
216            .filter(|(_, drawing)| drawing.saveable)
217            .map(|(index, drawing)| (index, drawing.clone()))
218            .collect();
219        if removed.is_empty() && drawings.is_empty() {
220            return;
221        }
222        let mut ids: Vec<_> = removed.iter().map(|(_, drawing)| drawing.id).collect();
223        // Taken while the replaced drawings are listed, so that no id is handed out twice.
224        let mut added = Vec::with_capacity(drawings.len());
225        for mut drawing in drawings {
226            drawing.id = self.take_id();
227            ids.push(drawing.id);
228            added.push(drawing);
229        }
230        for (_, drawing) in &removed {
231            self.queue(DrawingSyncAction::Delete(sync_id(drawing)));
232        }
233        self.drawings.retain(|drawing| !drawing.saveable);
234        for drawing in added {
235            let id = drawing.id;
236            self.drawings.push(drawing);
237            self.queue(DrawingSyncAction::SaveDrawing(id));
238        }
239        self.selection
240            .prune(&self.drawings.iter().map(|d| d.id).collect());
241        self.history
242            .push_command(crate::drawings::services::DrawingCommand::Snapshot {
243                drawings: removed,
244                ids,
245            });
246    }
247
248    /// Applies `edit` to the drawing with the given ID, records it for undo and queues it to
249    /// be saved. Edit a drawing through this rather than [`Self::drawings`] so the change is
250    /// saved. A locked drawing accepts only visibility, name and folder changes, or an edit that unlocks it.
251    /// Does nothing if there is no such drawing.
252    pub fn edit_drawing(&mut self, id: usize, edit: impl FnOnce(&mut Drawing)) {
253        if let Some(drawing) = self.drawings.iter_mut().find(|d| d.id == id) {
254            let old = drawing.clone();
255            edit(drawing);
256            if old.locked && drawing.locked {
257                let visible = drawing.visible;
258                let label = drawing.label.clone();
259                let folder = drawing.folder.clone();
260                *drawing = old.clone();
261                if visible == old.visible && label == old.label && folder == old.folder {
262                    return;
263                }
264                drawing.visible = visible;
265                drawing.label = label;
266                drawing.folder = folder;
267            }
268            if !drawing.selectable {
269                self.selection.deselect(id);
270            }
271            self.queue_edit(&old);
272            self.history.push_modify(id, old);
273            self.painted
274                .get_mut()
275                .unwrap_or_else(|e| e.into_inner())
276                .retain(|p| p.id != id);
277        }
278    }
279
280    /// Gives the drawings whose post waits for `link` the host's answer: its content, or `None`
281    /// when the host could not resolve it. Not an undoable edit, also on locked drawings;
282    /// queues each drawing that received content to be saved. Returns whether any did.
283    pub(crate) fn resolve_link(&mut self, link: &str, content: Option<&PostContent>) -> bool {
284        let mut updated = Vec::new();
285        for drawing in &mut self.drawings {
286            if let Some(post) = &mut drawing.post
287                && post.link == link
288                && post.content.is_none()
289            {
290                if let Some(content) = content {
291                    post.content = Some(content.clone());
292                    post.failed = false;
293                    updated.push(drawing.id);
294                } else {
295                    post.failed = true;
296                }
297            }
298        }
299        for &id in &updated {
300            self.queue_update(id);
301        }
302        !updated.is_empty()
303    }
304
305    /// Edits live drawing ids as one undo step and queues their saved changes.
306    /// Locked drawings accept visibility, name and folder changes, or an edit that unlocks them.
307    pub fn edit_drawings(&mut self, ids: &[usize], mut edit: impl FnMut(&mut Drawing)) {
308        if ids.is_empty() {
309            return;
310        }
311        let ids: HashSet<_> = ids.iter().copied().collect();
312        let mut before = Vec::new();
313        for (index, drawing) in self
314            .drawings
315            .iter_mut()
316            .enumerate()
317            .filter(|(_, d)| ids.contains(&d.id))
318        {
319            let old = drawing.clone();
320            edit(drawing);
321            if old.locked && drawing.locked {
322                let (visible, label, folder) = (
323                    drawing.visible,
324                    drawing.label.clone(),
325                    drawing.folder.clone(),
326                );
327                *drawing = old.clone();
328                if visible == old.visible && label == old.label && folder == old.folder {
329                    continue;
330                }
331                drawing.visible = visible;
332                drawing.label = label;
333                drawing.folder = folder;
334            }
335            if !drawing.selectable {
336                self.selection.deselect(old.id);
337            }
338            before.push((index, old));
339        }
340        for (_, old) in &before {
341            self.queue_edit(old);
342        }
343        self.history.push_snapshot(before);
344        self.painted
345            .get_mut()
346            .unwrap_or_else(|e| e.into_inner())
347            .retain(|p| !ids.contains(&p.id));
348    }
349
350    /// Applies `edit` as one frame of a continuous edit, such as dragging a colour picker: the
351    /// first frame records the drawing's state and nothing is queued until
352    /// [`Self::end_drag_handle`], which makes the whole gesture one undo step and queues one
353    /// update. Does nothing if there is no such drawing or it is locked.
354    pub fn drag_edit_drawing(&mut self, id: usize, edit: impl FnOnce(&mut Drawing)) {
355        if self.drag_old_state.as_ref().is_some_and(|d| d.id != id) {
356            self.end_drag_handle();
357        }
358        if let Some(drawing) = self.drawings.iter_mut().find(|d| d.id == id && !d.locked) {
359            self.drag_old_state.get_or_insert_with(|| drawing.clone());
360            edit(drawing);
361            self.painted
362                .get_mut()
363                .unwrap_or_else(|e| e.into_inner())
364                .retain(|p| p.id != id);
365        }
366    }
367
368    // === Coordinate updates ===
369
370    /// Recomputes screen coordinates for all drawings (completed and in-progress)
371    /// from their persistent chart coordinates using the current pan/zoom transforms.
372    ///
373    /// Must be called each frame before rendering.
374    pub fn update_all_screen_coords<F, G>(&mut self, bar_to_x: F, price_to_y: G)
375    where
376        F: Fn(f32) -> f32 + Copy,
377        G: Fn(f64) -> f32 + Copy,
378    {
379        for drawing in &mut self.drawings {
380            drawing.update_screen_coords(bar_to_x, price_to_y);
381        }
382        if let Some(ref mut drawing) = self.curr_drawing {
383            drawing.update_screen_coords(bar_to_x, price_to_y);
384        }
385        if let Some(drawing) = &mut self.measurement {
386            drawing.update_screen_coords(bar_to_x, price_to_y);
387        }
388    }
389
390    /// Starts a widget pane pass. Standalone managers keep the default main-pane context.
391    pub(crate) fn set_pane(
392        &mut self,
393        pane: Option<usize>,
394        members: Vec<Option<usize>>,
395        rect: Rect,
396    ) {
397        if pane.is_none() {
398            self.pane_clips.clear();
399        }
400        for member in &members {
401            self.pane_clips.insert(*member, rect);
402        }
403        self.pane = pane;
404        self.pane_members = members;
405        self.pane_rect = rect;
406    }
407
408    pub(crate) fn owns_pane(&self, drawing: &Drawing) -> bool {
409        self.pane_members.contains(&drawing.pane)
410    }
411
412    pub(crate) fn update_pane_screen_coords(
413        &mut self,
414        bar_to_x: impl Fn(f32) -> f32 + Copy,
415        price_to_y: impl Fn(f64) -> f32 + Copy,
416    ) {
417        for drawing in self
418            .drawings
419            .iter_mut()
420            .chain(&mut self.curr_drawing)
421            .chain(&mut self.measurement)
422        {
423            if self.pane_members.contains(&drawing.pane) {
424                drawing.update_screen_coords(bar_to_x, price_to_y);
425                drawing.update_pane_coords(self.pane_rect);
426            }
427        }
428    }
429
430    /// Projects anchored annotations into `pane` after chart-coordinate updates.
431    /// The chart calls this automatically; standalone managers call it before rendering.
432    pub fn update_pane_anchors(&mut self, pane: Rect) {
433        self.pane_rect = pane;
434        for drawing in self
435            .drawings
436            .iter_mut()
437            .chain(&mut self.curr_drawing)
438            .chain(&mut self.measurement)
439        {
440            drawing.update_pane_coords(pane);
441        }
442    }
443
444    /// Shifts every bar index at or after `from` by `shift`, across all
445    /// drawings, the in-progress drawing, the state a handle drag restores on
446    /// undo, and the undo history.
447    ///
448    /// This is called when bars are inserted in front of existing ones, causing
449    /// the bar indices from there on to shift right. Pass `f32::NEG_INFINITY`
450    /// for bars inserted in front of the first bar.
451    pub fn shift_bar_indices(&mut self, from: f32, shift: f32) {
452        if shift.abs() < 0.001 {
453            return;
454        }
455        for drawing in &mut self.drawings {
456            drawing.shift_bar_indices(from, shift);
457        }
458        if let Some(ref mut curr) = self.curr_drawing {
459            curr.shift_bar_indices(from, shift);
460        }
461        if let Some(drawing) = &mut self.measurement {
462            drawing.shift_bar_indices(from, shift);
463        }
464        if let Some(old) = &mut self.drag_old_state {
465            old.shift_bar_indices(from, shift);
466        }
467        for drawing in &mut self.drag_group {
468            drawing.shift_bar_indices(from, shift);
469        }
470        self.history.shift_bar_indices(from, shift);
471    }
472
473    // === Visibility ===
474
475    /// Toggles the visibility of all drawings. If all are visible, hides them
476    /// all; otherwise shows them all.
477    pub fn toggle_visibility_all(&mut self) {
478        let visible = !self.drawings.iter().all(|drawing| drawing.visible);
479        self.set_visibility_all(visible);
480    }
481
482    /// Set every drawing's visibility as one undoable edit.
483    pub fn set_visibility_all(&mut self, visible: bool) {
484        let mut before = Vec::new();
485        for (index, drawing) in self
486            .drawings
487            .iter_mut()
488            .enumerate()
489            .filter(|(_, drawing)| drawing.visible != visible)
490        {
491            before.push((index, drawing.clone()));
492            drawing.visible = visible;
493            if self.sync_enabled
494                && drawing.saveable
495                && let Ok(mut sync) = self.pending_sync.lock()
496            {
497                queue_sync(
498                    &mut sync,
499                    DrawingSyncAction::UpdateDrawing(drawing.id),
500                    Some(&sync_id(drawing)),
501                );
502            }
503        }
504        self.history.push_snapshot(before);
505    }
506
507    /// Toggles the lock state of all drawings. If all are locked, unlocks them
508    /// all; otherwise locks them all.
509    pub fn toggle_lock_all(&mut self) {
510        let all_locked = self.drawings.iter().all(|d| d.locked);
511        let mut before = Vec::new();
512        for (index, drawing) in self
513            .drawings
514            .iter_mut()
515            .enumerate()
516            .filter(|(_, d)| d.locked == all_locked)
517        {
518            before.push((index, drawing.clone()));
519            drawing.locked = !all_locked;
520            if self.sync_enabled
521                && drawing.saveable
522                && let Ok(mut sync) = self.pending_sync.lock()
523            {
524                queue_sync(
525                    &mut sync,
526                    DrawingSyncAction::UpdateDrawing(drawing.id),
527                    Some(&sync_id(drawing)),
528                );
529            }
530        }
531        self.history.push_snapshot(before);
532    }
533
534    /// Adds a completed drawing with validated chart coordinates and properties as one undo
535    /// step. Assigns a fresh id, retains the supplied layer, and queues a save if `saveable`.
536    /// Use `Chart::create_drawing` when the anchors are UTC times rather than bar indices.
537    pub fn add_drawing(&mut self, mut drawing: Drawing) -> Result<usize, &'static str> {
538        self.validate_drawing(&drawing)?;
539        if drawing.custom_tool.is_none() && !self.registry.availability(drawing.tool_type).enabled {
540            return Err("Drawing tool is disabled");
541        }
542        drawing.id = self.take_id();
543        drawing.stored_id = None;
544        drawing.created_at = None;
545        drawing.completed = true;
546        drawing.points.clear();
547        drawing.level_ys.clear();
548        let id = drawing.id;
549        self.history.push_add(drawing.clone());
550        self.drawings.push(drawing);
551        self.queue(DrawingSyncAction::SaveDrawing(id));
552        Ok(id)
553    }
554
555    pub(crate) fn validate_drawing(&self, drawing: &Drawing) -> Result<(), &'static str> {
556        crate::drawings::validation::validate(drawing)?;
557        self.validate_custom_tool(drawing)
558    }
559
560    /// A registered tool's drawing must be a polyline with exactly the tool's anchors.
561    fn validate_custom_tool(&self, drawing: &Drawing) -> Result<(), &'static str> {
562        if let Some(id) = &drawing.custom_tool {
563            let tool = self.registry.get(id).ok_or("Unknown drawing tool")?;
564            if drawing.tool_type != DrawingToolType::Polyline
565                || drawing.chart_points.len() != tool.anchors
566            {
567                return Err("Invalid custom drawing anchors");
568            }
569        }
570        Ok(())
571    }
572
573    /// Checks a drawing the user creates by duplicating, pasting, copy dragging or placing it:
574    /// what saving needs, a registered tool's anchors and the tool's availability, as
575    /// `add_drawing` does. A drawing finished with fewer anchors than the toolbar collects
576    /// passes when its tool draws with them.
577    pub(super) fn validate_created(&self, drawing: &Drawing) -> Result<(), &'static str> {
578        crate::drawings::validation::validate_stored(drawing)?;
579        self.validate_custom_tool(drawing)?;
580        if drawing.custom_tool.is_none() && !self.registry.availability(drawing.tool_type).enabled {
581            return Err("Drawing tool is disabled");
582        }
583        Ok(())
584    }
585
586    /// Drawings included in host object lists, in drawing-list order.
587    pub fn listed_drawings(&self) -> impl Iterator<Item = &Drawing> {
588        self.drawings.iter().filter(|drawing| drawing.listed)
589    }
590
591    /// Members of a host-defined group, including hidden and locked drawings.
592    pub fn group_drawings<'a>(&'a self, group: &'a str) -> impl Iterator<Item = &'a Drawing> {
593        self.drawings
594            .iter()
595            .filter(move |drawing| drawing.group.as_deref() == Some(group))
596    }
597
598    /// Assigns or removes group membership for live ids as one undo step. Locked drawings
599    /// keep their membership. Group names are host-owned and are saved with each member.
600    pub fn set_group(&mut self, ids: &[usize], group: Option<String>) {
601        let mut before = Vec::new();
602        for (index, drawing) in self.drawings.iter_mut().enumerate() {
603            if ids.contains(&drawing.id) && !drawing.locked && drawing.group != group {
604                before.push((index, drawing.clone()));
605                drawing.group = group.clone();
606            }
607        }
608        for (_, old) in &before {
609            self.queue_update(old.id);
610        }
611        self.history.push_snapshot(before);
612    }
613
614    /// Selects all selectable members of a group for the existing move, edit and delete
615    /// operations. Those operations retain their lock checks and single undo step.
616    pub fn select_group(&mut self, group: &str) {
617        let ids: Vec<_> = self.group_drawings(group).map(|d| d.id).collect();
618        self.select_drawings(ids);
619    }
620
621    /// Add a drawing from another source (for multi-chart sync, or restored from storage)
622    /// under a new id, and return the id. A drawing restored from storage keeps its stored
623    /// identity (`stored_id`, `created_at`, `notes`) and its z-order. A drawing that was never
624    /// stored goes on top, and so does a copy, one stored under the id of a drawing already
625    /// here, which is saved as a new drawing.
626    ///
627    /// Refuses, and adds nothing, a drawing that saving would refuse: a cursor tool, fewer
628    /// anchors than the tool draws with, a non-finite coordinate, an anchor too far out to map
629    /// to a time or a setting out of range. Tool availability does not apply: drawings of a
630    /// disabled tool are restored and synced.
631    pub fn add_synced_drawing(&mut self, drawing: Drawing) -> Result<usize, &'static str> {
632        crate::drawings::validation::validate_stored(&drawing)?;
633        Ok(self.push_synced(drawing))
634    }
635
636    /// Adds a checked drawing under a new id, as [`Self::add_synced_drawing`] describes.
637    fn push_synced(&mut self, mut drawing: Drawing) -> usize {
638        if drawing.stored_id.is_some()
639            && self
640                .drawings
641                .iter()
642                .any(|d| d.stored_id == drawing.stored_id)
643        {
644            (drawing.stored_id, drawing.created_at) = (None, None);
645        }
646        if drawing.stored_id.is_none() {
647            drawing.z_order = self.top_z_order();
648        }
649        // Assign a new ID to avoid conflicts
650        drawing.id = self.take_id();
651        if drawing.stored_id.is_none() {
652            if drawing.saveable {
653                self.queue(DrawingSyncAction::SaveDrawing(drawing.id));
654            }
655            self.history.push_add(drawing.clone());
656        }
657        let id = drawing.id;
658        self.drawings.push(drawing);
659        id
660    }
661}