@@ -59,12 +59,78 @@ impl Folder {
59
59
}
60
60
61
61
pub fn remove_layer ( & mut self , id : LayerId ) -> Result < ( ) , DocumentError > {
62
- let pos = self . layer_ids . iter ( ) . position ( |x| * x == id ) . ok_or ( DocumentError :: LayerNotFound ) ?;
62
+ let pos = self . position_of_layer ( id ) ?;
63
63
self . layers . remove ( pos) ;
64
64
self . layer_ids . remove ( pos) ;
65
65
Ok ( ( ) )
66
66
}
67
67
68
+ pub fn reorder_layers ( & mut self , source_ids : Vec < LayerId > , target_id : LayerId ) -> Result < ( ) , DocumentError > {
69
+ let source_pos = self . position_of_layer ( source_ids[ 0 ] ) ?;
70
+ let source_pos_end = source_pos + source_ids. len ( ) - 1 ;
71
+ let target_pos = self . position_of_layer ( target_id) ?;
72
+
73
+ let mut last_pos = source_pos;
74
+ for layer_id in & source_ids[ 1 ..] {
75
+ let layer_pos = self . position_of_layer ( * layer_id) ?;
76
+ if ( layer_pos as i32 - last_pos as i32 ) . abs ( ) > 1 {
77
+ // Selection is not contiguous
78
+ return Err ( DocumentError :: NonReorderableSelection ) ;
79
+ }
80
+ last_pos = layer_pos;
81
+ }
82
+
83
+ if source_pos < target_pos {
84
+ // Moving layers up the hierarchy
85
+
86
+ // Prevent shifting past end
87
+ if source_pos_end + 1 >= self . layers . len ( ) {
88
+ return Err ( DocumentError :: NonReorderableSelection ) ;
89
+ }
90
+
91
+ fn reorder_up < T > ( arr : & mut Vec < T > , source_pos : usize , source_pos_end : usize , target_pos : usize )
92
+ where
93
+ T : Clone ,
94
+ {
95
+ * arr = [
96
+ & arr[ 0 ..source_pos] , // Elements before selection
97
+ & arr[ source_pos_end + 1 ..=target_pos] , // Elements between selection end and target
98
+ & arr[ source_pos..=source_pos_end] , // Selection itself
99
+ & arr[ target_pos + 1 ..] , // Elements before target
100
+ ]
101
+ . concat ( ) ;
102
+ }
103
+
104
+ reorder_up ( & mut self . layers , source_pos, source_pos_end, target_pos) ;
105
+ reorder_up ( & mut self . layer_ids , source_pos, source_pos_end, target_pos) ;
106
+ } else {
107
+ // Moving layers down the hierarchy
108
+
109
+ // Prevent shifting past end
110
+ if source_pos == 0 {
111
+ return Err ( DocumentError :: NonReorderableSelection ) ;
112
+ }
113
+
114
+ fn reorder_down < T > ( arr : & mut Vec < T > , source_pos : usize , source_pos_end : usize , target_pos : usize )
115
+ where
116
+ T : Clone ,
117
+ {
118
+ * arr = [
119
+ & arr[ 0 ..target_pos] , // Elements before target
120
+ & arr[ source_pos..=source_pos_end] , // Selection itself
121
+ & arr[ target_pos..source_pos] , // Elements between selection and target
122
+ & arr[ source_pos_end + 1 ..] , // Elements before selection
123
+ ]
124
+ . concat ( ) ;
125
+ }
126
+
127
+ reorder_down ( & mut self . layers , source_pos, source_pos_end, target_pos) ;
128
+ reorder_down ( & mut self . layer_ids , source_pos, source_pos_end, target_pos) ;
129
+ }
130
+
131
+ Ok ( ( ) )
132
+ }
133
+
68
134
/// Returns a list of layers in the folder
69
135
pub fn list_layers ( & self ) -> & [ LayerId ] {
70
136
self . layer_ids . as_slice ( )
@@ -79,15 +145,19 @@ impl Folder {
79
145
}
80
146
81
147
pub fn layer ( & self , id : LayerId ) -> Option < & Layer > {
82
- let pos = self . layer_ids . iter ( ) . position ( |x| * x == id ) ?;
148
+ let pos = self . position_of_layer ( id ) . ok ( ) ?;
83
149
Some ( & self . layers [ pos] )
84
150
}
85
151
86
152
pub fn layer_mut ( & mut self , id : LayerId ) -> Option < & mut Layer > {
87
- let pos = self . layer_ids . iter ( ) . position ( |x| * x == id ) ?;
153
+ let pos = self . position_of_layer ( id ) . ok ( ) ?;
88
154
Some ( & mut self . layers [ pos] )
89
155
}
90
156
157
+ pub fn position_of_layer ( & self , layer_id : LayerId ) -> Result < usize , DocumentError > {
158
+ self . layer_ids . iter ( ) . position ( |x| * x == layer_id) . ok_or ( DocumentError :: LayerNotFound )
159
+ }
160
+
91
161
pub fn folder ( & self , id : LayerId ) -> Option < & Folder > {
92
162
match self . layer ( id) {
93
163
Some ( Layer {
@@ -143,3 +213,144 @@ impl Default for Folder {
143
213
}
144
214
}
145
215
}
216
+
217
+ #[ cfg( test) ]
218
+ mod test {
219
+ use glam:: { DAffine2 , DVec2 } ;
220
+
221
+ use crate :: layers:: { style:: PathStyle , Ellipse , Layer , LayerDataTypes , Line , PolyLine , Rect , Shape } ;
222
+
223
+ use super :: Folder ;
224
+
225
+ #[ test]
226
+ fn reorder_layers ( ) {
227
+ let mut folder = Folder :: default ( ) ;
228
+
229
+ let identity_transform = DAffine2 :: IDENTITY . to_cols_array ( ) ;
230
+ folder. add_layer ( Layer :: new ( LayerDataTypes :: Shape ( Shape :: new ( true , 3 ) ) , identity_transform, PathStyle :: default ( ) ) , 0 ) ;
231
+ folder. add_layer ( Layer :: new ( LayerDataTypes :: Rect ( Rect :: default ( ) ) , identity_transform, PathStyle :: default ( ) ) , 1 ) ;
232
+ folder. add_layer ( Layer :: new ( LayerDataTypes :: Ellipse ( Ellipse :: default ( ) ) , identity_transform, PathStyle :: default ( ) ) , 2 ) ;
233
+ folder. add_layer ( Layer :: new ( LayerDataTypes :: Line ( Line :: default ( ) ) , identity_transform, PathStyle :: default ( ) ) , 3 ) ;
234
+ folder. add_layer (
235
+ Layer :: new ( LayerDataTypes :: PolyLine ( PolyLine :: new ( vec ! [ DVec2 :: ZERO , DVec2 :: ONE ] ) ) , identity_transform, PathStyle :: default ( ) ) ,
236
+ 4 ,
237
+ ) ;
238
+
239
+ assert_eq ! ( folder. layer_ids[ 0 ] , 0 ) ;
240
+ assert_eq ! ( folder. layer_ids[ 1 ] , 1 ) ;
241
+ assert_eq ! ( folder. layer_ids[ 2 ] , 2 ) ;
242
+ assert_eq ! ( folder. layer_ids[ 3 ] , 3 ) ;
243
+ assert_eq ! ( folder. layer_ids[ 4 ] , 4 ) ;
244
+
245
+ assert ! ( matches!( folder. layer( 0 ) . unwrap( ) . data, LayerDataTypes :: Shape ( _) ) ) ;
246
+ assert ! ( matches!( folder. layer( 1 ) . unwrap( ) . data, LayerDataTypes :: Rect ( _) ) ) ;
247
+ assert ! ( matches!( folder. layer( 2 ) . unwrap( ) . data, LayerDataTypes :: Ellipse ( _) ) ) ;
248
+ assert ! ( matches!( folder. layer( 3 ) . unwrap( ) . data, LayerDataTypes :: Line ( _) ) ) ;
249
+ assert ! ( matches!( folder. layer( 4 ) . unwrap( ) . data, LayerDataTypes :: PolyLine ( _) ) ) ;
250
+
251
+ assert_eq ! ( folder. layer_ids. len( ) , 5 ) ;
252
+ assert_eq ! ( folder. layers. len( ) , 5 ) ;
253
+
254
+ folder. reorder_layers ( vec ! [ 0 , 1 ] , 2 ) . unwrap ( ) ;
255
+
256
+ assert_eq ! ( folder. layer_ids[ 0 ] , 2 ) ;
257
+ // Moved layers
258
+ assert_eq ! ( folder. layer_ids[ 1 ] , 0 ) ;
259
+ assert_eq ! ( folder. layer_ids[ 2 ] , 1 ) ;
260
+
261
+ assert_eq ! ( folder. layer_ids[ 3 ] , 3 ) ;
262
+ assert_eq ! ( folder. layer_ids[ 4 ] , 4 ) ;
263
+
264
+ assert ! ( matches!( folder. layer( 2 ) . unwrap( ) . data, LayerDataTypes :: Ellipse ( _) ) ) ;
265
+ // Moved layers
266
+ assert ! ( matches!( folder. layer( 0 ) . unwrap( ) . data, LayerDataTypes :: Shape ( _) ) ) ;
267
+ assert ! ( matches!( folder. layer( 1 ) . unwrap( ) . data, LayerDataTypes :: Rect ( _) ) ) ;
268
+
269
+ assert ! ( matches!( folder. layer( 3 ) . unwrap( ) . data, LayerDataTypes :: Line ( _) ) ) ;
270
+ assert ! ( matches!( folder. layer( 4 ) . unwrap( ) . data, LayerDataTypes :: PolyLine ( _) ) ) ;
271
+
272
+ assert_eq ! ( folder. layer_ids. len( ) , 5 ) ;
273
+ assert_eq ! ( folder. layers. len( ) , 5 ) ;
274
+ }
275
+
276
+ #[ test]
277
+ fn reorder_layer_to_top ( ) {
278
+ let mut folder = Folder :: default ( ) ;
279
+
280
+ let identity_transform = DAffine2 :: IDENTITY . to_cols_array ( ) ;
281
+ folder. add_layer ( Layer :: new ( LayerDataTypes :: Shape ( Shape :: new ( true , 3 ) ) , identity_transform, PathStyle :: default ( ) ) , 0 ) ;
282
+ folder. add_layer ( Layer :: new ( LayerDataTypes :: Rect ( Rect :: default ( ) ) , identity_transform, PathStyle :: default ( ) ) , 1 ) ;
283
+ folder. add_layer ( Layer :: new ( LayerDataTypes :: Ellipse ( Ellipse :: default ( ) ) , identity_transform, PathStyle :: default ( ) ) , 2 ) ;
284
+ folder. add_layer ( Layer :: new ( LayerDataTypes :: Line ( Line :: default ( ) ) , identity_transform, PathStyle :: default ( ) ) , 3 ) ;
285
+
286
+ assert_eq ! ( folder. layer_ids[ 0 ] , 0 ) ;
287
+ assert_eq ! ( folder. layer_ids[ 1 ] , 1 ) ;
288
+ assert_eq ! ( folder. layer_ids[ 2 ] , 2 ) ;
289
+ assert_eq ! ( folder. layer_ids[ 3 ] , 3 ) ;
290
+
291
+ assert ! ( matches!( folder. layer( 0 ) . unwrap( ) . data, LayerDataTypes :: Shape ( _) ) ) ;
292
+ assert ! ( matches!( folder. layer( 1 ) . unwrap( ) . data, LayerDataTypes :: Rect ( _) ) ) ;
293
+ assert ! ( matches!( folder. layer( 2 ) . unwrap( ) . data, LayerDataTypes :: Ellipse ( _) ) ) ;
294
+ assert ! ( matches!( folder. layer( 3 ) . unwrap( ) . data, LayerDataTypes :: Line ( _) ) ) ;
295
+
296
+ assert_eq ! ( folder. layer_ids. len( ) , 4 ) ;
297
+ assert_eq ! ( folder. layers. len( ) , 4 ) ;
298
+
299
+ folder. reorder_layers ( vec ! [ 1 ] , 3 ) . unwrap ( ) ;
300
+
301
+ assert_eq ! ( folder. layer_ids[ 0 ] , 0 ) ;
302
+ assert_eq ! ( folder. layer_ids[ 1 ] , 2 ) ;
303
+ assert_eq ! ( folder. layer_ids[ 2 ] , 3 ) ;
304
+ // Moved layer
305
+ assert_eq ! ( folder. layer_ids[ 3 ] , 1 ) ;
306
+
307
+ assert ! ( matches!( folder. layer( 0 ) . unwrap( ) . data, LayerDataTypes :: Shape ( _) ) ) ;
308
+ assert ! ( matches!( folder. layer( 2 ) . unwrap( ) . data, LayerDataTypes :: Ellipse ( _) ) ) ;
309
+ assert ! ( matches!( folder. layer( 3 ) . unwrap( ) . data, LayerDataTypes :: Line ( _) ) ) ;
310
+ // Moved layer
311
+ assert ! ( matches!( folder. layer( 1 ) . unwrap( ) . data, LayerDataTypes :: Rect ( _) ) ) ;
312
+
313
+ assert_eq ! ( folder. layer_ids. len( ) , 4 ) ;
314
+ assert_eq ! ( folder. layers. len( ) , 4 ) ;
315
+ }
316
+
317
+ #[ test]
318
+ fn reorder_non_contiguous_selection ( ) {
319
+ let mut folder = Folder :: default ( ) ;
320
+
321
+ let identity_transform = DAffine2 :: IDENTITY . to_cols_array ( ) ;
322
+ folder. add_layer ( Layer :: new ( LayerDataTypes :: Shape ( Shape :: new ( true , 3 ) ) , identity_transform, PathStyle :: default ( ) ) , 0 ) ;
323
+ folder. add_layer ( Layer :: new ( LayerDataTypes :: Rect ( Rect :: default ( ) ) , identity_transform, PathStyle :: default ( ) ) , 1 ) ;
324
+ folder. add_layer ( Layer :: new ( LayerDataTypes :: Ellipse ( Ellipse :: default ( ) ) , identity_transform, PathStyle :: default ( ) ) , 2 ) ;
325
+ folder. add_layer ( Layer :: new ( LayerDataTypes :: Line ( Line :: default ( ) ) , identity_transform, PathStyle :: default ( ) ) , 3 ) ;
326
+
327
+ assert_eq ! ( folder. layer_ids[ 0 ] , 0 ) ;
328
+ assert_eq ! ( folder. layer_ids[ 1 ] , 1 ) ;
329
+ assert_eq ! ( folder. layer_ids[ 2 ] , 2 ) ;
330
+ assert_eq ! ( folder. layer_ids[ 3 ] , 3 ) ;
331
+
332
+ assert ! ( matches!( folder. layer( 0 ) . unwrap( ) . data, LayerDataTypes :: Shape ( _) ) ) ;
333
+ assert ! ( matches!( folder. layer( 1 ) . unwrap( ) . data, LayerDataTypes :: Rect ( _) ) ) ;
334
+ assert ! ( matches!( folder. layer( 2 ) . unwrap( ) . data, LayerDataTypes :: Ellipse ( _) ) ) ;
335
+ assert ! ( matches!( folder. layer( 3 ) . unwrap( ) . data, LayerDataTypes :: Line ( _) ) ) ;
336
+
337
+ assert_eq ! ( folder. layer_ids. len( ) , 4 ) ;
338
+ assert_eq ! ( folder. layers. len( ) , 4 ) ;
339
+
340
+ folder. reorder_layers ( vec ! [ 0 , 2 ] , 3 ) . expect_err ( "Non-contiguous selections can't be reordered" ) ;
341
+
342
+ // Expect identical state
343
+ assert_eq ! ( folder. layer_ids[ 0 ] , 0 ) ;
344
+ assert_eq ! ( folder. layer_ids[ 1 ] , 1 ) ;
345
+ assert_eq ! ( folder. layer_ids[ 2 ] , 2 ) ;
346
+ assert_eq ! ( folder. layer_ids[ 3 ] , 3 ) ;
347
+
348
+ assert ! ( matches!( folder. layer( 0 ) . unwrap( ) . data, LayerDataTypes :: Shape ( _) ) ) ;
349
+ assert ! ( matches!( folder. layer( 1 ) . unwrap( ) . data, LayerDataTypes :: Rect ( _) ) ) ;
350
+ assert ! ( matches!( folder. layer( 2 ) . unwrap( ) . data, LayerDataTypes :: Ellipse ( _) ) ) ;
351
+ assert ! ( matches!( folder. layer( 3 ) . unwrap( ) . data, LayerDataTypes :: Line ( _) ) ) ;
352
+
353
+ assert_eq ! ( folder. layer_ids. len( ) , 4 ) ;
354
+ assert_eq ! ( folder. layers. len( ) , 4 ) ;
355
+ }
356
+ }
0 commit comments