refactor: button, menu and widget stack apis
This commit is contained in:
parent
dcd9e119da
commit
2f9a0e2eb0
9 changed files with 348 additions and 130 deletions
|
@ -11,16 +11,16 @@ pub fn Options(context: type) type {
|
|||
return struct {
|
||||
label: []const u8 = "button",
|
||||
pos: Widget.Box = .{ .y = 0, .x = 0, .w = 8, .h = 1 },
|
||||
ctx: context = {},
|
||||
ctx: Context,
|
||||
|
||||
on_click: *const fn (ctx: *context, button: *State(Context)) void = do_nothing,
|
||||
on_render: *const fn (ctx: *context, button: *State(Context), theme: *const Widget.Theme) bool = on_render_default,
|
||||
on_layout: *const fn (ctx: *context, button: *State(Context)) Widget.Layout = on_layout_default,
|
||||
on_click: *const fn (ctx: context, button: *State(Context)) void = do_nothing,
|
||||
on_render: *const fn (ctx: context, button: *State(Context), theme: *const Widget.Theme) bool = on_render_default,
|
||||
on_layout: *const fn (ctx: context, button: *State(Context)) Widget.Layout = on_layout_default,
|
||||
|
||||
pub const Context = context;
|
||||
pub fn do_nothing(_: *context, _: *State(Context)) void {}
|
||||
pub fn do_nothing(_: context, _: *State(Context)) void {}
|
||||
|
||||
pub fn on_render_default(_: *context, self: *State(Context), theme: *const Widget.Theme) bool {
|
||||
pub fn on_render_default(_: context, self: *State(Context), theme: *const Widget.Theme) bool {
|
||||
tui.set_base_style(&self.plane, " ", if (self.active) theme.scrollbar_active else if (self.hover) theme.scrollbar_hover else theme.scrollbar);
|
||||
self.plane.erase();
|
||||
self.plane.home();
|
||||
|
@ -28,14 +28,14 @@ pub fn Options(context: type) type {
|
|||
return false;
|
||||
}
|
||||
|
||||
pub fn on_layout_default(_: *context, self: *State(Context)) Widget.Layout {
|
||||
pub fn on_layout_default(_: context, self: *State(Context)) Widget.Layout {
|
||||
return .{ .static = self.opts.label.len + 2 };
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn create(ctx: anytype, a: std.mem.Allocator, parent: nc.Plane, opts: Options(@TypeOf(ctx))) !Widget {
|
||||
const Self = State(@TypeOf(ctx));
|
||||
pub fn create(ctx_type: type, a: std.mem.Allocator, parent: nc.Plane, opts: Options(ctx_type)) !Widget {
|
||||
const Self = State(ctx_type);
|
||||
const self = try a.create(Self);
|
||||
var n = try nc.Plane.init(&opts.pos.opts(@typeName(Self)), parent);
|
||||
errdefer n.deinit();
|
||||
|
@ -64,27 +64,31 @@ pub fn State(ctx_type: type) type {
|
|||
}
|
||||
|
||||
pub fn layout(self: *Self) Widget.Layout {
|
||||
return self.opts.on_layout(&self.opts.ctx, self);
|
||||
return self.opts.on_layout(self.opts.ctx, self);
|
||||
}
|
||||
|
||||
pub fn render(self: *Self, theme: *const Widget.Theme) bool {
|
||||
return self.opts.on_render(&self.opts.ctx, self, theme);
|
||||
return self.opts.on_render(self.opts.ctx, self, theme);
|
||||
}
|
||||
|
||||
pub fn receive(self: *Self, _: tp.pid_ref, m: tp.message) error{Exit}!bool {
|
||||
if (try m.match(.{ "B", nc.event_type.PRESS, nc.key.BUTTON1, tp.any, tp.any, tp.any, tp.any, tp.any })) {
|
||||
self.active = true;
|
||||
tui.need_render();
|
||||
return true;
|
||||
} else if (try m.match(.{ "B", nc.event_type.RELEASE, nc.key.BUTTON1, tp.any, tp.any, tp.any, tp.any, tp.any })) {
|
||||
self.opts.on_click(&self.opts.ctx, self);
|
||||
self.opts.on_click(self.opts.ctx, self);
|
||||
self.active = false;
|
||||
tui.need_render();
|
||||
return true;
|
||||
} else if (try m.match(.{ "D", nc.event_type.RELEASE, nc.key.BUTTON1, tp.any, tp.any, tp.any, tp.any, tp.any })) {
|
||||
self.opts.on_click(&self.opts.ctx, self);
|
||||
self.opts.on_click(self.opts.ctx, self);
|
||||
self.active = false;
|
||||
tui.need_render();
|
||||
return true;
|
||||
} else if (try m.match(.{ "H", tp.extract(&self.hover) })) {
|
||||
tui.current().request_mouse_cursor_pointer(self.hover);
|
||||
tui.need_render();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
120
src/tui/Menu.zig
120
src/tui/Menu.zig
|
@ -7,70 +7,102 @@ const WidgetList = @import("WidgetList.zig");
|
|||
const Button = @import("Button.zig");
|
||||
const tui = @import("tui.zig");
|
||||
|
||||
a: std.mem.Allocator,
|
||||
menu: *WidgetList,
|
||||
menu_widget: Widget,
|
||||
pub fn Options(context: type) type {
|
||||
return struct {
|
||||
ctx: Context,
|
||||
|
||||
const Self = @This();
|
||||
on_click: *const fn (ctx: context, button: *Button.State(*State(Context))) void = do_nothing,
|
||||
on_render: *const fn (ctx: context, button: *Button.State(*State(Context)), theme: *const Widget.Theme) bool = on_render_default,
|
||||
on_layout: *const fn (ctx: context, button: *Button.State(*State(Context))) Widget.Layout = on_layout_default,
|
||||
|
||||
pub fn create(a: std.mem.Allocator, parent: Widget) !*Self {
|
||||
const self: *Self = try a.create(Self);
|
||||
pub const Context = context;
|
||||
pub fn do_nothing(_: context, _: *Button.State(*State(Context))) void {}
|
||||
|
||||
pub fn on_render_default(_: context, button: *Button.State(*State(Context)), theme: *const Widget.Theme) bool {
|
||||
const style_base = if (button.active) theme.editor_cursor else if (button.hover) theme.editor_selection else theme.editor;
|
||||
const bg_alpha: c_uint = if (button.active or button.hover) nc.ALPHA_OPAQUE else nc.ALPHA_TRANSPARENT;
|
||||
try tui.set_base_style_alpha(button.plane, " ", style_base, nc.ALPHA_TRANSPARENT, bg_alpha);
|
||||
button.plane.erase();
|
||||
button.plane.home();
|
||||
_ = button.plane.print(" {s} ", .{button.opts.label}) catch {};
|
||||
return false;
|
||||
}
|
||||
|
||||
pub fn on_layout_default(_: context, _: *Button.State(*State(Context))) Widget.Layout {
|
||||
return .{ .static = 1 };
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn create(ctx_type: type, a: std.mem.Allocator, parent: Widget, opts: Options(ctx_type)) !*State(ctx_type) {
|
||||
const self = try a.create(State(ctx_type));
|
||||
self.* = .{
|
||||
.a = a,
|
||||
.menu = try WidgetList.createV(a, parent, @typeName(Self), .dynamic),
|
||||
.menu = try WidgetList.createV(a, parent, @typeName(@This()), .dynamic),
|
||||
.menu_widget = self.menu.widget(),
|
||||
.opts = opts,
|
||||
};
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn add_item(self: *Self, label: []const u8, on_click: *const fn (_: *void, _: *Button.State(void)) void) !void {
|
||||
try self.menu.add(try Button.create({}, self.a, self.menu.parent, .{
|
||||
.on_layout = menu_layout,
|
||||
.label = label,
|
||||
.on_click = on_click,
|
||||
.on_render = render_menu_item,
|
||||
}));
|
||||
}
|
||||
pub fn State(ctx_type: type) type {
|
||||
return struct {
|
||||
a: std.mem.Allocator,
|
||||
menu: *WidgetList,
|
||||
menu_widget: Widget,
|
||||
opts: Options(ctx_type),
|
||||
|
||||
pub fn deinit(self: *Self, a: std.mem.Allocator) void {
|
||||
const Self = @This();
|
||||
|
||||
pub fn deinit(self: *Self, a: std.mem.Allocator) void {
|
||||
self.menu.deinit(a);
|
||||
a.destroy(self);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render(self: *Self, theme: *const Widget.Theme) bool {
|
||||
pub fn add_item(self: *Self, label: []const u8) !void {
|
||||
try self.menu.add(try Button.create(*Self, self.a, self.menu.parent, .{
|
||||
.ctx = self,
|
||||
.on_layout = self.opts.on_layout,
|
||||
.label = label,
|
||||
.on_click = self.opts.on_click,
|
||||
.on_render = self.opts.on_render,
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn add_item_with_handler(self: *Self, label: []const u8, on_click: *const fn (_: *Self, _: *Button.State(*Self)) void) !void {
|
||||
try self.menu.add(try Button.create(*Self, self.a, self.menu.parent, .{
|
||||
.ctx = self,
|
||||
.on_layout = on_layout,
|
||||
.label = label,
|
||||
.on_click = on_click,
|
||||
.on_render = on_render,
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn render(self: *Self, theme: *const Widget.Theme) bool {
|
||||
return self.menu.render(theme);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resize(self: *Self, box_: Widget.Box) void {
|
||||
pub fn on_layout(self: *Self, button: *Button.State(*Self)) Widget.Layout {
|
||||
return self.opts.on_layout(self.opts.ctx, button);
|
||||
}
|
||||
|
||||
pub fn on_render(self: *Self, button: *Button.State(*Self), theme: *const Widget.Theme) bool {
|
||||
return self.opts.on_render(self.opts.ctx, button, theme);
|
||||
}
|
||||
|
||||
pub fn resize(self: *Self, box_: Widget.Box) void {
|
||||
var box = box_;
|
||||
box.h = self.menu.widgets.items.len;
|
||||
self.menu.resize(box);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(self: *Self) void {
|
||||
pub fn update(self: *Self) void {
|
||||
self.menu.update();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn walk(self: *Self, walk_ctx: *anyopaque, f: Widget.WalkFn) bool {
|
||||
pub fn walk(self: *Self, walk_ctx: *anyopaque, f: Widget.WalkFn) bool {
|
||||
return self.menu.walk(walk_ctx, f, &self.menu_widget);
|
||||
}
|
||||
|
||||
fn menu_layout(_: *void, _: *Button.State(void)) Widget.Layout {
|
||||
return .{ .static = 1 };
|
||||
}
|
||||
|
||||
fn render_menu_item(_: *void, button: *Button.State(void), theme: *const Widget.Theme) bool {
|
||||
tui.set_base_style(&button.plane, " ", if (button.active) theme.editor_cursor else if (button.hover) theme.editor_selection else theme.editor);
|
||||
button.plane.erase();
|
||||
button.plane.home();
|
||||
const style_subtext = if (tui.find_scope_style(theme, "comment")) |sty| sty.style else theme.editor;
|
||||
const style_text = if (tui.find_scope_style(theme, "keyword")) |sty| sty.style else theme.editor;
|
||||
const style_keybind = if (tui.find_scope_style(theme, "entity.name")) |sty| sty.style else theme.editor;
|
||||
const sep = std.mem.indexOfScalar(u8, button.opts.label, ':') orelse button.opts.label.len;
|
||||
tui.set_style(&button.plane, style_subtext);
|
||||
tui.set_style(&button.plane, style_text);
|
||||
_ = button.plane.print(" {s}", .{button.opts.label[0..sep]}) catch {};
|
||||
tui.set_style(&button.plane, style_keybind);
|
||||
_ = button.plane.print("{s}", .{button.opts.label[sep + 1 ..]}) catch {};
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -25,22 +25,27 @@ pub fn deinit(self: *Self) void {
|
|||
self.widgets.deinit();
|
||||
}
|
||||
|
||||
pub fn addWidget(self: *Self, widget: Widget) !void {
|
||||
pub fn add(self: *Self, widget: Widget) !void {
|
||||
(try self.widgets.addOne()).* = widget;
|
||||
}
|
||||
|
||||
pub fn swapWidget(self: *Self, n: usize, widget: Widget) Widget {
|
||||
pub fn swap(self: *Self, n: usize, widget: Widget) Widget {
|
||||
const old = self.widgets.items[n];
|
||||
self.widgets.items[n] = widget;
|
||||
return old;
|
||||
}
|
||||
|
||||
pub fn replaceWidget(self: *Self, n: usize, widget: Widget) void {
|
||||
pub fn replace(self: *Self, n: usize, widget: Widget) void {
|
||||
const old = self.swapWidget(n, widget);
|
||||
old.deinit(self.a);
|
||||
}
|
||||
|
||||
pub fn deleteWidget(self: *Self, name: []const u8) bool {
|
||||
pub fn remove(self: *Self, w: Widget) void {
|
||||
for (self.widgets.items, 0..) |p, i| if (p.ptr == w.ptr)
|
||||
self.widgets.orderedRemove(i).deinit(self.a);
|
||||
}
|
||||
|
||||
pub fn delete(self: *Self, name: []const u8) bool {
|
||||
for (self.widgets.items, 0..) |*widget, i| {
|
||||
var buf: [64]u8 = undefined;
|
||||
const wname = widget.name(&buf);
|
||||
|
@ -52,7 +57,7 @@ pub fn deleteWidget(self: *Self, name: []const u8) bool {
|
|||
return false;
|
||||
}
|
||||
|
||||
pub fn findWidget(self: *Self, name: []const u8) ?*Widget {
|
||||
pub fn find(self: *Self, name: []const u8) ?*Widget {
|
||||
for (self.widgets.items) |*widget| {
|
||||
var buf: [64]u8 = undefined;
|
||||
const wname = widget.name(&buf);
|
||||
|
|
124
src/tui/home.zig
124
src/tui/home.zig
|
@ -11,16 +11,19 @@ const command = @import("command.zig");
|
|||
const fonts = @import("fonts.zig");
|
||||
|
||||
a: std.mem.Allocator,
|
||||
background: nc.Plane,
|
||||
plane: nc.Plane,
|
||||
parent: nc.Plane,
|
||||
fire: ?Fire = null,
|
||||
commands: Commands = undefined,
|
||||
menu: *Menu,
|
||||
menu: *Menu.State(*Self),
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub fn create(a: std.mem.Allocator, parent: Widget) !Widget {
|
||||
const self: *Self = try a.create(Self);
|
||||
var background = try nc.Plane.init(&(Widget.Box{}).opts("background"), parent.plane.*);
|
||||
errdefer background.deinit();
|
||||
var n = try nc.Plane.init(&(Widget.Box{}).opts("editor"), parent.plane.*);
|
||||
errdefer n.deinit();
|
||||
|
||||
|
@ -28,35 +31,28 @@ pub fn create(a: std.mem.Allocator, parent: Widget) !Widget {
|
|||
self.* = .{
|
||||
.a = a,
|
||||
.parent = parent.plane.*,
|
||||
.background = background,
|
||||
.plane = n,
|
||||
.menu = try Menu.create(a, w),
|
||||
.menu = try Menu.create(*Self, a, w, .{ .ctx = self, .on_render = menu_on_render }),
|
||||
};
|
||||
try self.commands.init(self);
|
||||
try self.menu.add_item("Help ······················· :h", menu_action_help);
|
||||
try self.menu.add_item("Open file ·················· :o", menu_action_open_file);
|
||||
try self.menu.add_item("Open recent file ····(wip)·· :e", menu_action_open_recent_file);
|
||||
try self.menu.add_item("Open recent project ·(wip)·· :r", menu_action_open_recent_project);
|
||||
try self.menu.add_item("Show/Run commands ···(wip)·· :p", menu_action_show_commands);
|
||||
try self.menu.add_item("Open config file ··········· :c", menu_action_open_config);
|
||||
try self.menu.add_item("Quit/Close ················· :q", menu_action_quit);
|
||||
try self.menu.add_item_with_handler("Help ······················· :h", menu_action_help);
|
||||
try self.menu.add_item_with_handler("Open file ·················· :o", menu_action_open_file);
|
||||
try self.menu.add_item_with_handler("Open recent file ····(wip)·· :e", menu_action_open_recent_file);
|
||||
try self.menu.add_item_with_handler("Open recent project ·(wip)·· :r", menu_action_open_recent_project);
|
||||
try self.menu.add_item_with_handler("Show/Run commands ···(wip)·· :p", menu_action_show_commands);
|
||||
try self.menu.add_item_with_handler("Open config file ··········· :c", menu_action_open_config);
|
||||
try self.menu.add_item_with_handler("Quit/Close ················· :q", menu_action_quit);
|
||||
self.menu.resize(.{ .y = 15, .x = 9, .w = 32 });
|
||||
command.executeName("enter_mode", command.Context.fmt(.{"home"})) catch {};
|
||||
return w;
|
||||
}
|
||||
|
||||
fn menu_item(self: *Self, label: []const u8, on_click: *const fn (_: *void, _: *Button.State(void)) void) !void {
|
||||
try self.menu.add(try Button.create({}, self.a, self.parent, .{
|
||||
.on_layout = menu_layout,
|
||||
.label = label,
|
||||
.on_click = on_click,
|
||||
.on_render = render_menu_item,
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self, a: std.mem.Allocator) void {
|
||||
self.menu.deinit(a);
|
||||
self.commands.deinit();
|
||||
self.plane.deinit();
|
||||
self.background.deinit();
|
||||
if (self.fire) |*fire| fire.deinit();
|
||||
a.destroy(self);
|
||||
}
|
||||
|
@ -73,47 +69,85 @@ pub fn receive(_: *Self, _: tp.pid_ref, m: tp.message) error{Exit}!bool {
|
|||
var hover: bool = false;
|
||||
if (try m.match(.{ "H", tp.extract(&hover) })) {
|
||||
tui.current().request_mouse_cursor_default(hover);
|
||||
tui.need_render();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fn menu_layout(_: *void, _: *Button.State(void)) Widget.Layout {
|
||||
return .{ .static = 1 };
|
||||
fn set_style(plane: nc.Plane, style: Widget.Theme.Style) void {
|
||||
var channels: u64 = 0;
|
||||
if (style.fg) |fg| {
|
||||
nc.channels_set_fg_rgb(&channels, fg) catch {};
|
||||
nc.channels_set_fg_alpha(&channels, nc.ALPHA_OPAQUE) catch {};
|
||||
}
|
||||
if (style.bg) |bg| {
|
||||
nc.channels_set_bg_rgb(&channels, bg) catch {};
|
||||
nc.channels_set_bg_alpha(&channels, nc.ALPHA_TRANSPARENT) catch {};
|
||||
}
|
||||
plane.set_channels(channels);
|
||||
if (style.fs) |fs| switch (fs) {
|
||||
.normal => plane.set_styles(nc.style.none),
|
||||
.bold => plane.set_styles(nc.style.bold),
|
||||
.italic => plane.set_styles(nc.style.italic),
|
||||
.underline => plane.set_styles(nc.style.underline),
|
||||
.strikethrough => plane.set_styles(nc.style.struck),
|
||||
};
|
||||
}
|
||||
|
||||
fn menu_action_help(_: *void, _: *Button.State(void)) void {
|
||||
fn menu_on_render(_: *Self, button: *Button.State(*Menu.State(*Self)), theme: *const Widget.Theme) bool {
|
||||
const style_base = if (button.active) theme.editor_cursor else if (button.hover) theme.editor_selection else theme.editor;
|
||||
const bg_alpha: c_uint = if (button.active or button.hover) nc.ALPHA_OPAQUE else nc.ALPHA_TRANSPARENT;
|
||||
try tui.set_base_style_alpha(button.plane, " ", style_base, nc.ALPHA_OPAQUE, bg_alpha);
|
||||
button.plane.erase();
|
||||
button.plane.home();
|
||||
const style_subtext = if (tui.find_scope_style(theme, "comment")) |sty| sty.style else theme.editor;
|
||||
const style_text = if (tui.find_scope_style(theme, "keyword")) |sty| sty.style else theme.editor;
|
||||
const style_keybind = if (tui.find_scope_style(theme, "entity.name")) |sty| sty.style else theme.editor;
|
||||
const sep = std.mem.indexOfScalar(u8, button.opts.label, ':') orelse button.opts.label.len;
|
||||
set_style(button.plane, style_subtext);
|
||||
set_style(button.plane, style_text);
|
||||
_ = button.plane.print(" {s}", .{button.opts.label[0..sep]}) catch {};
|
||||
set_style(button.plane, style_keybind);
|
||||
_ = button.plane.print("{s}", .{button.opts.label[sep + 1 ..]}) catch {};
|
||||
return false;
|
||||
}
|
||||
|
||||
fn menu_action_help(_: *Menu.State(*Self), _: *Button.State(*Menu.State(*Self))) void {
|
||||
command.executeName("open_help", .{}) catch {};
|
||||
}
|
||||
|
||||
fn menu_action_open_file(_: *void, _: *Button.State(void)) void {
|
||||
fn menu_action_open_file(_: *Menu.State(*Self), _: *Button.State(*Menu.State(*Self))) void {
|
||||
command.executeName("enter_open_file_mode", .{}) catch {};
|
||||
}
|
||||
|
||||
fn menu_action_open_recent_file(_: *void, _: *Button.State(void)) void {
|
||||
fn menu_action_open_recent_file(_: *Menu.State(*Self), _: *Button.State(*Menu.State(*Self))) void {
|
||||
tp.self_pid().send(.{ "log", "home", "open recent file not implemented" }) catch {};
|
||||
}
|
||||
|
||||
fn menu_action_open_recent_project(_: *void, _: *Button.State(void)) void {
|
||||
fn menu_action_open_recent_project(_: *Menu.State(*Self), _: *Button.State(*Menu.State(*Self))) void {
|
||||
tp.self_pid().send(.{ "log", "home", "open recent project not implemented" }) catch {};
|
||||
}
|
||||
|
||||
fn menu_action_show_commands(_: *void, _: *Button.State(void)) void {
|
||||
fn menu_action_show_commands(_: *Menu.State(*Self), _: *Button.State(*Menu.State(*Self))) void {
|
||||
tp.self_pid().send(.{ "log", "home", "open command palette not implemented" }) catch {};
|
||||
}
|
||||
|
||||
fn menu_action_open_config(_: *void, _: *Button.State(void)) void {
|
||||
fn menu_action_open_config(_: *Menu.State(*Self), _: *Button.State(*Menu.State(*Self))) void {
|
||||
command.executeName("open_config", .{}) catch {};
|
||||
}
|
||||
|
||||
fn menu_action_quit(_: *void, _: *Button.State(void)) void {
|
||||
fn menu_action_quit(_: *Menu.State(*Self), _: *Button.State(*Menu.State(*Self))) void {
|
||||
command.executeName("quit", .{}) catch {};
|
||||
}
|
||||
|
||||
pub fn render(self: *Self, theme: *const Widget.Theme) bool {
|
||||
const more = self.menu.render(theme);
|
||||
|
||||
tui.set_base_style(&self.plane, " ", theme.editor);
|
||||
try tui.set_base_style_alpha(self.background, " ", theme.editor, nc.ALPHA_OPAQUE, nc.ALPHA_TRANSPARENT);
|
||||
self.background.erase();
|
||||
self.background.home();
|
||||
try tui.set_base_style_alpha(self.plane, "", theme.editor, nc.ALPHA_TRANSPARENT, nc.ALPHA_TRANSPARENT);
|
||||
self.plane.erase();
|
||||
self.plane.home();
|
||||
if (self.fire) |*fire| fire.render() catch unreachable;
|
||||
|
@ -125,56 +159,42 @@ pub fn render(self: *Self, theme: *const Widget.Theme) bool {
|
|||
const subtext = "a programmer's text editor";
|
||||
|
||||
if (self.plane.dim_x() > 120 and self.plane.dim_y() > 22) {
|
||||
tui.set_style(&self.plane, style_title);
|
||||
set_style(self.plane, style_title);
|
||||
self.plane.cursor_move_yx(2, 4) catch return more;
|
||||
fonts.print_string_large(self.plane, title) catch return more;
|
||||
|
||||
tui.set_style(&self.plane, style_subtext);
|
||||
set_style(self.plane, style_subtext);
|
||||
self.plane.cursor_move_yx(10, 8) catch return more;
|
||||
fonts.print_string_medium(self.plane, subtext) catch return more;
|
||||
|
||||
self.menu.resize(.{ .y = 15, .x = 10, .w = 32 });
|
||||
} else if (self.plane.dim_x() > 55 and self.plane.dim_y() > 16) {
|
||||
tui.set_style(&self.plane, style_title);
|
||||
set_style(self.plane, style_title);
|
||||
self.plane.cursor_move_yx(2, 4) catch return more;
|
||||
fonts.print_string_medium(self.plane, title) catch return more;
|
||||
|
||||
tui.set_style(&self.plane, style_subtext);
|
||||
set_style(self.plane, style_subtext);
|
||||
self.plane.cursor_move_yx(7, 6) catch return more;
|
||||
_ = self.plane.print(subtext, .{}) catch {};
|
||||
|
||||
self.menu.resize(.{ .y = 9, .x = 8, .w = 32 });
|
||||
} else {
|
||||
tui.set_style(&self.plane, style_title);
|
||||
set_style(self.plane, style_title);
|
||||
self.plane.cursor_move_yx(1, 4) catch return more;
|
||||
_ = self.plane.print(title, .{}) catch return more;
|
||||
|
||||
tui.set_style(&self.plane, style_subtext);
|
||||
set_style(self.plane, style_subtext);
|
||||
self.plane.cursor_move_yx(3, 6) catch return more;
|
||||
_ = self.plane.print(subtext, .{}) catch {};
|
||||
|
||||
self.menu.resize(.{ .y = 5, .x = 8, .w = 32 });
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
fn render_menu_item(_: *void, button: *Button.State(void), theme: *const Widget.Theme) bool {
|
||||
tui.set_base_style(&button.plane, " ", if (button.active) theme.editor_cursor else if (button.hover) theme.editor_selection else theme.editor);
|
||||
button.plane.erase();
|
||||
button.plane.home();
|
||||
const style_subtext = if (tui.find_scope_style(theme, "comment")) |sty| sty.style else theme.editor;
|
||||
const style_text = if (tui.find_scope_style(theme, "keyword")) |sty| sty.style else theme.editor;
|
||||
const style_keybind = if (tui.find_scope_style(theme, "entity.name")) |sty| sty.style else theme.editor;
|
||||
const sep = std.mem.indexOfScalar(u8, button.opts.label, ':') orelse button.opts.label.len;
|
||||
tui.set_style(&button.plane, style_subtext);
|
||||
tui.set_style(&button.plane, style_text);
|
||||
_ = button.plane.print(" {s}", .{button.opts.label[0..sep]}) catch {};
|
||||
tui.set_style(&button.plane, style_keybind);
|
||||
_ = button.plane.print("{s}", .{button.opts.label[sep + 1 ..]}) catch {};
|
||||
return false;
|
||||
return more or self.fire != null;
|
||||
}
|
||||
|
||||
pub fn handle_resize(self: *Self, pos: Widget.Box) void {
|
||||
self.background.move_yx(@intCast(pos.y), @intCast(pos.x)) catch return;
|
||||
self.background.resize_simple(@intCast(pos.h), @intCast(pos.w)) catch return;
|
||||
self.plane.move_yx(@intCast(pos.y), @intCast(pos.x)) catch return;
|
||||
self.plane.resize_simple(@intCast(pos.h), @intCast(pos.w)) catch return;
|
||||
if (self.fire) |*fire| {
|
||||
|
@ -193,7 +213,7 @@ const cmds = struct {
|
|||
self.fire = if (self.fire) |*fire| ret: {
|
||||
fire.deinit();
|
||||
break :ret null;
|
||||
} else Fire.init(self.a, self.plane, Widget.Box.from(self.plane)) catch |e| return tp.exit_error(e);
|
||||
} else Fire.init(self.a, self.background, Widget.Box.from(self.background)) catch |e| return tp.exit_error(e);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -87,10 +87,9 @@ pub fn update(self: *Self) void {
|
|||
}
|
||||
|
||||
pub fn render(self: *Self, theme: *const Widget.Theme) bool {
|
||||
var more = self.widgets.render(theme);
|
||||
if (self.floating_views.render(theme))
|
||||
more = true;
|
||||
return more;
|
||||
const widgets_more = self.widgets.render(theme);
|
||||
const views_more = self.floating_views.render(theme);
|
||||
return widgets_more or views_more;
|
||||
}
|
||||
|
||||
pub fn resize(self: *Self) void {
|
||||
|
|
|
@ -72,6 +72,7 @@ fn mapPress(self: *Self, keypress: u32, egc: u32, modifiers: u32) tp.result {
|
|||
if (self.leader) |_| return self.mapFollower(keynormal, egc, modifiers);
|
||||
return switch (modifiers) {
|
||||
mod.CTRL => switch (keynormal) {
|
||||
'E' => self.cmd("enter_overlay_mode", command.fmt(.{"open_recent"})),
|
||||
'J' => self.cmd("toggle_logview", .{}),
|
||||
'Z' => self.cmd("undo", .{}),
|
||||
'Y' => self.cmd("redo", .{}),
|
||||
|
|
108
src/tui/mode/overlay/open_recent.zig
Normal file
108
src/tui/mode/overlay/open_recent.zig
Normal file
|
@ -0,0 +1,108 @@
|
|||
const std = @import("std");
|
||||
const nc = @import("notcurses");
|
||||
const tp = @import("thespian");
|
||||
const root = @import("root");
|
||||
|
||||
const tui = @import("../../tui.zig");
|
||||
const command = @import("../../command.zig");
|
||||
const EventHandler = @import("../../EventHandler.zig");
|
||||
const Button = @import("../../Button.zig");
|
||||
const Menu = @import("../../Menu.zig");
|
||||
const mainview = @import("../../mainview.zig");
|
||||
|
||||
const Self = @This();
|
||||
|
||||
a: std.mem.Allocator,
|
||||
f: usize = 0,
|
||||
menu: *Menu.State(*Self),
|
||||
|
||||
pub fn create(a: std.mem.Allocator) !tui.Mode {
|
||||
const mv = if (tui.current().mainview.dynamic_cast(mainview)) |mv_| mv_ else return error.NotFound;
|
||||
const self: *Self = try a.create(Self);
|
||||
self.* = .{
|
||||
.a = a,
|
||||
.menu = try Menu.create(*Self, a, tui.current().mainview, .{ .ctx = self }),
|
||||
};
|
||||
try self.menu.add_item_with_handler("open help", menu_action_help);
|
||||
self.menu.resize(.{ .y = 0, .x = 25, .w = 32 });
|
||||
try mv.floating_views.add(self.menu.menu_widget);
|
||||
return .{
|
||||
.handler = EventHandler.to_owned(self),
|
||||
.name = " open recent",
|
||||
.description = "open recent",
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
if (tui.current().mainview.dynamic_cast(mainview)) |mv|
|
||||
mv.floating_views.remove(self.menu.menu_widget);
|
||||
self.a.destroy(self);
|
||||
}
|
||||
|
||||
fn menu_action_help(_: *Menu.State(*Self), _: *Button.State(*Menu.State(*Self))) void {
|
||||
command.executeName("open_help", .{}) catch {};
|
||||
}
|
||||
|
||||
pub fn receive(self: *Self, _: tp.pid_ref, m: tp.message) error{Exit}!bool {
|
||||
var evtype: u32 = undefined;
|
||||
var keypress: u32 = undefined;
|
||||
var modifiers: u32 = undefined;
|
||||
|
||||
if (try m.match(.{ "I", tp.extract(&evtype), tp.extract(&keypress), tp.any, tp.string, tp.extract(&modifiers) })) {
|
||||
try self.mapEvent(evtype, keypress, modifiers);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fn mapEvent(self: *Self, evtype: u32, keypress: u32, modifiers: u32) tp.result {
|
||||
return switch (evtype) {
|
||||
nc.event_type.PRESS => self.mapPress(keypress, modifiers),
|
||||
else => {},
|
||||
};
|
||||
}
|
||||
|
||||
fn mapPress(self: *Self, keypress: u32, modifiers: u32) tp.result {
|
||||
const keynormal = if ('a' <= keypress and keypress <= 'z') keypress - ('a' - 'A') else keypress;
|
||||
return switch (modifiers) {
|
||||
nc.mod.CTRL => switch (keynormal) {
|
||||
'J' => self.cmd("toggle_logview", .{}),
|
||||
'Q' => self.cmd("quit", .{}),
|
||||
'W' => self.cmd("close_file", .{}),
|
||||
else => {},
|
||||
},
|
||||
nc.mod.CTRL | nc.mod.SHIFT => switch (keynormal) {
|
||||
'Q' => self.cmd("quit_without_saving", .{}),
|
||||
'R' => self.cmd("restart", .{}),
|
||||
'L' => self.cmd_async("toggle_logview"),
|
||||
'I' => self.cmd_async("toggle_inputview"),
|
||||
else => {},
|
||||
},
|
||||
nc.mod.ALT => switch (keynormal) {
|
||||
'L' => self.cmd("toggle_logview", .{}),
|
||||
'I' => self.cmd("toggle_inputview", .{}),
|
||||
else => {},
|
||||
},
|
||||
0 => switch (keypress) {
|
||||
nc.key.F09 => self.cmd("theme_prev", .{}),
|
||||
nc.key.F10 => self.cmd("theme_next", .{}),
|
||||
nc.key.F11 => self.cmd("toggle_logview", .{}),
|
||||
nc.key.F12 => self.cmd("toggle_inputview", .{}),
|
||||
nc.key.ESC => self.cmd("exit_overlay_mode", .{}),
|
||||
nc.key.ENTER => self.cmd("exit_overlay_mode", .{}),
|
||||
else => {},
|
||||
},
|
||||
else => {},
|
||||
};
|
||||
}
|
||||
|
||||
fn cmd(_: *Self, name_: []const u8, ctx: command.Context) tp.result {
|
||||
try command.executeName(name_, ctx);
|
||||
}
|
||||
|
||||
fn msg(_: *Self, text: []const u8) tp.result {
|
||||
return tp.self_pid().send(.{ "log", "home", text });
|
||||
}
|
||||
|
||||
fn cmd_async(_: *Self, name_: []const u8) tp.result {
|
||||
return tp.self_pid().send(.{ "cmd", name_ });
|
||||
}
|
|
@ -14,7 +14,8 @@ const ed = @import("../editor.zig");
|
|||
const tui = @import("../tui.zig");
|
||||
|
||||
pub fn create(a: Allocator, parent: nc.Plane) !Widget {
|
||||
return Button.create({}, a, parent, .{
|
||||
return Button.create(void, a, parent, .{
|
||||
.ctx = {},
|
||||
.label = tui.get_mode(),
|
||||
.on_click = on_click,
|
||||
.on_layout = layout,
|
||||
|
@ -22,7 +23,7 @@ pub fn create(a: Allocator, parent: nc.Plane) !Widget {
|
|||
});
|
||||
}
|
||||
|
||||
pub fn layout(_: *void, _: *Button.State(void)) Widget.Layout {
|
||||
pub fn layout(_: void, _: *Button.State(void)) Widget.Layout {
|
||||
const name = tui.get_mode();
|
||||
const width = Buffer.egc_chunk_width(name, 0);
|
||||
const padding: usize = if (is_mini_mode()) 3 else 2;
|
||||
|
@ -33,7 +34,7 @@ fn is_mini_mode() bool {
|
|||
return if (tui.current().mini_mode) |_| true else false;
|
||||
}
|
||||
|
||||
pub fn render(state: *void, self: *Button.State(void), theme: *const Widget.Theme) bool {
|
||||
pub fn render(_: void, self: *Button.State(void), theme: *const Widget.Theme) bool {
|
||||
tui.set_base_style(&self.plane, " ", if (self.active) theme.editor_cursor else if (self.hover) theme.editor_selection else theme.statusbar_hover);
|
||||
self.plane.on_styles(nc.style.bold);
|
||||
self.plane.erase();
|
||||
|
@ -41,16 +42,16 @@ pub fn render(state: *void, self: *Button.State(void), theme: *const Widget.Them
|
|||
var buf: [31:0]u8 = undefined;
|
||||
_ = self.plane.putstr(std.fmt.bufPrintZ(&buf, " {s} ", .{tui.get_mode()}) catch return false) catch {};
|
||||
if (is_mini_mode())
|
||||
render_separator(state, self, theme);
|
||||
render_separator(self, theme);
|
||||
return false;
|
||||
}
|
||||
|
||||
fn render_separator(_: *void, self: *Button.State(void), theme: *const Widget.Theme) void {
|
||||
fn render_separator(self: *Button.State(void), theme: *const Widget.Theme) void {
|
||||
if (theme.statusbar_hover.bg) |bg| self.plane.set_fg_rgb(bg) catch {};
|
||||
if (theme.statusbar.bg) |bg| self.plane.set_bg_rgb(bg) catch {};
|
||||
_ = self.plane.putstr("") catch {};
|
||||
}
|
||||
|
||||
fn on_click(_: *void, _: *Button.State(void)) void {
|
||||
fn on_click(_: void, _: *Button.State(void)) void {
|
||||
command.executeName("toggle_input_mode", .{}) catch {};
|
||||
}
|
||||
|
|
|
@ -692,6 +692,10 @@ const cmds = struct {
|
|||
return tp.exit_error(error.InvalidArgument);
|
||||
if (self.mini_mode) |_| try cmds.exit_mini_mode(self, .{});
|
||||
if (self.input_mode) |*m| m.deinit();
|
||||
if (self.input_mode_outer) |*m| {
|
||||
m.deinit();
|
||||
self.input_mode_outer = null;
|
||||
}
|
||||
self.input_mode = if (std.mem.eql(u8, mode, "vim/normal"))
|
||||
@import("mode/input/vim/normal.zig").create(self.a) catch |e| return tp.exit_error(e)
|
||||
else if (std.mem.eql(u8, mode, "vim/insert"))
|
||||
|
@ -713,6 +717,34 @@ const cmds = struct {
|
|||
return enter_mode(self, Ctx.fmt(.{self.config.input_mode}));
|
||||
}
|
||||
|
||||
pub fn enter_overlay_mode(self: *Self, ctx: Ctx) tp.result {
|
||||
var mode: []const u8 = undefined;
|
||||
if (!try ctx.args.match(.{tp.extract(&mode)}))
|
||||
return tp.exit_error(error.InvalidArgument);
|
||||
if (self.mini_mode) |_| try cmds.exit_mini_mode(self, .{});
|
||||
if (self.input_mode_outer) |*m| {
|
||||
m.deinit();
|
||||
self.input_mode_outer = null;
|
||||
}
|
||||
self.input_mode = if (std.mem.eql(u8, mode, "open_recent")) ret: {
|
||||
self.input_mode_outer = self.input_mode;
|
||||
break :ret @import("mode/overlay/open_recent.zig").create(self.a) catch |e| return tp.exit_error(e);
|
||||
} else {
|
||||
self.logger.print("unknown mode {s}", .{mode});
|
||||
return;
|
||||
};
|
||||
self.logger.print("input mode: {s}", .{(self.input_mode orelse return).description});
|
||||
}
|
||||
|
||||
pub fn exit_overlay_mode(self: *Self, _: Ctx) tp.result {
|
||||
if (self.input_mode_outer) |_| {} else return;
|
||||
defer {
|
||||
self.input_mode = self.input_mode_outer;
|
||||
self.input_mode_outer = null;
|
||||
}
|
||||
if (self.input_mode) |*mode| mode.deinit();
|
||||
}
|
||||
|
||||
pub fn enter_find_mode(self: *Self, ctx: Ctx) tp.result {
|
||||
return enter_mini_mode(self, @import("mode/mini/find.zig"), ctx);
|
||||
}
|
||||
|
@ -755,6 +787,7 @@ const cmds = struct {
|
|||
if (self.mini_mode) |_| {} else return;
|
||||
defer {
|
||||
self.input_mode = self.input_mode_outer;
|
||||
self.input_mode_outer = null;
|
||||
self.mini_mode = null;
|
||||
}
|
||||
if (self.input_mode) |*mode| mode.deinit();
|
||||
|
@ -919,6 +952,21 @@ pub inline fn set_base_style(plane: *const nc.Plane, egc: [*c]const u8, style: W
|
|||
_ = plane.set_base(egc, 0, channels) catch {};
|
||||
}
|
||||
|
||||
pub fn set_base_style_alpha(plane: nc.Plane, egc: [*:0]const u8, style: Widget.Theme.Style, fg_alpha: c_uint, bg_alpha: c_uint) !void {
|
||||
var channels: u64 = 0;
|
||||
if (style.fg) |fg| {
|
||||
nc.channels_set_fg_rgb(&channels, fg) catch {};
|
||||
nc.channels_set_fg_alpha(&channels, fg_alpha) catch {};
|
||||
}
|
||||
if (style.bg) |bg| {
|
||||
nc.channels_set_bg_rgb(&channels, bg) catch {};
|
||||
nc.channels_set_bg_alpha(&channels, bg_alpha) catch {};
|
||||
}
|
||||
if (style.fg) |fg| plane.set_fg_rgb(fg) catch {};
|
||||
if (style.bg) |bg| plane.set_bg_rgb(bg) catch {};
|
||||
_ = plane.set_base(egc, 0, channels) catch {};
|
||||
}
|
||||
|
||||
pub inline fn set_style(plane: *const nc.Plane, style: Widget.Theme.Style) void {
|
||||
var channels: u64 = 0;
|
||||
channels_from_style(&channels, style);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue