feat: add expanded sidebar collapse control (#291)
* feat: add expanded sidebar collapse control refs #278 * fix: place sidebar collapse control inside content refs #278 --------- Co-authored-by: Can Celik <ogulcancelik@gmail.com>
This commit is contained in:
parent
09169d59c1
commit
4db4ffad99
|
|
@ -449,12 +449,12 @@ impl AppState {
|
|||
}
|
||||
|
||||
if in_sidebar {
|
||||
if self.sidebar_collapsed {
|
||||
if self.on_collapsed_sidebar_toggle(mouse.column, mouse.row) {
|
||||
self.sidebar_collapsed = false;
|
||||
return None;
|
||||
}
|
||||
if self.on_sidebar_toggle(mouse.column, mouse.row) {
|
||||
self.sidebar_collapsed = !self.sidebar_collapsed;
|
||||
return None;
|
||||
}
|
||||
|
||||
if self.sidebar_collapsed {
|
||||
if let Some(idx) = self.collapsed_workspace_at_row(mouse.row) {
|
||||
self.switch_workspace(idx);
|
||||
self.mode = Mode::Terminal;
|
||||
|
|
|
|||
|
|
@ -238,17 +238,25 @@ impl AppState {
|
|||
return false;
|
||||
}
|
||||
let sidebar = self.view.sidebar_rect;
|
||||
let toggle = crate::ui::expanded_sidebar_toggle_rect(sidebar);
|
||||
let on_toggle = toggle.width > 0
|
||||
&& col >= toggle.x
|
||||
&& col < toggle.x + toggle.width
|
||||
&& row >= toggle.y
|
||||
&& row < toggle.y + toggle.height;
|
||||
sidebar.width > 0
|
||||
&& !on_toggle
|
||||
&& col == sidebar.x + sidebar.width.saturating_sub(1)
|
||||
&& row >= sidebar.y
|
||||
&& row < sidebar.y + sidebar.height
|
||||
}
|
||||
|
||||
pub(super) fn on_collapsed_sidebar_toggle(&self, col: u16, row: u16) -> bool {
|
||||
if !self.sidebar_collapsed {
|
||||
return false;
|
||||
}
|
||||
let rect = crate::ui::collapsed_sidebar_toggle_rect(self.view.sidebar_rect);
|
||||
pub(super) fn on_sidebar_toggle(&self, col: u16, row: u16) -> bool {
|
||||
let rect = if self.sidebar_collapsed {
|
||||
crate::ui::collapsed_sidebar_toggle_rect(self.view.sidebar_rect)
|
||||
} else {
|
||||
crate::ui::expanded_sidebar_toggle_rect(self.view.sidebar_rect)
|
||||
};
|
||||
rect.width > 0
|
||||
&& col >= rect.x
|
||||
&& col < rect.x + rect.width
|
||||
|
|
@ -969,6 +977,24 @@ mod tests {
|
|||
assert!(!app.state.sidebar_collapsed);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn clicking_expanded_sidebar_toggle_collapses_sidebar() {
|
||||
let mut app = app_for_mouse_test();
|
||||
app.state.sidebar_collapsed = false;
|
||||
app.state.view.sidebar_rect = Rect::new(0, 0, 26, 20);
|
||||
app.state.view.terminal_area = Rect::new(26, 0, 80, 20);
|
||||
|
||||
let toggle = crate::ui::expanded_sidebar_toggle_rect(app.state.view.sidebar_rect);
|
||||
app.handle_mouse(mouse(
|
||||
MouseEventKind::Down(MouseButton::Left),
|
||||
toggle.x,
|
||||
toggle.y,
|
||||
));
|
||||
|
||||
assert!(app.state.sidebar_collapsed);
|
||||
assert!(app.state.drag.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn clicking_workspace_switches_on_mouse_up() {
|
||||
let mut app = app_for_mouse_test();
|
||||
|
|
@ -1454,6 +1480,26 @@ mod tests {
|
|||
assert_eq!(snapshot.sidebar_width, Some(31));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dragging_sidebar_bottom_divider_still_sets_manual_width() {
|
||||
let mut app = app_for_mouse_test();
|
||||
let divider_col = app.state.view.sidebar_rect.x + app.state.view.sidebar_rect.width - 1;
|
||||
let bottom_row = app.state.view.sidebar_rect.y + app.state.view.sidebar_rect.height - 1;
|
||||
|
||||
app.handle_mouse(mouse(
|
||||
MouseEventKind::Down(MouseButton::Left),
|
||||
divider_col,
|
||||
bottom_row,
|
||||
));
|
||||
app.handle_mouse(mouse(
|
||||
MouseEventKind::Drag(MouseButton::Left),
|
||||
divider_col + 5,
|
||||
bottom_row,
|
||||
));
|
||||
|
||||
assert_eq!(app.state.sidebar_width, 31);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dragging_past_max_clamps_to_configured_max() {
|
||||
let mut app = app_for_mouse_test();
|
||||
|
|
|
|||
|
|
@ -68,9 +68,10 @@ pub(crate) use self::{
|
|||
agent_panel_body_rect, agent_panel_entries, agent_panel_scroll_metrics,
|
||||
agent_panel_scrollbar_rect, agent_panel_toggle_rect, collapsed_sidebar_sections,
|
||||
collapsed_sidebar_toggle_rect, compute_workspace_card_areas, expanded_sidebar_sections,
|
||||
normalized_workspace_scroll, sidebar_section_divider_rect, workspace_drop_indicator_row,
|
||||
workspace_list_entries, workspace_list_rect, workspace_list_scroll_metrics,
|
||||
workspace_list_scrollbar_rect, workspace_parent_group_state, WorkspaceListEntry,
|
||||
expanded_sidebar_toggle_rect, normalized_workspace_scroll, sidebar_section_divider_rect,
|
||||
workspace_drop_indicator_row, workspace_list_entries, workspace_list_rect,
|
||||
workspace_list_scroll_metrics, workspace_list_scrollbar_rect, workspace_parent_group_state,
|
||||
WorkspaceListEntry,
|
||||
},
|
||||
};
|
||||
pub(crate) use self::{
|
||||
|
|
|
|||
|
|
@ -1179,6 +1179,18 @@ pub(crate) fn collapsed_sidebar_toggle_rect(area: Rect) -> Rect {
|
|||
Rect::new(x, bottom_y, 1, 1)
|
||||
}
|
||||
|
||||
pub(crate) fn expanded_sidebar_toggle_rect(area: Rect) -> Rect {
|
||||
if area.width <= 1 || area.height == 0 {
|
||||
return Rect::default();
|
||||
}
|
||||
Rect::new(
|
||||
area.x + area.width.saturating_sub(2),
|
||||
area.y + area.height.saturating_sub(1),
|
||||
1,
|
||||
1,
|
||||
)
|
||||
}
|
||||
|
||||
fn render_sidebar_toggle(
|
||||
app: &AppState,
|
||||
frame: &mut Frame,
|
||||
|
|
@ -1186,25 +1198,55 @@ fn render_sidebar_toggle(
|
|||
collapsed: bool,
|
||||
p: &Palette,
|
||||
) {
|
||||
if !collapsed {
|
||||
return;
|
||||
}
|
||||
let toggle_area = collapsed_sidebar_toggle_rect(area);
|
||||
let toggle_area = if collapsed {
|
||||
collapsed_sidebar_toggle_rect(area)
|
||||
} else {
|
||||
expanded_sidebar_toggle_rect(area)
|
||||
};
|
||||
if toggle_area == Rect::default() {
|
||||
return;
|
||||
}
|
||||
let icon_style = if app.global_menu_attention_badge_visible() {
|
||||
let icon = if collapsed { "»" } else { "«" };
|
||||
let icon_style = if collapsed && app.global_menu_attention_badge_visible() {
|
||||
Style::default().fg(p.accent).add_modifier(Modifier::BOLD)
|
||||
} else {
|
||||
Style::default().fg(p.overlay0)
|
||||
};
|
||||
frame.render_widget(Paragraph::new(Span::styled("»", icon_style)), toggle_area);
|
||||
frame.render_widget(Paragraph::new(Span::styled(icon, icon_style)), toggle_area);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{detect::Agent, workspace::Workspace};
|
||||
use ratatui::{backend::TestBackend, Terminal};
|
||||
|
||||
#[test]
|
||||
fn render_sidebar_toggle_draws_expanded_collapse_icon() {
|
||||
let app = crate::app::state::AppState::test_new();
|
||||
let area = Rect::new(0, 0, 26, 20);
|
||||
let mut terminal =
|
||||
Terminal::new(TestBackend::new(26, 20)).expect("test terminal should initialize");
|
||||
|
||||
terminal
|
||||
.draw(|frame| render_sidebar_toggle(&app, frame, area, false, &app.palette))
|
||||
.expect("sidebar toggle should render");
|
||||
|
||||
let toggle = expanded_sidebar_toggle_rect(area);
|
||||
assert_eq!(
|
||||
terminal.backend().buffer()[(toggle.x, toggle.y)].symbol(),
|
||||
"«"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expanded_sidebar_toggle_sits_inside_sidebar_content() {
|
||||
let area = Rect::new(0, 0, 26, 20);
|
||||
let toggle = expanded_sidebar_toggle_rect(area);
|
||||
|
||||
assert_eq!(toggle.x, area.x + area.width - 2);
|
||||
assert_eq!(toggle.y, area.y + area.height - 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_workspaces_agent_panel_entries_use_workspace_and_optional_tab_labels() {
|
||||
|
|
|
|||
Loading…
Reference in New Issue