From c24bef1bf2972900a91fd8b09d266d8a6de50151 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Mon, 20 Apr 2026 08:57:13 -0300 Subject: [PATCH] lesavka: stabilize diagnostics scrolling --- client/Cargo.toml | 2 +- client/src/launcher/ui.rs | 7 +++- client/src/launcher/ui_components.rs | 3 ++ client/src/launcher/ui_runtime.rs | 52 ++++++++++++++++++++++------ common/Cargo.toml | 2 +- common/src/cli.rs | 2 +- server/Cargo.toml | 2 +- 7 files changed, 55 insertions(+), 15 deletions(-) diff --git a/client/Cargo.toml b/client/Cargo.toml index 211f16a..0d7d5c7 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -4,7 +4,7 @@ path = "src/main.rs" [package] name = "lesavka_client" -version = "0.11.23" +version = "0.11.24" edition = "2024" [dependencies] diff --git a/client/src/launcher/ui.rs b/client/src/launcher/ui.rs index 36c067f..a364dd8 100644 --- a/client/src/launcher/ui.rs +++ b/client/src/launcher/ui.rs @@ -1359,7 +1359,12 @@ pub fn run_gui_launcher(server_addr: String) -> Result<()> { let widgets = widgets.clone(); let diagnostics_popout = Rc::clone(&diagnostics_popout); widgets.diagnostics_popout_button.connect_clicked(move |_| { - open_diagnostics_popout(&app, &diagnostics_popout, &widgets.diagnostics_buffer); + open_diagnostics_popout( + &app, + &diagnostics_popout, + &widgets.diagnostics_popout_scroll, + &widgets.diagnostics_buffer, + ); widgets .status_label .set_text("Diagnostics report moved into its own window."); diff --git a/client/src/launcher/ui_components.rs b/client/src/launcher/ui_components.rs index 953fa24..74a956d 100644 --- a/client/src/launcher/ui_components.rs +++ b/client/src/launcher/ui_components.rs @@ -54,6 +54,7 @@ pub struct LauncherWidgets { pub diagnostics_log: Rc>, pub diagnostics_buffer: gtk::TextBuffer, pub diagnostics_scroll: gtk::ScrolledWindow, + pub diagnostics_popout_scroll: Rc>>, pub session_log_buffer: gtk::TextBuffer, pub session_log_view: gtk::TextView, pub summary: SummaryWidgets, @@ -620,12 +621,14 @@ pub fn build_launcher_view( state.breakout_size_options(1), state.breakout_size_preset(1), ); + let diagnostics_popout_scroll = Rc::new(RefCell::new(None)); let widgets = LauncherWidgets { status_label: status_label.clone(), diagnostics_log: diagnostics_log.clone(), diagnostics_buffer: diagnostics_buffer.clone(), diagnostics_scroll: diagnostics_scroll.clone(), + diagnostics_popout_scroll: diagnostics_popout_scroll.clone(), session_log_buffer: session_log_buffer.clone(), session_log_view: session_log_view.clone(), summary: SummaryWidgets { diff --git a/client/src/launcher/ui_runtime.rs b/client/src/launcher/ui_runtime.rs index f84c545..fd77da2 100644 --- a/client/src/launcher/ui_runtime.rs +++ b/client/src/launcher/ui_runtime.rs @@ -948,15 +948,21 @@ pub fn refresh_diagnostics_report( child_running: bool, ) { let diagnostics_adjustment = widgets.diagnostics_scroll.vadjustment(); + let previous_value = diagnostics_adjustment.value(); let previous_max = (diagnostics_adjustment.upper() - diagnostics_adjustment.page_size()).max(0.0); - let was_at_bottom = - previous_max <= 0.0 || diagnostics_adjustment.value() >= (previous_max - 4.0); - let previous_ratio = if previous_max > 0.0 { - (diagnostics_adjustment.value() / previous_max).clamp(0.0, 1.0) - } else { - 0.0 - }; + let was_at_bottom = previous_max <= 0.0 || previous_value >= (previous_max - 4.0); + let popout_adjustment = widgets + .diagnostics_popout_scroll + .borrow() + .as_ref() + .map(|scroll| scroll.vadjustment()); + let popout_state = popout_adjustment.as_ref().map(|adjustment| { + let previous_value = adjustment.value(); + let previous_max = (adjustment.upper() - adjustment.page_size()).max(0.0); + let was_at_bottom = previous_max <= 0.0 || previous_value >= (previous_max - 4.0); + (adjustment.clone(), previous_value, was_at_bottom) + }); let mut snapshot = SnapshotReport::from_state( state, &widgets.diagnostics_log.borrow(), @@ -976,9 +982,18 @@ pub fn refresh_diagnostics_report( let target = if was_at_bottom { max } else { - (previous_ratio * max).clamp(0.0, max) + previous_value.min(max) }; diagnostics_adjustment.set_value(target); + if let Some((adjustment, previous_value, was_at_bottom)) = popout_state { + let max = (adjustment.upper() - adjustment.page_size()).max(0.0); + let target = if was_at_bottom { + max + } else { + previous_value.min(max) + }; + adjustment.set_value(target); + } }); } @@ -987,20 +1002,29 @@ pub fn open_session_log_popout( handle: &Rc>>, buffer: >k::TextBuffer, ) { - open_text_buffer_popout(app, handle, buffer, "Lesavka Log", "Copy Log"); + open_text_buffer_popout(app, handle, None, buffer, "Lesavka Log", "Copy Log"); } pub fn open_diagnostics_popout( app: >k::Application, handle: &Rc>>, + scroll_handle: &Rc>>, buffer: >k::TextBuffer, ) { - open_text_buffer_popout(app, handle, buffer, "Lesavka Diagnostics", "Copy Report"); + open_text_buffer_popout( + app, + handle, + Some(scroll_handle), + buffer, + "Lesavka Diagnostics", + "Copy Report", + ); } fn open_text_buffer_popout( app: >k::Application, handle: &Rc>>, + scroll_handle: Option<&Rc>>>, buffer: >k::TextBuffer, title: &str, copy_button_label: &str, @@ -1041,6 +1065,9 @@ fn open_text_buffer_popout( .vexpand(true) .child(&view) .build(); + if let Some(scroll_handle) = scroll_handle { + *scroll_handle.borrow_mut() = Some(scroll.clone()); + } root.append(&scroll); window.set_child(Some(&root)); window.maximize(); @@ -1054,8 +1081,12 @@ fn open_text_buffer_popout( { let handle = Rc::clone(handle); + let scroll_handle = scroll_handle.cloned(); window.connect_close_request(move |_| { handle.borrow_mut().take(); + if let Some(scroll_handle) = &scroll_handle { + scroll_handle.borrow_mut().take(); + } glib::Propagation::Proceed }); } @@ -1115,6 +1146,7 @@ pub fn shutdown_launcher_runtime( } if let Some(window) = diagnostics_popout.borrow_mut().take() { + widgets.diagnostics_popout_scroll.borrow_mut().take(); window.set_child(Option::<>k::Widget>::None); window.hide(); } diff --git a/common/Cargo.toml b/common/Cargo.toml index e49c95f..00069b6 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lesavka_common" -version = "0.11.23" +version = "0.11.24" edition = "2024" build = "build.rs" diff --git a/common/src/cli.rs b/common/src/cli.rs index ff85e83..d50503b 100644 --- a/common/src/cli.rs +++ b/common/src/cli.rs @@ -17,6 +17,6 @@ mod tests { #[test] fn banner_includes_version() { - assert_eq!(banner("0.11.23"), "lesavka-common CLI (v0.11.23)"); + assert_eq!(banner("0.11.24"), "lesavka-common CLI (v0.11.24)"); } } diff --git a/server/Cargo.toml b/server/Cargo.toml index 513bd9e..501426d 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -10,7 +10,7 @@ bench = false [package] name = "lesavka_server" -version = "0.11.23" +version = "0.11.24" edition = "2024" autobins = false