this repo has no description
13
fork

Configure Feed

Select the types of activity you want to include in your feed.

vxfw: fix focus handler

The focus handler was clearing memory after each frame, losing track of
the focused widget unless a new request was made. Properly handle focus
nodes and focused widgets. Send focus_in and focus_out events on
requestFocus

+38 -19
+38 -19
src/vxfw/App.zig
··· 139 139 } 140 140 } 141 141 142 + if (self.wants_focus) |wants_focus| { 143 + try focus_handler.focusWidget(&ctx, wants_focus); 144 + try self.handleCommand(&ctx.cmds); 145 + } 146 + 142 147 // Check if we should quit 143 148 if (ctx.quit) return; 144 149 ··· 164 169 win.setCursorShape(.default); 165 170 const surface = try widget.draw(draw_context); 166 171 167 - const focused = self.wants_focus orelse focus_handler.focused.widget; 168 172 const root_win = win.child(.{ 169 173 .width = surface.size.width, 170 174 .height = surface.size.height, 171 175 }); 172 - surface.render(root_win, focused); 176 + surface.render(root_win, focus_handler.focused_widget); 173 177 try vx.render(buffered.writer().any()); 174 178 try buffered.flush(); 175 179 176 180 // Store the last frame 177 181 mouse_handler.last_frame = surface; 178 - try focus_handler.update(surface, self.wants_focus); 182 + try focus_handler.update(surface); 179 183 self.wants_focus = null; 180 184 } 181 185 } ··· 346 350 347 351 root: Node, 348 352 focused: *Node, 353 + focused_widget: vxfw.Widget, 349 354 path_to_focused: std.ArrayList(Widget), 350 - maybe_wants_focus: ?vxfw.Widget = null, 351 355 352 356 const Node = struct { 353 357 widget: Widget, ··· 460 464 return .{ 461 465 .root = node, 462 466 .focused = undefined, 467 + .focused_widget = root, 463 468 .arena = std.heap.ArenaAllocator.init(allocator), 464 - .maybe_wants_focus = null, 465 469 .path_to_focused = std.ArrayList(Widget).init(allocator), 466 470 }; 467 471 } ··· 476 480 } 477 481 478 482 /// Update the focus list 479 - fn update(self: *FocusHandler, root: vxfw.Surface, maybe_wants_focus: ?vxfw.Widget) Allocator.Error!void { 483 + fn update(self: *FocusHandler, root: vxfw.Surface) Allocator.Error!void { 480 484 _ = self.arena.reset(.retain_capacity); 481 - self.maybe_wants_focus = maybe_wants_focus; 482 485 483 486 var list = std.ArrayList(*Node).init(self.arena.allocator()); 484 487 for (root.children) |child| { 485 488 try self.findFocusableChildren(&self.root, &list, child.surface); 486 489 } 490 + 491 + // Update children 492 + self.root.children = list.items; 493 + 494 + // Update path 487 495 self.path_to_focused.clearAndFree(); 496 + if (!self.root.widget.eql(root.widget)) { 497 + // Always make sure the root widget (the one we started with) is the first item, even if 498 + // it isn't focusable or in the path 499 + try self.path_to_focused.append(self.root.widget); 500 + } 488 501 _ = try childHasFocus(root, &self.path_to_focused, self.focused.widget); 489 - try self.path_to_focused.append(root.widget); 502 + 490 503 // reverse path_to_focused so that it is root first 491 504 std.mem.reverse(Widget, self.path_to_focused.items); 492 - self.root = .{ 493 - .widget = root.widget, 494 - .children = list.items, 495 - .parent = null, 496 - }; 497 505 } 498 506 499 507 /// Returns true if a child of surface is the focused widget ··· 524 532 list: *std.ArrayList(*Node), 525 533 surface: vxfw.Surface, 526 534 ) Allocator.Error!void { 527 - if (surface.focusable) { 535 + if (self.root.widget.eql(surface.widget)) { 536 + // Never add the root_widget. We will always have this as the root 537 + for (surface.children) |child| { 538 + try self.findFocusableChildren(parent, list, child.surface); 539 + } 540 + } else if (surface.focusable) { 528 541 // We are a focusable child of parent. Create a new node, and find our own focusable 529 542 // children 530 543 const node = try self.arena.allocator().create(Node); ··· 537 550 .parent = parent, 538 551 .children = child_list.items, 539 552 }; 540 - if (self.maybe_wants_focus) |wants_focus| { 541 - if (wants_focus.eql(surface.widget)) { 542 - self.focused = node; 543 - self.maybe_wants_focus = null; 544 - } 553 + if (self.focused_widget.eql(surface.widget)) { 554 + self.focused = node; 545 555 } 546 556 try list.append(node); 547 557 } else { ··· 549 559 try self.findFocusableChildren(parent, list, child.surface); 550 560 } 551 561 } 562 + } 563 + 564 + fn focusWidget(self: *FocusHandler, ctx: *vxfw.EventContext, widget: vxfw.Widget) anyerror!void { 565 + if (self.focused_widget.eql(widget)) return; 566 + 567 + ctx.phase = .at_target; 568 + try self.focused_widget.handleEvent(ctx, .focus_out); 569 + self.focused_widget = widget; 570 + try self.focused_widget.handleEvent(ctx, .focus_in); 552 571 } 553 572 554 573 fn focusNode(self: *FocusHandler, ctx: *vxfw.EventContext, node: *Node) anyerror!void {