diff --git a/src/widgets/tabs.zig b/src/widgets/tabs.zig index 46538b3..8feddc0 100644 --- a/src/widgets/tabs.zig +++ b/src/widgets/tabs.zig @@ -247,9 +247,53 @@ pub fn tabsRect( total_width += width; } - // Draw tabs + // ========================================================================== + // PASS 1: Process input events BEFORE drawing (fix visual lag on click) + // ========================================================================== var tab_x = bar_rect.x; + for (tab_list, 0..) |tab, i| { + if (i >= tab_widths.len) break; + + const tab_width = tab_widths[i]; + const tab_rect = Layout.Rect.init(tab_x, bar_rect.y, tab_width, config.tab_height); + const is_hovered_input = tab_rect.contains(mouse.x, mouse.y) and !tab.disabled; + + // Handle close button click (pass 1) + if (config.show_close and tab.closable) { + const close_x = tab_x + @as(i32, @intCast(tab_width - config.close_size - 8)); + const close_y = bar_rect.y + @as(i32, @intCast((config.tab_height - config.close_size) / 2)); + const close_rect = Layout.Rect.init(close_x, close_y, config.close_size, config.close_size); + const close_hovered = close_rect.contains(mouse.x, mouse.y); + + if (close_hovered) { + state.close_hovered = @intCast(i); + } + + if (mouse_pressed and close_hovered) { + result.closed = true; + result.closed_index = i; + } + } + + // Handle tab click (pass 1) - update state BEFORE drawing + if (mouse_pressed and is_hovered_input and state.close_hovered != @as(i32, @intCast(i))) { + ctx.requestFocus(widget_id); + if (state.selected != i) { + state.selected = i; + result.changed = true; + result.selected = i; + } + } + + tab_x += @as(i32, @intCast(tab_width)); + } + + // ========================================================================== + // PASS 2: Draw tabs with UPDATED state + // ========================================================================== + tab_x = bar_rect.x; + for (tab_list, 0..) |tab, i| { if (i >= tab_widths.len) break; @@ -328,38 +372,20 @@ pub fn tabsRect( const text_y = bar_rect.y + @as(i32, @intCast((config.tab_height - 8) / 2)); ctx.pushCommand(Command.text(tab_x + @as(i32, @intCast(config.padding_h)), text_y, tab.label, text_color)); - // Draw close button + // Draw close button (events already handled in Pass 1) if (config.show_close and tab.closable) { const close_x = tab_x + @as(i32, @intCast(tab_width - config.close_size - 8)); const close_y = bar_rect.y + @as(i32, @intCast((config.tab_height - config.close_size) / 2)); const close_rect = Layout.Rect.init(close_x, close_y, config.close_size, config.close_size); const close_hovered = close_rect.contains(mouse.x, mouse.y); - if (close_hovered) { - state.close_hovered = @intCast(i); - } - const close_color = if (close_hovered) colors.close_hover else colors.close_color; // Draw X ctx.pushCommand(Command.text(close_x + 3, close_y + 2, "x", close_color)); - - // Handle close click - if (mouse_pressed and close_hovered) { - result.closed = true; - result.closed_index = i; - } } - // Handle tab click - if (mouse_pressed and is_hovered and state.close_hovered != @as(i32, @intCast(i))) { - ctx.requestFocus(widget_id); - if (state.selected != i) { - state.selected = i; - result.changed = true; - result.selected = i; - } - } + // Note: Tab click handling moved to Pass 1 (before drawing) tab_x += @as(i32, @intCast(tab_width)); }