Compare commits

...

4 commits

Author SHA1 Message Date
R.Eugenio
7b2ba06035 fix(virtual_list): Borde completo + centrado texto
- Añadir borde alrededor de toda la lista (siempre visible)
- Mejorar centrado vertical del texto (y+4 → y+3)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-23 14:41:56 +01:00
R.Eugenio
b2bcdeae1a fix(virtual_list): Scroll arriba + footer compacto
- moveUp: Calcular screen position para scroll correcto
- footer_h: Reducir de 20 a 16 pixels para más filas visibles

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-23 14:31:12 +01:00
R.Eugenio
ae993a0f6f fix(virtual_list): Scroll en última fila y posición correcta
- moveDown: Calcular screen position antes de decidir scroll
- Scroll ahora ocurre cuando llega a última fila visible (no penúltima)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-23 14:18:47 +01:00
R.Eugenio
b9e7434ef7 fix(virtual_list): Scroll visual y click con offset correcto
- drawRows: Calcular window_offset para dibujar desde scroll_offset
- handleMouseClick: Convertir screen_row a data_idx con offset
- Antes siempre dibujaba desde índice 0 del buffer

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-23 14:11:12 +01:00
2 changed files with 50 additions and 17 deletions

View file

@ -200,16 +200,30 @@ pub const VirtualListState = struct {
/// Mueve la selección hacia arriba
pub fn moveUp(self: *Self) void {
const window_offset = self.scroll_offset -| self.window_start;
if (self.findSelectedInWindow()) |window_idx| {
// Calcular posición en pantalla actual
const screen_pos = window_idx -| window_offset;
if (window_idx > 0) {
// Hay item anterior en buffer, seleccionarlo
self.selectByWindowIndex(window_idx - 1);
} else if (self.window_start > 0) {
// Necesita scroll up - el widget debe manejar esto
self.scroll_offset = if (self.scroll_offset > 0) self.scroll_offset - 1 else 0;
// Si estábamos en la primera fila visible, hacer scroll
if (screen_pos == 0 and self.scroll_offset > 0) {
self.scroll_offset -= 1;
}
} else {
// Estamos en el inicio del buffer
// Scroll up para cargar más datos (si es posible)
if (self.scroll_offset > 0) {
self.scroll_offset -= 1;
}
}
} else if (self.current_window.len > 0) {
// Sin selección: seleccionar primera fila visible
self.selectByWindowIndex(0);
self.selectByWindowIndex(window_offset);
}
}
@ -219,8 +233,13 @@ pub const VirtualListState = struct {
if (window_idx + 1 < self.current_window.len) {
self.selectByWindowIndex(window_idx + 1);
// Si la nueva selección está cerca del final visible, scroll
if (window_idx + 1 >= visible_rows - 1) {
// Calcular posición en pantalla de la nueva selección
const window_offset = self.scroll_offset -| self.window_start;
const new_screen_pos = (window_idx + 1) -| window_offset;
// Scroll cuando la nueva posición llega a la ÚLTIMA fila visible
// (visible_rows - 1 es el índice de la última fila, 0-indexed)
if (new_screen_pos >= visible_rows) {
self.scroll_offset += 1;
}
}

View file

@ -116,7 +116,7 @@ pub fn virtualListRect(
// Calculate dimensions
const header_h: u32 = config.row_height;
const footer_h: u32 = if (config.show_count) 20 else 0;
const footer_h: u32 = if (config.show_count) 16 else 0; // 16px para footer compacto
const content_h = bounds.h -| header_h -| footer_h;
const visible_rows: usize = @intCast(content_h / config.row_height);
@ -173,7 +173,10 @@ pub fn virtualListRect(
drawScrollbar(ctx, bounds, header_h, footer_h, list_state, visible_rows, total_rows, &colors);
}
// Draw focus ring
// Draw border around the entire list (always visible)
ctx.pushCommand(Command.rectOutline(bounds.x, bounds.y, bounds.w, bounds.h, colors.border));
// Draw focus ring (additional highlight when focused)
if (has_focus) {
if (Style.isFancy()) {
ctx.pushCommand(Command.focusRing(bounds.x, bounds.y, bounds.w, bounds.h, 4));
@ -259,7 +262,7 @@ fn drawHeader(
// Column title
ctx.pushCommand(Command.text(
x + 4,
bounds.y + 4,
bounds.y + 3, // Centrado vertical mejorado
col.title,
colors.text,
));
@ -270,7 +273,7 @@ fn drawHeader(
const indicator = list_state.sort_direction.symbol();
ctx.pushCommand(Command.text(
x + @as(i32, @intCast(col.width)) - 20,
bounds.y + 4,
bounds.y + 3, // Centrado vertical mejorado
indicator,
colors.text,
));
@ -321,12 +324,19 @@ fn drawRows(
const row_h = config.row_height;
// Calculate offset within the window buffer
// scroll_offset es la posición global, window_start es donde empieza el buffer
const window_offset = list_state.scroll_offset -| list_state.window_start;
// Draw each visible row
var row_idx: usize = 0;
while (row_idx < visible_rows and row_idx < list_state.current_window.len) : (row_idx += 1) {
while (row_idx < visible_rows) : (row_idx += 1) {
const data_idx = window_offset + row_idx;
if (data_idx >= list_state.current_window.len) break;
const row_y = content_bounds.y + @as(i32, @intCast(row_idx * row_h));
const global_idx = list_state.windowToGlobalIndex(row_idx);
const row = list_state.current_window[row_idx];
const global_idx = list_state.scroll_offset + row_idx; // Índice global real
const row = list_state.current_window[data_idx];
// Determine row background
const is_selected = list_state.selected_id != null and row.id == list_state.selected_id.?;
@ -359,7 +369,7 @@ fn drawRows(
ctx.pushCommand(Command.text(
x + 4,
row_y + 4,
row_y + 3, // Centrado vertical mejorado
row.values[col_idx],
text_color,
));
@ -498,10 +508,14 @@ fn handleMouseClick(
// Check if click is in content area (not header)
if (mouse.y >= content_y) {
const relative_y = mouse.y - content_y;
const row_idx = @as(usize, @intCast(relative_y)) / config.row_height;
const screen_row = @as(usize, @intCast(relative_y)) / config.row_height;
if (row_idx < list_state.current_window.len) {
list_state.selectByWindowIndex(row_idx);
// Convert screen row to buffer index (accounting for scroll)
const window_offset = list_state.scroll_offset -| list_state.window_start;
const data_idx = window_offset + screen_row;
if (data_idx < list_state.current_window.len) {
list_state.selectById(list_state.current_window[data_idx].id);
// Check for double click
// TODO: implement double click detection with timing