Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

vxfw fix focus #139

Merged
merged 2 commits into from
Dec 19, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 38 additions & 20 deletions src/vxfw/App.zig
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,6 @@ pub fn run(self: *App, widget: vxfw.Widget, opts: Options) anyerror!void {
// Reset our context
ctx.consume_event = false;
ctx.phase = .capturing;
ctx.cmds.clearRetainingCapacity();
}
switch (event) {
.key_press => {
Expand All @@ -139,6 +138,11 @@ pub fn run(self: *App, widget: vxfw.Widget, opts: Options) anyerror!void {
}
}

if (self.wants_focus) |wants_focus| {
try focus_handler.focusWidget(&ctx, wants_focus);
try self.handleCommand(&ctx.cmds);
}

// Check if we should quit
if (ctx.quit) return;

Expand All @@ -164,18 +168,17 @@ pub fn run(self: *App, widget: vxfw.Widget, opts: Options) anyerror!void {
win.setCursorShape(.default);
const surface = try widget.draw(draw_context);

const focused = self.wants_focus orelse focus_handler.focused.widget;
const root_win = win.child(.{
.width = surface.size.width,
.height = surface.size.height,
});
surface.render(root_win, focused);
surface.render(root_win, focus_handler.focused_widget);
try vx.render(buffered.writer().any());
try buffered.flush();

// Store the last frame
mouse_handler.last_frame = surface;
try focus_handler.update(surface, self.wants_focus);
try focus_handler.update(surface);
self.wants_focus = null;
}
}
Expand Down Expand Up @@ -346,8 +349,8 @@ const FocusHandler = struct {

root: Node,
focused: *Node,
focused_widget: vxfw.Widget,
path_to_focused: std.ArrayList(Widget),
maybe_wants_focus: ?vxfw.Widget = null,

const Node = struct {
widget: Widget,
Expand Down Expand Up @@ -460,8 +463,8 @@ const FocusHandler = struct {
return .{
.root = node,
.focused = undefined,
.focused_widget = root,
.arena = std.heap.ArenaAllocator.init(allocator),
.maybe_wants_focus = null,
.path_to_focused = std.ArrayList(Widget).init(allocator),
};
}
Expand All @@ -476,24 +479,28 @@ const FocusHandler = struct {
}

/// Update the focus list
fn update(self: *FocusHandler, root: vxfw.Surface, maybe_wants_focus: ?vxfw.Widget) Allocator.Error!void {
fn update(self: *FocusHandler, root: vxfw.Surface) Allocator.Error!void {
_ = self.arena.reset(.retain_capacity);
self.maybe_wants_focus = maybe_wants_focus;

var list = std.ArrayList(*Node).init(self.arena.allocator());
for (root.children) |child| {
try self.findFocusableChildren(&self.root, &list, child.surface);
}

// Update children
self.root.children = list.items;

// Update path
self.path_to_focused.clearAndFree();
if (!self.root.widget.eql(root.widget)) {
// Always make sure the root widget (the one we started with) is the first item, even if
// it isn't focusable or in the path
try self.path_to_focused.append(self.root.widget);
}
_ = try childHasFocus(root, &self.path_to_focused, self.focused.widget);
try self.path_to_focused.append(root.widget);

// reverse path_to_focused so that it is root first
std.mem.reverse(Widget, self.path_to_focused.items);
self.root = .{
.widget = root.widget,
.children = list.items,
.parent = null,
};
}

/// Returns true if a child of surface is the focused widget
Expand Down Expand Up @@ -524,7 +531,12 @@ const FocusHandler = struct {
list: *std.ArrayList(*Node),
surface: vxfw.Surface,
) Allocator.Error!void {
if (surface.focusable) {
if (self.root.widget.eql(surface.widget)) {
// Never add the root_widget. We will always have this as the root
for (surface.children) |child| {
try self.findFocusableChildren(parent, list, child.surface);
}
} else if (surface.focusable) {
// We are a focusable child of parent. Create a new node, and find our own focusable
// children
const node = try self.arena.allocator().create(Node);
Expand All @@ -537,11 +549,8 @@ const FocusHandler = struct {
.parent = parent,
.children = child_list.items,
};
if (self.maybe_wants_focus) |wants_focus| {
if (wants_focus.eql(surface.widget)) {
self.focused = node;
self.maybe_wants_focus = null;
}
if (self.focused_widget.eql(surface.widget)) {
self.focused = node;
}
try list.append(node);
} else {
Expand All @@ -551,6 +560,15 @@ const FocusHandler = struct {
}
}

fn focusWidget(self: *FocusHandler, ctx: *vxfw.EventContext, widget: vxfw.Widget) anyerror!void {
if (self.focused_widget.eql(widget)) return;

ctx.phase = .at_target;
try self.focused_widget.handleEvent(ctx, .focus_out);
self.focused_widget = widget;
try self.focused_widget.handleEvent(ctx, .focus_in);
}

fn focusNode(self: *FocusHandler, ctx: *vxfw.EventContext, node: *Node) anyerror!void {
if (self.focused.widget.eql(node.widget)) return;

Expand Down
Loading