Implement configurable component locations in bar
This allows the user to configure which component (title, wm_info, clock) is rendered to which part of the bar (left, right, center). You can also use `none` to hide the location.
This commit is contained in:
parent
bce58855ab
commit
040ccc14f3
5 changed files with 104 additions and 68 deletions
|
|
@ -126,9 +126,8 @@ do not re-trigger rules.
|
||||||
|
|
||||||
## Bar
|
## Bar
|
||||||
|
|
||||||
The bar is an optional widget that shows the focused window title on the left,
|
The bar is an optional widget that displays configurable components in three
|
||||||
the date/time in the center, and layout info (primary count/ratio and visible
|
slots: left, center, and right. It is only created when a `bar` block is present in
|
||||||
window count) on the right. It is only created when a `bar` block is present in
|
|
||||||
the config. All settings have defaults, with the color based on the Catppuccin
|
the config. All settings have defaults, with the color based on the Catppuccin
|
||||||
Mocha theme. An empty block can be used to enable the widget with all defaults:
|
Mocha theme. An empty block can be used to enable the widget with all defaults:
|
||||||
|
|
||||||
|
|
@ -145,9 +144,12 @@ bar {
|
||||||
| `text_color` | color | `0xcdd6f4` | Text color |
|
| `text_color` | color | `0xcdd6f4` | Text color |
|
||||||
| `background_color` | color | `0x1e1e2e` | Background color |
|
| `background_color` | color | `0x1e1e2e` | Background color |
|
||||||
| `position` | enum | `top` | Bar position (`top` or `bottom`) |
|
| `position` | enum | `top` | Bar position (`top` or `bottom`) |
|
||||||
|
| `left` | enum | `title` | Component in the left slot (`title`, `clock`, `wm_info`, `none`) |
|
||||||
|
| `center` | enum | `clock` | Component in the center slot (`title`, `clock`, `wm_info`, `none`) |
|
||||||
|
| `right` | enum | `wm_info` | Component in the right slot (`title`, `clock`, `wm_info`, `none`) |
|
||||||
| `vertical_padding` | u8 | `5` | Vertical padding above and below text |
|
| `vertical_padding` | u8 | `5` | Vertical padding above and below text |
|
||||||
| `horizontal_padding` | u8 | `5` | Horizontal padding between bar edges and text |
|
| `horizontal_padding` | u8 | `5` | Horizontal padding between bar edges and text |
|
||||||
| `time_format` | string | `%Y-%m-%d %H:%M, %A` | strftime format string for the clock display (an empty string hides the clock) |
|
| `time_format` | string | `%Y-%m-%d %H:%M, %A` | strftime format string for the clock display |
|
||||||
|
|
||||||
### Margins
|
### Margins
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,12 @@
|
||||||
|
|
||||||
These are in rough order of my priority, though no promises I do them in this order.
|
These are in rough order of my priority, though no promises I do them in this order.
|
||||||
|
|
||||||
|
- [ ] Support window tag/order caching between WM restarts (within a river session)
|
||||||
- [ ] Add gap support
|
- [ ] Add gap support
|
||||||
- [ ] Add build-time options for including the wallpaper (and maybe bar)
|
- [ ] Add build-time options for including the wallpaper (and maybe bar)
|
||||||
- [ ] Check pointer position and only warp if not on focused window already
|
- [ ] Check pointer position and only warp if not on focused window already
|
||||||
- [ ] Change focus direction when closing window
|
- [ ] Change focus direction when closing window
|
||||||
- [ ] Use set_xcursor_theme request
|
- [ ] Use set_xcursor_theme request
|
||||||
- [ ] Support configuring bar item positions (left/center/right)
|
|
||||||
- [ ] Support overriding config location
|
- [ ] Support overriding config location
|
||||||
- [ ] Add support for center-primary layout
|
- [ ] Add support for center-primary layout
|
||||||
- [ ] Support per-output bar visibility
|
- [ ] Support per-output bar visibility
|
||||||
|
|
@ -50,3 +50,4 @@ These are in rough order of my priority, though no promises I do them in this or
|
||||||
- [x] Add focused window title to bar
|
- [x] Add focused window title to bar
|
||||||
- [x] Add bar padding to config
|
- [x] Add bar padding to config
|
||||||
- [x] Support 12-hour clock format (maybe take any time format string?)
|
- [x] Support 12-hour clock format (maybe take any time format string?)
|
||||||
|
- [x] Support configuring bar item positions (left/center/right)
|
||||||
|
|
|
||||||
|
|
@ -114,9 +114,8 @@ initialization do not re-trigger rules.
|
||||||
|
|
||||||
# BAR
|
# BAR
|
||||||
|
|
||||||
The bar is an optional widget that shows the focused window title on the left,
|
The bar is an optional widget that displays configurable components in three
|
||||||
the date/time in the center, and layout info (primary count/ratio and visible
|
slots: left, center, and right. It is only created when a *bar* block is present
|
||||||
window count) on the right. It is only created when a *bar* block is present
|
|
||||||
in the config:
|
in the config:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
@ -137,6 +136,15 @@ bar {
|
||||||
*position* *top*|*bottom*
|
*position* *top*|*bottom*
|
||||||
Bar position. (Default: *top*)
|
Bar position. (Default: *top*)
|
||||||
|
|
||||||
|
*left* *title*|*clock*|*wm_info*|*none*
|
||||||
|
Component shown in the left slot. (Default: *title*)
|
||||||
|
|
||||||
|
*center* *title*|*clock*|*wm_info*|*none*
|
||||||
|
Component shown in the center slot. (Default: *clock*)
|
||||||
|
|
||||||
|
*right* *title*|*clock*|*wm_info*|*none*
|
||||||
|
Component shown in the right slot. (Default: *wm_info*)
|
||||||
|
|
||||||
*vertical_padding* _pixels_
|
*vertical_padding* _pixels_
|
||||||
Vertical padding above and below text. (Default: 5)
|
Vertical padding above and below text. (Default: 5)
|
||||||
|
|
||||||
|
|
@ -145,8 +153,7 @@ bar {
|
||||||
|
|
||||||
*time_format* _format_
|
*time_format* _format_
|
||||||
strftime format string for the clock display. Invalid format strings
|
strftime format string for the clock display. Invalid format strings
|
||||||
are ignored and the default is used instead. Set to an empty string
|
are ignored and the default is used instead. (Default: "%Y-%m-%d %H:%M, %A")
|
||||||
to hide the clock. (Default: "%Y-%m-%d %H:%M, %A")
|
|
||||||
|
|
||||||
The bar also supports *margins* and *anchors* child blocks; see *TAG OVERLAY*
|
The bar also supports *margins* and *anchors* child blocks; see *TAG OVERLAY*
|
||||||
for their format.
|
for their format.
|
||||||
|
|
|
||||||
120
src/Bar.zig
120
src/Bar.zig
|
|
@ -46,6 +46,7 @@ pub const PendingRender = struct {
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Position = enum { top, bottom };
|
pub const Position = enum { top, bottom };
|
||||||
|
pub const Component = enum { title, clock, wm_info, none };
|
||||||
|
|
||||||
pub const Options = struct {
|
pub const Options = struct {
|
||||||
/// Comma separated list of FontConfig formatted font specifications
|
/// Comma separated list of FontConfig formatted font specifications
|
||||||
|
|
@ -67,6 +68,13 @@ pub const Options = struct {
|
||||||
|
|
||||||
/// strftime format string for the clock display
|
/// strftime format string for the clock display
|
||||||
time_format: []const u8 = default_time_format,
|
time_format: []const u8 = default_time_format,
|
||||||
|
|
||||||
|
/// Which component to show on the left side of the bar
|
||||||
|
left: Component = .title,
|
||||||
|
/// Which component to show in the center of the bar
|
||||||
|
center: Component = .clock,
|
||||||
|
/// Which component to show on the right side of the bar
|
||||||
|
right: Component = .wm_info,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn init(context: *Context, output: *Output, options: Options) !Bar {
|
pub fn init(context: *Context, output: *Output, options: Options) !Bar {
|
||||||
|
|
@ -234,55 +242,26 @@ pub fn draw(bar: *Bar) !void {
|
||||||
// Y is shared between all components
|
// Y is shared between all components
|
||||||
const y: i32 = @divFloor(buffer.height - bar.fcft_fonts.height, 2);
|
const y: i32 = @divFloor(buffer.height - bar.fcft_fonts.height, 2);
|
||||||
|
|
||||||
// Get the current time in seconds since the epoch,
|
// Pre-compute codepoints for each component type
|
||||||
// then load the local timezone,
|
|
||||||
// then convert `now` to the `local` timezone
|
|
||||||
const now = try zeit.instant(.{});
|
|
||||||
const now_local = now.in(&bar.timezone);
|
|
||||||
|
|
||||||
// Generate date/time info for this instant
|
// Clock
|
||||||
const dt = now_local.time();
|
|
||||||
|
|
||||||
// Convert time to a string
|
|
||||||
var time_buf: [255:0]u8 = undefined;
|
var time_buf: [255:0]u8 = undefined;
|
||||||
var time_writer = Io.Writer.fixed(&time_buf);
|
var time_writer = Io.Writer.fixed(&time_buf);
|
||||||
try dt.strftime(&time_writer, options.time_format);
|
const now = try zeit.instant(.{});
|
||||||
|
const now_local = now.in(&bar.timezone);
|
||||||
|
try now_local.time().strftime(&time_writer, options.time_format);
|
||||||
|
const clock_codepoints = try utils.utf8ToCodepoints(time_writer.buffered());
|
||||||
|
defer utils.gpa.free(clock_codepoints);
|
||||||
|
|
||||||
// Convert date string to Unicode codepoints
|
// Title (empty string if no focused window)
|
||||||
const time_codepoints = try utils.utf8ToCodepoints(time_writer.buffered());
|
const focused_title: []const u8 = if (context.wm.seats.first()) |seat|
|
||||||
defer utils.gpa.free(time_codepoints);
|
if (seat.focused_window) |window| window.title orelse "" else ""
|
||||||
|
else
|
||||||
|
"";
|
||||||
|
const title_codepoints = try utils.utf8ToCodepoints(focused_title);
|
||||||
|
defer utils.gpa.free(title_codepoints);
|
||||||
|
|
||||||
// Get the width of the date string so we can truncate title
|
// WM info
|
||||||
const center_width = try bar.textWidth(time_codepoints);
|
|
||||||
// X changes
|
|
||||||
var center_x: i32 = @divFloor(buffer.width - center_width, 2);
|
|
||||||
|
|
||||||
// Write title of focused window to the left side of the bar
|
|
||||||
if (context.wm.seats.first()) |seat| {
|
|
||||||
if (seat.focused_window) |window| {
|
|
||||||
if (window.title) |title| {
|
|
||||||
if (title.len > 0) {
|
|
||||||
const title_codepoints = try utils.utf8ToCodepoints(title);
|
|
||||||
defer utils.gpa.free(title_codepoints);
|
|
||||||
|
|
||||||
const max_left_width = center_x - 2 * options.horizontal_padding;
|
|
||||||
const truncated_codepoints = try bar.truncateToWidth(title_codepoints, max_left_width);
|
|
||||||
|
|
||||||
var left_x: i32 = options.horizontal_padding;
|
|
||||||
|
|
||||||
try bar.renderChars(
|
|
||||||
truncated_codepoints,
|
|
||||||
buffer,
|
|
||||||
&left_x,
|
|
||||||
y,
|
|
||||||
text_color,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Put WM info on the right side of the bar
|
|
||||||
const output = bar.output;
|
const output = bar.output;
|
||||||
var wm_info_buf: [255:0]u8 = undefined;
|
var wm_info_buf: [255:0]u8 = undefined;
|
||||||
var wm_info_writer = Io.Writer.fixed(&wm_info_buf);
|
var wm_info_writer = Io.Writer.fixed(&wm_info_buf);
|
||||||
|
|
@ -296,23 +275,48 @@ pub fn draw(bar: *Bar) !void {
|
||||||
const wm_info_codepoints = try utils.utf8ToCodepoints(wm_info_writer.buffered());
|
const wm_info_codepoints = try utils.utf8ToCodepoints(wm_info_writer.buffered());
|
||||||
defer utils.gpa.free(wm_info_codepoints);
|
defer utils.gpa.free(wm_info_codepoints);
|
||||||
|
|
||||||
const max_right_width = buffer.width - (center_x + center_width) - 2 * options.horizontal_padding;
|
// Map a Component to its pre-computed codepoints slice
|
||||||
const right_truncated = try bar.truncateToWidth(wm_info_codepoints, max_right_width);
|
const componentSlice = struct {
|
||||||
const right_text_width = try bar.textWidth(right_truncated);
|
fn f(component: Component, clock: []u32, title: []u32, wm_info: []u32) []u32 {
|
||||||
|
return switch (component) {
|
||||||
|
.clock => clock,
|
||||||
|
.title => title,
|
||||||
|
.wm_info => wm_info,
|
||||||
|
.none => &.{},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}.f;
|
||||||
|
|
||||||
var right_x: i32 = buffer.width - right_text_width - options.horizontal_padding;
|
// Measure center first — needed to constrain left and right widths
|
||||||
try bar.renderChars(
|
const center_codepoints = componentSlice(options.center, clock_codepoints, title_codepoints, wm_info_codepoints);
|
||||||
right_truncated,
|
const center_width = try bar.textWidth(center_codepoints);
|
||||||
buffer,
|
var center_x: i32 = @divFloor(buffer.width - center_width, 2);
|
||||||
&right_x,
|
|
||||||
y,
|
|
||||||
text_color,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Finally, put the time in the center of the bar
|
// Render left slot
|
||||||
try bar.renderChars(time_codepoints, buffer, ¢er_x, y, text_color);
|
const left_codepoints = componentSlice(options.left, clock_codepoints, title_codepoints, wm_info_codepoints);
|
||||||
|
if (left_codepoints.len > 0) {
|
||||||
|
const max_width = center_x - 2 * options.horizontal_padding;
|
||||||
|
const truncated = try bar.truncateToWidth(left_codepoints, max_width);
|
||||||
|
var x: i32 = options.horizontal_padding;
|
||||||
|
try bar.renderChars(truncated, buffer, &x, y, text_color);
|
||||||
|
}
|
||||||
|
|
||||||
// Really finally, attach the buffer to the surface
|
// Render right slot
|
||||||
|
const right_codepoints = componentSlice(options.right, clock_codepoints, title_codepoints, wm_info_codepoints);
|
||||||
|
if (right_codepoints.len > 0) {
|
||||||
|
const max_width = buffer.width - (center_x + center_width) - 2 * options.horizontal_padding;
|
||||||
|
const truncated = try bar.truncateToWidth(right_codepoints, max_width);
|
||||||
|
const text_width = try bar.textWidth(truncated);
|
||||||
|
var x: i32 = buffer.width - text_width - options.horizontal_padding;
|
||||||
|
try bar.renderChars(truncated, buffer, &x, y, text_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render center slot
|
||||||
|
if (center_codepoints.len > 0) {
|
||||||
|
try bar.renderChars(center_codepoints, buffer, ¢er_x, y, text_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attach the buffer to the surface
|
||||||
const surfaces = bar.surfaces orelse return error.NoSurfaces;
|
const surfaces = bar.surfaces orelse return error.NoSurfaces;
|
||||||
const wl_surface = surfaces.wl_surface;
|
const wl_surface = surfaces.wl_surface;
|
||||||
// sync_next_commit ensures frame-perfect application
|
// sync_next_commit ensures frame-perfect application
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,9 @@ const NodeName = enum {
|
||||||
text_color,
|
text_color,
|
||||||
background_color,
|
background_color,
|
||||||
position,
|
position,
|
||||||
|
left,
|
||||||
|
center,
|
||||||
|
right,
|
||||||
vertical_padding,
|
vertical_padding,
|
||||||
horizontal_padding,
|
horizontal_padding,
|
||||||
margins,
|
margins,
|
||||||
|
|
@ -25,6 +28,13 @@ background_color: pixman.Color = utils.parseRgbaPixmanComptime("0x1e1e2e"),
|
||||||
/// Whether the bar is at the top or bottom of the screen
|
/// Whether the bar is at the top or bottom of the screen
|
||||||
position: Bar.Position = .top,
|
position: Bar.Position = .top,
|
||||||
|
|
||||||
|
/// Which component to show on the left side of the bar
|
||||||
|
left: Bar.Component = .title,
|
||||||
|
/// Which component to show in the center of the bar
|
||||||
|
center: Bar.Component = .clock,
|
||||||
|
/// Which component to show on the right side of the bar
|
||||||
|
right: Bar.Component = .wm_info,
|
||||||
|
|
||||||
/// Margin above the top of the bar and another element (a window or the top of the output)
|
/// Margin above the top of the bar and another element (a window or the top of the output)
|
||||||
margin_top: u8 = 0,
|
margin_top: u8 = 0,
|
||||||
/// Margin above the right of the bar and another element (a window or the top of the output)
|
/// Margin above the right of the bar and another element (a window or the top of the output)
|
||||||
|
|
@ -49,6 +59,9 @@ pub fn toBarOptions(config: BarConfig) Bar.Options {
|
||||||
.text_color = config.text_color,
|
.text_color = config.text_color,
|
||||||
.background_color = config.background_color,
|
.background_color = config.background_color,
|
||||||
.position = config.position,
|
.position = config.position,
|
||||||
|
.left = config.left,
|
||||||
|
.center = config.center,
|
||||||
|
.right = config.right,
|
||||||
.margins = .{
|
.margins = .{
|
||||||
.top = config.margin_top,
|
.top = config.margin_top,
|
||||||
.right = config.margin_right,
|
.right = config.margin_right,
|
||||||
|
|
@ -129,6 +142,15 @@ pub fn load(config: *Config, parser: *kdl.Parser, hostname: ?[]const u8) !void {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
@field(config.bar_config.?, @tagName(tag)) = padding;
|
@field(config.bar_config.?, @tagName(tag)) = padding;
|
||||||
|
logDebugSettingNode(name, padding_str);
|
||||||
|
},
|
||||||
|
inline .left, .center, .right => |tag| {
|
||||||
|
if (std.meta.stringToEnum(Bar.Component, val_str)) |component| {
|
||||||
|
@field(config.bar_config.?, @tagName(tag)) = component;
|
||||||
|
logDebugSettingNode(name, val_str);
|
||||||
|
} else {
|
||||||
|
logWarnInvalidNodeArg(name, val_str);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue