2
2
use std:: string:: FromUtf16Error ;
3
3
use std:: {
4
4
mem:: take,
5
- path:: { PathBuf , MAIN_SEPARATOR } ,
5
+ path:: { Path , PathBuf , MAIN_SEPARATOR } ,
6
6
} ;
7
7
8
8
#[ cfg( all( windows, feature = "wsl" ) ) ]
@@ -96,6 +96,7 @@ impl ConfigViewState {
96
96
reverse_fn_order : None ,
97
97
complete : None ,
98
98
scratch : None ,
99
+ source_path : None ,
99
100
} ) ;
100
101
} else if let Ok ( obj_path) = path. strip_prefix ( target_dir) {
101
102
let base_path = base_dir. join ( obj_path) ;
@@ -106,6 +107,7 @@ impl ConfigViewState {
106
107
reverse_fn_order : None ,
107
108
complete : None ,
108
109
scratch : None ,
110
+ source_path : None ,
109
111
} ) ;
110
112
}
111
113
}
@@ -174,7 +176,10 @@ pub fn config_ui(
174
176
) {
175
177
let mut state_guard = state. write ( ) . unwrap ( ) ;
176
178
let AppState {
177
- config : AppConfig { target_obj_dir, base_obj_dir, selected_obj, auto_update_check, .. } ,
179
+ config :
180
+ AppConfig {
181
+ project_dir, target_obj_dir, base_obj_dir, selected_obj, auto_update_check, ..
182
+ } ,
178
183
objects,
179
184
object_nodes,
180
185
..
@@ -318,7 +323,14 @@ pub fn config_ui(
318
323
config_state. show_hidden ,
319
324
)
320
325
} ) {
321
- display_node ( ui, & mut new_selected_obj, & node, appearance, node_open) ;
326
+ display_node (
327
+ ui,
328
+ & mut new_selected_obj,
329
+ project_dir. as_deref ( ) ,
330
+ & node,
331
+ appearance,
332
+ node_open,
333
+ ) ;
322
334
}
323
335
} ) ;
324
336
}
@@ -333,13 +345,12 @@ pub fn config_ui(
333
345
{
334
346
config_state. queue_build = true ;
335
347
}
336
-
337
- ui. separator ( ) ;
338
348
}
339
349
340
350
fn display_object (
341
351
ui : & mut egui:: Ui ,
342
352
selected_obj : & mut Option < ObjectConfig > ,
353
+ project_dir : Option < & Path > ,
343
354
name : & str ,
344
355
object : & ProjectObject ,
345
356
appearance : & Appearance ,
@@ -357,7 +368,7 @@ fn display_object(
357
368
} else {
358
369
appearance. text_color
359
370
} ;
360
- let clicked = SelectableLabel :: new (
371
+ let response = SelectableLabel :: new (
361
372
selected,
362
373
RichText :: new ( name)
363
374
. font ( FontId {
@@ -366,22 +377,45 @@ fn display_object(
366
377
} )
367
378
. color ( color) ,
368
379
)
369
- . ui ( ui)
370
- . clicked ( ) ;
380
+ . ui ( ui) ;
381
+ if get_source_path ( project_dir, object) . is_some ( ) {
382
+ response. context_menu ( |ui| object_context_ui ( ui, object, project_dir) ) ;
383
+ }
371
384
// Always recreate ObjectConfig if selected, in case the project config changed.
372
385
// ObjectConfig is compared using equality, so this won't unnecessarily trigger a rebuild.
373
- if selected || clicked {
386
+ if selected || response . clicked ( ) {
374
387
* selected_obj = Some ( ObjectConfig {
375
388
name : object_name. to_string ( ) ,
376
389
target_path : object. target_path . clone ( ) ,
377
390
base_path : object. base_path . clone ( ) ,
378
391
reverse_fn_order : object. reverse_fn_order ( ) ,
379
392
complete : object. complete ( ) ,
380
393
scratch : object. scratch . clone ( ) ,
394
+ source_path : object. source_path ( ) . cloned ( ) ,
381
395
} ) ;
382
396
}
383
397
}
384
398
399
+ fn get_source_path ( project_dir : Option < & Path > , object : & ProjectObject ) -> Option < PathBuf > {
400
+ project_dir. and_then ( |dir| object. source_path ( ) . map ( |path| dir. join ( path) ) )
401
+ }
402
+
403
+ fn object_context_ui ( ui : & mut egui:: Ui , object : & ProjectObject , project_dir : Option < & Path > ) {
404
+ if let Some ( source_path) = get_source_path ( project_dir, object) {
405
+ if ui
406
+ . button ( "Open source file" )
407
+ . on_hover_text ( "Open the source file in the default editor" )
408
+ . clicked ( )
409
+ {
410
+ log:: info!( "Opening file {}" , source_path. display( ) ) ;
411
+ if let Err ( e) = open:: that_detached ( & source_path) {
412
+ log:: error!( "Failed to open source file: {e}" ) ;
413
+ }
414
+ ui. close_menu ( ) ;
415
+ }
416
+ }
417
+ }
418
+
385
419
#[ derive( Default , Copy , Clone , PartialEq , Eq , Debug ) ]
386
420
enum NodeOpen {
387
421
#[ default]
@@ -394,13 +428,14 @@ enum NodeOpen {
394
428
fn display_node (
395
429
ui : & mut egui:: Ui ,
396
430
selected_obj : & mut Option < ObjectConfig > ,
431
+ project_dir : Option < & Path > ,
397
432
node : & ProjectObjectNode ,
398
433
appearance : & Appearance ,
399
434
node_open : NodeOpen ,
400
435
) {
401
436
match node {
402
437
ProjectObjectNode :: File ( name, object) => {
403
- display_object ( ui, selected_obj, name, object, appearance) ;
438
+ display_object ( ui, selected_obj, project_dir , name, object, appearance) ;
404
439
}
405
440
ProjectObjectNode :: Dir ( name, children) => {
406
441
let contains_obj = selected_obj. as_ref ( ) . map ( |path| contains_node ( node, path) ) ;
@@ -426,7 +461,7 @@ fn display_node(
426
461
. open ( open)
427
462
. show ( ui, |ui| {
428
463
for node in children {
429
- display_node ( ui, selected_obj, node, appearance, node_open) ;
464
+ display_node ( ui, selected_obj, project_dir , node, appearance, node_open) ;
430
465
}
431
466
} ) ;
432
467
}
0 commit comments