cli/list-themes: add ability to search theme names

This commit is contained in:
Jeffrey C. Ollie
2024-09-25 12:07:14 -05:00
committed by Mitchell Hashimoto
parent e313352c1f
commit a969364f93
4 changed files with 434 additions and 290 deletions

View File

@ -1043,6 +1043,10 @@ fn addDeps(
.target = target, .target = target,
.optimize = optimize, .optimize = optimize,
}); });
const zf_dep = b.dependency("zf", .{
.target = target,
.optimize = optimize,
});
// Wasm we do manually since it is such a different build. // Wasm we do manually since it is such a different build.
if (step.rootModuleTarget().cpu.arch == .wasm32) { if (step.rootModuleTarget().cpu.arch == .wasm32) {
@ -1129,6 +1133,7 @@ fn addDeps(
step.root_module.addImport("ziglyph", ziglyph_dep.module("ziglyph")); step.root_module.addImport("ziglyph", ziglyph_dep.module("ziglyph"));
step.root_module.addImport("vaxis", vaxis_dep.module("vaxis")); step.root_module.addImport("vaxis", vaxis_dep.module("vaxis"));
step.root_module.addImport("wuffs", wuffs_dep.module("wuffs")); step.root_module.addImport("wuffs", wuffs_dep.module("wuffs"));
step.root_module.addImport("zf", zf_dep.module("zf"));
// Mac Stuff // Mac Stuff
if (step.rootModuleTarget().isDarwin()) { if (step.rootModuleTarget().isDarwin()) {

View File

@ -54,8 +54,12 @@
.hash = "122056fbb29863ec1678b7954fb76b1533ad8c581a34577c1b2efe419e29e05596df", .hash = "122056fbb29863ec1678b7954fb76b1533ad8c581a34577c1b2efe419e29e05596df",
}, },
.vaxis = .{ .vaxis = .{
.url = "git+https://github.com/rockorager/libvaxis?ref=main#2cc1eb77f842dd8587dfc9cf399d42e4c1369175", .url = "git+https://github.com/rockorager/libvaxis?ref=main#1961712c1f0cf46b235dd31418dc1b52442abbd5",
.hash = "12203c2d83911e6aacfbfdd48d31d6fc36e89947dfc7aec104debe3ac85e9f3a44f2", .hash = "12208cfdda4d5fdbc81b0c44b82e4d6dba2d4a86bff644a153e026fdfc80f8469133",
},
.zf = .{
.url = "git+https://github.com/natecraddock/zf.git?ref=main#bb27a917c3513785c6a91f0b1c10002a5029cacc",
.hash = "1220a74107c7f153a2f809e41c7fa7e8dbf75c91043e39fad998247804e5edac2cc8",
}, },
}, },
} }

View File

@ -1,3 +1,3 @@
# This file is auto-generated! check build-support/check-zig-cache-hash.sh for # This file is auto-generated! check build-support/check-zig-cache-hash.sh for
# more details. # more details.
"sha256-MAzGg4tWlyv2X/GjAwm7s2whojawIKNMx1xWR+cZffQ=" "sha256-qFt9sC3GekfU940Gd9oV9Gcbs5MdxVMojIMbkDo3m2A="

View File

@ -9,6 +9,7 @@ const internal_os = @import("../os/main.zig");
const global_state = &@import("../global.zig").state; const global_state = &@import("../global.zig").state;
const vaxis = @import("vaxis"); const vaxis = @import("vaxis");
const zf = @import("zf");
pub const Options = struct { pub const Options = struct {
/// If true, print the full path to the theme. /// If true, print the full path to the theme.
@ -32,6 +33,7 @@ const ThemeListElement = struct {
location: themepkg.Location, location: themepkg.Location,
path: []const u8, path: []const u8,
theme: []const u8, theme: []const u8,
rank: ?f64 = null,
fn lessThan(_: void, lhs: @This(), rhs: @This()) bool { fn lessThan(_: void, lhs: @This(), rhs: @This()) bool {
// TODO: use Unicode-aware comparison // TODO: use Unicode-aware comparison
@ -173,29 +175,51 @@ const Preview = struct {
vx: vaxis.Vaxis, vx: vaxis.Vaxis,
mouse: ?vaxis.Mouse, mouse: ?vaxis.Mouse,
themes: []ThemeListElement, themes: []ThemeListElement,
filtered: std.ArrayList(usize),
current: usize, current: usize,
window: usize,
hex: bool, hex: bool,
help_visible: bool, mode: enum {
normal,
help,
search,
},
color_scheme: vaxis.Color.Scheme, color_scheme: vaxis.Color.Scheme,
text_input: vaxis.widgets.TextInput,
pub fn init(allocator: std.mem.Allocator, themes: []ThemeListElement) !Preview { pub fn init(allocator: std.mem.Allocator, themes: []ThemeListElement) !*Preview {
return .{ const self = try allocator.create(Preview);
self.* = .{
.allocator = allocator, .allocator = allocator,
.should_quit = false, .should_quit = false,
.tty = try vaxis.Tty.init(), .tty = try vaxis.Tty.init(),
.vx = try vaxis.init(allocator, .{}), .vx = try vaxis.init(allocator, .{}),
.mouse = null, .mouse = null,
.themes = themes, .themes = themes,
.filtered = try std.ArrayList(usize).initCapacity(allocator, themes.len),
.current = 0, .current = 0,
.window = 0,
.hex = false, .hex = false,
.help_visible = false, .mode = .normal,
.color_scheme = .light, .color_scheme = .light,
.text_input = vaxis.widgets.TextInput.init(allocator, &self.vx.unicode),
}; };
for (0..themes.len) |i| {
try self.filtered.append(i);
}
return self;
} }
pub fn deinit(self: *Preview) void { pub fn deinit(self: *Preview) void {
self.vx.deinit(self.allocator, self.tty.anyWriter()); const allocator = self.allocator;
self.filtered.deinit();
self.text_input.deinit();
self.vx.deinit(allocator, self.tty.anyWriter());
self.tty.deinit(); self.tty.deinit();
allocator.destroy(self);
} }
pub fn run(self: *Preview) !void { pub fn run(self: *Preview) !void {
@ -230,12 +254,92 @@ const Preview = struct {
} }
} }
fn updateFiltered(self: *Preview) !void {
const relative = self.current -| self.window;
const selected = self.themes[self.filtered.items[self.current]].theme;
const hash_algorithm = std.hash.Wyhash;
const old_digest = d: {
var hash = hash_algorithm.init(0);
for (self.filtered.items) |item|
hash.update(std.mem.asBytes(&item));
break :d hash.final();
};
self.filtered.clearRetainingCapacity();
if (self.text_input.buf.realLength() > 0) {
const first_half = self.text_input.buf.firstHalf();
const second_half = self.text_input.buf.secondHalf();
const buffer = try self.allocator.alloc(u8, first_half.len + second_half.len);
defer self.allocator.free(buffer);
@memcpy(buffer[0..first_half.len], first_half);
@memcpy(buffer[first_half.len..], second_half);
const string = try std.ascii.allocLowerString(self.allocator, buffer);
defer self.allocator.free(string);
var tokens = std.ArrayList([]const u8).init(self.allocator);
defer tokens.deinit();
var it = std.mem.tokenizeScalar(u8, string, ' ');
while (it.next()) |token| try tokens.append(token);
for (self.themes, 0..) |*theme, i| {
theme.rank = zf.rank(theme.theme, tokens.items, false, true);
if (theme.rank) |_|
try self.filtered.append(i);
}
} else {
for (self.themes, 0..) |*theme, i| {
try self.filtered.append(i);
theme.rank = null;
}
}
const new_digest = d: {
var hash = hash_algorithm.init(0);
for (self.filtered.items) |item|
hash.update(std.mem.asBytes(&item));
break :d hash.final();
};
if (old_digest == new_digest) return;
if (self.filtered.items.len == 0) {
self.current = 0;
self.window = 0;
return;
}
self.current, self.window = current: {
for (self.filtered.items, 0..) |index, i| {
if (std.mem.eql(u8, self.themes[index].theme, selected))
break :current .{ i, i -| relative };
}
break :current .{ 0, 0 };
};
}
fn up(self: *Preview, count: usize) void { fn up(self: *Preview, count: usize) void {
self.current = std.math.sub(usize, self.current, count) catch self.themes.len + self.current - count; if (self.filtered.items.len == 0) {
self.current = 0;
return;
}
self.current -|= count;
} }
fn down(self: *Preview, count: usize) void { fn down(self: *Preview, count: usize) void {
self.current = (self.current + count) % self.themes.len; if (self.filtered.items.len == 0) {
self.current = 0;
return;
}
self.current += count;
if (self.current >= self.filtered.items.len)
self.current = self.filtered.items.len - 1;
} }
pub fn update(self: *Preview, event: Event, alloc: std.mem.Allocator) !void { pub fn update(self: *Preview, event: Event, alloc: std.mem.Allocator) !void {
@ -243,72 +347,71 @@ const Preview = struct {
.key_press => |key| { .key_press => |key| {
if (key.matches('c', .{ .ctrl = true })) if (key.matches('c', .{ .ctrl = true }))
self.should_quit = true; self.should_quit = true;
if (key.matches('q', .{})) switch (self.mode) {
self.should_quit = true; .normal => {
if (key.matches(vaxis.Key.escape, .{})) if (key.matchesAny(&.{ 'q', vaxis.Key.escape }, .{}))
self.should_quit = true; self.should_quit = true;
if (key.matches('?', .{})) if (key.matchesAny(&.{ '?', vaxis.Key.f1 }, .{}))
self.help_visible = !self.help_visible; self.mode = .help;
if (key.matches('h', .{ .ctrl = true })) if (key.matches('h', .{ .ctrl = true }))
self.help_visible = !self.help_visible; self.mode = .help;
if (key.matches(vaxis.Key.f1, .{})) if (key.matches('/', .{}))
self.help_visible = !self.help_visible; self.mode = .search;
if (key.matches('0', .{})) if (key.matchesAny(&.{ 'x', '/' }, .{ .ctrl = true })) {
self.current = 0; self.text_input.buf.clearRetainingCapacity();
if (key.matches(vaxis.Key.home, .{})) try self.updateFiltered();
self.current = 0; }
if (key.matches(vaxis.Key.kp_home, .{})) if (key.matchesAny(&.{ vaxis.Key.home, vaxis.Key.kp_home }, .{}))
self.current = 0; self.current = 0;
if (key.matches(vaxis.Key.end, .{})) if (key.matchesAny(&.{ vaxis.Key.end, vaxis.Key.kp_end }, .{}))
self.current = self.themes.len - 1; self.current = self.filtered.items.len - 1;
if (key.matches(vaxis.Key.kp_end, .{})) if (key.matchesAny(&.{ 'j', '+', vaxis.Key.down, vaxis.Key.kp_down, vaxis.Key.kp_add }, .{}))
self.current = self.themes.len - 1; self.down(1);
if (key.matches('j', .{})) if (key.matchesAny(&.{ vaxis.Key.page_down, vaxis.Key.kp_down }, .{}))
self.down(1); self.down(20);
if (key.matches('+', .{})) if (key.matchesAny(&.{ 'k', '-', vaxis.Key.up, vaxis.Key.kp_up, vaxis.Key.kp_subtract }, .{}))
self.down(1); self.up(1);
if (key.matches(vaxis.Key.down, .{})) if (key.matchesAny(&.{ vaxis.Key.page_up, vaxis.Key.kp_page_up }, .{}))
self.down(1); self.up(20);
if (key.matches(vaxis.Key.kp_down, .{})) if (key.matchesAny(&.{ 'h', 'x' }, .{}))
self.down(1); self.hex = true;
if (key.matches(vaxis.Key.kp_add, .{})) if (key.matches('d', .{}))
self.down(1); self.hex = false;
if (key.matches(vaxis.Key.page_down, .{})) if (key.matches('c', .{}))
self.down(20); try self.vx.copyToSystemClipboard(
if (key.matches(vaxis.Key.kp_page_down, .{})) self.tty.anyWriter(),
self.down(20); self.themes[self.filtered.items[self.current]].theme,
if (key.matches('k', .{})) alloc,
self.up(1); );
if (key.matches('-', .{})) if (key.matches('c', .{ .shift = true }))
self.up(1); try self.vx.copyToSystemClipboard(
if (key.matches(vaxis.Key.up, .{})) self.tty.anyWriter(),
self.up(1); self.themes[self.filtered.items[self.current]].path,
if (key.matches(vaxis.Key.kp_up, .{})) alloc,
self.up(1); );
if (key.matches(vaxis.Key.kp_subtract, .{})) },
self.up(1); .help => {
if (key.matches(vaxis.Key.page_up, .{})) if (key.matches('q', .{}))
self.up(20); self.should_quit = true;
if (key.matches(vaxis.Key.kp_page_up, .{})) if (key.matchesAny(&.{ '?', vaxis.Key.escape, vaxis.Key.f1 }, .{}))
self.up(20); self.mode = .normal;
if (key.matches('h', .{})) if (key.matches('h', .{ .ctrl = true }))
self.hex = true; self.mode = .normal;
if (key.matches('x', .{})) },
self.hex = true; .search => search: {
if (key.matches('d', .{})) if (key.matchesAny(&.{ vaxis.Key.escape, vaxis.Key.enter }, .{})) {
self.hex = false; self.mode = .normal;
if (key.matches('c', .{})) break :search;
try self.vx.copyToSystemClipboard( }
self.tty.anyWriter(), if (key.matchesAny(&.{ 'x', '/' }, .{ .ctrl = true })) {
self.themes[self.current].theme, self.text_input.clearRetainingCapacity();
alloc, try self.updateFiltered();
); break :search;
if (key.matches('c', .{ .shift = true })) }
try self.vx.copyToSystemClipboard( try self.text_input.update(.{ .key_press = key });
self.tty.anyWriter(), try self.updateFiltered();
self.themes[self.current].path, },
alloc, }
);
}, },
.color_scheme => |color_scheme| self.color_scheme = color_scheme, .color_scheme => |color_scheme| self.color_scheme = color_scheme,
.mouse => |mouse| self.mouse = mouse, .mouse => |mouse| self.mouse = mouse,
@ -316,43 +419,82 @@ const Preview = struct {
} }
} }
pub fn ui_fg(self: *Preview) vaxis.Color {
return switch (self.color_scheme) {
.light => .{ .rgb = [_]u8{ 0x00, 0x00, 0x00 } },
.dark => .{ .rgb = [_]u8{ 0xff, 0xff, 0xff } },
};
}
pub fn ui_bg(self: *Preview) vaxis.Color {
return switch (self.color_scheme) {
.light => .{ .rgb = [_]u8{ 0xff, 0xff, 0xff } },
.dark => .{ .rgb = [_]u8{ 0x00, 0x00, 0x00 } },
};
}
pub fn ui_standard(self: *Preview) vaxis.Style {
return .{
.fg = self.ui_fg(),
.bg = self.ui_bg(),
};
}
pub fn ui_hover_bg(self: *Preview) vaxis.Color {
return switch (self.color_scheme) {
.light => .{ .rgb = [_]u8{ 0xbb, 0xbb, 0xbb } },
.dark => .{ .rgb = [_]u8{ 0x22, 0x22, 0x22 } },
};
}
pub fn ui_highlighted(self: *Preview) vaxis.Style {
return .{
.fg = self.ui_fg(),
.bg = self.ui_hover_bg(),
};
}
pub fn ui_selected_fg(self: *Preview) vaxis.Color {
return switch (self.color_scheme) {
.light => .{ .rgb = [_]u8{ 0x00, 0xaa, 0x00 } },
.dark => .{ .rgb = [_]u8{ 0x00, 0xaa, 0x00 } },
};
}
pub fn ui_selected_bg(self: *Preview) vaxis.Color {
return switch (self.color_scheme) {
.light => .{ .rgb = [_]u8{ 0xaa, 0xaa, 0xaa } },
.dark => .{ .rgb = [_]u8{ 0x33, 0x33, 0x33 } },
};
}
pub fn ui_selected(self: *Preview) vaxis.Style {
return .{
.fg = self.ui_selected_fg(),
.bg = self.ui_selected_bg(),
};
}
pub fn ui_err_fg(self: *Preview) vaxis.Color {
return switch (self.color_scheme) {
.light => .{ .rgb = [_]u8{ 0xff, 0x00, 0x00 } },
.dark => .{ .rgb = [_]u8{ 0xff, 0x00, 0x00 } },
};
}
pub fn ui_err(self: *Preview) vaxis.Style {
return .{
.fg = self.ui_err_fg(),
.bg = self.ui_bg(),
};
}
pub fn draw(self: *Preview, alloc: std.mem.Allocator) !void { pub fn draw(self: *Preview, alloc: std.mem.Allocator) !void {
const win = self.vx.window(); const win = self.vx.window();
win.clear(); win.clear();
self.vx.setMouseShape(.default); self.vx.setMouseShape(.default);
const ui_fg: vaxis.Color = switch (self.color_scheme) {
.light => .{ .rgb = [_]u8{ 0x00, 0x00, 0x00 } },
.dark => .{ .rgb = [_]u8{ 0xff, 0xff, 0xff } },
};
const ui_bg: vaxis.Color = switch (self.color_scheme) {
.light => .{ .rgb = [_]u8{ 0xff, 0xff, 0xff } },
.dark => .{ .rgb = [_]u8{ 0x00, 0x00, 0x00 } },
};
const ui_standard: vaxis.Style = .{
.fg = ui_fg,
.bg = ui_bg,
};
const ui_hover_bg: vaxis.Color = switch (self.color_scheme) {
.light => .{ .rgb = [_]u8{ 0xbb, 0xbb, 0xbb } },
.dark => .{ .rgb = [_]u8{ 0x22, 0x22, 0x22 } },
};
const ui_selected_fg: vaxis.Color = switch (self.color_scheme) {
.light => .{ .rgb = [_]u8{ 0x00, 0xaa, 0x00 } },
.dark => .{ .rgb = [_]u8{ 0x00, 0xaa, 0x00 } },
};
const ui_selected_bg: vaxis.Color = switch (self.color_scheme) {
.light => .{ .rgb = [_]u8{ 0xaa, 0xaa, 0xaa } },
.dark => .{ .rgb = [_]u8{ 0x33, 0x33, 0x33 } },
};
const ui_selected: vaxis.Style = .{
.fg = ui_selected_fg,
.bg = ui_selected_bg,
};
const theme_list = win.child(.{ const theme_list = win.child(.{
.x_off = 0, .x_off = 0,
.y_off = 0, .y_off = 0,
@ -360,43 +502,73 @@ const Preview = struct {
.height = .{ .limit = win.height }, .height = .{ .limit = win.height },
}); });
const split = theme_list.height / 2; if (self.filtered.items.len == 0) {
self.current = 0;
self.window = 0;
} else {
const start = self.window;
const end = self.window + theme_list.height - 1;
if (self.current > end)
self.window = self.current - theme_list.height + 1;
if (self.current < start)
self.window = self.current;
if (self.window >= self.filtered.items.len)
self.window = self.filtered.items.len - 1;
}
var highlight: ?usize = null; var highlight: ?usize = null;
if (self.mouse) |mouse| { if (self.mouse) |mouse| {
self.mouse = null; self.mouse = null;
if (mouse.button == .wheel_up) { if (self.mode == .normal) {
self.up(1); if (mouse.button == .wheel_up) {
} self.up(1);
if (mouse.button == .wheel_down) { }
self.down(1); if (mouse.button == .wheel_down) {
} self.down(1);
if (theme_list.hasMouse(mouse)) |_| { }
if (mouse.button == .left and mouse.type == .release) { if (theme_list.hasMouse(mouse)) |_| {
if (mouse.row < split) self.up(split - mouse.row); if (mouse.button == .left and mouse.type == .release) {
if (mouse.row > split) self.down(mouse.row - split); self.current = self.window + mouse.row;
}
highlight = mouse.row;
} }
highlight = mouse.row;
} }
} }
theme_list.fill(.{ .style = ui_standard }); theme_list.fill(.{ .style = self.ui_standard() });
for (0..split) |i| { for (0..theme_list.height) |row| {
const j = std.math.sub(usize, self.current, i + 1) catch self.themes.len + self.current - i - 1; const index = self.window + row;
const theme = self.themes[j]; if (index >= self.filtered.items.len) break;
const row = split - i - 1;
const theme = self.themes[self.filtered.items[index]];
const style: enum { normal, highlighted, selected } = style: {
if (index == self.current) break :style .selected;
if (highlight) |h| if (h == row) break :style .highlighted;
break :style .normal;
};
if (style == .selected) {
_ = try theme_list.printSegment(
.{
.text = " ",
.style = self.ui_selected(),
},
.{
.row_offset = row,
.col_offset = 0,
},
);
}
_ = try theme_list.printSegment( _ = try theme_list.printSegment(
.{ .{
.text = theme.theme, .text = theme.theme,
.style = .{ .style = switch (style) {
.fg = ui_fg, .normal => self.ui_standard(),
.bg = bg: { .highlighted => self.ui_highlighted(),
if (highlight) |h| if (h == row) break :bg ui_hover_bg; .selected => self.ui_selected(),
break :bg ui_bg;
},
}, },
.link = .{ .link = .{
.uri = try theme.toUri(alloc), .uri = try theme.toUri(alloc),
@ -407,163 +579,140 @@ const Preview = struct {
.col_offset = 2, .col_offset = 2,
}, },
); );
} if (style == .selected) {
{ if (theme.theme.len < theme_list.width - 4) {
const theme = self.themes[self.current]; for (2 + theme.theme.len..theme_list.width - 2) |i|
_ = try theme_list.printSegment( _ = try theme_list.printSegment(
.{ .{
.text = " ", .text = " ",
.style = ui_selected, .style = self.ui_selected(),
}, },
.{ .{
.row_offset = split, .row_offset = row,
.col_offset = 0, .col_offset = i,
}, },
); );
_ = try theme_list.printSegment( }
.{ _ = try theme_list.printSegment(
.text = theme.theme, .{
.style = ui_selected, .text = " ",
.link = .{ .style = self.ui_selected(),
.uri = try theme.toUri(alloc),
}, },
}, .{
.{ .row_offset = row,
.row_offset = split, .col_offset = theme_list.width - 2,
.col_offset = 2, },
}, );
); }
if (theme.theme.len < theme_list.width - 4) { }
for (2 + theme.theme.len..theme_list.width - 2) |i|
_ = try theme_list.printSegment( try self.drawPreview(alloc, win, theme_list.x_off + theme_list.width);
switch (self.mode) {
.normal => {
win.hideCursor();
},
.help => {
win.hideCursor();
const width = 60;
const height = 20;
const child = win.child(
.{
.x_off = win.width / 2 -| width / 2,
.y_off = win.height / 2 -| height / 2,
.width = .{
.limit = width,
},
.height = .{
.limit = height,
},
.border = .{
.where = .all,
.style = self.ui_standard(),
},
},
);
child.fill(.{ .style = self.ui_standard() });
const key_help = [_]struct { keys: []const u8, help: []const u8 }{
.{ .keys = "^C, q, ESC", .help = "Quit." },
.{ .keys = "F1, ?, ^H", .help = "Toggle help window." },
.{ .keys = "k, ↑", .help = "Move up 1 theme." },
.{ .keys = "ScrollUp", .help = "Move up 1 theme." },
.{ .keys = "PgUp", .help = "Move up 20 themes." },
.{ .keys = "j, ↓", .help = "Move down 1 theme." },
.{ .keys = "ScrollDown", .help = "Move down 1 theme." },
.{ .keys = "PgDown", .help = "Move down 20 themes." },
.{ .keys = "h, x", .help = "Show palette numbers in hexadecimal." },
.{ .keys = "d", .help = "Show palette numbers in decimal." },
.{ .keys = "c", .help = "Copy theme name to the clipboard." },
.{ .keys = "C", .help = "Copy theme path to the clipboard." },
.{ .keys = "Home", .help = "Go to the start of the list." },
.{ .keys = "End", .help = "Go to the end of the list." },
.{ .keys = "/", .help = "Start search." },
.{ .keys = "^X, ^/", .help = "Clear search." },
.{ .keys = "", .help = "Close search window." },
};
for (key_help, 0..) |help, i| {
_ = try child.printSegment(
.{ .{
.text = " ", .text = help.keys,
.style = ui_selected, .style = self.ui_standard(),
}, },
.{ .{
.row_offset = split, .row_offset = i + 1,
.col_offset = i, .col_offset = 2,
}, },
); );
} _ = try child.printSegment(
_ = try theme_list.printSegment( .{
.{ .text = "",
.text = " ", .style = self.ui_standard(),
.style = ui_selected, },
}, .{
.{ .row_offset = i + 1,
.row_offset = split, .col_offset = 15,
.col_offset = theme_list.width - 2, },
}, );
); _ = try child.printSegment(
} .{
for (split + 1..theme_list.height) |i| { .text = help.help,
const j = (self.current + i - split) % self.themes.len; .style = self.ui_standard(),
const row = i; },
const theme = self.themes[j]; .{
_ = try theme_list.printSegment(.{ .row_offset = i + 1,
.text = theme.theme, .col_offset = 17,
.style = .{ },
.fg = ui_fg, );
.bg = bg: { }
if (highlight) |h| if (h == row) break :bg ui_hover_bg; },
break :bg ui_bg; .search => {
}, const child = win.child(.{
}, .x_off = 20,
.link = .{ .y_off = win.height - 5,
.uri = try theme.toUri(alloc),
},
}, .{
.row_offset = i,
.col_offset = 2,
});
}
try self.drawPreview(alloc, win, theme_list.x_off + theme_list.width, ui_fg, ui_bg);
if (self.help_visible) {
const width = 60;
const height = 20;
const child = win.child(
.{
.x_off = win.width / 2 -| width / 2,
.y_off = win.height / 2 -| height / 2,
.width = .{ .width = .{
.limit = width, .limit = win.width - 40,
}, },
.height = .{ .height = .{
.limit = height, .limit = 3,
}, },
.border = .{ .border = .{
.where = .all, .where = .all,
.style = ui_standard, .style = self.ui_standard(),
}, },
}, });
); child.fill(.{ .style = self.ui_standard() });
self.text_input.drawWithStyle(child, self.ui_standard());
child.fill(.{ .style = ui_standard }); },
const key_help = [_]struct { keys: []const u8, help: []const u8 }{
.{ .keys = "^C, q, ESC", .help = "Quit." },
.{ .keys = "F1, ?, ^H", .help = "Toggle help window." },
.{ .keys = "k, ↑", .help = "Move up 1 theme." },
.{ .keys = "ScrollUp", .help = "Move up 1 theme." },
.{ .keys = "PgUp", .help = "Move up 20 themes." },
.{ .keys = "j, ↓", .help = "Move down 1 theme." },
.{ .keys = "ScrollDown", .help = "Move down 1 theme." },
.{ .keys = "PgDown", .help = "Move down 20 themes." },
.{ .keys = "h, x", .help = "Show palette numbers in hexadecimal." },
.{ .keys = "d", .help = "Show palette numbers in decimal." },
.{ .keys = "c", .help = "Copy theme name to the clipboard." },
.{ .keys = "C", .help = "Copy theme path to the clipboard." },
.{ .keys = "0, Home", .help = "Go to the start of the list." },
.{ .keys = "End", .help = "Go to the end of the list." },
};
for (key_help, 0..) |help, i| {
_ = try child.printSegment(
.{
.text = help.keys,
.style = ui_standard,
},
.{
.row_offset = i + 1,
.col_offset = 2,
},
);
_ = try child.printSegment(
.{
.text = "",
.style = ui_standard,
},
.{
.row_offset = i + 1,
.col_offset = 15,
},
);
_ = try child.printSegment(
.{
.text = help.help,
.style = ui_standard,
},
.{
.row_offset = i + 1,
.col_offset = 17,
},
);
}
} }
} }
pub fn drawPreview(self: *Preview, alloc: std.mem.Allocator, win: vaxis.Window, x_off: usize, ui_fg: vaxis.Color, ui_bg: vaxis.Color) !void { pub fn drawPreview(self: *Preview, alloc: std.mem.Allocator, win: vaxis.Window, x_off: usize) !void {
const width = win.width - x_off; const width = win.width - x_off;
const ui_err_fg: vaxis.Color = switch (self.color_scheme) { const theme = self.themes[self.filtered.items[self.current]];
.light => .{ .rgb = [_]u8{ 0xff, 0x00, 0x00 } },
.dark => .{ .rgb = [_]u8{ 0xff, 0x00, 0x00 } },
};
const theme = self.themes[self.current];
var config = try Config.default(alloc); var config = try Config.default(alloc);
defer config.deinit(); defer config.deinit();
@ -581,17 +730,14 @@ const Preview = struct {
}, },
}, },
); );
child.fill(.{ .style = .{ .fg = ui_fg, .bg = ui_bg } }); child.fill(.{ .style = self.ui_standard() });
const middle = child.height / 2; const middle = child.height / 2;
{ {
const text = try std.fmt.allocPrint(alloc, "Unable to open {s} from:", .{theme.theme}); const text = try std.fmt.allocPrint(alloc, "Unable to open {s} from:", .{theme.theme});
_ = try child.printSegment( _ = try child.printSegment(
.{ .{
.text = text, .text = text,
.style = .{ .style = self.ui_err(),
.fg = ui_err_fg,
.bg = ui_bg,
},
}, },
.{ .{
.row_offset = middle -| 1, .row_offset = middle -| 1,
@ -603,10 +749,7 @@ const Preview = struct {
_ = try child.printSegment( _ = try child.printSegment(
.{ .{
.text = theme.path, .text = theme.path,
.style = .{ .style = self.ui_err(),
.fg = ui_err_fg,
.bg = ui_bg,
},
.link = .{ .link = .{
.uri = try theme.toUri(alloc), .uri = try theme.toUri(alloc),
}, },
@ -622,10 +765,7 @@ const Preview = struct {
_ = try child.printSegment( _ = try child.printSegment(
.{ .{
.text = text, .text = text,
.style = .{ .style = self.ui_err(),
.fg = ui_err_fg,
.bg = ui_bg,
},
}, },
.{ .{
.row_offset = middle + 1, .row_offset = middle + 1,
@ -637,6 +777,7 @@ const Preview = struct {
}; };
var next_start: usize = 0; var next_start: usize = 0;
const fg: vaxis.Color = .{ const fg: vaxis.Color = .{
.rgb = [_]u8{ .rgb = [_]u8{
config.foreground.r, config.foreground.r,
@ -759,10 +900,7 @@ const Preview = struct {
_ = try child.printSegment( _ = try child.printSegment(
.{ .{
.text = text, .text = text,
.style = .{ .style = self.ui_err(),
.fg = ui_err_fg,
.bg = ui_bg,
},
}, },
.{ .{
.row_offset = 0, .row_offset = 0,
@ -774,10 +912,7 @@ const Preview = struct {
_ = try child.printSegment( _ = try child.printSegment(
.{ .{
.text = err.message, .text = err.message,
.style = .{ .style = self.ui_err(),
.fg = ui_err_fg,
.bg = ui_bg,
},
}, },
.{ .{
.row_offset = 2 + i, .row_offset = 2 + i,