Compare commits

...

8 commits

Author SHA1 Message Date
c702bc2cc8
Merge branch 'master' into zig-0.15.0 2025-08-03 18:17:20 +02:00
Ingo Lohmar
28da270834 harmonize selection display 2025-08-03 18:07:58 +02:00
a5af34e13f feat: select mode widget style "fancy" or "plain" with descriptor arg
"fancy" mode is the previous behaviour and the default. "plain" mode makes the
mode widget use the same styling as other status bar widgets.

Select "plain" mode with a descriptor arg in the config. e.g:

`
bottom_bar "mode:plain file log selection diagnostics keybind branch linenumber clock spacer"
`
2025-08-03 17:53:34 +02:00
Ingo Lohmar
d011294c69 use statusbar.hover style for actual hovering
The style was used for regular display of the style_label instead, which
gives the current keymap/mode weird prominence, and which means that
actual (mouse) hovering needed to use the unrelated "selection" style.

This commit uses the hover style for just hovering, or if a mini mode is
used (which, to me, sounds at least plausible).

For modal-workflow users, it may be more helpful to actually highlight
the mode (sometimes?), but I have no experience with that.
2025-08-03 17:53:34 +02:00
0c98460ca8
refactor: remove duplicate style definition in modestate widget 2025-08-03 17:28:37 +02:00
b97b8e8fb7
feat: auto hide tabs widget if there are less than {n} tabs
This can be configured via an integer argument in the bar widget descriptor, or
via the tab styles configuration file. A value of 0 will effectively disable
auto hide.
2025-08-03 17:08:53 +02:00
dfcc825e4b
feat: auto hide bars with empty widget layouts 2025-08-03 16:17:05 +02:00
ce204d650a
feat: add WidgetList support for overriding layouts 2025-08-03 16:16:15 +02:00
5 changed files with 53 additions and 20 deletions

View file

@ -24,12 +24,14 @@ parent: Plane,
allocator: Allocator,
widgets: ArrayList(WidgetState),
layout_: Layout,
layout_empty: bool = true,
direction: Direction,
box: ?Widget.Box = null,
ctx: ?*anyopaque = null,
on_render: *const fn (ctx: ?*anyopaque, theme: *const Widget.Theme) void = on_render_default,
after_render: *const fn (ctx: ?*anyopaque, theme: *const Widget.Theme) void = on_render_default,
on_resize: *const fn (ctx: ?*anyopaque, self: *Self, pos_: Widget.Box) void = on_resize_default,
on_layout: *const fn (ctx: ?*anyopaque, self: *Self) Widget.Layout = on_layout_default,
pub fn createH(allocator: Allocator, parent: Plane, name: [:0]const u8, layout_: Layout) error{OutOfMemory}!*Self {
const self = try allocator.create(Self);
@ -71,7 +73,7 @@ pub fn widget(self: *Self) Widget {
}
pub fn layout(self: *Self) Widget.Layout {
return self.layout_;
return self.on_layout(self.ctx, self);
}
pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
@ -214,6 +216,10 @@ fn on_resize_default(_: ?*anyopaque, self: *Self, pos: Widget.Box) void {
self.resize(pos);
}
fn on_layout_default(_: ?*anyopaque, self: *Self) Widget.Layout {
return self.layout_;
}
pub fn resize(self: *Self, pos_: Widget.Box) void {
self.box = pos_;
var pos = pos_;
@ -235,6 +241,7 @@ pub fn resize(self: *Self, pos_: Widget.Box) void {
},
}
}
self.layout_empty = avail == total and dynamics == 0;
const dyn_size = avail / if (dynamics > 0) dynamics else 1;
const rounded: usize = if (dyn_size * dynamics < avail) avail - dyn_size * dynamics else 0;

View file

@ -12,6 +12,7 @@ pub fn create(allocator: std.mem.Allocator, parent: Plane, config: []const u8, s
var w = try WidgetList.createH(allocator, parent, "statusbar", .{ .static = 1 });
if (style == .grip) w.after_render = render_grip;
w.ctx = w;
w.on_layout = on_layout;
var it = std.mem.splitScalar(u8, config, ' ');
while (it.next()) |widget_name| {
try w.add(status_widget.create(widget_name, allocator, w.plane, event_handler) catch |e| switch (e) {
@ -22,6 +23,13 @@ pub fn create(allocator: std.mem.Allocator, parent: Plane, config: []const u8, s
return w.widget();
}
fn on_layout(_: ?*anyopaque, w: *WidgetList) Widget.Layout {
return if (w.layout_empty)
.{ .static = 0 }
else
.{ .static = 1 };
}
fn render_grip(ctx: ?*anyopaque, theme: *const Widget.Theme) void {
const w: *WidgetList = @ptrCast(@alignCast(ctx.?));
if (w.hover()) {

View file

@ -12,9 +12,16 @@ const Button = @import("../Button.zig");
const tui = @import("../tui.zig");
const CreateError = @import("widget.zig").CreateError;
pub fn create(allocator: Allocator, parent: Plane, event_handler: ?EventHandler, _: ?[]const u8) CreateError!Widget {
return Button.create_widget(void, allocator, parent, .{
.ctx = {},
const Style = enum {
plain,
fancy,
};
const default_style = .fancy;
pub fn create(allocator: Allocator, parent: Plane, event_handler: ?EventHandler, arg: ?[]const u8) CreateError!Widget {
const style_ = if (arg) |str_style| std.meta.stringToEnum(Style, str_style) orelse default_style else default_style;
return Button.create_widget(Style, allocator, parent, .{
.ctx = style_,
.label = tui.get_mode(),
.on_click = on_click,
.on_click2 = toggle_panel,
@ -25,7 +32,7 @@ pub fn create(allocator: Allocator, parent: Plane, event_handler: ?EventHandler,
});
}
pub fn layout(_: *void, btn: *Button.State(void)) Widget.Layout {
pub fn layout(_: *Style, btn: *Button.State(Style)) Widget.Layout {
const name = btn.plane.egc_chunk_width(tui.get_mode(), 0, 1);
const logo = if (is_mini_mode() or is_overlay_mode()) 1 else btn.plane.egc_chunk_width(left ++ symbol ++ right, 0, 1);
const padding: usize = 2;
@ -41,9 +48,12 @@ fn is_overlay_mode() bool {
return tui.input_mode_outer() != null;
}
pub fn render(_: *void, self: *Button.State(void), theme: *const Widget.Theme) bool {
pub fn render(ctx: *Style, self: *Button.State(Style), theme: *const Widget.Theme) bool {
const style_base = theme.statusbar;
const style_label = if (self.active) theme.editor_cursor else if (self.hover) theme.editor_selection else theme.statusbar_hover;
const style_label = switch (ctx.*) {
.fancy => if (self.active) theme.editor_cursor else if (self.hover) theme.editor_selection else theme.statusbar_hover,
.plain => if (self.active) theme.editor_cursor else if (self.hover or is_mini_mode()) theme.statusbar_hover else style_base,
};
self.plane.set_base_style(theme.editor);
self.plane.erase();
self.plane.home();
@ -68,7 +78,7 @@ pub fn render(_: *void, self: *Button.State(void), theme: *const Widget.Theme) b
return false;
}
fn render_separator(self: *Button.State(void), theme: *const Widget.Theme) void {
fn render_separator(self: *Button.State(Style), theme: *const Widget.Theme) void {
self.plane.reverse_style();
self.plane.set_base_style(.{ .bg = theme.editor.bg });
if (theme.statusbar.bg) |bg| self.plane.set_style(.{ .bg = bg });
@ -79,15 +89,13 @@ const left = " ";
const symbol = "󱞏";
const right = " ";
fn render_logo(self: *Button.State(void), theme: *const Widget.Theme, base_style: Widget.Theme.Style) void {
// const style_symbol: Widget.Theme.Style = if (tui.find_scope_style(theme, "number")) |sty| .{ .fg = sty.style.fg, .bg = base_style.bg, .fs = base_style.fs } else base_style;
const style_symbol = if (self.active) theme.editor_cursor else if (self.hover) theme.editor_selection else theme.statusbar_hover;
const style_braces: Widget.Theme.Style = if (tui.find_scope_style(theme, "punctuation")) |sty| .{ .fg = sty.style.fg, .bg = base_style.bg, .fs = base_style.fs } else base_style;
fn render_logo(self: *Button.State(Style), theme: *const Widget.Theme, style_label: Widget.Theme.Style) void {
const style_braces: Widget.Theme.Style = if (tui.find_scope_style(theme, "punctuation")) |sty| .{ .fg = sty.style.fg, .bg = style_label.bg, .fs = style_label.fs } else style_label;
if (left.len > 0) {
self.plane.set_style(style_braces);
_ = self.plane.putstr(" " ++ left) catch {};
}
self.plane.set_style(style_symbol);
self.plane.set_style(style_label);
_ = self.plane.putstr(symbol) catch {};
if (right.len > 0) {
self.plane.set_style(style_braces);
@ -95,7 +103,7 @@ fn render_logo(self: *Button.State(void), theme: *const Widget.Theme, base_style
}
}
fn on_click(_: *void, _: *Button.State(void)) void {
fn on_click(_: *Style, _: *Button.State(Style)) void {
if (is_mini_mode()) {
command.executeName("exit_mini_mode", .{}) catch {};
} else if (is_overlay_mode()) {
@ -105,6 +113,6 @@ fn on_click(_: *void, _: *Button.State(void)) void {
}
}
fn toggle_panel(_: *void, _: *Button.State(void)) void {
fn toggle_panel(_: *Style, _: *Button.State(Style)) void {
command.executeName("toggle_panel", .{}) catch {};
}

View file

@ -69,7 +69,7 @@ fn format(self: *Self) void {
sel.normalize();
const lines = sel.end.row - sel.begin.row;
if (lines == 0) {
std.fmt.format(writer, "({d} selected)", .{sel.end.col - sel.begin.col}) catch {};
std.fmt.format(writer, "({d} columns selected)", .{sel.end.col - sel.begin.col}) catch {};
} else {
std.fmt.format(writer, "({d} lines selected)", .{if (sel.end.col == 0) lines else lines + 1}) catch {};
}

View file

@ -11,7 +11,11 @@ const Widget = @import("../Widget.zig");
const WidgetList = @import("../WidgetList.zig");
const Button = @import("../Button.zig");
const default_min_tabs = 2;
const @"style.config" = struct {
default_minimum_tabs_shown: usize = 2,
dirty_indicator: []const u8 = "",
spacer: []const u8 = "|",
@ -52,10 +56,11 @@ const @"style.config" = struct {
};
pub const Style = @"style.config";
pub fn create(allocator: std.mem.Allocator, parent: Plane, event_handler: ?EventHandler, _: ?[]const u8) @import("widget.zig").CreateError!Widget {
pub fn create(allocator: std.mem.Allocator, parent: Plane, event_handler: ?EventHandler, arg: ?[]const u8) @import("widget.zig").CreateError!Widget {
const min_tabs = if (arg) |str_size| std.fmt.parseInt(usize, str_size, 10) catch null else null;
const self = try allocator.create(TabBar);
errdefer allocator.destroy(self);
self.* = try TabBar.init(allocator, parent, event_handler);
self.* = try TabBar.init(allocator, parent, event_handler, min_tabs);
return Widget.to(self);
}
@ -67,6 +72,7 @@ const TabBar = struct {
event_handler: ?EventHandler,
tabs: []TabBarTab = &[_]TabBarTab{},
active_buffer_ref: ?usize = null,
minimum_tabs_shown: usize,
tab_style: Style,
tab_style_bufs: [][]const u8,
@ -78,7 +84,7 @@ const TabBar = struct {
widget: Widget,
};
fn init(allocator: std.mem.Allocator, parent: Plane, event_handler: ?EventHandler) !Self {
fn init(allocator: std.mem.Allocator, parent: Plane, event_handler: ?EventHandler, min_tabs: ?usize) !Self {
var w = try WidgetList.createH(allocator, parent, "tabs", .dynamic);
w.ctx = w;
const tab_style, const tab_style_bufs = root.read_config(Style, allocator);
@ -90,6 +96,7 @@ const TabBar = struct {
.event_handler = event_handler,
.tab_style = tab_style,
.tab_style_bufs = tab_style_bufs,
.minimum_tabs_shown = min_tabs orelse tab_style.default_minimum_tabs_shown,
};
}
@ -101,7 +108,10 @@ const TabBar = struct {
}
pub fn layout(self: *Self) Widget.Layout {
return self.widget_list_widget.layout();
return if (self.tabs.len >= self.minimum_tabs_shown)
self.widget_list_widget.layout()
else
.{ .static = 0 };
}
pub fn update(self: *Self) void {