feat: add keyboard support to menus
This commit is contained in:
parent
ad2d82ce43
commit
2c4452dd81
5 changed files with 107 additions and 23 deletions
|
@ -12,16 +12,16 @@ pub fn Options(context: type) type {
|
|||
ctx: Context,
|
||||
|
||||
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_render: *const fn (ctx: context, button: *Button.State(*State(Context)), theme: *const Widget.Theme, selected: bool) bool = on_render_default,
|
||||
on_layout: *const fn (ctx: context, button: *Button.State(*State(Context))) Widget.Layout = on_layout_default,
|
||||
on_resize: *const fn (ctx: context, menu: *State(Context), box: Widget.Box) void = on_resize_default,
|
||||
|
||||
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;
|
||||
pub fn on_render_default(_: context, button: *Button.State(*State(Context)), theme: *const Widget.Theme, selected: bool) bool {
|
||||
const style_base = if (button.active) theme.editor_cursor else if (button.hover or selected) theme.editor_selection else theme.editor;
|
||||
const bg_alpha: c_uint = if (button.active or button.hover or selected) 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();
|
||||
|
@ -49,7 +49,8 @@ pub fn create(ctx_type: type, a: std.mem.Allocator, parent: Widget, opts: Option
|
|||
.menu_widget = self.menu.widget(),
|
||||
.opts = opts,
|
||||
};
|
||||
self.menu.on_resize_ctx = self;
|
||||
self.menu.ctx = self;
|
||||
self.menu.on_render = State(ctx_type).on_render_menu_widgetlist;
|
||||
self.menu.on_resize = State(ctx_type).on_resize_menu_widgetlist;
|
||||
return self;
|
||||
}
|
||||
|
@ -59,9 +60,14 @@ pub fn State(ctx_type: type) type {
|
|||
a: std.mem.Allocator,
|
||||
menu: *WidgetList,
|
||||
menu_widget: Widget,
|
||||
opts: Options(ctx_type),
|
||||
opts: options_type,
|
||||
selected: ?usize = null,
|
||||
render_idx: usize = 0,
|
||||
selected_active: bool = false,
|
||||
|
||||
const Self = @This();
|
||||
const options_type = Options(ctx_type);
|
||||
const button_type = Button.State(*Self);
|
||||
|
||||
pub fn deinit(self: *Self, a: std.mem.Allocator) void {
|
||||
self.menu.deinit(a);
|
||||
|
@ -92,12 +98,19 @@ pub fn State(ctx_type: type) type {
|
|||
return self.menu.render(theme);
|
||||
}
|
||||
|
||||
fn on_render_menu_widgetlist(ctx: ?*anyopaque, _: *const Widget.Theme) void {
|
||||
const self: *Self = @ptrCast(@alignCast(ctx));
|
||||
self.render_idx = 0;
|
||||
}
|
||||
|
||||
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);
|
||||
defer self.render_idx += 1;
|
||||
std.debug.assert(self.render_idx < self.menu.widgets.items.len);
|
||||
return self.opts.on_render(self.opts.ctx, button, theme, self.render_idx == self.selected);
|
||||
}
|
||||
|
||||
fn on_resize_menu_widgetlist(ctx: ?*anyopaque, _: *WidgetList, box: Widget.Box) void {
|
||||
|
@ -116,5 +129,31 @@ pub fn State(ctx_type: type) type {
|
|||
pub fn walk(self: *Self, walk_ctx: *anyopaque, f: Widget.WalkFn) bool {
|
||||
return self.menu.walk(walk_ctx, f, &self.menu_widget);
|
||||
}
|
||||
|
||||
pub fn count(self: *Self) usize {
|
||||
return self.menu.widgets.items.len;
|
||||
}
|
||||
|
||||
pub fn select_down(self: *Self) void {
|
||||
const current = self.selected orelse {
|
||||
if (self.count() > 0)
|
||||
self.selected = 0;
|
||||
return;
|
||||
};
|
||||
self.selected = @min(current + 1, self.count() - 1);
|
||||
}
|
||||
|
||||
pub fn select_up(self: *Self) void {
|
||||
if (self.selected) |current| {
|
||||
self.selected = if (self.count() > 0) @min(self.count() - 1, @max(current, 1) - 1) else null;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn activate_selected(self: *Self) void {
|
||||
const selected = self.selected orelse return;
|
||||
self.selected_active = true;
|
||||
const button = self.menu.widgets.items[selected].widget.dynamic_cast(button_type) orelse return;
|
||||
button.opts.on_click(button.opts.ctx, button);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue