this repo has no description
13
fork

Configure Feed

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

vxfw(App): implement event capturing phase

+66 -27
+61 -23
src/vxfw/App.zig
··· 11 11 12 12 const App = @This(); 13 13 14 - quit_key: vaxis.Key = .{ .codepoint = 'c', .mods = .{ .ctrl = true } }, 15 - 16 14 allocator: Allocator, 17 15 tty: vaxis.Tty, 18 16 vx: vaxis.Vaxis, ··· 85 83 var mouse_handler = MouseHandler.init(widget); 86 84 var focus_handler = FocusHandler.init(self.allocator, widget); 87 85 focus_handler.intrusiveInit(); 86 + try focus_handler.path_to_focused.append(widget); 88 87 defer focus_handler.deinit(); 89 88 90 89 // Timestamp of our next frame ··· 92 91 93 92 // Create our event context 94 93 var ctx: vxfw.EventContext = .{ 95 - .phase = .at_target, 94 + .phase = .capturing, 96 95 .cmds = vxfw.CommandList.init(self.allocator), 97 96 .consume_event = false, 98 97 .redraw = false, ··· 114 113 try self.checkTimers(&ctx); 115 114 116 115 while (loop.tryEvent()) |event| { 117 - ctx.consume_event = false; 116 + defer { 117 + // Reset our context 118 + ctx.consume_event = false; 119 + ctx.phase = .capturing; 120 + ctx.cmds.clearRetainingCapacity(); 121 + } 118 122 switch (event) { 119 - .key_press => |key| { 123 + .key_press => { 120 124 try focus_handler.handleEvent(&ctx, event); 121 125 try self.handleCommand(&ctx.cmds); 122 - if (!ctx.consume_event) { 123 - if (key.matches(self.quit_key.codepoint, self.quit_key.mods)) { 124 - ctx.quit = true; 125 - } 126 - if (key.matches(vaxis.Key.tab, .{})) { 127 - try focus_handler.focusNext(&ctx); 128 - try self.handleCommand(&ctx.cmds); 129 - } 130 - if (key.matches(vaxis.Key.tab, .{ .shift = true })) { 131 - try focus_handler.focusPrev(&ctx); 132 - try self.handleCommand(&ctx.cmds); 133 - } 134 - } 135 126 }, 136 127 .focus_out => try mouse_handler.mouseExit(self, &ctx), 137 128 .mouse => |mouse| try mouse_handler.handleMouse(self, &ctx, mouse), ··· 141 132 ctx.redraw = true; 142 133 }, 143 134 else => { 144 - try widget.handleEvent(&ctx, event); 135 + try focus_handler.handleEvent(&ctx, event); 145 136 try self.handleCommand(&ctx.cmds); 146 137 }, 147 138 } ··· 286 277 287 278 root: Node, 288 279 focused: *Node, 280 + path_to_focused: std.ArrayList(Widget), 289 281 maybe_wants_focus: ?vxfw.Widget = null, 290 282 291 283 const Node = struct { ··· 401 393 .focused = undefined, 402 394 .arena = std.heap.ArenaAllocator.init(allocator), 403 395 .maybe_wants_focus = null, 396 + .path_to_focused = std.ArrayList(Widget).init(allocator), 404 397 }; 405 398 } 406 399 ··· 421 414 for (root.children) |child| { 422 415 try self.findFocusableChildren(&self.root, &list, child.surface); 423 416 } 417 + self.path_to_focused.clearAndFree(); 418 + _ = try childHasFocus(root, &self.path_to_focused, self.focused.widget); 419 + try self.path_to_focused.append(root.widget); 420 + // reverse path_to_focused so that it is root first 421 + std.mem.reverse(Widget, self.path_to_focused.items); 424 422 self.root = .{ 425 423 .widget = root.widget, 426 424 .children = list.items, ··· 428 426 }; 429 427 } 430 428 429 + /// Returns true if a child of surface is the focused widget 430 + fn childHasFocus( 431 + surface: vxfw.Surface, 432 + list: *std.ArrayList(Widget), 433 + focused: Widget, 434 + ) Allocator.Error!bool { 435 + // Check if we are the focused widget 436 + if (focused.eql(surface.widget)) { 437 + try list.append(surface.widget); 438 + return true; 439 + } 440 + for (surface.children) |child| { 441 + // Add child to list if it is the focused widget or one of it's own children is 442 + if (try childHasFocus(child.surface, list, focused)) { 443 + try list.append(surface.widget); 444 + return true; 445 + } 446 + } 447 + return false; 448 + } 449 + 431 450 /// Walks the surface tree, adding all focusable nodes to list 432 451 fn findFocusableChildren( 433 452 self: *FocusHandler, ··· 481 500 } 482 501 483 502 fn handleEvent(self: *FocusHandler, ctx: *vxfw.EventContext, event: vxfw.Event) anyerror!void { 484 - var maybe_node: ?*Node = self.focused; 485 - while (maybe_node) |node| { 486 - try node.widget.handleEvent(ctx, event); 503 + const path = self.path_to_focused.items; 504 + if (path.len == 0) return; 505 + 506 + const target_idx = path.len - 1; 507 + 508 + // Capturing phase 509 + ctx.phase = .capturing; 510 + for (path[0..target_idx]) |widget| { 511 + try widget.handleEvent(ctx, event); 512 + if (ctx.consume_event) return; 513 + } 514 + 515 + // Target phase 516 + ctx.phase = .at_target; 517 + const target = path[target_idx]; 518 + try target.handleEvent(ctx, event); 519 + if (ctx.consume_event) return; 520 + 521 + // Bubbling phase 522 + ctx.phase = .bubbling; 523 + var iter = std.mem.reverseIterator(path[0..target_idx]); 524 + while (iter.next()) |widget| { 525 + try widget.handleEvent(ctx, event); 487 526 if (ctx.consume_event) return; 488 - maybe_node = node.parent; 489 527 } 490 528 } 491 529 };
+5 -4
src/vxfw/vxfw.zig
··· 86 86 quit: bool = false, 87 87 88 88 pub const Phase = enum { 89 - // TODO: Capturing phase 90 - // capturing, 89 + capturing, 91 90 at_target, 92 91 bubbling, 93 92 }; ··· 196 195 /// The Widget interface 197 196 pub const Widget = struct { 198 197 userdata: *anyopaque, 199 - eventHandler: *const fn (userdata: *anyopaque, ctx: *EventContext, event: Event) anyerror!void, 198 + eventHandler: ?*const fn (userdata: *anyopaque, ctx: *EventContext, event: Event) anyerror!void = null, 200 199 drawFn: *const fn (userdata: *anyopaque, ctx: DrawContext) Allocator.Error!Surface, 201 200 202 201 pub fn handleEvent(self: Widget, ctx: *EventContext, event: Event) anyerror!void { 203 - return self.eventHandler(self.userdata, ctx, event); 202 + if (self.eventHandler) |handle| { 203 + return handle(self.userdata, ctx, event); 204 + } 204 205 } 205 206 206 207 pub fn draw(self: Widget, ctx: DrawContext) Allocator.Error!Surface {