feat: make tabs draggable
This commit is contained in:
parent
1426142d35
commit
265bff583b
1 changed files with 123 additions and 75 deletions
|
|
@ -6,6 +6,7 @@ const root = @import("soft_root").root;
|
||||||
const EventHandler = @import("EventHandler");
|
const EventHandler = @import("EventHandler");
|
||||||
const Plane = @import("renderer").Plane;
|
const Plane = @import("renderer").Plane;
|
||||||
const Buffer = @import("Buffer");
|
const Buffer = @import("Buffer");
|
||||||
|
const input = @import("input");
|
||||||
|
|
||||||
const tui = @import("../tui.zig");
|
const tui = @import("../tui.zig");
|
||||||
const Widget = @import("../Widget.zig");
|
const Widget = @import("../Widget.zig");
|
||||||
|
|
@ -187,6 +188,30 @@ pub const TabBar = struct {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_event(self: *Self, from: tp.pid_ref, m: tp.message) tp.result {
|
||||||
|
if (self.event_handler) |event_handler| try event_handler.send(from, m);
|
||||||
|
if (try m.match(.{ "D", input.event.press, @intFromEnum(input.mouse.BUTTON1), tp.more })) {
|
||||||
|
const dragging = for (self.tabs, 0..) |*tab, idx| {
|
||||||
|
if (tab.widget.dynamic_cast(Tab.ButtonType)) |btn|
|
||||||
|
if (btn.drag_pos) |_| break idx;
|
||||||
|
} else return;
|
||||||
|
const hover_ = for (self.tabs, 0..) |*tab, idx| {
|
||||||
|
if (tab.widget.dynamic_cast(Tab.ButtonType)) |btn|
|
||||||
|
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;
|
||||||
|
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.set_drag_source(&widgetstate.widget);
|
||||||
|
tui.refresh_hover();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn handle_resize(self: *Self, pos: Widget.Box) void {
|
pub fn handle_resize(self: *Self, pos: Widget.Box) void {
|
||||||
self.widget_list_widget.resize(pos);
|
self.widget_list_widget.resize(pos);
|
||||||
self.plane = self.widget_list.plane;
|
self.plane = self.widget_list.plane;
|
||||||
|
|
@ -258,7 +283,7 @@ pub const TabBar = struct {
|
||||||
fn place_new_tab(self: *Self, result: *std.ArrayListUnmanaged(TabBarTab), buffer: *Buffer) !void {
|
fn place_new_tab(self: *Self, result: *std.ArrayListUnmanaged(TabBarTab), buffer: *Buffer) !void {
|
||||||
const buffer_manager = tui.get_buffer_manager() orelse @panic("tabs no buffer manager");
|
const buffer_manager = tui.get_buffer_manager() orelse @panic("tabs no buffer manager");
|
||||||
const buffer_ref = buffer_manager.buffer_to_ref(buffer);
|
const buffer_ref = buffer_manager.buffer_to_ref(buffer);
|
||||||
const tab = try Tab.create(self, buffer_ref, &self.tab_style, self.event_handler);
|
const tab = try Tab.create(self, buffer_ref, &self.tab_style);
|
||||||
const pos = switch (self.place_next) {
|
const pos = switch (self.place_next) {
|
||||||
.atend => try result.addOne(self.allocator),
|
.atend => try result.addOne(self.allocator),
|
||||||
.before => |i| if (i < result.items.len)
|
.before => |i| if (i < result.items.len)
|
||||||
|
|
@ -395,7 +420,7 @@ pub const TabBar = struct {
|
||||||
if (buffer_name) |name| if (name_to_ref(name)) |buffer_ref| {
|
if (buffer_name) |name| if (name_to_ref(name)) |buffer_ref| {
|
||||||
(try result.addOne(self.allocator)).* = .{
|
(try result.addOne(self.allocator)).* = .{
|
||||||
.buffer_ref = buffer_ref,
|
.buffer_ref = buffer_ref,
|
||||||
.widget = try Tab.create(self, buffer_ref, &self.tab_style, self.event_handler),
|
.widget = try Tab.create(self, buffer_ref, &self.tab_style),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -416,6 +441,7 @@ const Tab = struct {
|
||||||
tab_style: *const Style,
|
tab_style: *const Style,
|
||||||
close_pos: ?c_uint = null,
|
close_pos: ?c_uint = null,
|
||||||
save_pos: ?c_uint = null,
|
save_pos: ?c_uint = null,
|
||||||
|
on_event: ?EventHandler = null,
|
||||||
|
|
||||||
const Mode = enum { active, inactive, selected };
|
const Mode = enum { active, inactive, selected };
|
||||||
|
|
||||||
|
|
@ -425,7 +451,6 @@ const Tab = struct {
|
||||||
tabbar: *TabBar,
|
tabbar: *TabBar,
|
||||||
buffer_ref: usize,
|
buffer_ref: usize,
|
||||||
tab_style: *const Style,
|
tab_style: *const Style,
|
||||||
event_handler: ?EventHandler,
|
|
||||||
) !Widget {
|
) !Widget {
|
||||||
const buffer_manager = tui.get_buffer_manager() orelse @panic("tabs no buffer manager");
|
const buffer_manager = tui.get_buffer_manager() orelse @panic("tabs no buffer manager");
|
||||||
const buffer = buffer_manager.buffer_from_ref(buffer_ref);
|
const buffer = buffer_manager.buffer_from_ref(buffer_ref);
|
||||||
|
|
@ -436,7 +461,7 @@ const Tab = struct {
|
||||||
.on_click2 = Tab.on_click2,
|
.on_click2 = Tab.on_click2,
|
||||||
.on_layout = Tab.layout,
|
.on_layout = Tab.layout,
|
||||||
.on_render = Tab.render,
|
.on_render = Tab.render,
|
||||||
.on_event = event_handler,
|
.on_event = EventHandler.bind(tabbar, TabBar.handle_event),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -463,159 +488,182 @@ const Tab = struct {
|
||||||
|
|
||||||
fn render(self: *@This(), btn: *ButtonType, theme: *const Widget.Theme) bool {
|
fn render(self: *@This(), btn: *ButtonType, theme: *const Widget.Theme) bool {
|
||||||
const active = self.tabbar.active_buffer_ref == self.buffer_ref;
|
const active = self.tabbar.active_buffer_ref == self.buffer_ref;
|
||||||
const mode: Mode = if (btn.hover) .selected else if (active) .active else .inactive;
|
if (btn.drag_pos) |pos| {
|
||||||
switch (mode) {
|
self.render_dragging(&btn.plane, theme);
|
||||||
.selected => self.render_selected(btn, theme, active),
|
const anchor: Widget.Pos = btn.drag_anchor orelse .{};
|
||||||
.active => self.render_active(btn, theme),
|
var box = Widget.Box.from(btn.plane);
|
||||||
.inactive => self.render_inactive(btn, theme),
|
box.y = @intCast(@max(pos.y, anchor.y) - anchor.y);
|
||||||
|
box.x = @intCast(@max(pos.x, anchor.x) - anchor.x);
|
||||||
|
if (tui.top_layer(box.to_layer())) |top_layer| {
|
||||||
|
self.render_selected(top_layer, btn.opts.label, false, theme, active);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const mode: Mode = if (btn.hover) .selected else if (active) .active else .inactive;
|
||||||
|
switch (mode) {
|
||||||
|
.selected => self.render_selected(&btn.plane, btn.opts.label, btn.hover, theme, active),
|
||||||
|
.active => self.render_active(&btn.plane, btn.opts.label, btn.hover, theme),
|
||||||
|
.inactive => self.render_inactive(&btn.plane, btn.opts.label, btn.hover, theme),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_selected(self: *@This(), btn: *ButtonType, theme: *const Widget.Theme, active: bool) void {
|
fn render_selected(self: *@This(), plane: *Plane, label: []const u8, hover: bool, theme: *const Widget.Theme, active: bool) void {
|
||||||
btn.plane.set_base_style(theme.editor);
|
plane.set_base_style(theme.editor);
|
||||||
btn.plane.erase();
|
plane.erase();
|
||||||
btn.plane.home();
|
plane.home();
|
||||||
btn.plane.set_style(.{
|
plane.set_style(.{
|
||||||
.fg = self.tab_style.inactive_fg.from_theme(theme),
|
.fg = self.tab_style.inactive_fg.from_theme(theme),
|
||||||
.bg = self.tab_style.inactive_bg.from_theme(theme),
|
.bg = self.tab_style.inactive_bg.from_theme(theme),
|
||||||
});
|
});
|
||||||
btn.plane.fill(" ");
|
plane.fill(" ");
|
||||||
btn.plane.home();
|
plane.home();
|
||||||
if (active) {
|
if (active) {
|
||||||
btn.plane.set_style(.{
|
plane.set_style(.{
|
||||||
.fg = self.tab_style.selected_fg.from_theme(theme),
|
.fg = self.tab_style.selected_fg.from_theme(theme),
|
||||||
.bg = self.tab_style.selected_bg.from_theme(theme),
|
.bg = self.tab_style.selected_bg.from_theme(theme),
|
||||||
});
|
});
|
||||||
btn.plane.fill(" ");
|
plane.fill(" ");
|
||||||
btn.plane.home();
|
plane.home();
|
||||||
}
|
}
|
||||||
|
|
||||||
btn.plane.set_style(.{
|
plane.set_style(.{
|
||||||
.fg = self.tab_style.selected_left_fg.from_theme(theme),
|
.fg = self.tab_style.selected_left_fg.from_theme(theme),
|
||||||
.bg = self.tab_style.selected_left_bg.from_theme(theme),
|
.bg = self.tab_style.selected_left_bg.from_theme(theme),
|
||||||
});
|
});
|
||||||
_ = btn.plane.putstr(self.tab_style.selected_left) catch {};
|
_ = plane.putstr(self.tab_style.selected_left) catch {};
|
||||||
|
|
||||||
btn.plane.set_style(.{
|
plane.set_style(.{
|
||||||
.fg = self.tab_style.selected_fg.from_theme(theme),
|
.fg = self.tab_style.selected_fg.from_theme(theme),
|
||||||
.bg = self.tab_style.selected_bg.from_theme(theme),
|
.bg = self.tab_style.selected_bg.from_theme(theme),
|
||||||
});
|
});
|
||||||
self.render_content(btn, self.tab_style.selected_fg.from_theme(theme), theme);
|
self.render_content(plane, label, hover, self.tab_style.selected_fg.from_theme(theme), theme);
|
||||||
|
|
||||||
btn.plane.set_style(.{
|
plane.set_style(.{
|
||||||
.fg = self.tab_style.selected_right_fg.from_theme(theme),
|
.fg = self.tab_style.selected_right_fg.from_theme(theme),
|
||||||
.bg = self.tab_style.selected_right_bg.from_theme(theme),
|
.bg = self.tab_style.selected_right_bg.from_theme(theme),
|
||||||
});
|
});
|
||||||
_ = btn.plane.putstr(self.tab_style.selected_right) catch {};
|
_ = plane.putstr(self.tab_style.selected_right) catch {};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_active(self: *@This(), btn: *ButtonType, theme: *const Widget.Theme) void {
|
fn render_active(self: *@This(), plane: *Plane, label: []const u8, hover: bool, theme: *const Widget.Theme) void {
|
||||||
btn.plane.set_base_style(theme.editor);
|
plane.set_base_style(theme.editor);
|
||||||
btn.plane.erase();
|
plane.erase();
|
||||||
btn.plane.home();
|
plane.home();
|
||||||
btn.plane.set_style(.{
|
plane.set_style(.{
|
||||||
.fg = self.tab_style.inactive_fg.from_theme(theme),
|
.fg = self.tab_style.inactive_fg.from_theme(theme),
|
||||||
.bg = self.tab_style.inactive_bg.from_theme(theme),
|
.bg = self.tab_style.inactive_bg.from_theme(theme),
|
||||||
});
|
});
|
||||||
btn.plane.fill(" ");
|
plane.fill(" ");
|
||||||
btn.plane.home();
|
plane.home();
|
||||||
btn.plane.set_style(.{
|
plane.set_style(.{
|
||||||
.fg = self.tab_style.active_fg.from_theme(theme),
|
.fg = self.tab_style.active_fg.from_theme(theme),
|
||||||
.bg = self.tab_style.active_bg.from_theme(theme),
|
.bg = self.tab_style.active_bg.from_theme(theme),
|
||||||
});
|
});
|
||||||
btn.plane.fill(" ");
|
plane.fill(" ");
|
||||||
btn.plane.home();
|
plane.home();
|
||||||
|
|
||||||
btn.plane.set_style(.{
|
plane.set_style(.{
|
||||||
.fg = self.tab_style.active_left_fg.from_theme(theme),
|
.fg = self.tab_style.active_left_fg.from_theme(theme),
|
||||||
.bg = self.tab_style.active_left_bg.from_theme(theme),
|
.bg = self.tab_style.active_left_bg.from_theme(theme),
|
||||||
});
|
});
|
||||||
_ = btn.plane.putstr(self.tab_style.active_left) catch {};
|
_ = plane.putstr(self.tab_style.active_left) catch {};
|
||||||
|
|
||||||
btn.plane.set_style(.{
|
plane.set_style(.{
|
||||||
.fg = self.tab_style.active_fg.from_theme(theme),
|
.fg = self.tab_style.active_fg.from_theme(theme),
|
||||||
.bg = self.tab_style.active_bg.from_theme(theme),
|
.bg = self.tab_style.active_bg.from_theme(theme),
|
||||||
});
|
});
|
||||||
self.render_content(btn, self.tab_style.active_fg.from_theme(theme), theme);
|
self.render_content(plane, label, hover, self.tab_style.active_fg.from_theme(theme), theme);
|
||||||
|
|
||||||
btn.plane.set_style(.{
|
plane.set_style(.{
|
||||||
.fg = self.tab_style.active_right_fg.from_theme(theme),
|
.fg = self.tab_style.active_right_fg.from_theme(theme),
|
||||||
.bg = self.tab_style.active_right_bg.from_theme(theme),
|
.bg = self.tab_style.active_right_bg.from_theme(theme),
|
||||||
});
|
});
|
||||||
_ = btn.plane.putstr(self.tab_style.active_right) catch {};
|
_ = plane.putstr(self.tab_style.active_right) catch {};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_inactive(self: *@This(), btn: *ButtonType, theme: *const Widget.Theme) void {
|
fn render_inactive(self: *@This(), plane: *Plane, label: []const u8, hover: bool, theme: *const Widget.Theme) void {
|
||||||
btn.plane.set_base_style(theme.editor);
|
plane.set_base_style(theme.editor);
|
||||||
btn.plane.erase();
|
plane.erase();
|
||||||
btn.plane.home();
|
plane.home();
|
||||||
btn.plane.set_style(.{
|
plane.set_style(.{
|
||||||
.fg = self.tab_style.inactive_fg.from_theme(theme),
|
.fg = self.tab_style.inactive_fg.from_theme(theme),
|
||||||
.bg = self.tab_style.inactive_bg.from_theme(theme),
|
.bg = self.tab_style.inactive_bg.from_theme(theme),
|
||||||
});
|
});
|
||||||
btn.plane.fill(" ");
|
plane.fill(" ");
|
||||||
btn.plane.home();
|
plane.home();
|
||||||
|
|
||||||
btn.plane.set_style(.{
|
plane.set_style(.{
|
||||||
.fg = self.tab_style.inactive_left_fg.from_theme(theme),
|
.fg = self.tab_style.inactive_left_fg.from_theme(theme),
|
||||||
.bg = self.tab_style.inactive_left_bg.from_theme(theme),
|
.bg = self.tab_style.inactive_left_bg.from_theme(theme),
|
||||||
});
|
});
|
||||||
_ = btn.plane.putstr(self.tab_style.inactive_left) catch {};
|
_ = plane.putstr(self.tab_style.inactive_left) catch {};
|
||||||
|
|
||||||
btn.plane.set_style(.{
|
plane.set_style(.{
|
||||||
.fg = self.tab_style.inactive_fg.from_theme(theme),
|
.fg = self.tab_style.inactive_fg.from_theme(theme),
|
||||||
.bg = self.tab_style.inactive_bg.from_theme(theme),
|
.bg = self.tab_style.inactive_bg.from_theme(theme),
|
||||||
});
|
});
|
||||||
self.render_content(btn, self.tab_style.inactive_fg.from_theme(theme), theme);
|
self.render_content(plane, label, hover, self.tab_style.inactive_fg.from_theme(theme), theme);
|
||||||
|
|
||||||
btn.plane.set_style(.{
|
plane.set_style(.{
|
||||||
.fg = self.tab_style.inactive_right_fg.from_theme(theme),
|
.fg = self.tab_style.inactive_right_fg.from_theme(theme),
|
||||||
.bg = self.tab_style.inactive_right_bg.from_theme(theme),
|
.bg = self.tab_style.inactive_right_bg.from_theme(theme),
|
||||||
});
|
});
|
||||||
_ = btn.plane.putstr(self.tab_style.inactive_right) catch {};
|
_ = plane.putstr(self.tab_style.inactive_right) catch {};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_content(self: *@This(), btn: *ButtonType, fg: ?Widget.Theme.Color, theme: *const Widget.Theme) void {
|
fn render_dragging(self: *@This(), plane: *Plane, theme: *const Widget.Theme) void {
|
||||||
|
plane.set_base_style(theme.editor);
|
||||||
|
plane.erase();
|
||||||
|
plane.home();
|
||||||
|
plane.set_style(.{
|
||||||
|
.fg = self.tab_style.inactive_fg.from_theme(theme),
|
||||||
|
.bg = self.tab_style.inactive_bg.from_theme(theme),
|
||||||
|
});
|
||||||
|
plane.fill(" ");
|
||||||
|
plane.home();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_content(self: *@This(), plane: *Plane, label: []const u8, hover: bool, fg: ?Widget.Theme.Color, theme: *const Widget.Theme) void {
|
||||||
const buffer_manager = tui.get_buffer_manager() orelse @panic("tabs no buffer manager");
|
const buffer_manager = tui.get_buffer_manager() orelse @panic("tabs no buffer manager");
|
||||||
const buffer_ = buffer_manager.buffer_from_ref(self.buffer_ref);
|
const buffer_ = buffer_manager.buffer_from_ref(self.buffer_ref);
|
||||||
const is_dirty = if (buffer_) |buffer| buffer.is_dirty() else false;
|
const is_dirty = if (buffer_) |buffer| buffer.is_dirty() else false;
|
||||||
self.render_padding(&btn.plane, .left);
|
self.render_padding(plane, .left);
|
||||||
if (self.tab_style.file_type_icon) if (buffer_) |buffer| if (buffer.file_type_icon) |icon| {
|
if (self.tab_style.file_type_icon) if (buffer_) |buffer| if (buffer.file_type_icon) |icon| {
|
||||||
const color_: ?u24 = if (buffer.file_type_color) |color| if (!(color == 0xFFFFFF or color == 0x000000 or color == 0x000001)) color else null else null;
|
const color_: ?u24 = if (buffer.file_type_color) |color| if (!(color == 0xFFFFFF or color == 0x000000 or color == 0x000001)) color else null else null;
|
||||||
if (color_) |color|
|
if (color_) |color|
|
||||||
btn.plane.set_style(.{ .fg = .{ .color = color } });
|
plane.set_style(.{ .fg = .{ .color = color } });
|
||||||
_ = btn.plane.putstr(icon) catch {};
|
_ = plane.putstr(icon) catch {};
|
||||||
if (color_) |_|
|
if (color_) |_|
|
||||||
btn.plane.set_style(.{ .fg = fg });
|
plane.set_style(.{ .fg = fg });
|
||||||
_ = btn.plane.putstr(" ") catch {};
|
_ = plane.putstr(" ") catch {};
|
||||||
};
|
};
|
||||||
_ = btn.plane.putstr(btn.opts.label) catch {};
|
_ = plane.putstr(label) catch {};
|
||||||
_ = btn.plane.putstr(" ") catch {};
|
_ = plane.putstr(" ") catch {};
|
||||||
self.close_pos = null;
|
self.close_pos = null;
|
||||||
self.save_pos = null;
|
self.save_pos = null;
|
||||||
if (btn.hover) {
|
if (hover) {
|
||||||
if (is_dirty) {
|
if (is_dirty) {
|
||||||
if (self.tab_style.save_icon_fg) |color|
|
if (self.tab_style.save_icon_fg) |color|
|
||||||
btn.plane.set_style(.{ .fg = color.from_theme(theme) });
|
plane.set_style(.{ .fg = color.from_theme(theme) });
|
||||||
self.save_pos = btn.plane.cursor_x();
|
self.save_pos = plane.cursor_x();
|
||||||
_ = btn.plane.putstr(self.tabbar.tab_style.save_icon) catch {};
|
_ = plane.putstr(self.tabbar.tab_style.save_icon) catch {};
|
||||||
} else {
|
} else {
|
||||||
btn.plane.set_style(.{ .fg = self.tab_style.close_icon_fg.from_theme(theme) });
|
plane.set_style(.{ .fg = self.tab_style.close_icon_fg.from_theme(theme) });
|
||||||
self.close_pos = btn.plane.cursor_x();
|
self.close_pos = plane.cursor_x();
|
||||||
_ = btn.plane.putstr(self.tabbar.tab_style.close_icon) catch {};
|
_ = plane.putstr(self.tabbar.tab_style.close_icon) catch {};
|
||||||
}
|
}
|
||||||
} else if (is_dirty) {
|
} else if (is_dirty) {
|
||||||
if (self.tab_style.dirty_indicator_fg) |color|
|
if (self.tab_style.dirty_indicator_fg) |color|
|
||||||
btn.plane.set_style(.{ .fg = color.from_theme(theme) });
|
plane.set_style(.{ .fg = color.from_theme(theme) });
|
||||||
_ = btn.plane.putstr(self.tabbar.tab_style.dirty_indicator) catch {};
|
_ = plane.putstr(self.tabbar.tab_style.dirty_indicator) catch {};
|
||||||
} else {
|
} else {
|
||||||
if (self.tab_style.clean_indicator_fg) |color|
|
if (self.tab_style.clean_indicator_fg) |color|
|
||||||
btn.plane.set_style(.{ .fg = color.from_theme(theme) });
|
plane.set_style(.{ .fg = color.from_theme(theme) });
|
||||||
_ = btn.plane.putstr(self.tabbar.tab_style.clean_indicator) catch {};
|
_ = plane.putstr(self.tabbar.tab_style.clean_indicator) catch {};
|
||||||
}
|
}
|
||||||
btn.plane.set_style(.{ .fg = fg });
|
plane.set_style(.{ .fg = fg });
|
||||||
self.render_padding(&btn.plane, .right);
|
self.render_padding(plane, .right);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_padding(self: *@This(), plane: *Plane, side: enum { left, right }) void {
|
fn render_padding(self: *@This(), plane: *Plane, side: enum { left, right }) void {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue