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
|
||||
|
||||
The bar is an optional widget that shows the focused window title on the left,
|
||||
the date/time in the center, and layout info (primary count/ratio and visible
|
||||
window count) on the right. It is only created when a `bar` block is present in
|
||||
The bar is an optional widget that displays configurable components in three
|
||||
slots: left, center, and 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
|
||||
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 |
|
||||
| `background_color` | color | `0x1e1e2e` | Background color |
|
||||
| `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 |
|
||||
| `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
|
||||
|
||||
|
|
|
|||
|
|
@ -2,12 +2,12 @@
|
|||
|
||||
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 build-time options for including the wallpaper (and maybe bar)
|
||||
- [ ] Check pointer position and only warp if not on focused window already
|
||||
- [ ] Change focus direction when closing window
|
||||
- [ ] Use set_xcursor_theme request
|
||||
- [ ] Support configuring bar item positions (left/center/right)
|
||||
- [ ] Support overriding config location
|
||||
- [ ] Add support for center-primary layout
|
||||
- [ ] 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 bar padding to config
|
||||
- [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
|
||||
|
||||
The bar is an optional widget that shows the focused window title on the left,
|
||||
the date/time in the center, and layout info (primary count/ratio and visible
|
||||
window count) on the right. It is only created when a *bar* block is present
|
||||
The bar is an optional widget that displays configurable components in three
|
||||
slots: left, center, and right. It is only created when a *bar* block is present
|
||||
in the config:
|
||||
|
||||
```
|
||||
|
|
@ -137,6 +136,15 @@ bar {
|
|||
*position* *top*|*bottom*
|
||||
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 above and below text. (Default: 5)
|
||||
|
||||
|
|
@ -145,8 +153,7 @@ bar {
|
|||
|
||||
*time_format* _format_
|
||||
strftime format string for the clock display. Invalid format strings
|
||||
are ignored and the default is used instead. Set to an empty string
|
||||
to hide the clock. (Default: "%Y-%m-%d %H:%M, %A")
|
||||
are ignored and the default is used instead. (Default: "%Y-%m-%d %H:%M, %A")
|
||||
|
||||
The bar also supports *margins* and *anchors* child blocks; see *TAG OVERLAY*
|
||||
for their format.
|
||||
|
|
|
|||
118
src/Bar.zig
118
src/Bar.zig
|
|
@ -46,6 +46,7 @@ pub const PendingRender = struct {
|
|||
};
|
||||
|
||||
pub const Position = enum { top, bottom };
|
||||
pub const Component = enum { title, clock, wm_info, none };
|
||||
|
||||
pub const Options = struct {
|
||||
/// Comma separated list of FontConfig formatted font specifications
|
||||
|
|
@ -67,6 +68,13 @@ pub const Options = struct {
|
|||
|
||||
/// strftime format string for the clock display
|
||||
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 {
|
||||
|
|
@ -234,55 +242,26 @@ pub fn draw(bar: *Bar) !void {
|
|||
// Y is shared between all components
|
||||
const y: i32 = @divFloor(buffer.height - bar.fcft_fonts.height, 2);
|
||||
|
||||
// Get the current time in seconds since the epoch,
|
||||
// then load the local timezone,
|
||||
// then convert `now` to the `local` timezone
|
||||
const now = try zeit.instant(.{});
|
||||
const now_local = now.in(&bar.timezone);
|
||||
// Pre-compute codepoints for each component type
|
||||
|
||||
// Generate date/time info for this instant
|
||||
const dt = now_local.time();
|
||||
|
||||
// Convert time to a string
|
||||
// Clock
|
||||
var time_buf: [255:0]u8 = undefined;
|
||||
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
|
||||
const time_codepoints = try utils.utf8ToCodepoints(time_writer.buffered());
|
||||
defer utils.gpa.free(time_codepoints);
|
||||
|
||||
// Get the width of the date string so we can truncate title
|
||||
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);
|
||||
// Title (empty string if no focused window)
|
||||
const focused_title: []const u8 = if (context.wm.seats.first()) |seat|
|
||||
if (seat.focused_window) |window| window.title orelse "" else ""
|
||||
else
|
||||
"";
|
||||
const title_codepoints = try utils.utf8ToCodepoints(focused_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
|
||||
// WM info
|
||||
const output = bar.output;
|
||||
var wm_info_buf: [255:0]u8 = undefined;
|
||||
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());
|
||||
defer utils.gpa.free(wm_info_codepoints);
|
||||
|
||||
const max_right_width = buffer.width - (center_x + center_width) - 2 * options.horizontal_padding;
|
||||
const right_truncated = try bar.truncateToWidth(wm_info_codepoints, max_right_width);
|
||||
const right_text_width = try bar.textWidth(right_truncated);
|
||||
// Map a Component to its pre-computed codepoints slice
|
||||
const componentSlice = struct {
|
||||
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;
|
||||
try bar.renderChars(
|
||||
right_truncated,
|
||||
buffer,
|
||||
&right_x,
|
||||
y,
|
||||
text_color,
|
||||
);
|
||||
// Measure center first — needed to constrain left and right widths
|
||||
const center_codepoints = componentSlice(options.center, clock_codepoints, title_codepoints, wm_info_codepoints);
|
||||
const center_width = try bar.textWidth(center_codepoints);
|
||||
var center_x: i32 = @divFloor(buffer.width - center_width, 2);
|
||||
|
||||
// Finally, put the time in the center of the bar
|
||||
try bar.renderChars(time_codepoints, buffer, ¢er_x, y, text_color);
|
||||
// Render left slot
|
||||
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 wl_surface = surfaces.wl_surface;
|
||||
// sync_next_commit ensures frame-perfect application
|
||||
|
|
|
|||
|
|
@ -9,6 +9,9 @@ const NodeName = enum {
|
|||
text_color,
|
||||
background_color,
|
||||
position,
|
||||
left,
|
||||
center,
|
||||
right,
|
||||
vertical_padding,
|
||||
horizontal_padding,
|
||||
margins,
|
||||
|
|
@ -25,6 +28,13 @@ background_color: pixman.Color = utils.parseRgbaPixmanComptime("0x1e1e2e"),
|
|||
/// Whether the bar is at the top or bottom of the screen
|
||||
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_top: u8 = 0,
|
||||
/// 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,
|
||||
.background_color = config.background_color,
|
||||
.position = config.position,
|
||||
.left = config.left,
|
||||
.center = config.center,
|
||||
.right = config.right,
|
||||
.margins = .{
|
||||
.top = config.margin_top,
|
||||
.right = config.margin_right,
|
||||
|
|
@ -129,6 +142,15 @@ pub fn load(config: *Config, parser: *kdl.Parser, hostname: ?[]const u8) !void {
|
|||
continue;
|
||||
};
|
||||
@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 {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue