╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ UserNotifications.zig ────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │http│ ssh │ │ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
214 return self.box.getFocus().child_id == self.headerBox().getFocus().id; 215 } 216 217 pub fn build(self: *View, allocator: std.mem.Allocator, constraint: layout.Constraint, root_focus: *Focus) !void { 218 self.clearGrid(); 219 const header = self.headerBox(); 220 // focusing a sub tab shows its content, while the logout button leaves it 221 if (header.getFocus().child_id) |child_id| if (std.mem.indexOfScalar(usize, self.control_ids[0..tab_count], child_id)) |index| { 222 self.view_index = index; 223 }; 224 for (self.control_ids[0..tab_count], 0..) |id, index| { 225 const tab = header.children.getPtr(id) orelse unreachable; 226 ui.widget.markSelected(&tab.widget.text_box, index == self.view_index); 227 } 228 self.contentStack().getFocus().child_id = self.contentStack().children.keys()[self.view_index]; 229 // clear the incoming min height so the list scrolls within what's left 230 try self.box.build(allocator, .{ 231 .min_size = .{ .width = null, .height = null }, 232 .max_size = constraint.max_size, 233 }, root_focus); 234 } 235 236 pub fn input(self: *View, allocator: std.mem.Allocator, key: Key, root_focus: *Focus) !void { 237 const logout_id = self.control_ids[logout_index]; 238 if (key == .mouse and inp.leftClickOn(root_focus, logout_id, key.mouse)) return logOut(self.session); 239 const direction = inp.vertDirection(key); 240 if (self.headerActive()) { 241 const focused = root_focus.grandchild_id orelse return; 242 if (key == .enter and focused == logout_id) return logOut(self.session); 243 if (direction == .down) { 244 // the empty message has nothing to focus 245 if (self.view_index == @backingInt(ui.RoutablePage.UserView.notifications) and self.data.isEmpty()) return; 246 root_focus.setFocus(self.contentStack().getFocus().id); 247 } else if (std.mem.indexOfScalar(usize, &self.control_ids, focused)) |current| { 248 if (inp.moveTab(key, current, self.control_ids.len)) |next| root_focus.setFocus(self.control_ids[next]); 249 } 250 return; 251 } 252 if (direction == .up) if (self.contentStack().getSelected()) |selected| if (selected.atTop(root_focus)) { 253 root_focus.setFocus(self.control_ids[self.view_index]); 254 return; 255 }; 256 try self.contentStack().input(allocator, key, root_focus); 257 } 258 259 // moving up from the sub header returns to the page's tabs 260 pub fn atTop(self: *View) bool { 261 return self.headerActive(); 262 } 263 264 // arriving from the page's tabs lands on the sub header, not the content 265 pub fn focusHeader(self: *View, root_focus: *Focus) bool { 266 root_focus.setFocus(self.control_ids[self.view_index]); 267 return true; 268 } 269 270 pub fn clearGrid(self: *View) void { 271 self.box.clearGrid(); 272 } 273 274 pub fn getGrid(self: View) ?Grid { 275 return self.box.getGrid(); 276 } 277 278 pub fn getFocus(self: *View) *Focus { 279 return self.box.getFocus(); 280 } 281 }; 282