@@ -4,7 +4,7 @@ use std::hash::Hash;
44
55use egui:: {
66 Align , Button , Color32 , CornerRadius , Image , Layout , Pos2 , Rect , Sense , Stroke , StrokeKind , Ui ,
7- Widget as _, vec2,
7+ Vec2 , Widget as _, vec2,
88} ;
99use slotmap:: { Key as _, SecondaryMap , SlotMap } ;
1010
@@ -15,19 +15,26 @@ pub const WIRE_HIT_DISTANCE: f32 = 10.0;
1515
1616pub const COLOR_PIN_DETACH_HINT : Color32 = Color32 :: RED ;
1717pub const COLOR_PIN_POWERED_OUTLINE : Color32 = Color32 :: BLUE ;
18- pub const COLOR_WIRE_POWERED : Color32 = Color32 :: BLUE ;
19- pub const COLOR_WIRE_IDLE : Color32 = Color32 :: DARK_BLUE ;
20- pub const COLOR_WIRE_HOVER : Color32 = Color32 :: GREEN ;
18+ pub const COLOR_WIRE_POWERED : Color32 = Color32 :: GREEN ;
19+ pub const COLOR_WIRE_IDLE : Color32 = Color32 :: LIGHT_BLUE ;
20+ pub const COLOR_WIRE_HOVER : Color32 = Color32 :: GRAY ;
2121pub const COLOR_HOVER_OUTLINE : Color32 = Color32 :: GRAY ;
2222pub const COLOR_ENDPOINT_HOVER : Color32 = Color32 :: LIGHT_YELLOW ;
2323pub const COLOR_POTENTIAL_CONN_HIGHLIGHT : Color32 = Color32 :: LIGHT_YELLOW ;
24- pub const COLOR_SELECTION_HIGHLIGHT : Color32 = Color32 :: LIGHT_YELLOW ;
25- pub const COLOR_SELECTION_BOX : Color32 = Color32 :: YELLOW ;
24+ pub const COLOR_SELECTION_HIGHLIGHT : Color32 = Color32 :: GRAY ;
25+ pub const COLOR_SELECTION_BOX : Color32 = Color32 :: LIGHT_BLUE ;
2626
2727slotmap:: new_key_type! {
2828 pub struct InstanceId ;
2929}
3030
31+ #[ derive( serde:: Deserialize , serde:: Serialize , Copy , Debug , Clone ) ]
32+ pub enum InstancePosOffset {
33+ Gate ( GateKind , Vec2 ) ,
34+ Power ( Vec2 ) ,
35+ Wire ( Vec2 , Vec2 ) ,
36+ }
37+
3138#[ derive( serde:: Deserialize , serde:: Serialize , Copy , Debug , Clone ) ]
3239pub enum InstanceKind {
3340 Gate ( GateKind ) ,
@@ -164,7 +171,7 @@ pub struct DB {
164171}
165172
166173impl DB {
167- pub fn new_gate ( & mut self , g : Gate ) {
174+ pub fn new_gate ( & mut self , g : Gate ) -> InstanceId {
168175 let k = self . instances . insert ( ( ) ) ;
169176 self . gates . insert ( k, g) ;
170177 let kind = self
@@ -173,18 +180,21 @@ impl DB {
173180 . expect ( "gate must exist right after insertion" )
174181 . kind ;
175182 self . types . insert ( k, InstanceKind :: Gate ( kind) ) ;
183+ k
176184 }
177185
178- pub fn new_power ( & mut self , p : Power ) {
186+ pub fn new_power ( & mut self , p : Power ) -> InstanceId {
179187 let k = self . instances . insert ( ( ) ) ;
180188 self . powers . insert ( k, p) ;
181189 self . types . insert ( k, InstanceKind :: Power ) ;
190+ k
182191 }
183192
184- pub fn new_wire ( & mut self , w : Wire ) {
193+ pub fn new_wire ( & mut self , w : Wire ) -> InstanceId {
185194 let k = self . instances . insert ( ( ) ) ;
186195 self . wires . insert ( k, w) ;
187196 self . types . insert ( k, InstanceKind :: Wire ) ;
197+ k
188198 }
189199
190200 pub fn ty ( & self , id : InstanceId ) -> InstanceKind {
@@ -280,6 +290,8 @@ pub struct App {
280290 pub show_debug : bool ,
281291 // selection set and move preview
282292 pub selected : std:: collections:: HashSet < InstanceId > ,
293+ //Copied. Items with their offset compared to a middle point in the rectangle
294+ pub clipboard : Vec < InstancePosOffset > ,
283295}
284296
285297impl Default for App {
@@ -301,6 +313,7 @@ impl Default for App {
301313 current_dirty : true ,
302314 show_debug : true ,
303315 selected : Default :: default ( ) ,
316+ clipboard : Default :: default ( ) ,
304317 }
305318 }
306319}
@@ -394,6 +407,8 @@ impl App {
394407 writeln ! ( out, "hovered: {:?}" , self . hovered) . ok ( ) ;
395408 writeln ! ( out, "drag: {:?}" , self . drag) . ok ( ) ;
396409 writeln ! ( out, "potential_conns: {}" , self . potential_connections. len( ) ) . ok ( ) ;
410+ writeln ! ( out, "clipboard: {:?}" , self . clipboard) . ok ( ) ;
411+ writeln ! ( out, "selected: {:?}" , self . selected) . ok ( ) ;
397412
398413 writeln ! ( out, "\n Gates:" ) . ok ( ) ;
399414 for ( id, g) in & self . db . gates {
@@ -587,6 +602,7 @@ impl App {
587602 }
588603 }
589604
605+ #[ expect( clippy:: too_many_lines) ]
590606 fn draw_canvas ( & mut self , ui : & mut Ui ) {
591607 let ( resp, _painter) = ui. allocate_painter ( ui. available_size ( ) , Sense :: hover ( ) ) ;
592608 let canvas_rect = resp. rect ;
@@ -596,12 +612,96 @@ impl App {
596612 let right_clicked = ui. input ( |i| i. pointer . secondary_clicked ( ) ) ;
597613 let mouse_pos = ui. ctx ( ) . pointer_interact_pos ( ) ;
598614
615+ let mut copy_event_detected = false ;
616+ let mut paste_event_detected = false ;
617+ ui. ctx ( ) . input ( |i| {
618+ for event in & i. events {
619+ if matches ! ( event, egui:: Event :: Copy ) {
620+ log:: info!( "Copy detected" ) ;
621+ copy_event_detected = true ;
622+ }
623+ if let egui:: Event :: Paste ( _) = event {
624+ paste_event_detected = true ;
625+ }
626+ }
627+ } ) ;
628+
599629 let d_pressed = ui. input ( |i| i. key_pressed ( egui:: Key :: D ) ) ;
600630
601631 if d_pressed && let Some ( id) = self . hovered . take ( ) {
602632 self . delete_instance ( id) ;
603633 }
604634
635+ if copy_event_detected && !self . selected . is_empty ( ) {
636+ let mut points = vec ! [ ] ;
637+ for & selected in & self . selected {
638+ match self . db . ty ( selected) {
639+ InstanceKind :: Gate ( _) => {
640+ let g = self . db . get_gate ( selected) ;
641+ points. push ( g. pos ) ;
642+ }
643+ InstanceKind :: Power => {
644+ let p = self . db . get_power ( selected) ;
645+ points. push ( p. pos ) ;
646+ }
647+ InstanceKind :: Wire => {
648+ let w = self . db . get_wire ( selected) ;
649+ points. push ( w. start ) ;
650+ points. push ( w. end ) ;
651+ }
652+ }
653+ }
654+ let rect = Rect :: from_points ( & points) ;
655+ let center = rect. center ( ) ;
656+
657+ let mut object_pos = vec ! [ ] ;
658+
659+ for & selected in & self . selected {
660+ let ty = self . db . ty ( selected) ;
661+ match ty {
662+ InstanceKind :: Gate ( kind) => {
663+ let g = self . db . get_gate ( selected) ;
664+ object_pos. push ( InstancePosOffset :: Gate ( kind, center - g. pos ) ) ;
665+ }
666+ InstanceKind :: Power => {
667+ let p = self . db . get_power ( selected) ;
668+ object_pos. push ( InstancePosOffset :: Power ( center - p. pos ) ) ;
669+ }
670+ InstanceKind :: Wire => {
671+ let w = self . db . get_wire ( selected) ;
672+ object_pos. push ( InstancePosOffset :: Wire ( center - w. start , center - w. end ) ) ;
673+ }
674+ }
675+ }
676+
677+ self . clipboard = object_pos
678+ }
679+
680+ if paste_event_detected
681+ && !self . clipboard . is_empty ( )
682+ && let Some ( mouse) = mouse_pos
683+ {
684+ for to_paste in self . clipboard . clone ( ) {
685+ let id = match to_paste {
686+ InstancePosOffset :: Gate ( gate_kind, offset) => self . db . new_gate ( Gate {
687+ kind : gate_kind,
688+ pos : mouse - offset,
689+ } ) ,
690+ InstancePosOffset :: Power ( offset) => self . db . new_power ( Power {
691+ pos : mouse - offset,
692+ on : false ,
693+ } ) ,
694+ InstancePosOffset :: Wire ( s, e) => self . db . new_wire ( Wire {
695+ start : mouse - s,
696+ end : mouse - e,
697+ } ) ,
698+ } ;
699+ self . potential_connections = self . compute_potential_connections_for_instance ( id) ;
700+ self . finalize_connections_for_instance ( id, & canvas_rect) ;
701+ }
702+ self . selected . clear ( ) ;
703+ }
704+
605705 if let Some ( mouse) = mouse_pos {
606706 self . handle_dragging ( ui, mouse, & canvas_rect) ;
607707 self . hovered = self . interacted_instance ( mouse) ;
0 commit comments