diff --git a/src/tui/Button.zig b/src/tui/Button.zig index bca03a0..b8e29ae 100644 --- a/src/tui/Button.zig +++ b/src/tui/Button.zig @@ -123,7 +123,7 @@ fn State(ctx_type: type) type { input.mouse.BUTTON1 => { self.active = true; self.drag_anchor = self.to_rel_cursor(x, y); - tui.need_render(); + tui.need_render(@src()); }, input.mouse.BUTTON4, input.mouse.BUTTON5 => { self.call_click_handler(btn_enum, self.to_rel_cursor(x, y)); @@ -136,7 +136,7 @@ fn State(ctx_type: type) type { self.drag_anchor = null; self.drag_pos = null; self.call_click_handler(@enumFromInt(btn), self.to_rel_cursor(x, y)); - tui.need_render(); + tui.need_render(@src()); return true; } else if (try m.match(.{ "D", input.event.press, tp.extract(&btn), tp.any, tp.extract(&x), tp.extract(&y), tp.any, tp.any })) { self.drag_pos = .{ .x = x, .y = y }; @@ -153,11 +153,11 @@ fn State(ctx_type: type) type { self.drag_anchor = null; self.drag_pos = null; self.call_click_handler(@enumFromInt(btn), self.to_rel_cursor(x, y)); - tui.need_render(); + tui.need_render(@src()); return true; } else if (try m.match(.{ "H", tp.extract(&self.hover) })) { tui.rdr().request_mouse_cursor_pointer(self.hover); - tui.need_render(); + tui.need_render(@src()); return true; } self.drag_anchor = null; diff --git a/src/tui/InputBox.zig b/src/tui/InputBox.zig index cffeaa4..8a06b09 100644 --- a/src/tui/InputBox.zig +++ b/src/tui/InputBox.zig @@ -132,21 +132,21 @@ pub fn State(ctx_type: type) type { pub fn receive(self: *Self, _: tp.pid_ref, m: tp.message) error{Exit}!bool { if (try m.match(.{ "B", input.event.press, @intFromEnum(input.mouse.BUTTON1), tp.any, tp.any, tp.any, tp.any, tp.any })) { self.active = true; - tui.need_render(); + tui.need_render(@src()); return true; } else if (try m.match(.{ "B", input.event.release, @intFromEnum(input.mouse.BUTTON1), tp.any, tp.any, tp.any, tp.any, tp.any })) { self.opts.on_click(self.opts.ctx, self); self.active = false; - tui.need_render(); + tui.need_render(@src()); return true; } else if (try m.match(.{ "D", input.event.release, @intFromEnum(input.mouse.BUTTON1), tp.any, tp.any, tp.any, tp.any, tp.any })) { self.opts.on_click(self.opts.ctx, self); self.active = false; - tui.need_render(); + tui.need_render(@src()); return true; } else if (try m.match(.{ "H", tp.extract(&self.hover) })) { tui.rdr().request_mouse_cursor_pointer(self.hover); - tui.need_render(); + tui.need_render(@src()); return true; } return false; diff --git a/src/tui/Widget.zig b/src/tui/Widget.zig index 89a0aef..e298fb5 100644 --- a/src/tui/Widget.zig +++ b/src/tui/Widget.zig @@ -55,7 +55,7 @@ pub const VTable = struct { walk: *const fn (ctx: *anyopaque, walk_ctx: *anyopaque, f: WalkFn, self_widget: *Self) bool, focus: *const fn (ctx: *anyopaque) void, unfocus: *const fn (ctx: *anyopaque) void, - hover: *const fn (ctx: *anyopaque) bool, + hover: *const fn (ctx: *const anyopaque) bool, type_name: []const u8, }; @@ -154,8 +154,8 @@ pub fn to(pimpl: anytype) Self { } }.unfocus, .hover = struct { - pub fn hover(ctx: *anyopaque) bool { - return if (comptime @hasField(child, "hover")) @as(*child, @ptrCast(@alignCast(ctx))).hover else false; + pub fn hover(ctx: *const anyopaque) bool { + return if (comptime @hasField(child, "hover")) @as(*const child, @ptrCast(@alignCast(ctx))).hover else false; } }.hover, }, @@ -170,7 +170,7 @@ pub fn dynamic_cast(self: Self, comptime T: type) ?*T { } pub fn need_render() void { - tui.need_render(); + tui.need_render(@src()); } pub fn need_reflow() void { @@ -203,7 +203,10 @@ pub fn update(self: Self) void { } pub fn render(self: Self, theme: *const Theme) bool { - return self.vtable.render(self.ptr, theme); + const more = self.vtable.render(self.ptr, theme); + if (more) + tp.trace(tp.channel.widget, .{ "continue_by", self.vtable.type_name }); + return more; } pub fn resize(self: Self, pos: Box) void { @@ -242,7 +245,7 @@ pub fn unfocus(self: *Self) void { self.vtable.unfocus(self.ptr); } -pub fn hover(self: *Self) bool { +pub fn hover(self: *const Self) bool { return self.vtable.hover(self.ptr); } @@ -313,7 +316,7 @@ pub fn empty(allocator: Allocator, parent: Plane, layout_: Layout) !Self { pub fn unfocus(_: *anyopaque) void {} }.unfocus, .hover = struct { - pub fn hover(_: *anyopaque) bool { + pub fn hover(_: *const anyopaque) bool { return false; } }.hover, diff --git a/src/tui/editor.zig b/src/tui/editor.zig index 3406268..1bc51be 100644 --- a/src/tui/editor.zig +++ b/src/tui/editor.zig @@ -6934,7 +6934,7 @@ pub const EditorWidget = struct { return; } else { self.editor.cancel_all_matches(); - tui.need_render(); + tui.need_render(@src()); } }, .none => {}, diff --git a/src/tui/home.zig b/src/tui/home.zig index abc6381..5fae6c0 100644 --- a/src/tui/home.zig +++ b/src/tui/home.zig @@ -209,7 +209,7 @@ pub fn receive(_: *Self, _: tp.pid_ref, m: tp.message) error{Exit}!bool { var hover: bool = false; if (try m.match(.{ "H", tp.extract(&hover) })) { tui.rdr().request_mouse_cursor_default(hover); - tui.need_render(); + tui.need_render(@src()); return true; } if (try m.match(.{ "B", input.event.press, @intFromEnum(input.mouse.BUTTON1), tp.more }) or @@ -453,7 +453,7 @@ const cmds = struct { const padding = tui.get_widget_style(widget_type).padding; self.menu_len = self.menu_count + padding.top + padding.bottom; self.menu_w = self.menu_label_max + 2 + padding.left + padding.right; - tui.need_render(); + tui.need_render(@src()); try tui.save_config(); } pub const home_next_widget_style_meta: Meta = .{}; diff --git a/src/tui/mainview.zig b/src/tui/mainview.zig index 877981c..67f812a 100644 --- a/src/tui/mainview.zig +++ b/src/tui/mainview.zig @@ -605,7 +605,7 @@ const cmds = struct { if (view == null) try command.executeName("scroll_view_center", .{}); } - tui.need_render(); + tui.need_render(@src()); self.location_update_from_editor(); } @@ -613,7 +613,7 @@ const cmds = struct { tui.reset_drag_context(); try self.create_editor(); try command.executeName("open_scratch_buffer", command.fmt(.{ "help", @embedFile("help.md"), "markdown" })); - tui.need_render(); + tui.need_render(@src()); self.location_update_from_editor(); } pub const open_help_meta: Meta = .{ .description = "Open help" }; @@ -622,7 +622,7 @@ const cmds = struct { tui.reset_drag_context(); try self.create_editor(); try command.executeName("open_scratch_buffer", command.fmt(.{ "font test", @import("fonts.zig").font_test_text, "text" })); - tui.need_render(); + tui.need_render(@src()); self.location_update_from_editor(); } pub const open_font_test_text_meta: Meta = .{ .description = "Open font glyph test text" }; @@ -631,7 +631,7 @@ const cmds = struct { tui.reset_drag_context(); try self.create_editor(); try command.executeName("open_scratch_buffer", command.fmt(.{ "version", root.version_info, "gitcommit" })); - tui.need_render(); + tui.need_render(@src()); self.location_update_from_editor(); } pub const open_version_info_meta: Meta = .{ .description = "Version" }; @@ -726,7 +726,7 @@ const cmds = struct { tui.reset_drag_context(); try self.create_editor(); try command.executeName("open_scratch_buffer", .{ .args = args }); - tui.need_render(); + tui.need_render(@src()); self.location_update_from_editor(); } pub const create_scratch_buffer_meta: Meta = .{ .arguments = &.{ .string, .string, .string } }; @@ -807,7 +807,7 @@ const cmds = struct { new_buffer.mark_dirty(); new_editor.clamp(); new_editor.update_buf(new_buffer.root) catch {}; - tui.need_render(); + tui.need_render(@src()); } try command.executeName("save_file", .{}); try command.executeName("place_next_tab", command.fmt(.{ @@ -836,7 +836,7 @@ const cmds = struct { const logger = log.logger("buffer"); defer logger.deinit(); logger.print("deleted buffer {s}", .{file_path}); - tui.need_render(); + tui.need_render(@src()); } pub const delete_buffer_meta: Meta = .{ .arguments = &.{.string} }; @@ -852,13 +852,13 @@ const cmds = struct { return; } _ = self.buffer_manager.close_buffer(buffer); - tui.need_render(); + tui.need_render(@src()); } pub const close_buffer_meta: Meta = .{ .arguments = &.{.string} }; pub fn restore_session(self: *Self, _: Ctx) Result { try self.read_restore_info(); - tui.need_render(); + tui.need_render(@src()); } pub const restore_session_meta: Meta = .{}; @@ -1117,7 +1117,7 @@ const cmds = struct { if (self.get_active_editor()) |editor| if (std.mem.eql(u8, file_path, editor.file_path orelse "")) { self.symbols_complete = true; try tui.open_overlay(@import("mode/overlay/symbol_palette.zig").Type); - tui.need_render(); + tui.need_render(@src()); }; } pub const add_document_symbol_done_meta: Meta = .{ @@ -1181,7 +1181,7 @@ const cmds = struct { .palette => try tui.open_overlay(@import("mode/overlay/completion_palette.zig").Type), .dropdown => try tui.open_overlay(@import("mode/overlay/completion_dropdown.zig").Type), } - tui.need_render(); + tui.need_render(@src()); } }; } @@ -1383,7 +1383,7 @@ const cmds = struct { const buffer = self.buffer_manager.buffer_from_ref(buffer_ref) orelse return; if (self.get_editor_for_buffer(buffer)) |editor| if (editor.buffer) |eb| if (eb == buffer) { editor.smart_buffer_append(command.fmt(.{output})) catch {}; - tui.need_render(); + tui.need_render(@src()); return; }; var cursor: Buffer.Cursor = .{}; @@ -1393,7 +1393,7 @@ const cmds = struct { _, _, root_ = try root_.insert_chars(cursor.row, cursor.col, output, self.allocator, metrics); buffer.store_undo(&[_]u8{}) catch {}; buffer.update(root_); - tui.need_render(); + tui.need_render(@src()); } pub const shell_execute_stream_output_meta: Meta = .{ .arguments = &.{ .integer, .string } }; @@ -1407,7 +1407,7 @@ const cmds = struct { return; } buffer.mark_clean(); - tui.need_render(); + tui.need_render(@src()); } pub const shell_execute_stream_output_complete_meta: Meta = .{ .arguments = &.{ .integer, .string } }; @@ -2074,12 +2074,12 @@ pub fn set_info_content(self: *Self, content: []const u8, mode: enum { replace, .replace => info.set_content(content) catch |e| return tp.exit_error(e, @errorReturnTrace()), .append => info.append_content(content) catch |e| return tp.exit_error(e, @errorReturnTrace()), } - tui.need_render(); + tui.need_render(@src()); } pub fn cancel_info_content(self: *Self) tp.result { _ = self.toggle_panel_view(info_view, .disable) catch |e| return tp.exit_error(e, @errorReturnTrace()); - tui.need_render(); + tui.need_render(@src()); } pub fn vcs_id_update(self: *Self, m: tp.message) void { diff --git a/src/tui/mode/mini/file_browser.zig b/src/tui/mode/mini/file_browser.zig index 88f54ea..82e29d9 100644 --- a/src/tui/mode/mini/file_browser.zig +++ b/src/tui/mode/mini/file_browser.zig @@ -179,7 +179,7 @@ pub fn Create(options: type) type { } else { log.logger("file_browser").err("receive", tp.unexpected(m)); } - tui.need_render(); + tui.need_render(@src()); } fn add_entry(self: *Self, file_name: []const u8, entry_type: EntryType, file_type: []const u8, icon: []const u8, color: u24) !void { diff --git a/src/tui/mode/overlay/dropdown.zig b/src/tui/mode/overlay/dropdown.zig index 41448a9..e90e885 100644 --- a/src/tui/mode/overlay/dropdown.zig +++ b/src/tui/mode/overlay/dropdown.zig @@ -298,7 +298,7 @@ pub fn Create(options: type) type { self.menu.select_down(); const padding = tui.get_widget_style(widget_type).padding; self.do_resize(padding); - tui.refresh_hover(); + tui.refresh_hover(@src()); self.selection_updated(); } } @@ -580,7 +580,7 @@ pub fn Create(options: type) type { tui.set_next_style(widget_type); const padding = tui.get_widget_style(widget_type).padding; self.do_resize(padding); - tui.need_render(); + tui.need_render(@src()); try tui.save_config(); } pub const overlay_next_widget_style_meta: Meta = .{}; diff --git a/src/tui/mode/overlay/open_recent.zig b/src/tui/mode/overlay/open_recent.zig index ad8ae61..91a79ec 100644 --- a/src/tui/mode/overlay/open_recent.zig +++ b/src/tui/mode/overlay/open_recent.zig @@ -273,7 +273,7 @@ fn process_project_manager(self: *Self, m: tp.message) MessageFilter.Error!void self.menu.select_down(); self.need_select_first = false; } - tui.need_render(); + tui.need_render(@src()); } else if (try cbor.match(m.buf, .{ "PRJ", "recent", @@ -291,7 +291,7 @@ fn process_project_manager(self: *Self, m: tp.message) MessageFilter.Error!void self.menu.select_down(); self.need_select_first = false; } - tui.need_render(); + tui.need_render(@src()); } else if (try cbor.match(m.buf, .{ "PRJ", "recent_done", tp.extract(&self.longest), tp.extract(&query), tp.extract(&self.total_files_in_project) })) { self.update_count_hint(); self.query_pending = false; @@ -492,7 +492,7 @@ const cmds = struct { pub fn overlay_next_widget_style(self: *Self, _: Ctx) Result { tui.set_next_style(widget_type); self.do_resize(); - tui.need_render(); + tui.need_render(@src()); try tui.save_config(); } pub const overlay_next_widget_style_meta: Meta = .{}; diff --git a/src/tui/mode/overlay/palette.zig b/src/tui/mode/overlay/palette.zig index 1b4193b..f0c5ae9 100644 --- a/src/tui/mode/overlay/palette.zig +++ b/src/tui/mode/overlay/palette.zig @@ -349,7 +349,7 @@ pub fn Create(options: type) type { self.menu.select_down(); const padding = tui.get_widget_style(widget_type).padding; self.do_resize(padding); - tui.refresh_hover(); + tui.refresh_hover(@src()); self.selection_updated(); } } @@ -635,7 +635,7 @@ pub fn Create(options: type) type { tui.set_next_style(widget_type); const padding = tui.get_widget_style(widget_type).padding; self.do_resize(padding); - tui.need_render(); + tui.need_render(@src()); try tui.save_config(); } pub const overlay_next_widget_style_meta: Meta = .{}; diff --git a/src/tui/mode/overlay/vcs_status.zig b/src/tui/mode/overlay/vcs_status.zig index b25b698..a6687c7 100644 --- a/src/tui/mode/overlay/vcs_status.zig +++ b/src/tui/mode/overlay/vcs_status.zig @@ -188,7 +188,7 @@ fn process_project_manager(self: *Self, m: tp.message) MessageFilter.Error!void self.menu.select_down(); self.need_select_first = false; } - tui.need_render(); + tui.need_render(@src()); } else if (try cbor.match(m.buf, .{ "PRJ", "new_or_modified_files", @@ -207,7 +207,7 @@ fn process_project_manager(self: *Self, m: tp.message) MessageFilter.Error!void self.menu.select_down(); self.need_select_first = false; } - tui.need_render(); + tui.need_render(@src()); } else if (try cbor.match(m.buf, .{ "PRJ", "new_or_modified_files_done", tp.extract(&self.longest), tp.extract(&query) })) { self.query_pending = false; self.need_reset = true; @@ -369,7 +369,7 @@ const cmds = struct { pub fn overlay_next_widget_style(self: *Self, _: Ctx) Result { tui.set_next_style(widget_type); self.do_resize(); - tui.need_render(); + tui.need_render(@src()); try tui.save_config(); } pub const overlay_next_widget_style_meta: Meta = .{}; diff --git a/src/tui/status/clock.zig b/src/tui/status/clock.zig index fe5b6cd..a2f99aa 100644 --- a/src/tui/status/clock.zig +++ b/src/tui/status/clock.zig @@ -95,7 +95,7 @@ pub fn render(self: *Self, theme: *const Widget.Theme) bool { fn receive_tick(self: *Self, _: tp.pid_ref, m: tp.message) MessageFilter.Error!bool { if (try cbor.match(m.buf, .{"CLOCK"})) { - tui.need_render(); + tui.need_render(@src()); self.update_tick_timer(.ticked); return true; } diff --git a/src/tui/status/tabs.zig b/src/tui/status/tabs.zig index 4265eba..4e5128d 100644 --- a/src/tui/status/tabs.zig +++ b/src/tui/status/tabs.zig @@ -104,6 +104,7 @@ pub const TabBar = struct { const TabBarTab = struct { buffer_ref: usize, widget: Widget, + view: ?usize, }; fn init(allocator: std.mem.Allocator, parent: Plane, event_handler: ?EventHandler, min_tabs: ?usize) !Self { @@ -138,9 +139,14 @@ pub const TabBar = struct { } pub fn update(self: *Self) void { - self.update_tabs() catch {}; + const drag_source, const drag_btn = tui.get_drag_source(); + self.update_tabs(drag_source) catch {}; self.widget_list_widget.resize(Widget.Box.from(self.plane)); self.widget_list_widget.update(); + for (self.widget_list.widgets.items) |*split_widgetstate| if (split_widgetstate.widget.dynamic_cast(WidgetList)) |split| + for (split.widgets.items) |*widgetstate| if (widgetstate.widget.dynamic_cast(Tab.ButtonType)) |btn| if (btn.drag_pos) |_| + tui.update_drag_source(&widgetstate.widget, drag_btn); + tui.refresh_hover(@src()); } pub fn render(self: *Self, theme: *const Widget.Theme) bool { @@ -153,7 +159,8 @@ pub const TabBar = struct { }); self.plane.fill(" "); self.plane.home(); - return self.widget_list_widget.render(theme); + for (self.tabs) |*tab| _ = tab.widget.render(theme); + return false; } pub fn receive(self: *Self, _: tp.pid_ref, m: tp.message) error{Exit}!bool { @@ -200,14 +207,9 @@ pub const TabBar = struct { if (btn.hover) break idx; } else return; if (dragging != hover_) { - const tmp = self.tabs[dragging]; - self.tabs[dragging] = self.tabs[hover_]; - self.tabs[hover_] = tmp; + self.swap_tabs_by_index(dragging, hover_); if (self.tabs[dragging].widget.dynamic_cast(Tab.ButtonType)) |btn| btn.hover = false; self.update(); - for (self.widget_list.widgets.items) |*widgetstate| if (widgetstate.widget.dynamic_cast(Tab.ButtonType)) |btn| if (btn.drag_pos) |_| - tui.update_drag_source(&widgetstate.widget); - tui.refresh_hover(); } } } @@ -229,30 +231,61 @@ pub const TabBar = struct { return self.widget_list_widget.hover(); } - fn update_tabs(self: *Self) !void { + fn update_tabs(self: *Self, drag_source: ?*Widget) !void { const buffer_manager = tui.get_buffer_manager() orelse @panic("tabs no buffer manager"); - try self.update_tab_buffers(); - const prev_widget_count = self.widget_list.widgets.items.len; - while (self.widget_list.pop()) |widget| if (widget.dynamic_cast(Tab.ButtonType) == null) - widget.deinit(self.widget_list.allocator); - var first = true; - for (self.tabs) |tab| { - if (first) { - first = false; - } else { - try self.widget_list.add(try self.make_spacer()); - } - try self.widget_list.add(tab.widget); - if (tab.widget.dynamic_cast(Tab.ButtonType)) |btn| { - if (buffer_manager.buffer_from_ref(tab.buffer_ref)) |buffer| - try btn.update_label(Tab.name_from_buffer(buffer)); + const buffers_changed = try self.update_tab_buffers(); + const dragging = for (self.tabs) |*tab| { + if (tab.widget.dynamic_cast(Tab.ButtonType)) |btn| + if (btn.drag_pos) |_| break true; + } else false; + if (!dragging and !buffers_changed and self.widget_list.widgets.items.len > 0) return; + var prev_widget_count: usize = 0; + for (self.widget_list.widgets.items) |*split_widgetstate| if (split_widgetstate.widget.dynamic_cast(WidgetList)) |split| { + prev_widget_count += 1; + for (split.widgets.items) |_| prev_widget_count += 1; + }; + + for (self.widget_list.widgets.items) |*split_widget| if (split_widget.widget.dynamic_cast(WidgetList)) |split| { + for (split.widgets.items) |*widget| + if (&widget.widget == drag_source) tui.reset_drag_context(); + }; + while (self.widget_list.pop()) |split_widget| if (split_widget.dynamic_cast(WidgetList)) |split| { + while (split.pop()) |widget| if (widget.dynamic_cast(Tab.ButtonType) == null) + widget.deinit(self.widget_list.allocator); + split.deinit(self.widget_list.allocator); + }; + + var max_view: usize = 0; + for (self.tabs) |tab| max_view = @max(max_view, tab.view orelse 0); + + var widget_count: usize = 0; + for (0..max_view + 1) |view| { + var first = true; + var view_widget_list = try WidgetList.createH(self.allocator, self.widget_list.plane, "split", .dynamic); + try self.widget_list.add(view_widget_list.widget()); + widget_count += 1; + for (self.tabs) |tab| { + const tab_view = tab.view orelse 0; + if (tab_view != view) continue; + if (first) { + first = false; + } else { + try view_widget_list.add(try self.make_spacer(view_widget_list.plane)); + widget_count += 1; + } + try view_widget_list.add(tab.widget); + widget_count += 1; + if (tab.widget.dynamic_cast(Tab.ButtonType)) |btn| { + if (buffer_manager.buffer_from_ref(tab.buffer_ref)) |buffer| + try btn.update_label(Tab.name_from_buffer(buffer)); + } } } - if (prev_widget_count != self.widget_list.widgets.items.len) - tui.refresh_hover(); + if (prev_widget_count != widget_count) + tui.refresh_hover(@src()); } - fn update_tab_buffers(self: *Self) !void { + fn update_tab_buffers(self: *Self) !bool { const buffer_manager = tui.get_buffer_manager() orelse @panic("tabs no buffer manager"); const buffers = try buffer_manager.list_unordered(self.allocator); defer self.allocator.free(buffers); @@ -278,6 +311,16 @@ pub const TabBar = struct { } self.tabs = try result.toOwnedSlice(self.allocator); + + if (existing_tabs.len != self.tabs.len) + return true; + for (existing_tabs, self.tabs) |tab_a, tab_b| { + if (tab_a.buffer_ref == tab_b.buffer_ref and + tab_a.view == tab_b.view) + continue; + return true; + } + return false; } fn place_new_tab(self: *Self, result: *std.ArrayListUnmanaged(TabBarTab), buffer: *Buffer) !void { @@ -295,14 +338,14 @@ pub const TabBar = struct { else try result.addOne(self.allocator), }; - pos.* = .{ .buffer_ref = buffer_ref, .widget = tab }; + pos.* = .{ .buffer_ref = buffer_ref, .widget = tab, .view = buffer.get_last_view() }; self.place_next = .atend; } - fn make_spacer(self: @This()) !Widget { + fn make_spacer(self: @This(), parent: Plane) !Widget { return spacer.create( self.allocator, - self.widget_list.plane, + parent, self.tab_style.spacer, self.tab_style.spacer_fg, self.tab_style.spacer_bg, @@ -371,9 +414,23 @@ pub const TabBar = struct { tp.trace(tp.channel.debug, .{ "swap_tabs", "not_found", "buffer_ref_b" }); return; }; + self.swap_tabs_by_index(tab_a_idx, tab_b_idx); + } + + fn swap_tabs_by_index(self: *Self, tab_a_idx: usize, tab_b_idx: usize) void { const tmp = self.tabs[tab_a_idx]; self.tabs[tab_a_idx] = self.tabs[tab_b_idx]; self.tabs[tab_b_idx] = tmp; + const buffer_manager = tui.get_buffer_manager() orelse @panic("tabs no buffer manager"); + if (buffer_manager.buffer_from_ref(self.tabs[tab_a_idx].buffer_ref)) |buffer_a| + if (buffer_manager.buffer_from_ref(self.tabs[tab_b_idx].buffer_ref)) |buffer_b| { + const view_a = buffer_a.get_last_view(); + const view_b = buffer_b.get_last_view(); + if (view_a != view_b) { + buffer_a.set_last_view(view_b); + buffer_b.set_last_view(view_a); + } + }; tp.trace(tp.channel.debug, .{ "swap_tabs", "swapped", "indexes", tab_a_idx, tab_b_idx }); } @@ -417,21 +474,27 @@ pub const TabBar = struct { while (count > 0) : (count -= 1) { var buffer_name: ?[]const u8 = undefined; if (!(cbor.matchValue(&iter2, cbor.extract(&buffer_name)) catch false)) return error.MatchTabBufferNameFailed; - if (buffer_name) |name| if (name_to_ref(name)) |buffer_ref| { - (try result.addOne(self.allocator)).* = .{ - .buffer_ref = buffer_ref, - .widget = try Tab.create(self, buffer_ref, &self.tab_style), - }; - }; + if (buffer_name) |name| { + const buffer_ref_, const buffer_view = name_to_ref_and_view(name); + if (buffer_ref_) |buffer_ref| + (try result.addOne(self.allocator)).* = .{ + .buffer_ref = buffer_ref, + .widget = try Tab.create(self, buffer_ref, &self.tab_style), + .view = buffer_view, + }; + } } self.tabs = try result.toOwnedSlice(self.allocator); iter.* = iter2; } - fn name_to_ref(buffer_name: []const u8) ?usize { + fn name_to_ref_and_view(buffer_name: []const u8) struct { ?usize, ?usize } { const buffer_manager = tui.get_buffer_manager() orelse @panic("tabs no buffer manager"); - return if (buffer_manager.get_buffer_for_file(buffer_name)) |buffer| buffer_manager.buffer_to_ref(buffer) else null; + return if (buffer_manager.get_buffer_for_file(buffer_name)) |buffer| + .{ buffer_manager.buffer_to_ref(buffer), buffer.get_last_view() } + else + .{ null, null }; } }; diff --git a/src/tui/tui.zig b/src/tui/tui.zig index e89336c..b579062 100644 --- a/src/tui/tui.zig +++ b/src/tui/tui.zig @@ -58,7 +58,7 @@ last_hover_y: c_int = -1, commands: Commands = undefined, logger: log.Logger, drag_source: ?*Widget = null, -drag_button: ?input.MouseType = null, +drag_button: input.MouseType = 0, dark_theme: Widget.Theme, dark_parsed_theme: ?std.json.Parsed(Widget.Theme), light_theme: Widget.Theme, @@ -407,7 +407,7 @@ fn receive_safe(self: *Self, from: tp.pid_ref, m: tp.message) !void { }; try self.dispatch_flush_input_event(); if (self.unrendered_input_events_count > 0 and !self.frame_clock_running) - need_render(); + need_render(@src()); return; } @@ -556,7 +556,7 @@ fn receive_safe(self: *Self, from: tp.pid_ref, m: tp.message) !void { if (try m.match(.{"MOUSE_IDLE"})) { if (self.mouse_idle_timer) |*t| t.deinit(); self.mouse_idle_timer = null; - try self.clear_hover_focus(); + try self.clear_hover_focus(@src()); return; } @@ -668,11 +668,13 @@ fn render(self: *Self) void { if (!self.frame_clock_running) { self.frame_clock.start() catch {}; self.frame_clock_running = true; + tp.trace(tp.channel.widget, .{ "frame_clock_running", "started", more }); } } else { if (self.frame_clock_running) { self.frame_clock.stop() catch {}; self.frame_clock_running = false; + tp.trace(tp.channel.widget, .{ "frame_clock_running", "stopped", more }); } } } @@ -849,32 +851,34 @@ fn update_hover(self: *Self, y: c_int, x: c_int) !?*Widget { self.last_hover_x = x; if (y >= 0 and x >= 0) if (self.find_coord_widget(@intCast(y), @intCast(x))) |w| { if (if (self.hover_focus) |h| h != w else true) { - var buf: [256]u8 = undefined; - if (self.hover_focus) |h| { - if (self.is_live_widget_ptr(h)) - _ = try h.send(tp.self_pid(), tp.message.fmtbuf(&buf, .{ "H", false }) catch |e| return tp.exit_error(e, @errorReturnTrace())); - } + tp.trace(tp.channel.debug, .{ "update_hover", if (self.hover_focus) |h| @intFromPtr(h) else 0, @intFromPtr(w) }); + if (self.hover_focus) |h| if (self.is_live_widget_ptr(h)) + try send_hover_msg(h, false); self.hover_focus = w; - _ = try w.send(tp.self_pid(), tp.message.fmtbuf(&buf, .{ "H", true }) catch |e| return tp.exit_error(e, @errorReturnTrace())); + try send_hover_msg(w, true); } return w; }; - try self.clear_hover_focus(); + try self.clear_hover_focus(@src()); return null; } -fn clear_hover_focus(self: *Self) tp.result { - if (self.hover_focus) |h| { - var buf: [256]u8 = undefined; - if (self.is_live_widget_ptr(h)) - _ = try h.send(tp.self_pid(), tp.message.fmtbuf(&buf, .{ "H", false }) catch |e| return tp.exit_error(e, @errorReturnTrace())); - } +fn clear_hover_focus(self: *Self, src: std.builtin.SourceLocation) tp.result { + if (self.hover_focus) |h| if (self.is_live_widget_ptr(h)) + try send_hover_msg(h, false); + tp.trace(tp.channel.debug, .{ "tui", "clear_hover_focus", if (self.hover_focus) |h| @intFromPtr(h) else 0, src.fn_name, src.file, src.line }); self.hover_focus = null; } -pub fn refresh_hover() void { +fn send_hover_msg(widget: *const Widget, hover: bool) tp.result { + var buf: [256]u8 = undefined; + tp.trace(tp.channel.debug, .{ "hover_msg", @intFromPtr(widget), hover }); + _ = try widget.send(tp.self_pid(), tp.message.fmtbuf(&buf, .{ "H", hover }) catch |e| return tp.exit_error(e, @errorReturnTrace())); +} + +pub fn refresh_hover(src: std.builtin.SourceLocation) void { const self = current(); - self.clear_hover_focus() catch return; + tp.trace(tp.channel.debug, .{ "tui", "refresh_hover", if (self.hover_focus) |h| @intFromPtr(h) else 0, src.fn_name, src.file, src.line }); _ = self.update_hover(self.last_hover_y, self.last_hover_x) catch {}; } @@ -898,7 +902,7 @@ fn enter_overlay_mode(self: *Self, mode: type) command.Result { self.input_mode_outer_ = self.input_mode_; self.input_mode_ = new_mode; if (self.input_mode_) |*m| m.run_init(); - refresh_hover(); + refresh_hover(@src()); } fn enter_overlay_mode_with_args(self: *Self, mode: type, ctx: command.Context) command.Result { @@ -910,7 +914,7 @@ fn enter_overlay_mode_with_args(self: *Self, mode: type, ctx: command.Context) c self.input_mode_outer_ = self.input_mode_; self.input_mode_ = try mode.create_with_args(self.allocator, ctx); if (self.input_mode_) |*m| m.run_init(); - refresh_hover(); + refresh_hover(@src()); } fn get_input_mode(self: *Self, mode_name: []const u8) !Mode { @@ -1386,7 +1390,7 @@ const cmds = struct { if (self.input_mode_) |*mode| mode.deinit(); self.input_mode_ = self.input_mode_outer_; self.input_mode_outer_ = null; - refresh_hover(); + refresh_hover(@src()); } pub const exit_overlay_mode_meta: Meta = .{}; @@ -1613,21 +1617,21 @@ const cmds = struct { pub fn panel_next_widget_style(_: *Self, _: Ctx) Result { set_next_style(.panel); - need_render(); + need_render(@src()); try save_config(); } pub const panel_next_widget_style_meta: Meta = .{}; pub fn hint_window_next_widget_style(_: *Self, _: Ctx) Result { set_next_style(.hint_window); - need_render(); + need_render(@src()); try save_config(); } pub const hint_window_next_widget_style_meta: Meta = .{}; pub fn dropdown_next_widget_style(_: *Self, _: Ctx) Result { set_next_style(.dropdown); - need_render(); + need_render(@src()); try save_config(); } pub const dropdown_next_widget_style_meta: Meta = .{}; @@ -1775,9 +1779,10 @@ pub fn get_keybind_mode() ?Mode { return self.input_mode_ orelse self.delayed_init_input_mode; } -pub fn update_drag_source(drag_source: *Widget) void { +pub fn update_drag_source(drag_source: *Widget, btn: input.MouseType) void { const self = current(); self.drag_source = drag_source; + self.drag_button = btn; } fn set_drag_source(self: *Self, drag_source: ?*Widget, btn: input.MouseType) void { @@ -1785,6 +1790,11 @@ fn set_drag_source(self: *Self, drag_source: ?*Widget, btn: input.MouseType) voi self.drag_button = btn; } +pub fn get_drag_source() struct { ?*Widget, input.MouseType } { + const self = current(); + return .{ self.drag_source, self.drag_button }; +} + pub fn reset_drag_context() void { const self = current(); self.drag_source = null; @@ -1797,9 +1807,10 @@ fn maybe_reset_drag_source(self: *Self, btn: input.MouseType) void { self.drag_button = 0; } -pub fn need_render() void { +pub fn need_render(src: std.builtin.SourceLocation) void { const self = current(); if (!(self.render_pending or self.frame_clock_running)) { + tp.trace(tp.channel.debug, .{ "tui", "need_render", src.fn_name, src.file, src.line }); self.render_pending = true; tp.self_pid().send(.{"render"}) catch {}; } @@ -1812,8 +1823,8 @@ pub fn frames_rendered() usize { pub fn resize() void { mainview_widget().resize(screen()); - refresh_hover(); - need_render(); + refresh_hover(@src()); + need_render(@src()); } pub fn plane() renderer.Plane {