@@ -135,23 +135,36 @@ impl FileTreeComponent {
135
135
changed
136
136
}
137
137
138
+ const fn item_status_char ( item_type : StatusItemType ) -> char {
139
+ match item_type {
140
+ StatusItemType :: Modified => 'M' ,
141
+ StatusItemType :: New => '+' ,
142
+ StatusItemType :: Deleted => '-' ,
143
+ StatusItemType :: Renamed => 'R' ,
144
+ StatusItemType :: Typechange => ' ' ,
145
+ }
146
+ }
147
+
138
148
fn item_to_text < ' b > (
139
- item : & FileTreeItem ,
149
+ string : & str ,
150
+ indent : usize ,
151
+ visible : bool ,
152
+ file_item_kind : & FileTreeItemKind ,
140
153
width : u16 ,
141
154
selected : bool ,
142
155
theme : & ' b SharedTheme ,
143
156
) -> Option < Text < ' b > > {
144
- let indent_str = if item . info . indent == 0 {
157
+ let indent_str = if indent == 0 {
145
158
String :: from ( "" )
146
159
} else {
147
- format ! ( "{:w$}" , " " , w = ( item . info . indent as usize ) * 2 )
160
+ format ! ( "{:w$}" , " " , w = ( indent as usize ) * 2 )
148
161
} ;
149
162
150
- if !item . info . visible {
163
+ if !visible {
151
164
return None ;
152
165
}
153
166
154
- match & item . kind {
167
+ match file_item_kind {
155
168
FileTreeItemKind :: File ( status_item) => {
156
169
let status_char =
157
170
Self :: item_status_char ( status_item. status ) ;
@@ -187,13 +200,13 @@ impl FileTreeComponent {
187
200
" {}{}{:w$}" ,
188
201
indent_str,
189
202
collapse_char,
190
- item . info . path ,
203
+ string ,
191
204
w = width as usize
192
205
)
193
206
} else {
194
207
format ! (
195
208
" {}{}{}" ,
196
- indent_str, collapse_char, item . info . path ,
209
+ indent_str, collapse_char, string ,
197
210
)
198
211
} ;
199
212
@@ -205,17 +218,80 @@ impl FileTreeComponent {
205
218
}
206
219
}
207
220
208
- const fn item_status_char ( item_type : StatusItemType ) -> char {
209
- match item_type {
210
- StatusItemType :: Modified => 'M' ,
211
- StatusItemType :: New => '+' ,
212
- StatusItemType :: Deleted => '-' ,
213
- StatusItemType :: Renamed => 'R' ,
214
- StatusItemType :: Typechange => ' ' ,
221
+ /// Returns a Vec<TextDrawInfo> which is used to draw the `FileTreeComponent` correctly,
222
+ /// allowing folders to be folded up if they are alone in their directory
223
+ fn build_vec_text_draw_info_for_drawing (
224
+ & self ,
225
+ ) -> ( Vec < TextDrawInfo > , usize ) {
226
+ let mut should_skip_over: usize = 0 ;
227
+ let mut selection_offset: usize = 0 ;
228
+ let mut vec_draw_text_info: Vec < TextDrawInfo > = vec ! [ ] ;
229
+ let tree_items = self . tree . tree . items ( ) ;
230
+ for ( index, item) in tree_items. iter ( ) . enumerate ( ) {
231
+ if should_skip_over > 0 {
232
+ should_skip_over -= 1 ;
233
+ continue ;
234
+ }
235
+
236
+ let index_above_select =
237
+ index < self . tree . selection . unwrap_or ( 0 ) ;
238
+
239
+ vec_draw_text_info. push ( TextDrawInfo {
240
+ name : item. info . path . clone ( ) ,
241
+ indent : item. info . indent ,
242
+ visible : item. info . visible ,
243
+ item_kind : & item. kind ,
244
+ } ) ;
245
+
246
+ let mut idx_temp = index;
247
+
248
+ while idx_temp < tree_items. len ( ) . saturating_sub ( 2 )
249
+ && tree_items[ idx_temp] . info . indent
250
+ < tree_items[ idx_temp + 1 ] . info . indent
251
+ {
252
+ // fold up the folder/file
253
+ idx_temp += 1 ;
254
+ should_skip_over += 1 ;
255
+
256
+ // don't fold files up
257
+ if let FileTreeItemKind :: File ( _) =
258
+ & tree_items[ idx_temp] . kind
259
+ {
260
+ should_skip_over -= 1 ;
261
+ break ;
262
+ }
263
+
264
+ // don't fold up if more than one folder in folder
265
+ if self . tree . tree . multiple_items_at_path ( idx_temp) {
266
+ should_skip_over -= 1 ;
267
+ break ;
268
+ } else {
269
+ // There is only one item at this level (i.e only one folder in the folder),
270
+ // so do fold up
271
+
272
+ let vec_draw_text_info_len =
273
+ vec_draw_text_info. len ( ) ;
274
+ vec_draw_text_info[ vec_draw_text_info_len - 1 ]
275
+ . name += & ( String :: from ( "/" )
276
+ + & tree_items[ idx_temp] . info . path ) ;
277
+ if index_above_select {
278
+ selection_offset += 1 ;
279
+ }
280
+ }
281
+ }
215
282
}
283
+ ( vec_draw_text_info, selection_offset)
216
284
}
217
285
}
218
286
287
+ /// Used for drawing the `FileTreeComponent`
288
+ struct TextDrawInfo < ' a > {
289
+ name : String ,
290
+ indent : u8 ,
291
+ visible : bool ,
292
+ item_kind : & ' a FileTreeItemKind ,
293
+ }
294
+
219
295
impl DrawableComponent for FileTreeComponent {
220
296
fn draw < B : Backend > (
221
297
& self ,
@@ -238,21 +314,8 @@ impl DrawableComponent for FileTreeComponent {
238
314
& self . theme ,
239
315
) ;
240
316
} else {
241
- let selection_offset =
242
- self . tree . tree . items ( ) . iter ( ) . enumerate ( ) . fold (
243
- 0 ,
244
- |acc, ( idx, e) | {
245
- let visible = e. info . visible ;
246
- let index_above_select =
247
- idx < self . tree . selection . unwrap_or ( 0 ) ;
248
-
249
- if !visible && index_above_select {
250
- acc + 1
251
- } else {
252
- acc
253
- }
254
- } ,
255
- ) ;
317
+ let ( vec_draw_text_info, selection_offset) =
318
+ self . build_vec_text_draw_info_for_drawing ( ) ;
256
319
257
320
let select = self
258
321
. tree
@@ -267,26 +330,21 @@ impl DrawableComponent for FileTreeComponent {
267
330
select,
268
331
) ) ;
269
332
270
- let items = self
271
- . tree
272
- . tree
273
- . items ( )
333
+ let items = vec_draw_text_info
274
334
. iter ( )
275
335
. enumerate ( )
276
- . filter_map ( |( idx , e ) | {
336
+ . filter_map ( |( index , draw_text_info ) | {
277
337
Self :: item_to_text (
278
- e,
338
+ & draw_text_info. name ,
339
+ draw_text_info. indent as usize ,
340
+ draw_text_info. visible ,
341
+ draw_text_info. item_kind ,
279
342
r. width ,
280
- self . show_selection
281
- && self
282
- . tree
283
- . selection
284
- . map_or ( false , |e| e == idx) ,
343
+ self . show_selection && select == index,
285
344
& self . theme ,
286
345
)
287
346
} )
288
347
. skip ( self . scroll_top . get ( ) ) ;
289
-
290
348
ui:: draw_list (
291
349
f,
292
350
r,
0 commit comments