╭ changed ──────╮ │src/ui/Auth.zig│ ╰───────────────╯ ╭───────────────────────────────────╮ │2 const ui = @import("../ui.zig");│ │3 const xit = @import("xit"); │ │4 const xitui = xit.xitui; │ │5 +const wgt = xitui.widget; │ │6 const layout = xitui.layout; │ │7 const Key = xitui.input.Key; │ │8 const Grid = xitui.grid.Grid; │ ╰───────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────╮ │24 } │ │25 │ │26 pub const View = struct { │ │26 - // own container focus so callers (e.g. wgt.Stack) see a stable id. the │ │27 - // inner views' focus ids change as login/logout swap; if we exposed those │ │28 - // directly, anything keyed off our id at one moment would mismatch the │ │29 - // focus tree the next frame. │ │30 - focus: *Focus, │ │31 - login: Login.View, │ │32 - logout: Logout.View, │ │27 + stack: wgt.Stack(ui.Widget), │ │28 session: *ui.Session, │ │29 │ │30 + const login_index = 0; │ │31 + const logout_index = 1; │ │32 + │ │33 pub fn init(allocator: std.mem.Allocator, data: *const Self, session: *ui.Session) !View { │ │36 - var login_view = try Login.View.init(allocator, &data.login, session); │ │37 - errdefer login_view.deinit(allocator); │ │38 - var logout_view = try Logout.View.init(allocator, &data.logout, session); │ │39 - errdefer logout_view.deinit(allocator); │ │34 + var stack = try wgt.Stack(ui.Widget).init(allocator); │ │35 + errdefer stack.deinit(allocator); │ │36 + { │ │37 + var login = try Login.View.init(allocator, &data.login, session); │ │38 + errdefer login.deinit(allocator); │ │39 + try stack.children.put(allocator, login.getFocus().id, .{ .auth_login = login }); │ │40 + } │ │41 + { │ │42 + var logout = try Logout.View.init(allocator, &data.logout, session); │ │43 + errdefer logout.deinit(allocator); │ │44 + try stack.children.put(allocator, logout.getFocus().id, .{ .auth_logout = logout });│ │45 + } │ │46 return .{ │ │41 - .focus = try Focus.create(allocator, .container), │ │42 - .login = login_view, │ │43 - .logout = logout_view, │ │47 + .stack = stack, │ │48 .session = session, │ │49 }; │ │50 } │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │51 │ │52 pub fn deinit(self: *View, allocator: std.mem.Allocator) void { │ │49 - self.focus.destroy(allocator); │ │50 - self.login.deinit(allocator); │ │51 - self.logout.deinit(allocator); │ │53 + self.stack.deinit(allocator); │ │54 } │ │55 │ │56 pub fn build(self: *View, allocator: std.mem.Allocator, constraint: layout.Constraint, root_focus: *Focus) !void {│ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────╮ │55 - self.focus.clear(); │ │56 - if (self.session.data.user_id != null) { │ │57 - try self.logout.build(allocator, constraint, root_focus); │ │58 - if (self.logout.getGrid()) |inner_grid| { │ │59 - try self.focus.addChild(allocator, self.logout.getFocus(), inner_grid.size, 0, 0);│ │60 - } │ │61 - self.focus.child_id = self.logout.getFocus().id; │ │62 - } else { │ │63 - try self.login.build(allocator, constraint, root_focus); │ │64 - if (self.login.getGrid()) |inner_grid| { │ │65 - try self.focus.addChild(allocator, self.login.getFocus(), inner_grid.size, 0, 0); │ │66 - } │ │67 - self.focus.child_id = self.login.getFocus().id; │ │68 - } │ │57 + const index: usize = if (self.session.data.user_id != null) logout_index else login_index;│ │58 + self.stack.getFocus().child_id = self.stack.children.keys()[index]; │ │59 + try self.stack.build(allocator, constraint, root_focus); │ │60 } │ │61 │ │62 pub fn input(self: *View, allocator: std.mem.Allocator, key: Key, root_focus: *Focus) !void { │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────╮ │72 - if (self.session.data.user_id != null) { │ │73 - try self.logout.input(allocator, key, root_focus);│ │74 - } else { │ │75 - try self.login.input(allocator, key, root_focus); │ │76 - } │ │63 + try self.stack.input(allocator, key, root_focus); │ │64 } │ │65 │ │66 pub fn clearGrid(self: *View) void { │ ╰──────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────╮ │80 - self.login.clearGrid(); │ │81 - self.logout.clearGrid(); │ │67 + self.stack.clearGrid(); │ │68 } │ │69 │ │70 pub fn getGrid(self: View) ?Grid {│ ╰──────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────╮ │85 - if (self.session.data.user_id != null) return self.logout.getGrid();│ │86 - return self.login.getGrid(); │ │71 + return self.stack.getGrid(); │ │72 } │ │73 │ │74 pub fn getFocus(self: *View) *Focus { │ ╰────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────╮ │90 - return self.focus; │ │75 + return self.stack.getFocus(); │ │76 } │ │77 │ │93 - pub fn atTop(self: View) bool { │ │94 - if (self.session.data.user_id != null) return self.logout.atTop();│ │95 - return self.login.atTop(); │ │78 + pub fn atTop(self: View, root_focus: *Focus) bool { │ │79 + const selected = self.stack.getSelected() orelse return false; │ │80 + return selected.atTop(root_focus); │ │81 } │ │82 }; │ │83 │ ╰──────────────────────────────────────────────────────────────────────────────╯ ╭ changed ──────────╮ │src/ui/Settings.zig│ ╰───────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │16 } │ │17 │ │18 pub const View = struct { │ │19 - // own container focus so callers see a stable id while login/logout swap │ │20 - // the shown view │ │21 - focus: *Focus, │ │22 - // the settings controls, shown when logged in │ │23 - authorized: ui.widget.Center, │ │24 - unauthorized: ui.Unauthorized.View, │ │19 + stack: wgt.Stack(ui.Widget), │ │20 session: *ui.Session, │ │21 button_id: usize, │ │22 + │ │23 + const authorized_index = 0; │ │24 + const unauthorized_index = 1; │ │25 │ │26 pub fn init(allocator: std.mem.Allocator, session: *ui.Session) !View { │ │27 + var stack = try wgt.Stack(ui.Widget).init(allocator); │ │28 + errdefer stack.deinit(allocator); │ │29 var button_id: usize = undefined; │ │30 - var authorized = blk: { │ │31 - var box = try wgt.Box(ui.Widget).init(allocator, .{ .border_style = null, .direction = .vert }); │ │32 - errdefer box.deinit(allocator); │ │30 + { │ │31 + var authorized = blk: { │ │32 + var box = try wgt.Box(ui.Widget).init(allocator, .{ .border_style = null, .direction = .vert }); │ │33 + errdefer box.deinit(allocator); │ │34 │ │34 - // on the web each control posts to a page-scoped form │ │35 - box.getFocus().kind = .{ .custom = "form:ansi" }; │ │35 + // on the web each control posts to a page-scoped form │ │36 + box.getFocus().kind = .{ .custom = "form:ansi" }; │ │37 │ │37 - { │ │38 - var button = try wgt.TextBox.init(allocator, ansiLabel(session), .{ .border_style = .single, .rounded_corners = true, .wrap_kind = .none }); │ │39 - errdefer button.deinit(allocator); │ │40 - // the renderer distinguishes plain clickables from buttons that │ │41 - // should POST to a server route by this kind. │ │42 - button.getFocus().kind = .{ .custom = "submit" }; │ │43 - button.getFocus().mode = .all; │ │44 - button_id = button.getFocus().id; │ │45 - try box.children.put(allocator, button.getFocus().id, .{ .widget = .{ .text_box = button }, .rect = null, .min_size = null }); │ │46 - } │ │47 - │ │48 - box.getFocus().child_id = button_id; │ │49 - break :blk try ui.widget.Center.init(allocator, .{ .box = box }); │ │50 - }; │ │51 - errdefer authorized.deinit(allocator); │ │38 + { │ │39 + var button = try wgt.TextBox.init(allocator, ansiLabel(session), .{ .border_style = .single, .rounded_corners = true, .wrap_kind = .none });│ │40 + errdefer button.deinit(allocator); │ │41 + // the renderer distinguishes plain clickables from buttons that │ │42 + // should POST to a server route by this kind. │ │43 + button.getFocus().kind = .{ .custom = "submit" }; │ │44 + button.getFocus().mode = .all; │ │45 + button_id = button.getFocus().id; │ │46 + try box.children.put(allocator, button.getFocus().id, .{ .widget = .{ .text_box = button }, .rect = null, .min_size = null }); │ │47 + } │ │48 │ │53 - var unauthorized = try ui.Unauthorized.View.init(allocator); │ │54 - errdefer unauthorized.deinit(allocator); │ │49 + box.getFocus().child_id = button_id; │ │50 + break :blk try ui.widget.Center.init(allocator, .{ .box = box }); │ │51 + }; │ │52 + errdefer authorized.deinit(allocator); │ │53 + try stack.children.put(allocator, authorized.getFocus().id, .{ .center = authorized }); │ │54 + } │ │55 + { │ │56 + var unauthorized = try ui.Unauthorized.View.init(allocator); │ │57 + errdefer unauthorized.deinit(allocator); │ │58 + try stack.children.put(allocator, unauthorized.getFocus().id, .{ .unauthorized = unauthorized }); │ │59 + } │ │60 │ │61 return .{ │ │57 - .focus = try Focus.create(allocator, .container), │ │58 - .authorized = authorized, │ │59 - .unauthorized = unauthorized, │ │62 + .stack = stack, │ │63 .session = session, │ │64 .button_id = button_id, │ │65 }; │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │66 } │ │67 │ │68 pub fn deinit(self: *View, allocator: std.mem.Allocator) void { │ │66 - self.focus.destroy(allocator); │ │67 - self.authorized.deinit(allocator); │ │68 - self.unauthorized.deinit(allocator); │ │69 + self.stack.deinit(allocator); │ │70 } │ │71 │ │72 pub fn build(self: *View, allocator: std.mem.Allocator, constraint: layout.Constraint, root_focus: *Focus) !void {│ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯