From 7d51b09aac98b74dec8450342eae5ecb1ebff37d Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Wed, 25 Feb 2026 10:56:45 +0100 Subject: [PATCH] refactor(terminal): add click-to-focus handling for terminal --- src/tui/terminal_view.zig | 16 +++++++++++++++- src/tui/tui.zig | 18 +++++++++++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/tui/terminal_view.zig b/src/tui/terminal_view.zig index a33c473..87a3966 100644 --- a/src/tui/terminal_view.zig +++ b/src/tui/terminal_view.zig @@ -33,6 +33,7 @@ focused: bool = false, cwd: std.ArrayListUnmanaged(u8) = .empty, title: std.ArrayListUnmanaged(u8) = .empty, input_mode: Mode, +hover: bool = false, pub fn create(allocator: Allocator, parent: Plane) !Widget { return create_with_args(allocator, parent, .{}); @@ -129,7 +130,20 @@ pub fn receive(self: *Self, from: tp.pid_ref, m: tp.message) error{Exit}!bool { if (try m.match(.{ "terminal_view", "output" })) { tui.need_render(@src()); return true; - } else if (!(try m.match(.{ "I", tp.more }) + } else if (try m.match(.{ "H", tp.extract(&self.hover) })) { + tui.rdr().request_mouse_cursor_default(self.hover); + tui.need_render(@src()); + return true; + } + if (try m.match(.{ "B", input.event.press, @intFromEnum(input.mouse.BUTTON1), tp.more }) or + try m.match(.{ "B", input.event.press, @intFromEnum(input.mouse.BUTTON2), tp.more }) or + try m.match(.{ "B", input.event.press, @intFromEnum(input.mouse.BUTTON3), tp.more })) + switch (tui.set_focus_by_mouse_event()) { + .changed => return true, + .same, .notfound => {}, + }; + + if (!(try m.match(.{ "I", tp.more }) // or // try m.match(.{ "B", tp.more }) or // try m.match(.{ "D", tp.more }) or diff --git a/src/tui/tui.zig b/src/tui/tui.zig index aeab6c6..836be97 100644 --- a/src/tui/tui.zig +++ b/src/tui/tui.zig @@ -833,13 +833,23 @@ pub const FocusAction = enum { same, changed, notfound }; pub fn set_focus_by_widget(w: Widget) FocusAction { const mv = mainview() orelse return .notfound; + clear_keyboard_focus(); return mv.focus_view_by_widget(w); } pub fn set_focus_by_mouse_event() FocusAction { const self = current(); const mv = mainview() orelse return .notfound; - return mv.focus_view_by_widget(self.hover_focus orelse return .notfound); + const hover_focus = self.hover_focus orelse return .notfound; + const keyboard_focus = if (self.keyboard_focus) |prev| prev.ptr else null; + if (hover_focus.ptr == keyboard_focus) return .same; + clear_keyboard_focus(); + switch (mv.focus_view_by_widget(hover_focus)) { + .notfound => {}, + else => |action| return action, + } + hover_focus.focus(); + return .changed; } pub fn set_keyboard_focus(w: Widget) void { @@ -855,6 +865,12 @@ pub fn release_keyboard_focus(w: Widget) void { }; } +pub fn clear_keyboard_focus() void { + const self = current(); + if (self.keyboard_focus) |prev| prev.unfocus(); + self.keyboard_focus = null; +} + fn send_widgets(self: *Self, from: tp.pid_ref, m: tp.message) error{Exit}!bool { const frame = tracy.initZone(@src(), .{ .name = "tui widgets" }); defer frame.deinit();