@@ -14,6 +14,11 @@ use std::collections::BTreeSet;
1414
1515const MIN_ZOOM : f32 = 0.2 ;
1616const MAX_ZOOM : f32 = 2.5 ;
17+ const MIN_CANVAS_WIDTH : f32 = 300.0 ;
18+ const DETAILS_WIDTH_RATIO : f32 = 0.28 ;
19+ const DETAILS_MIN : f32 = 180.0 ;
20+ const DETAILS_MAX : f32 = 280.0 ;
21+ const MIN_CANVAS_HEIGHT : f32 = 300.0 ;
1722
1823#[ derive( Default , Deserialize ) ]
1924struct NoteGraphDialogArgs {
@@ -227,18 +232,37 @@ impl NoteGraphDialog {
227232 persist_requested |= self . filters_top_panel ( ui, & notes) ;
228233 ui. separator ( ) ;
229234
230- ui. horizontal ( |ui| {
231- ui. set_min_height ( ui. available_height ( ) ) ;
232- let available_width = ui. available_width ( ) . max ( 320.0 ) ;
233- let right_panel_width = ( available_width * 0.28 ) . clamp ( 180.0 , 280.0 ) ;
234- ui. vertical ( |ui| self . main_canvas ( ui, ctx, app) ) ;
235- if self . show_details_panel {
236- ui. separator ( ) ;
237- persist_requested |= ui
238- . vertical ( |ui| self . right_panel ( ui, app, & notes, right_panel_width) )
239- . inner ;
240- }
235+ let available_rect = ui. available_rect_before_wrap ( ) ;
236+ let total_width = available_rect. width ( ) ;
237+ let total_height = available_rect. height ( ) . max ( MIN_CANVAS_HEIGHT ) ;
238+ let panel_rect = Rect :: from_min_size (
239+ available_rect. min ,
240+ egui:: vec2 ( total_width. max ( MIN_CANVAS_WIDTH ) , total_height) ,
241+ ) ;
242+ ui. allocate_rect ( panel_rect, Sense :: hover ( ) ) ;
243+
244+ let ( canvas_width, details_width) =
245+ compute_graph_layout ( panel_rect. width ( ) , self . show_details_panel ) ;
246+ let canvas_rect =
247+ Rect :: from_min_size ( panel_rect. min , egui:: vec2 ( canvas_width, total_height) ) ;
248+ let details_rect = details_width. map ( |width| {
249+ Rect :: from_min_size (
250+ Pos2 :: new ( canvas_rect. right ( ) , panel_rect. top ( ) ) ,
251+ egui:: vec2 ( width, total_height) ,
252+ )
253+ } ) ;
254+
255+ ui. allocate_ui_at_rect ( canvas_rect, |ui| {
256+ self . main_canvas ( ui, ctx, app, canvas_rect. size ( ) )
241257 } ) ;
258+
259+ if let Some ( ( details_rect, details_width) ) = details_rect. zip ( details_width) {
260+ persist_requested |= ui
261+ . allocate_ui_at_rect ( details_rect, |ui| {
262+ self . right_panel ( ui, app, & notes, details_width)
263+ } )
264+ . inner ;
265+ }
242266 } ) ;
243267 self . open = window_open;
244268 self . was_open_last_frame = self . open ;
@@ -525,10 +549,16 @@ impl NoteGraphDialog {
525549 changed
526550 }
527551
528- fn main_canvas ( & mut self , ui : & mut egui:: Ui , ctx : & egui:: Context , app : & mut LauncherApp ) {
552+ fn main_canvas (
553+ & mut self ,
554+ ui : & mut egui:: Ui ,
555+ ctx : & egui:: Context ,
556+ app : & mut LauncherApp ,
557+ canvas_size : Vec2 ,
558+ ) {
529559 let desired = egui:: vec2 (
530- ui . available_width ( ) . max ( 300.0 ) ,
531- ui . available_height ( ) . max ( 300.0 ) ,
560+ canvas_size . x . max ( MIN_CANVAS_WIDTH ) ,
561+ canvas_size . y . max ( MIN_CANVAS_HEIGHT ) ,
532562 ) ;
533563 let ( rect, response) = ui. allocate_exact_size ( desired, Sense :: click_and_drag ( ) ) ;
534564 let painter = ui. painter_at ( rect) ;
@@ -662,7 +692,7 @@ impl NoteGraphDialog {
662692 width : f32 ,
663693 ) -> bool {
664694 ui. set_min_width ( width) ;
665- ui. set_max_width ( width + 24.0 ) ;
695+ ui. set_max_width ( width) ;
666696 ui. label ( "Details" ) ;
667697 let Some ( slug) = self . selected_node_id . as_deref ( ) else {
668698 ui. label ( "Select a node" ) ;
@@ -743,6 +773,19 @@ impl NoteGraphDialog {
743773 }
744774}
745775
776+ fn compute_graph_layout ( total_width : f32 , details_visible : bool ) -> ( f32 , Option < f32 > ) {
777+ let total_width = total_width. max ( 0.0 ) ;
778+ if !details_visible {
779+ return ( total_width. max ( MIN_CANVAS_WIDTH ) , None ) ;
780+ }
781+
782+ let details_width = ( total_width * DETAILS_WIDTH_RATIO )
783+ . clamp ( DETAILS_MIN , DETAILS_MAX )
784+ . min ( ( total_width - MIN_CANVAS_WIDTH ) . max ( 0.0 ) ) ;
785+ let canvas_width = ( total_width - details_width) . max ( MIN_CANVAS_WIDTH ) ;
786+ ( canvas_width, Some ( details_width) )
787+ }
788+
746789fn normalize_tag ( tag : & str ) -> String {
747790 tag. trim ( )
748791 . trim_start_matches ( '#' )
@@ -866,4 +909,69 @@ mod tests {
866909 assert ! ( dlg. open) ;
867910 assert ! ( dlg. pending_args. is_some( ) ) ;
868911 }
912+
913+ #[ test]
914+ fn compute_graph_layout_table_driven_cases ( ) {
915+ struct Case {
916+ total_width : f32 ,
917+ details_visible : bool ,
918+ expected_canvas : f32 ,
919+ expected_details : Option < f32 > ,
920+ }
921+
922+ let cases = [
923+ Case {
924+ total_width : 260.0 ,
925+ details_visible : false ,
926+ expected_canvas : MIN_CANVAS_WIDTH ,
927+ expected_details : None ,
928+ } ,
929+ Case {
930+ total_width : 420.0 ,
931+ details_visible : true ,
932+ expected_canvas : MIN_CANVAS_WIDTH ,
933+ expected_details : Some ( 120.0 ) ,
934+ } ,
935+ Case {
936+ total_width : 1000.0 ,
937+ details_visible : true ,
938+ expected_canvas : 720.0 ,
939+ expected_details : Some ( 280.0 ) ,
940+ } ,
941+ Case {
942+ total_width : 1000.0 ,
943+ details_visible : false ,
944+ expected_canvas : 1000.0 ,
945+ expected_details : None ,
946+ } ,
947+ ] ;
948+
949+ for case in cases {
950+ let ( canvas, details) = compute_graph_layout ( case. total_width , case. details_visible ) ;
951+ assert ! ( ( canvas - case. expected_canvas) . abs( ) < f32 :: EPSILON ) ;
952+ match ( details, case. expected_details ) {
953+ ( Some ( actual) , Some ( expected) ) => {
954+ assert ! ( ( actual - expected) . abs( ) < f32 :: EPSILON ) ;
955+ }
956+ ( None , None ) => { }
957+ _ => panic ! (
958+ "details width mismatch for total width {}" ,
959+ case. total_width
960+ ) ,
961+ }
962+ }
963+ }
964+
965+ #[ test]
966+ fn details_visible_canvas_only_shrinks_by_computed_details_width ( ) {
967+ let total_width = 900.0 ;
968+ let ( hidden_canvas, hidden_details) = compute_graph_layout ( total_width, false ) ;
969+ let ( visible_canvas, visible_details) = compute_graph_layout ( total_width, true ) ;
970+
971+ assert_eq ! ( hidden_details, None ) ;
972+ let details_width = visible_details. expect ( "details panel should have a width" ) ;
973+ assert ! ( ( hidden_canvas - total_width) . abs( ) < f32 :: EPSILON ) ;
974+ assert ! ( ( visible_canvas - ( hidden_canvas - details_width) ) . abs( ) < f32 :: EPSILON ) ;
975+ assert ! ( ( visible_canvas + details_width - total_width) . abs( ) < f32 :: EPSILON ) ;
976+ }
869977}
0 commit comments