@@ -11,14 +11,15 @@ use std::{
1111 time:: Instant ,
1212} ;
1313
14+ use egui:: text:: LayoutJob ;
1415use filetime:: FileTime ;
1516use globset:: Glob ;
1617use objdiff_core:: {
1718 build:: watcher:: { Watcher , create_watcher} ,
1819 config:: {
19- ProjectConfig , ProjectConfigInfo , ProjectObject , ScratchConfig , build_globset ,
20- default_ignore_patterns , default_watch_patterns , path :: platform_path_serde_option ,
21- save_project_config,
20+ ProjectConfig , ProjectConfigInfo , ProjectObject , ScratchConfig , apply_project_options ,
21+ build_globset , default_ignore_patterns , default_watch_patterns ,
22+ path :: platform_path_serde_option , save_project_config,
2223 } ,
2324 diff:: DiffObjConfig ,
2425 jobs:: { Job , JobQueue , JobResult } ,
@@ -164,7 +165,7 @@ pub struct AppState {
164165 pub selecting_left : Option < String > ,
165166 /// The left object symbol name that we're selecting a right symbol for
166167 pub selecting_right : Option < String > ,
167- pub config_error : Option < String > ,
168+ pub top_left_toasts : egui_notify :: Toasts ,
168169}
169170
170171impl Default for AppState {
@@ -183,11 +184,23 @@ impl Default for AppState {
183184 last_mod_check : Instant :: now ( ) ,
184185 selecting_left : None ,
185186 selecting_right : None ,
186- config_error : None ,
187+ top_left_toasts : create_toasts ( egui_notify :: Anchor :: TopLeft ) ,
187188 }
188189 }
189190}
190191
192+ pub fn create_toasts ( anchor : egui_notify:: Anchor ) -> egui_notify:: Toasts {
193+ egui_notify:: Toasts :: default ( )
194+ . with_anchor ( anchor)
195+ . with_margin ( egui:: vec2 ( 10.0 , 32.0 ) )
196+ . with_shadow ( egui:: Shadow {
197+ offset : [ 0 , 0 ] ,
198+ blur : 0 ,
199+ spread : 1 ,
200+ color : egui:: Color32 :: GRAY ,
201+ } )
202+ }
203+
191204#[ derive( Clone , serde:: Deserialize , serde:: Serialize ) ]
192205pub struct AppConfig {
193206 // TODO: https://github.com/ron-rs/ron/pull/455
@@ -321,6 +334,17 @@ impl AppState {
321334 self . selecting_right = None ;
322335 }
323336
337+ pub fn effective_diff_config ( & self ) -> DiffObjConfig {
338+ let mut config = self . config . diff_obj_config . clone ( ) ;
339+ if let Some ( options) =
340+ self . current_project_config . as_ref ( ) . and_then ( |project| project. options . as_ref ( ) )
341+ {
342+ // Ignore errors here, we display them when loading the project config
343+ let _ = apply_project_options ( & mut config, options) ;
344+ }
345+ config
346+ }
347+
324348 pub fn set_selecting_left ( & mut self , right : & str ) {
325349 let Some ( object) = self . config . selected_obj . as_mut ( ) else {
326350 return ;
@@ -401,11 +425,22 @@ impl AppState {
401425 match save_project_config ( config, info) {
402426 Ok ( new_info) => * info = new_info,
403427 Err ( e) => {
404- log:: error!( "Failed to save project config: {e}" ) ;
405- self . config_error = Some ( format ! ( "Failed to save project config: {e}" ) ) ;
428+ log:: error!( "Failed to save project config: {e:# }" ) ;
429+ self . show_error_toast ( "Failed to save project config" , & e ) ;
406430 }
407431 }
408432 }
433+
434+ pub fn show_error_toast ( & mut self , context : & str , e : & anyhow:: Error ) {
435+ let mut job = LayoutJob :: default ( ) ;
436+ job. append ( context, 0.0 , Default :: default ( ) ) ;
437+ job. append ( "\n " , 0.0 , Default :: default ( ) ) ;
438+ job. append ( & format ! ( "{e:#}" ) , 0.0 , egui:: TextFormat {
439+ color : egui:: Color32 :: LIGHT_RED ,
440+ ..Default :: default ( )
441+ } ) ;
442+ self . top_left_toasts . error ( job) . closable ( true ) . duration ( None ) ;
443+ }
409444}
410445
411446pub type AppStateRef = Arc < RwLock < AppState > > ;
@@ -548,12 +583,9 @@ impl App {
548583
549584 if state. config_change {
550585 state. config_change = false ;
551- match load_project_config ( state) {
552- Ok ( ( ) ) => state. config_error = None ,
553- Err ( e) => {
554- log:: error!( "Failed to load project config: {e}" ) ;
555- state. config_error = Some ( e. to_string ( ) ) ;
556- }
586+ if let Err ( e) = load_project_config ( state) {
587+ log:: error!( "Failed to load project config: {e:#}" ) ;
588+ state. show_error_toast ( "Failed to load project config" , & e) ;
557589 }
558590 }
559591
@@ -579,7 +611,10 @@ impl App {
579611 . map_err ( anyhow:: Error :: new)
580612 } ) {
581613 Ok ( watcher) => self . watcher = Some ( watcher) ,
582- Err ( e) => log:: error!( "Failed to create watcher: {e}" ) ,
614+ Err ( e) => {
615+ log:: error!( "Failed to create watcher: {e:#}" ) ;
616+ state. show_error_toast ( "Failed to create file watcher" , & e) ;
617+ }
583618 }
584619 state. watcher_change = false ;
585620 }
@@ -806,7 +841,7 @@ impl eframe::App for App {
806841 let mut action = None ;
807842 egui:: CentralPanel :: default ( ) . show ( ctx, |ui| {
808843 let state = state. read ( ) . unwrap ( ) ;
809- action = diff_view_ui ( ui, diff_state, appearance, & state. config . diff_obj_config ) ;
844+ action = diff_view_ui ( ui, diff_state, appearance, & state. effective_diff_config ( ) ) ;
810845 } ) ;
811846
812847 project_window ( ctx, state, show_project_config, config_state, appearance) ;
@@ -818,6 +853,10 @@ impl eframe::App for App {
818853 graphics_window ( ctx, show_graphics, frame_history, graphics_state, appearance) ;
819854 jobs_window ( ctx, show_jobs, jobs, appearance) ;
820855
856+ if let Ok ( mut state) = self . state . write ( ) {
857+ state. top_left_toasts . show ( ctx) ;
858+ }
859+
821860 self . post_update ( ctx, action) ;
822861 }
823862
0 commit comments