notedeck

One damus client to rule them all
git clone git://jb55.com/notedeck
Log | Files | Refs | README | LICENSE

commit 293262d5536b911bc096c619fbb01e8a69361296
parent 4db688f55d8e60573c402281943beb3ceb5ce2a0
Author: William Casarin <jb55@jb55.com>
Date:   Fri, 23 Jan 2026 15:23:59 -0800

columns: auto-hide toolbar+header when scrolling

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: William Casarin <jb55@jb55.com>

Diffstat:
Mcrates/notedeck_columns/src/actionbar.rs | 16+++++++++++++++-
Mcrates/notedeck_columns/src/app.rs | 42+++++++++++++++++++++++++++++++++++++-----
Mcrates/notedeck_columns/src/ui/column/header.rs | 24++++++++++++++++++++++--
Mshell.nix | 1+
4 files changed, 75 insertions(+), 8 deletions(-)

diff --git a/crates/notedeck_columns/src/actionbar.rs b/crates/notedeck_columns/src/actionbar.rs @@ -69,7 +69,21 @@ fn execute_note_action( match action { NoteAction::Scroll(ref scroll_info) => { - tracing::trace!("timeline scroll {scroll_info:?}") + tracing::trace!("timeline scroll {scroll_info:?}"); + + // Update toolbar visibility based on scroll velocity + let toolbar_visible_id = egui::Id::new("toolbar_visible"); + let velocity_threshold = 50.0; // pixels per second + + // velocity.y > 0 means scrolling up (content moving down) - show toolbar + // velocity.y < 0 means scrolling down (content moving up) - hide toolbar + if scroll_info.velocity.y > velocity_threshold { + ui.ctx() + .data_mut(|d| d.insert_temp(toolbar_visible_id, true)); + } else if scroll_info.velocity.y < -velocity_threshold { + ui.ctx() + .data_mut(|d| d.insert_temp(toolbar_visible_id, false)); + } } NoteAction::Reply(note_id) => { diff --git a/crates/notedeck_columns/src/app.rs b/crates/notedeck_columns/src/app.rs @@ -632,6 +632,42 @@ fn circle_icon(ui: &mut egui::Ui, openness: f32, response: &egui::Response) { } */ +/// Logic that handles toolbar visibility +fn toolbar_visibility_height(skb_rect: Option<egui::Rect>, ui: &mut egui::Ui) -> f32 { + // Auto-hide toolbar when scrolling down + let toolbar_visible_id = egui::Id::new("toolbar_visible"); + + // Detect scroll direction using egui input state + let scroll_delta = ui.ctx().input(|i| i.smooth_scroll_delta.y); + let velocity_threshold = 1.0; + + // Update toolbar visibility based on scroll direction + if scroll_delta > velocity_threshold { + // Scrolling up (content moving down) - show toolbar + ui.ctx() + .data_mut(|d| d.insert_temp(toolbar_visible_id, true)); + } else if scroll_delta < -velocity_threshold { + // Scrolling down (content moving up) - hide toolbar + ui.ctx() + .data_mut(|d| d.insert_temp(toolbar_visible_id, false)); + } + + let toolbar_visible = ui + .ctx() + .data(|d| d.get_temp::<bool>(toolbar_visible_id)) + .unwrap_or(true); // Default to visible + + let toolbar_anim = ui + .ctx() + .animate_bool_responsive(toolbar_visible_id.with("anim"), toolbar_visible); + + if skb_rect.is_none() { + Damus::toolbar_height() * toolbar_anim + } else { + 0.0 + } +} + #[profiling::function] fn render_damus_mobile( app: &mut Damus, @@ -648,12 +684,8 @@ fn render_damus_mobile( ui.ctx().screen_rect(), notedeck::SoftKeyboardContext::platform(ui.ctx()), ); - let toolbar_height = if skb_rect.is_none() { - Damus::toolbar_height() - } else { - 0.0 - }; + let toolbar_height = toolbar_visibility_height(skb_rect, ui); StripBuilder::new(ui) .size(Size::remainder()) // top cell .size(Size::exact(toolbar_height)) // bottom cell diff --git a/crates/notedeck_columns/src/ui/column/header.rs b/crates/notedeck_columns/src/ui/column/header.rs @@ -61,9 +61,29 @@ impl<'a> NavTitle<'a> { } pub fn show(&mut self, ui: &mut egui::Ui) -> Option<RenderNavAction> { - notedeck_ui::padding(8.0, ui, |ui| { + // On mobile, animate navbar visibility in sync with the toolbar + // (toolbar_visible is set in render_damus_mobile based on scroll direction) + let toolbar_visible_id = egui::Id::new("toolbar_visible"); + let toolbar_visible = ui + .ctx() + .data(|d| d.get_temp::<bool>(toolbar_visible_id)) + .unwrap_or(true); + let navbar_anim = ui + .ctx() + .animate_bool_responsive(toolbar_visible_id.with("anim"), toolbar_visible); + + // Skip rendering if almost completely hidden + if navbar_anim < 0.01 { + return None; + } + + let base_height = 48.0; + let animated_height = base_height * navbar_anim; + let animated_padding = 8.0 * navbar_anim; + + notedeck_ui::padding(animated_padding, ui, |ui| { let mut rect = ui.available_rect_before_wrap(); - rect.set_height(48.0); + rect.set_height(animated_height); let mut child_ui = ui.new_child( UiBuilder::new() diff --git a/shell.nix b/shell.nix @@ -14,6 +14,7 @@ mkShell ({ #cargo-edit #cargo-watch rustup + gdb libiconv pkg-config cmake