Compare commits
4 commits
ab39830477
...
7b2ba06035
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7b2ba06035 | ||
|
|
b2bcdeae1a | ||
|
|
ae993a0f6f | ||
|
|
b9e7434ef7 |
2 changed files with 50 additions and 17 deletions
|
|
@ -200,16 +200,30 @@ pub const VirtualListState = struct {
|
||||||
|
|
||||||
/// Mueve la selección hacia arriba
|
/// Mueve la selección hacia arriba
|
||||||
pub fn moveUp(self: *Self) void {
|
pub fn moveUp(self: *Self) void {
|
||||||
|
const window_offset = self.scroll_offset -| self.window_start;
|
||||||
|
|
||||||
if (self.findSelectedInWindow()) |window_idx| {
|
if (self.findSelectedInWindow()) |window_idx| {
|
||||||
|
// Calcular posición en pantalla actual
|
||||||
|
const screen_pos = window_idx -| window_offset;
|
||||||
|
|
||||||
if (window_idx > 0) {
|
if (window_idx > 0) {
|
||||||
|
// Hay item anterior en buffer, seleccionarlo
|
||||||
self.selectByWindowIndex(window_idx - 1);
|
self.selectByWindowIndex(window_idx - 1);
|
||||||
} else if (self.window_start > 0) {
|
|
||||||
// Necesita scroll up - el widget debe manejar esto
|
// Si estábamos en la primera fila visible, hacer scroll
|
||||||
self.scroll_offset = if (self.scroll_offset > 0) self.scroll_offset - 1 else 0;
|
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) {
|
} else if (self.current_window.len > 0) {
|
||||||
// Sin selección: seleccionar primera fila visible
|
// 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) {
|
if (window_idx + 1 < self.current_window.len) {
|
||||||
self.selectByWindowIndex(window_idx + 1);
|
self.selectByWindowIndex(window_idx + 1);
|
||||||
|
|
||||||
// Si la nueva selección está cerca del final visible, scroll
|
// Calcular posición en pantalla de la nueva selección
|
||||||
if (window_idx + 1 >= visible_rows - 1) {
|
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;
|
self.scroll_offset += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -116,7 +116,7 @@ pub fn virtualListRect(
|
||||||
|
|
||||||
// Calculate dimensions
|
// Calculate dimensions
|
||||||
const header_h: u32 = config.row_height;
|
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 content_h = bounds.h -| header_h -| footer_h;
|
||||||
const visible_rows: usize = @intCast(content_h / config.row_height);
|
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);
|
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 (has_focus) {
|
||||||
if (Style.isFancy()) {
|
if (Style.isFancy()) {
|
||||||
ctx.pushCommand(Command.focusRing(bounds.x, bounds.y, bounds.w, bounds.h, 4));
|
ctx.pushCommand(Command.focusRing(bounds.x, bounds.y, bounds.w, bounds.h, 4));
|
||||||
|
|
@ -259,7 +262,7 @@ fn drawHeader(
|
||||||
// Column title
|
// Column title
|
||||||
ctx.pushCommand(Command.text(
|
ctx.pushCommand(Command.text(
|
||||||
x + 4,
|
x + 4,
|
||||||
bounds.y + 4,
|
bounds.y + 3, // Centrado vertical mejorado
|
||||||
col.title,
|
col.title,
|
||||||
colors.text,
|
colors.text,
|
||||||
));
|
));
|
||||||
|
|
@ -270,7 +273,7 @@ fn drawHeader(
|
||||||
const indicator = list_state.sort_direction.symbol();
|
const indicator = list_state.sort_direction.symbol();
|
||||||
ctx.pushCommand(Command.text(
|
ctx.pushCommand(Command.text(
|
||||||
x + @as(i32, @intCast(col.width)) - 20,
|
x + @as(i32, @intCast(col.width)) - 20,
|
||||||
bounds.y + 4,
|
bounds.y + 3, // Centrado vertical mejorado
|
||||||
indicator,
|
indicator,
|
||||||
colors.text,
|
colors.text,
|
||||||
));
|
));
|
||||||
|
|
@ -321,12 +324,19 @@ fn drawRows(
|
||||||
|
|
||||||
const row_h = config.row_height;
|
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
|
// Draw each visible row
|
||||||
var row_idx: usize = 0;
|
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 row_y = content_bounds.y + @as(i32, @intCast(row_idx * row_h));
|
||||||
const global_idx = list_state.windowToGlobalIndex(row_idx);
|
const global_idx = list_state.scroll_offset + row_idx; // Índice global real
|
||||||
const row = list_state.current_window[row_idx];
|
const row = list_state.current_window[data_idx];
|
||||||
|
|
||||||
// Determine row background
|
// Determine row background
|
||||||
const is_selected = list_state.selected_id != null and row.id == list_state.selected_id.?;
|
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(
|
ctx.pushCommand(Command.text(
|
||||||
x + 4,
|
x + 4,
|
||||||
row_y + 4,
|
row_y + 3, // Centrado vertical mejorado
|
||||||
row.values[col_idx],
|
row.values[col_idx],
|
||||||
text_color,
|
text_color,
|
||||||
));
|
));
|
||||||
|
|
@ -498,10 +508,14 @@ fn handleMouseClick(
|
||||||
// Check if click is in content area (not header)
|
// Check if click is in content area (not header)
|
||||||
if (mouse.y >= content_y) {
|
if (mouse.y >= content_y) {
|
||||||
const relative_y = 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) {
|
// Convert screen row to buffer index (accounting for scroll)
|
||||||
list_state.selectByWindowIndex(row_idx);
|
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
|
// Check for double click
|
||||||
// TODO: implement double click detection with timing
|
// TODO: implement double click detection with timing
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue