aboutsummaryrefslogtreecommitdiff
path: root/src/app/input/mouse.rs
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-06-29 18:09:25 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-06-29 18:09:25 +0300
commit1a4e94e57e181379e2ed42d63a49fbc56ed1dba4 (patch)
tree0319544374f1149a405259070b90df9d62854789 /src/app/input/mouse.rs
parenta937f7e85bbf8780c6030c57f48dab2168bca932 (diff)
refactor: route tui mutations through runtime authority
Diffstat (limited to 'src/app/input/mouse.rs')
-rw-r--r--src/app/input/mouse.rs133
1 files changed, 94 insertions, 39 deletions
diff --git a/src/app/input/mouse.rs b/src/app/input/mouse.rs
index c8a03674..71c0cfbd 100644
--- a/src/app/input/mouse.rs
+++ b/src/app/input/mouse.rs
@@ -26,6 +26,40 @@ use super::{
ScrollbarClickTarget, TAB_DRAG_THRESHOLD, WORKSPACE_DRAG_THRESHOLD,
};
+pub(super) enum MouseAction {
+ Settings(SettingsAction),
+ FocusWorkspace {
+ ws_idx: usize,
+ },
+ FocusTab {
+ tab_idx: usize,
+ },
+ FocusPane {
+ ws_idx: usize,
+ pane_id: crate::layout::PaneId,
+ },
+ FocusToastTarget,
+ MoveWorkspace {
+ source_ws_idx: usize,
+ insert_idx: usize,
+ },
+ MoveTab {
+ ws_idx: usize,
+ source_tab_idx: usize,
+ insert_idx: usize,
+ },
+ SetSplitRatio {
+ path: Vec<bool>,
+ ratio: f32,
+ },
+}
+
+enum MobileMouseResult {
+ Ignored,
+ Consumed,
+ Action(MouseAction),
+}
+
impl AppState {
pub(crate) fn handle_pane_mouse_only(
&mut self,
@@ -59,7 +93,7 @@ impl AppState {
&mut self,
terminal_runtimes: &mut TerminalRuntimeRegistry,
mouse: MouseEvent,
- ) -> Option<SettingsAction> {
+ ) -> Option<MouseAction> {
if self.mode == Mode::Onboarding {
self.handle_onboarding_mouse(mouse);
return None;
@@ -69,8 +103,7 @@ impl AppState {
&& self.clickable_toast_at(mouse.column, mouse.row)
&& matches!(mouse.kind, MouseEventKind::Down(MouseButton::Left))
{
- self.focus_toast_target();
- return None;
+ return Some(MouseAction::FocusToastTarget);
}
if self.mode == Mode::Terminal
@@ -81,7 +114,7 @@ impl AppState {
}
if self.mode == Mode::Settings {
- return self.handle_settings_mouse(mouse);
+ return self.handle_settings_mouse(mouse).map(MouseAction::Settings);
}
let launcher_enabled = self.view.layout != ViewLayout::Mobile
@@ -134,8 +167,12 @@ impl AppState {
return None;
}
- if self.view.layout == ViewLayout::Mobile && self.handle_mobile_mouse(mouse) {
- return None;
+ if self.view.layout == ViewLayout::Mobile {
+ match self.handle_mobile_mouse(mouse) {
+ MobileMouseResult::Ignored => {}
+ MobileMouseResult::Consumed => return None,
+ MobileMouseResult::Action(action) => return Some(action),
+ }
}
let sidebar = self.view.sidebar_rect;
@@ -473,16 +510,15 @@ impl AppState {
if self.sidebar_collapsed {
if let Some(idx) = self.collapsed_workspace_at_row(mouse.row) {
- self.switch_workspace(idx);
self.mode = Mode::Terminal;
- return None;
+ return Some(MouseAction::FocusWorkspace { ws_idx: idx });
}
if let Some((ws_idx, _tab_idx, pane_id)) =
self.collapsed_agent_detail_target_at(mouse.row)
{
- self.focus_pane_in_workspace(ws_idx, pane_id);
self.mode = Mode::Terminal;
+ return Some(MouseAction::FocusPane { ws_idx, pane_id });
}
return None;
}
@@ -574,12 +610,10 @@ impl AppState {
if let Some((ws_idx, _tab_idx, pane_id)) =
self.agent_detail_target_at(mouse.row)
{
- self.focus_pane_in_workspace(ws_idx, pane_id);
self.mode = Mode::Terminal;
- return None;
+ return Some(MouseAction::FocusPane { ws_idx, pane_id });
}
} else if let Some(info) = self.pane_at(mouse.column, mouse.row).cloned() {
- self.focus_pane(info.id);
if self.mode != Mode::Terminal {
self.mode = Mode::Terminal;
}
@@ -587,6 +621,12 @@ impl AppState {
if self.forward_pane_mouse_button(terminal_runtimes, &info, mouse) {
self.selection = None;
self.selection_autoscroll = None;
+ if let Some(ws_idx) = self.active {
+ return Some(MouseAction::FocusPane {
+ ws_idx,
+ pane_id: info.id,
+ });
+ }
return None;
}
@@ -600,6 +640,12 @@ impl AppState {
col,
self.pane_scroll_metrics(terminal_runtimes, info.id),
));
+ if let Some(ws_idx) = self.active {
+ return Some(MouseAction::FocusPane {
+ ws_idx,
+ pane_id: info.id,
+ });
+ }
} else if let Some(info) = self.view.pane_infos.iter().find(|p| {
mouse.column >= p.rect.x
&& mouse.column < p.rect.x + p.rect.width
@@ -607,10 +653,15 @@ impl AppState {
&& mouse.row < p.rect.y + p.rect.height
}) {
let id = info.id;
- self.focus_pane(id);
if self.mode != Mode::Terminal {
self.mode = Mode::Terminal;
}
+ if let Some(ws_idx) = self.active {
+ return Some(MouseAction::FocusPane {
+ ws_idx,
+ pane_id: id,
+ });
+ }
}
}
@@ -721,10 +772,7 @@ impl AppState {
};
let ratio = ratio.clamp(0.1, 0.9);
let path = path.clone();
- if let Some(ws) = self.active.and_then(|i| self.workspaces.get_mut(i)) {
- ws.layout.set_ratio_at(&path, ratio);
- self.mark_session_dirty();
- }
+ return Some(MouseAction::SetSplitRatio { path, ratio });
}
DragTarget::PaneScrollbar {
pane_id,
@@ -798,7 +846,10 @@ impl AppState {
insert_idx: Some(insert_idx),
},
}) => {
- self.move_workspace(source_ws_idx, insert_idx);
+ return Some(MouseAction::MoveWorkspace {
+ source_ws_idx,
+ insert_idx,
+ });
}
Some(DragState {
target:
@@ -809,22 +860,28 @@ impl AppState {
},
}) => {
if self.active == Some(ws_idx) {
- self.move_tab(source_tab_idx, insert_idx);
self.mode = Mode::Terminal;
+ return Some(MouseAction::MoveTab {
+ ws_idx,
+ source_tab_idx,
+ insert_idx,
+ });
}
}
Some(_) => {}
None => {
if let Some(press) = workspace_press {
- self.switch_workspace(press.ws_idx);
self.mode = Mode::Terminal;
- return None;
+ return Some(MouseAction::FocusWorkspace {
+ ws_idx: press.ws_idx,
+ });
}
if let Some(press) = tab_press {
if self.active == Some(press.ws_idx) {
- self.switch_tab(press.tab_idx);
self.mode = Mode::Terminal;
- return None;
+ return Some(MouseAction::FocusTab {
+ tab_idx: press.tab_idx,
+ });
}
}
}
@@ -1021,40 +1078,40 @@ impl AppState {
None
}
- fn handle_mobile_mouse(&mut self, mouse: MouseEvent) -> bool {
+ fn handle_mobile_mouse(&mut self, mouse: MouseEvent) -> MobileMouseResult {
if self.mode == Mode::Navigate {
match mouse.kind {
MouseEventKind::ScrollUp => {
self.scroll_mobile_switcher_at(mouse.column, mouse.row, -1);
- return true;
+ return MobileMouseResult::Consumed;
}
MouseEventKind::ScrollDown => {
self.scroll_mobile_switcher_at(mouse.column, mouse.row, 1);
- return true;
+ return MobileMouseResult::Consumed;
}
MouseEventKind::Down(MouseButton::Left) => {}
- _ => return true,
+ _ => return MobileMouseResult::Consumed,
}
} else if !matches!(mouse.kind, MouseEventKind::Down(MouseButton::Left)) {
- return false;
+ return MobileMouseResult::Ignored;
}
if self.mode != Mode::Navigate {
if !matches!(self.mode, Mode::Terminal | Mode::Resize) {
- return false;
+ return MobileMouseResult::Ignored;
}
if rect_contains(self.view.mobile_menu_hit_area, mouse.column, mouse.row) {
self.mobile_switcher_scroll = 0;
self.mode = Mode::Navigate;
- return true;
+ return MobileMouseResult::Consumed;
}
- return false;
+ return MobileMouseResult::Ignored;
}
let areas = crate::ui::mobile_switcher_areas(self);
if rect_contains(areas.close, mouse.column, mouse.row) {
self.mode = Mode::Terminal;
- return true;
+ return MobileMouseResult::Consumed;
}
match crate::ui::mobile_switcher_target_at(self, mouse.column, mouse.row) {
@@ -1062,8 +1119,8 @@ impl AppState {
self.request_new_workspace = true;
}
Some(crate::ui::MobileSwitcherTarget::Workspace(ws_idx)) => {
- self.switch_workspace(ws_idx);
self.mode = Mode::Terminal;
+ return MobileMouseResult::Action(MouseAction::FocusWorkspace { ws_idx });
}
Some(crate::ui::MobileSwitcherTarget::NewTab) => {
if self.prompt_new_tab_name {
@@ -1074,16 +1131,16 @@ impl AppState {
}
}
Some(crate::ui::MobileSwitcherTarget::Tab(tab_idx)) => {
- self.switch_tab(tab_idx);
self.mode = Mode::Terminal;
+ return MobileMouseResult::Action(MouseAction::FocusTab { tab_idx });
}
Some(crate::ui::MobileSwitcherTarget::Agent {
ws_idx,
tab_idx: _,
pane_id,
}) => {
- self.focus_pane_in_workspace(ws_idx, pane_id);
self.mode = Mode::Terminal;
+ return MobileMouseResult::Action(MouseAction::FocusPane { ws_idx, pane_id });
}
Some(crate::ui::MobileSwitcherTarget::Menu(action_idx)) => {
let actions = global_menu_actions(self);
@@ -1094,7 +1151,7 @@ impl AppState {
None => {}
}
- true
+ MobileMouseResult::Consumed
}
fn scroll_mobile_switcher_at(&mut self, _col: u16, _row: u16, delta: i16) {
@@ -1335,9 +1392,7 @@ impl AppState {
}
pub(super) fn focus_pane(&mut self, pane_id: crate::layout::PaneId) {
- if let Some(ws_idx) = self.active {
- self.focus_pane_in_workspace(ws_idx, pane_id);
- }
+ let _ = pane_id;
}
fn clickable_toast_at(&self, col: u16, row: u16) -> bool {