╭ changed ────╮ │build.zig.zon│ ╰─────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ 3 .version = "0.0.0", │ │ 4 .dependencies = .{ │ │ 5 .xit = .{ │ │ 6 - .url = "git+https://github.com/xit-vcs/xit#44100b2b92c42bd8696f9378afb65916165c8c88",│ │ 7 - .hash = "xit-0.0.0-Ub3nDH61UAELs5a4QvNrfP5fVhfHKQefQNVkk68SDfUe", │ │ 6 + .url = "git+https://github.com/xit-vcs/xit#33135ce2db0769c3b91dccc99b6a1808d576fd0c",│ │ 7 + .hash = "xit-0.0.0-Ub3nDMB6UAGO82WSukW82YY-OdAl-FylLLx6SX5wlQSq", │ │ 8 }, │ │ 9 }, │ │10 .paths = .{ │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ─╮ │src/ui.zig│ ╰──────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1111 } │ │1112 } │ │1113 }; │ │1114 + │ │1115 +// move the focused row in `box` by `delta`, clamped to the ends, scrolling │ │1116 +// `scroll` to keep it visible. │ │1117 +pub fn moveRowFocus(box: *wgt.Box(Widget), scroll: *wgt.Scroll(Widget), root_focus: *Focus, delta: isize) void {│ │1118 + const keys = box.children.keys(); │ │1119 + if (keys.len == 0) return; │ │1120 + const cur_id = box.getFocus().child_id orelse return; │ │1121 + const cur: isize = @intCast(box.children.getIndex(cur_id) orelse return); │ │1122 + const last: isize = @intCast(keys.len - 1); │ │1123 + const next: usize = @intCast(std.math.clamp(cur + delta, 0, last)); │ │1124 + if (next == @as(usize, @intCast(cur))) return; │ │1125 + root_focus.setFocus(keys[next]); │ │1126 + if (box.children.values()[next].rect) |rect| scroll.scrollToRect(rect); │ │1127 +} │ │1128 + │ │1129 +// keep a scroll within its content, using the last build's grids. the scroll │ │1130 +// bar's reserved column/row isn't part of the content viewport, so exclude it │ │1131 +// (as scrollToRect does) or the last column/row stays unreachable. │ │1132 +pub fn clampScroll(sc: *wgt.Scroll(Widget)) void { │ │1133 + const vp = sc.grid orelse return; │ │1134 + const content = sc.child.box.grid orelse return; │ │1135 + const view_w = vp.size.width - sc.bar_w; │ │1136 + const view_h = vp.size.height - sc.bar_h; │ │1137 + const max_y: isize = if (content.size.height > view_h) @intCast(content.size.height - view_h) else 0; │ │1138 + const max_x: isize = if (content.size.width > view_w) @intCast(content.size.width - view_w) else 0; │ │1139 + sc.y = std.math.clamp(sc.y, 0, max_y); │ │1140 + sc.x = std.math.clamp(sc.x, 0, max_x); │ │1141 +} │ │1142 │ │1143 pub const FlowBox = struct { │ │1144 focus: *Focus, │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ────────────╮ │src/ui/Auth/Login.zig│ ╰─────────────────────╯ ╭──────────────────────────────────────────╮ │2 const builtin = @import("builtin"); │ │3 const evt = @import("../../event.zig");│ │4 const ui = @import("../../ui.zig"); │ │5 +const inp = @import("../input.zig"); │ │6 const xit = @import("xit"); │ │7 const rp = xit.repo; │ │8 const hash = xit.hash; │ ╰──────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────╮ │162 if (current == button_index) { │ │163 switch (key) { │ │164 .mouse => |mouse| { │ │164 - if (mouse.action == .press and mouse.action.press == .left) { │ │165 - if (root_focus.children.get(child_id)) |entry| { │ │166 - const r = entry.rect; │ │167 - if (mouse.x >= r.x and mouse.y >= r.y and │ │168 - mouse.x < r.x + r.size.width and mouse.y < r.y + r.size.height)│ │169 - { │ │170 - try self.submit(allocator, root_focus); │ │171 - return; │ │172 - } │ │173 - } │ │165 + if (inp.leftClickOn(root_focus, child_id, mouse)) { │ │166 + try self.submit(allocator, root_focus); │ │167 + return; │ │168 } │ │169 }, │ │170 else => {}, │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ─────────────╮ │src/ui/Auth/Logout.zig│ ╰──────────────────────╯ ╭───────────────────────────────────────╮ │1 const std = @import("std"); │ │2 const ui = @import("../../ui.zig"); │ │3 +const inp = @import("../input.zig");│ │4 const xit = @import("xit"); │ │5 const xitui = xit.xitui; │ │6 const wgt = xitui.widget; │ ╰───────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────╮ │ 90 return; │ │ 91 }, │ │ 92 .mouse => |mouse| { │ │ 92 - if (mouse.action == .press and mouse.action.press == .left) { │ │ 93 - if (root_focus.children.get(self.button_id)) |entry| { │ │ 94 - const r = entry.rect; │ │ 95 - if (mouse.x >= r.x and mouse.y >= r.y and │ │ 96 - mouse.x < r.x + r.size.width and mouse.y < r.y + r.size.height)│ │ 97 - { │ │ 98 - try self.logout(root_focus); │ │ 99 - return; │ │100 - } │ │101 - } │ │ 93 + if (inp.leftClickOn(root_focus, self.button_id, mouse)) { │ │ 94 + try self.logout(root_focus); │ │ 95 + return; │ │ 96 } │ │ 97 }, │ │ 98 else => {}, │ ╰────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ─────────────╮ │src/ui/Home/Header.zig│ ╰──────────────────────╯ ╭───────────────────────────────────────╮ │1 const std = @import("std"); │ │2 const ui = @import("../../ui.zig"); │ │3 +const inp = @import("../input.zig");│ │4 const xit = @import("xit"); │ │5 const xitui = xit.xitui; │ │6 const wgt = xitui.widget; │ ╰───────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────╮ │155 pub fn input(self: *View, allocator: std.mem.Allocator, key: Key, root_focus: *Focus) !void {│ │156 _ = allocator; │ │157 const current_tab = self.currentTabIndex() orelse return; │ │157 - var new_tab = current_tab; │ │158 - switch (key) { │ │159 - .arrow_left => new_tab -|= 1, │ │160 - .arrow_right => if (new_tab + 1 < self.tab_ids.count()) { │ │161 - new_tab += 1; │ │162 - }, │ │163 - else => {}, │ │164 - } │ │165 - if (new_tab != current_tab) { │ │158 + if (inp.moveTab(key, current_tab, self.tab_ids.count())) |new_tab| { │ │159 root_focus.setFocus(self.tab_ids.keys()[new_tab]); │ │160 } │ │161 } │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ──────╮ │src/ui/Quit.zig│ ╰───────────────╯ ╭──────────────────────────────────────╮ │1 const std = @import("std"); │ │2 const ui = @import("../ui.zig"); │ │3 +const inp = @import("./input.zig");│ │4 const xit = @import("xit"); │ │5 const xitui = xit.xitui; │ │6 const wgt = xitui.widget; │ ╰──────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────╮ │78 return; │ │79 }, │ │80 .mouse => |mouse| { │ │80 - if (mouse.action == .press and mouse.action.press == .left) { │ │81 - if (root_focus.children.get(self.button_id)) |entry| { │ │82 - const r = entry.rect; │ │83 - if (mouse.x >= r.x and mouse.y >= r.y and │ │84 - mouse.x < r.x + r.size.width and mouse.y < r.y + r.size.height)│ │85 - { │ │86 - self.session.quit_requested = true; │ │87 - return; │ │88 - } │ │89 - } │ │81 + if (inp.leftClickOn(root_focus, self.button_id, mouse)) { │ │82 + self.session.quit_requested = true; │ │83 + return; │ │84 } │ │85 }, │ │86 else => {}, │ ╰───────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯