Add time_format config for custom strftime strings
This lets the user change to any time format they want in the bar. As part of this, we also change the bar to re-draw every second (to allow using seconds in the time format string).
This commit is contained in:
parent
efd0222899
commit
0e7d652d24
7 changed files with 50 additions and 11 deletions
|
|
@ -147,6 +147,7 @@ bar {
|
||||||
| `position` | enum | `top` | Bar position (`top` or `bottom`) |
|
| `position` | enum | `top` | Bar position (`top` or `bottom`) |
|
||||||
| `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) |
|
||||||
|
|
||||||
### Margins
|
### Margins
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ These are in rough order of my priority, though no promises I do them in this or
|
||||||
- [ ] Support configuring bar item positions (left/center/right)
|
- [ ] 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 12-hour clock format (maybe take any time format string?)
|
|
||||||
- [ ] Support per-output bar visibility
|
- [ ] Support per-output bar visibility
|
||||||
- [ ] Support more window rule options (e.g. ssd/csd)
|
- [ ] Support more window rule options (e.g. ssd/csd)
|
||||||
- [ ] Support solid `background-color` fallback (no wallpaper)
|
- [ ] Support solid `background-color` fallback (no wallpaper)
|
||||||
|
|
@ -50,3 +49,4 @@ These are in rough order of my priority, though no promises I do them in this or
|
||||||
- [x] Support configuring primary vs secondary stack side
|
- [x] Support configuring primary vs secondary stack side
|
||||||
- [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?)
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ borders {
|
||||||
// Bar widget; shows the time
|
// Bar widget; shows the time
|
||||||
bar {
|
bar {
|
||||||
position top
|
position top
|
||||||
|
time_format "%H:%M"
|
||||||
}
|
}
|
||||||
// Tag overlay widget; shown briefly when switching tags
|
// Tag overlay widget; shown briefly when switching tags
|
||||||
// Remove this block to disable the overlay entirely
|
// Remove this block to disable the overlay entirely
|
||||||
|
|
|
||||||
|
|
@ -143,6 +143,11 @@ bar {
|
||||||
*horizontal_padding* _pixels_
|
*horizontal_padding* _pixels_
|
||||||
Horizontal padding between bar edges and text. (Default: 5)
|
Horizontal padding between bar edges and text. (Default: 5)
|
||||||
|
|
||||||
|
*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")
|
||||||
|
|
||||||
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.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,9 @@ const Bar = @This();
|
||||||
/// Standard base DPI at a scale of 1
|
/// Standard base DPI at a scale of 1
|
||||||
const base_dpi = 96;
|
const base_dpi = 96;
|
||||||
|
|
||||||
|
/// Default strftime string to use for the clock
|
||||||
|
pub const default_time_format = "%Y-%m-%d %H:%M, %A";
|
||||||
|
|
||||||
context: *Context,
|
context: *Context,
|
||||||
|
|
||||||
/// The timezone of the computer.
|
/// The timezone of the computer.
|
||||||
|
|
@ -61,6 +64,9 @@ pub const Options = struct {
|
||||||
vertical_padding: u8 = 5,
|
vertical_padding: u8 = 5,
|
||||||
/// Horizontal padding between bar edges and content, in pixels
|
/// Horizontal padding between bar edges and content, in pixels
|
||||||
horizontal_padding: u8 = 5,
|
horizontal_padding: u8 = 5,
|
||||||
|
|
||||||
|
/// strftime format string for the clock display
|
||||||
|
time_format: []const u8 = default_time_format,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn init(context: *Context, output: *Output, options: Options) !Bar {
|
pub fn init(context: *Context, output: *Output, options: Options) !Bar {
|
||||||
|
|
@ -240,7 +246,7 @@ pub fn draw(bar: *Bar) !void {
|
||||||
// Convert time to a string
|
// 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, "%H:%M");
|
try dt.strftime(&time_writer, options.time_format);
|
||||||
|
|
||||||
// Convert date string to Unicode codepoints
|
// Convert date string to Unicode codepoints
|
||||||
const time_codepoints = try utils.utf8ToCodepoints(time_writer.buffered());
|
const time_codepoints = try utils.utf8ToCodepoints(time_writer.buffered());
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ const NodeName = enum {
|
||||||
vertical_padding,
|
vertical_padding,
|
||||||
horizontal_padding,
|
horizontal_padding,
|
||||||
margins,
|
margins,
|
||||||
|
time_format,
|
||||||
};
|
};
|
||||||
const MarginsNodeName = enum { top, right, bottom, left };
|
const MarginsNodeName = enum { top, right, bottom, left };
|
||||||
|
|
||||||
|
|
@ -38,6 +39,10 @@ vertical_padding: u8 = 5,
|
||||||
/// Horizontal padding between bar edges and content, in pixels
|
/// Horizontal padding between bar edges and content, in pixels
|
||||||
horizontal_padding: u8 = 5,
|
horizontal_padding: u8 = 5,
|
||||||
|
|
||||||
|
/// strftime format string for the clock display.
|
||||||
|
/// null means use the default.
|
||||||
|
time_format: ?[]const u8 = null,
|
||||||
|
|
||||||
pub fn toBarOptions(config: BarConfig) Bar.Options {
|
pub fn toBarOptions(config: BarConfig) Bar.Options {
|
||||||
return .{
|
return .{
|
||||||
.fonts = config.fonts orelse "monospace:size=14",
|
.fonts = config.fonts orelse "monospace:size=14",
|
||||||
|
|
@ -52,6 +57,7 @@ pub fn toBarOptions(config: BarConfig) Bar.Options {
|
||||||
},
|
},
|
||||||
.vertical_padding = config.vertical_padding,
|
.vertical_padding = config.vertical_padding,
|
||||||
.horizontal_padding = config.horizontal_padding,
|
.horizontal_padding = config.horizontal_padding,
|
||||||
|
.time_format = config.time_format orelse Bar.default_time_format,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -92,7 +98,18 @@ pub fn load(config: *Config, parser: *kdl.Parser, hostname: ?[]const u8) !void {
|
||||||
logWarnInvalidNodeArg(name, val_str);
|
logWarnInvalidNodeArg(name, val_str);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
.time_format => {
|
||||||
|
if (node.argcount() < 1) {
|
||||||
|
logWarnMissingNodeArg(name, "format string");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (validateTimeFormat(val_str)) {
|
||||||
|
config.bar_config.?.time_format = utils.gpa.dupe(u8, val_str) catch @panic("Out of memory");
|
||||||
|
logDebugSettingNode(name, val_str);
|
||||||
|
} else {
|
||||||
|
logWarnInvalidNodeArg(name, val_str);
|
||||||
|
}
|
||||||
|
},
|
||||||
.margins => next_child_block = .margins,
|
.margins => next_child_block = .margins,
|
||||||
inline .background_color,
|
inline .background_color,
|
||||||
.text_color,
|
.text_color,
|
||||||
|
|
@ -192,15 +209,26 @@ inline fn logWarnInvalidNodeArg(node_name: anytype, node_value: []const u8) void
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn validateTimeFormat(format: []const u8) bool {
|
||||||
|
// Try formatting with a dummy time to validate the format string
|
||||||
|
var buf: [255]u8 = undefined;
|
||||||
|
var writer = Io.Writer.fixed(&buf);
|
||||||
|
const dummy_time = zeit.Time{};
|
||||||
|
dummy_time.strftime(&writer, format) catch return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
inline fn logWarnMissingNodeArg(node_name: NodeName, comptime arg: []const u8) void {
|
inline fn logWarnMissingNodeArg(node_name: NodeName, comptime arg: []const u8) void {
|
||||||
log.warn("\"bar.{s}\" missing " ++ arg ++ " argument. Ignoring", .{@tagName(node_name)});
|
log.warn("\"bar.{s}\" missing " ++ arg ++ " argument. Ignoring", .{@tagName(node_name)});
|
||||||
}
|
}
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const fmt = std.fmt;
|
const fmt = std.fmt;
|
||||||
|
const Io = std.Io;
|
||||||
|
|
||||||
const kdl = @import("kdl");
|
const kdl = @import("kdl");
|
||||||
const pixman = @import("pixman");
|
const pixman = @import("pixman");
|
||||||
|
const zeit = @import("zeit");
|
||||||
|
|
||||||
const utils = @import("../utils.zig");
|
const utils = @import("../utils.zig");
|
||||||
const Bar = @import("../Bar.zig");
|
const Bar = @import("../Bar.zig");
|
||||||
|
|
|
||||||
14
src/main.zig
14
src/main.zig
|
|
@ -129,19 +129,17 @@ fn run(wl_display: *wl.Display, context: *Context) !void {
|
||||||
fatal("wl_display flush failed: E{s}", .{@tagName(errno)});
|
fatal("wl_display flush failed: E{s}", .{@tagName(errno)});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the number of milliseconds to the top of the next minute
|
// Get the number of milliseconds to the top of the next second
|
||||||
const time = std.time.timestamp();
|
const time_ns = std.time.nanoTimestamp();
|
||||||
if (time < 0) {
|
const ns_per_sec = std.time.ns_per_s;
|
||||||
log.err("Got a negative time ({d})", .{time});
|
const remainder_ns = @mod(time_ns, ns_per_sec);
|
||||||
return error.InvalidTime;
|
const timeout: i32 = @intCast(@divFloor(ns_per_sec - remainder_ns, std.time.ns_per_ms));
|
||||||
}
|
|
||||||
const timeout: i32 = @intCast((@divFloor(time, 60) * 60 + 60 - time) * 1000);
|
|
||||||
|
|
||||||
const poll_rc = posix.poll(&pollfds, timeout) catch |err| {
|
const poll_rc = posix.poll(&pollfds, timeout) catch |err| {
|
||||||
fatal("Failed to poll {s}", .{@errorName(err)});
|
fatal("Failed to poll {s}", .{@errorName(err)});
|
||||||
};
|
};
|
||||||
if (poll_rc == 0) {
|
if (poll_rc == 0) {
|
||||||
// If poll returns 0, it timed out, meaning we hit the top of the minute
|
// If poll returns 0, it timed out, meaning we hit the top of the next second
|
||||||
// and need to update the clock.
|
// and need to update the clock.
|
||||||
var it = context.wm.outputs.iterator(.forward);
|
var it = context.wm.outputs.iterator(.forward);
|
||||||
while (it.next()) |output| {
|
while (it.next()) |output| {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue