refactor(tables): FASE 5 - Embeber NavigationState en AdvancedTableState y VirtualAdvancedTableState
- Añadido nav: table_core.NavigationState en ambos estados - Eliminados campos duplicados (scroll_row, scroll_x, active_col, double_click) - Actualizado todas las referencias a usar nav.* - Conservado scroll_offset_pixels en Virtual (específico smooth scroll)
This commit is contained in:
parent
913652d864
commit
253c9b2449
4 changed files with 142 additions and 123 deletions
|
|
@ -119,7 +119,7 @@ pub fn advancedTableRect(
|
||||||
|
|
||||||
// Check if we have focus
|
// Check if we have focus
|
||||||
const has_focus = ctx.hasFocus(widget_id);
|
const has_focus = ctx.hasFocus(widget_id);
|
||||||
table_state.focused = has_focus;
|
table_state.nav.has_focus = has_focus;
|
||||||
|
|
||||||
// Calculate dimensions
|
// Calculate dimensions
|
||||||
const state_col_w: u32 = if (config.show_row_state_indicators) config.state_indicator_width else 0;
|
const state_col_w: u32 = if (config.show_row_state_indicators) config.state_indicator_width else 0;
|
||||||
|
|
@ -136,7 +136,7 @@ pub fn advancedTableRect(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate visible row range
|
// Calculate visible row range
|
||||||
const first_visible = table_state.scroll_row;
|
const first_visible = table_state.nav.scroll_row;
|
||||||
const last_visible = @min(first_visible + visible_rows, table_state.getRowCount());
|
const last_visible = @min(first_visible + visible_rows, table_state.getRowCount());
|
||||||
|
|
||||||
// Manejar clicks en filas (separado del renderizado)
|
// Manejar clicks en filas (separado del renderizado)
|
||||||
|
|
@ -359,10 +359,10 @@ fn handleRowClicks(
|
||||||
|
|
||||||
// Detectar doble-click
|
// Detectar doble-click
|
||||||
const current_time = ctx.current_time_ms;
|
const current_time = ctx.current_time_ms;
|
||||||
const same_cell = table_state.last_click_row == @as(i32, @intCast(row_idx)) and
|
const same_cell = table_state.nav.double_click.last_click_row == @as(i32, @intCast(row_idx)) and
|
||||||
table_state.last_click_col == @as(i32, @intCast(col_idx));
|
table_state.nav.double_click.last_click_col == @as(i32, @intCast(col_idx));
|
||||||
const time_diff = current_time -| table_state.last_click_time;
|
const time_diff = current_time -| table_state.nav.double_click.last_click_time;
|
||||||
const is_double_click = same_cell and time_diff < table_state.double_click_threshold_ms;
|
const is_double_click = same_cell and time_diff < table_state.nav.double_click.threshold_ms;
|
||||||
|
|
||||||
if (is_double_click and config.allow_edit and col_idx < table_schema.columns.len and
|
if (is_double_click and config.allow_edit and col_idx < table_schema.columns.len and
|
||||||
table_schema.columns[col_idx].editable and !table_state.isEditing())
|
table_schema.columns[col_idx].editable and !table_state.isEditing())
|
||||||
|
|
@ -377,9 +377,9 @@ fn handleRowClicks(
|
||||||
result.edit_started = true;
|
result.edit_started = true;
|
||||||
}
|
}
|
||||||
// Reset click tracking
|
// Reset click tracking
|
||||||
table_state.last_click_time = 0;
|
table_state.nav.double_click.last_click_time = 0;
|
||||||
table_state.last_click_row = -1;
|
table_state.nav.double_click.last_click_row = -1;
|
||||||
table_state.last_click_col = -1;
|
table_state.nav.double_click.last_click_col = -1;
|
||||||
} else {
|
} else {
|
||||||
// Single click: seleccionar celda
|
// Single click: seleccionar celda
|
||||||
if (!is_selected_cell) {
|
if (!is_selected_cell) {
|
||||||
|
|
@ -389,9 +389,9 @@ fn handleRowClicks(
|
||||||
result.selected_col = col_idx;
|
result.selected_col = col_idx;
|
||||||
}
|
}
|
||||||
// Actualizar tracking para posible doble-click
|
// Actualizar tracking para posible doble-click
|
||||||
table_state.last_click_time = current_time;
|
table_state.nav.double_click.last_click_time = current_time;
|
||||||
table_state.last_click_row = @intCast(row_idx);
|
table_state.nav.double_click.last_click_row = @intCast(row_idx);
|
||||||
table_state.last_click_col = @intCast(col_idx);
|
table_state.nav.double_click.last_click_col = @intCast(col_idx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -515,7 +515,7 @@ fn drawScrollbar(
|
||||||
const thumb_ratio = @as(f32, @floatFromInt(visible_rows)) / @as(f32, @floatFromInt(total_rows));
|
const thumb_ratio = @as(f32, @floatFromInt(visible_rows)) / @as(f32, @floatFromInt(total_rows));
|
||||||
const thumb_h = @max(20, @as(u32, @intFromFloat(@as(f32, @floatFromInt(scrollbar_h)) * thumb_ratio)));
|
const thumb_h = @max(20, @as(u32, @intFromFloat(@as(f32, @floatFromInt(scrollbar_h)) * thumb_ratio)));
|
||||||
|
|
||||||
const scroll_ratio = @as(f32, @floatFromInt(table_state.scroll_row)) /
|
const scroll_ratio = @as(f32, @floatFromInt(table_state.nav.scroll_row)) /
|
||||||
@as(f32, @floatFromInt(@max(1, total_rows - visible_rows)));
|
@as(f32, @floatFromInt(@max(1, total_rows - visible_rows)));
|
||||||
const thumb_y_offset = @as(u32, @intFromFloat(scroll_ratio * @as(f32, @floatFromInt(scrollbar_h - thumb_h))));
|
const thumb_y_offset = @as(u32, @intFromFloat(scroll_ratio * @as(f32, @floatFromInt(scrollbar_h - thumb_h))));
|
||||||
|
|
||||||
|
|
@ -544,8 +544,8 @@ fn drawEditingOverlay(
|
||||||
const config = table_schema.config;
|
const config = table_schema.config;
|
||||||
|
|
||||||
// Check if row is visible
|
// Check if row is visible
|
||||||
if (row_idx < table_state.scroll_row) return;
|
if (row_idx < table_state.nav.scroll_row) return;
|
||||||
const visible_row = row_idx - table_state.scroll_row;
|
const visible_row = row_idx - table_state.nav.scroll_row;
|
||||||
const visible_rows = (bounds.h -| header_h) / config.row_height;
|
const visible_rows = (bounds.h -| header_h) / config.row_height;
|
||||||
if (visible_row >= visible_rows) return;
|
if (visible_row >= visible_rows) return;
|
||||||
|
|
||||||
|
|
@ -1178,10 +1178,10 @@ fn ensureSelectionVisible(table_state: *AdvancedTableState, visible_rows: usize)
|
||||||
const row: usize = @intCast(table_state.selected_row);
|
const row: usize = @intCast(table_state.selected_row);
|
||||||
|
|
||||||
// Scroll to show selected row
|
// Scroll to show selected row
|
||||||
if (row < table_state.scroll_row) {
|
if (row < table_state.nav.scroll_row) {
|
||||||
table_state.scroll_row = row;
|
table_state.nav.scroll_row = row;
|
||||||
} else if (row >= table_state.scroll_row + visible_rows) {
|
} else if (row >= table_state.nav.scroll_row + visible_rows) {
|
||||||
table_state.scroll_row = row - visible_rows + 1;
|
table_state.nav.scroll_row = row - visible_rows + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -101,20 +101,16 @@ pub const AdvancedTableState = struct {
|
||||||
original_value: ?CellValue = null,
|
original_value: ?CellValue = null,
|
||||||
|
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
// Double-click detection
|
// Navegación (usa NavigationState de table_core para composición)
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
|
|
||||||
/// Time of last click (ms)
|
/// Estado de navegación embebido (FASE 5 refactor)
|
||||||
last_click_time: u64 = 0,
|
/// Incluye: active_col, scroll_row, scroll_x, has_focus, double_click
|
||||||
|
nav: table_core.NavigationState = .{},
|
||||||
|
|
||||||
/// Row of last click
|
// Aliases para backwards compatibility:
|
||||||
last_click_row: i32 = -1,
|
// - scroll_row → nav.scroll_row (acceso directo abajo)
|
||||||
|
// - double_click_* → nav.double_click.* (acceso directo abajo)
|
||||||
/// Column of last click
|
|
||||||
last_click_col: i32 = -1,
|
|
||||||
|
|
||||||
/// Double-click threshold in ms
|
|
||||||
double_click_threshold_ms: u64 = 400,
|
|
||||||
|
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
// Sorting
|
// Sorting
|
||||||
|
|
@ -133,14 +129,9 @@ pub const AdvancedTableState = struct {
|
||||||
has_original_order: bool = false,
|
has_original_order: bool = false,
|
||||||
|
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
// Scrolling
|
// Scrolling (delegado a nav - ver NavigationState)
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
|
// scroll_row y scroll_x ahora están en nav.scroll_row y nav.scroll_x
|
||||||
/// First visible row
|
|
||||||
scroll_row: usize = 0,
|
|
||||||
|
|
||||||
/// Horizontal scroll offset (in pixels)
|
|
||||||
scroll_x: usize = 0,
|
|
||||||
|
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
// State Maps (sparse - only modified rows)
|
// State Maps (sparse - only modified rows)
|
||||||
|
|
@ -165,13 +156,6 @@ pub const AdvancedTableState = struct {
|
||||||
/// Row snapshots captured when entering row
|
/// Row snapshots captured when entering row
|
||||||
row_snapshots: std.AutoHashMap(usize, Row),
|
row_snapshots: std.AutoHashMap(usize, Row),
|
||||||
|
|
||||||
// =========================================================================
|
|
||||||
// Focus
|
|
||||||
// =========================================================================
|
|
||||||
|
|
||||||
/// Does this table have focus
|
|
||||||
focused: bool = false,
|
|
||||||
|
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
// Callbacks & Debounce (Phase 8)
|
// Callbacks & Debounce (Phase 8)
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
|
|
@ -182,6 +166,60 @@ pub const AdvancedTableState = struct {
|
||||||
/// Last row that triggered on_active_row_changed (to avoid duplicate calls)
|
/// Last row that triggered on_active_row_changed (to avoid duplicate calls)
|
||||||
last_notified_row: i32 = -1,
|
last_notified_row: i32 = -1,
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// Propiedades de compatibilidad (FASE 5 - delegación a nav)
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
/// Alias para nav.scroll_row (backwards compatibility)
|
||||||
|
pub fn getScrollRow(self: *const AdvancedTableState) usize {
|
||||||
|
return self.nav.scroll_row;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setScrollRow(self: *AdvancedTableState, row: usize) void {
|
||||||
|
self.nav.scroll_row = row;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Alias para nav.has_focus (backwards compatibility)
|
||||||
|
pub fn hasFocus(self: *const AdvancedTableState) bool {
|
||||||
|
return self.nav.has_focus;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setFocus(self: *AdvancedTableState, focused: bool) void {
|
||||||
|
self.nav.has_focus = focused;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Double-click: último tiempo de click
|
||||||
|
pub fn getLastClickTime(self: *const AdvancedTableState) u64 {
|
||||||
|
return self.nav.double_click.last_click_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setLastClickTime(self: *AdvancedTableState, time: u64) void {
|
||||||
|
self.nav.double_click.last_click_time = time;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Double-click: última fila clickeada
|
||||||
|
pub fn getLastClickRow(self: *const AdvancedTableState) i64 {
|
||||||
|
return self.nav.double_click.last_click_row;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setLastClickRow(self: *AdvancedTableState, row: i64) void {
|
||||||
|
self.nav.double_click.last_click_row = row;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Double-click: última columna clickeada
|
||||||
|
pub fn getLastClickCol(self: *const AdvancedTableState) i32 {
|
||||||
|
return self.nav.double_click.last_click_col;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setLastClickCol(self: *AdvancedTableState, col: i32) void {
|
||||||
|
self.nav.double_click.last_click_col = col;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Double-click: threshold en ms
|
||||||
|
pub fn getDoubleClickThreshold(self: *const AdvancedTableState) u64 {
|
||||||
|
return self.nav.double_click.threshold_ms;
|
||||||
|
}
|
||||||
|
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
// Lifecycle
|
// Lifecycle
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
|
|
|
||||||
|
|
@ -24,39 +24,27 @@ pub const VirtualAdvancedTableState = struct {
|
||||||
/// Índice de la fila con hover
|
/// Índice de la fila con hover
|
||||||
hover_index: ?usize = null,
|
hover_index: ?usize = null,
|
||||||
|
|
||||||
/// Columna activa (para edición)
|
|
||||||
/// Cuando el usuario navega con flechas o hace click, se actualiza
|
|
||||||
active_col: usize = 0,
|
|
||||||
|
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
// Double-click detection
|
// Navegación (usa NavigationState de table_core para composición)
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
|
|
||||||
/// Time of last click (ms)
|
/// Estado de navegación embebido (FASE 5 refactor)
|
||||||
last_click_time: u64 = 0,
|
/// Incluye: active_col, scroll_row (como scroll_offset), scroll_x, double_click
|
||||||
|
nav: table_core.NavigationState = .{},
|
||||||
|
|
||||||
/// Row of last click (global index)
|
// Aliases:
|
||||||
last_click_row: i64 = -1,
|
// - active_col → nav.active_col
|
||||||
|
// - scroll_offset → nav.scroll_row
|
||||||
/// Column of last click
|
// - scroll_offset_x → nav.scroll_x
|
||||||
last_click_col: i32 = -1,
|
// - last_click_* → nav.double_click.*
|
||||||
|
|
||||||
/// Double-click threshold in ms
|
|
||||||
double_click_threshold_ms: u64 = 400,
|
|
||||||
|
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
// Scroll y ventana
|
// Scroll adicional (específico de Virtual - smooth scroll)
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
|
|
||||||
/// Offset actual del scroll vertical (en filas, no pixels)
|
|
||||||
scroll_offset: usize = 0,
|
|
||||||
|
|
||||||
/// Offset del scroll en pixels (para smooth scroll vertical)
|
/// Offset del scroll en pixels (para smooth scroll vertical)
|
||||||
scroll_offset_pixels: i32 = 0,
|
scroll_offset_pixels: i32 = 0,
|
||||||
|
|
||||||
/// Offset del scroll horizontal (en pixels)
|
|
||||||
scroll_offset_x: i32 = 0,
|
|
||||||
|
|
||||||
/// Ventana de datos actual (propiedad del DataProvider)
|
/// Ventana de datos actual (propiedad del DataProvider)
|
||||||
/// NO liberar - el provider lo gestiona
|
/// NO liberar - el provider lo gestiona
|
||||||
current_window: []const RowData = &.{},
|
current_window: []const RowData = &.{},
|
||||||
|
|
@ -350,7 +338,7 @@ pub const VirtualAdvancedTableState = 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;
|
const window_offset = self.nav.scroll_row -| self.window_start;
|
||||||
|
|
||||||
if (self.findSelectedInWindow()) |window_idx| {
|
if (self.findSelectedInWindow()) |window_idx| {
|
||||||
// Calcular posición en pantalla actual
|
// Calcular posición en pantalla actual
|
||||||
|
|
@ -361,14 +349,14 @@ pub const VirtualAdvancedTableState = struct {
|
||||||
self.selectByWindowIndex(window_idx - 1);
|
self.selectByWindowIndex(window_idx - 1);
|
||||||
|
|
||||||
// Si estábamos en la primera fila visible, hacer scroll
|
// Si estábamos en la primera fila visible, hacer scroll
|
||||||
if (screen_pos == 0 and self.scroll_offset > 0) {
|
if (screen_pos == 0 and self.nav.scroll_row > 0) {
|
||||||
self.scroll_offset -= 1;
|
self.nav.scroll_row -= 1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Estamos en el inicio del buffer
|
// Estamos en el inicio del buffer
|
||||||
// Scroll up para cargar más datos (si es posible)
|
// Scroll up para cargar más datos (si es posible)
|
||||||
if (self.scroll_offset > 0) {
|
if (self.nav.scroll_row > 0) {
|
||||||
self.scroll_offset -= 1;
|
self.nav.scroll_row -= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (self.current_window.len > 0) {
|
} else if (self.current_window.len > 0) {
|
||||||
|
|
@ -384,13 +372,13 @@ pub const VirtualAdvancedTableState = struct {
|
||||||
self.selectByWindowIndex(window_idx + 1);
|
self.selectByWindowIndex(window_idx + 1);
|
||||||
|
|
||||||
// Calcular posición en pantalla de la nueva selección
|
// Calcular posición en pantalla de la nueva selección
|
||||||
const window_offset = self.scroll_offset -| self.window_start;
|
const window_offset = self.nav.scroll_row -| self.window_start;
|
||||||
const new_screen_pos = (window_idx + 1) -| window_offset;
|
const new_screen_pos = (window_idx + 1) -| window_offset;
|
||||||
|
|
||||||
// Scroll cuando la nueva posición llega a la ÚLTIMA fila visible
|
// Scroll cuando la nueva posición llega a la ÚLTIMA fila visible
|
||||||
// (visible_rows - 1 es el índice de la última fila, 0-indexed)
|
// (visible_rows - 1 es el índice de la última fila, 0-indexed)
|
||||||
if (new_screen_pos >= visible_rows) {
|
if (new_screen_pos >= visible_rows) {
|
||||||
self.scroll_offset += 1;
|
self.nav.scroll_row += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (self.current_window.len > 0) {
|
} else if (self.current_window.len > 0) {
|
||||||
|
|
@ -401,22 +389,22 @@ pub const VirtualAdvancedTableState = struct {
|
||||||
|
|
||||||
/// Mueve página arriba
|
/// Mueve página arriba
|
||||||
pub fn pageUp(self: *Self, visible_rows: usize) void {
|
pub fn pageUp(self: *Self, visible_rows: usize) void {
|
||||||
if (self.scroll_offset >= visible_rows) {
|
if (self.nav.scroll_row >= visible_rows) {
|
||||||
self.scroll_offset -= visible_rows;
|
self.nav.scroll_row -= visible_rows;
|
||||||
} else {
|
} else {
|
||||||
self.scroll_offset = 0;
|
self.nav.scroll_row = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mueve página abajo
|
/// Mueve página abajo
|
||||||
pub fn pageDown(self: *Self, visible_rows: usize, total_rows: usize) void {
|
pub fn pageDown(self: *Self, visible_rows: usize, total_rows: usize) void {
|
||||||
const max_offset = if (total_rows > visible_rows) total_rows - visible_rows else 0;
|
const max_offset = if (total_rows > visible_rows) total_rows - visible_rows else 0;
|
||||||
self.scroll_offset = @min(self.scroll_offset + visible_rows, max_offset);
|
self.nav.scroll_row = @min(self.nav.scroll_row + visible_rows, max_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Va al inicio
|
/// Va al inicio
|
||||||
pub fn goToStart(self: *Self) void {
|
pub fn goToStart(self: *Self) void {
|
||||||
self.scroll_offset = 0;
|
self.nav.scroll_row = 0;
|
||||||
if (self.current_window.len > 0) {
|
if (self.current_window.len > 0) {
|
||||||
self.selectByWindowIndex(0);
|
self.selectByWindowIndex(0);
|
||||||
}
|
}
|
||||||
|
|
@ -425,7 +413,7 @@ pub const VirtualAdvancedTableState = struct {
|
||||||
/// Va al final
|
/// Va al final
|
||||||
pub fn goToEnd(self: *Self, visible_rows: usize, total_rows: usize) void {
|
pub fn goToEnd(self: *Self, visible_rows: usize, total_rows: usize) void {
|
||||||
if (total_rows > visible_rows) {
|
if (total_rows > visible_rows) {
|
||||||
self.scroll_offset = total_rows - visible_rows;
|
self.nav.scroll_row = total_rows - visible_rows;
|
||||||
}
|
}
|
||||||
if (self.current_window.len > 0) {
|
if (self.current_window.len > 0) {
|
||||||
self.selectByWindowIndex(self.current_window.len - 1);
|
self.selectByWindowIndex(self.current_window.len - 1);
|
||||||
|
|
@ -444,47 +432,47 @@ pub const VirtualAdvancedTableState = struct {
|
||||||
|
|
||||||
/// Scroll horizontal a la izquierda
|
/// Scroll horizontal a la izquierda
|
||||||
pub fn scrollLeft(self: *Self, amount: i32) void {
|
pub fn scrollLeft(self: *Self, amount: i32) void {
|
||||||
self.scroll_offset_x = @max(0, self.scroll_offset_x - amount);
|
self.nav.scroll_x = @max(0, self.nav.scroll_x - amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Scroll horizontal a la derecha
|
/// Scroll horizontal a la derecha
|
||||||
pub fn scrollRight(self: *Self, amount: i32, max_scroll: i32) void {
|
pub fn scrollRight(self: *Self, amount: i32, max_scroll: i32) void {
|
||||||
self.scroll_offset_x = @min(max_scroll, self.scroll_offset_x + amount);
|
self.nav.scroll_x = @min(max_scroll, self.nav.scroll_x + amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Va al inicio horizontal
|
/// Va al inicio horizontal
|
||||||
pub fn goToStartX(self: *Self) void {
|
pub fn goToStartX(self: *Self) void {
|
||||||
self.scroll_offset_x = 0;
|
self.nav.scroll_x = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Va al final horizontal
|
/// Va al final horizontal
|
||||||
pub fn goToEndX(self: *Self, max_scroll: i32) void {
|
pub fn goToEndX(self: *Self, max_scroll: i32) void {
|
||||||
self.scroll_offset_x = max_scroll;
|
self.nav.scroll_x = max_scroll;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mueve a columna anterior
|
/// Mueve a columna anterior
|
||||||
pub fn moveToPrevCol(self: *Self) void {
|
pub fn moveToPrevCol(self: *Self) void {
|
||||||
if (self.active_col > 0) {
|
if (self.nav.active_col > 0) {
|
||||||
self.active_col -= 1;
|
self.nav.active_col -= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mueve a columna siguiente
|
/// Mueve a columna siguiente
|
||||||
pub fn moveToNextCol(self: *Self, max_cols: usize) void {
|
pub fn moveToNextCol(self: *Self, max_cols: usize) void {
|
||||||
if (self.active_col + 1 < max_cols) {
|
if (self.nav.active_col + 1 < max_cols) {
|
||||||
self.active_col += 1;
|
self.nav.active_col += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Va a primera columna
|
/// Va a primera columna
|
||||||
pub fn goToFirstCol(self: *Self) void {
|
pub fn goToFirstCol(self: *Self) void {
|
||||||
self.active_col = 0;
|
self.nav.active_col = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Va a última columna
|
/// Va a última columna
|
||||||
pub fn goToLastCol(self: *Self, max_cols: usize) void {
|
pub fn goToLastCol(self: *Self, max_cols: usize) void {
|
||||||
if (max_cols > 0) {
|
if (max_cols > 0) {
|
||||||
self.active_col = max_cols - 1;
|
self.nav.active_col = max_cols - 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -505,10 +493,10 @@ pub const VirtualAdvancedTableState = struct {
|
||||||
const total_rows = self.current_window.len + self.window_start;
|
const total_rows = self.current_window.len + self.window_start;
|
||||||
|
|
||||||
// Usar función de table_core
|
// Usar función de table_core
|
||||||
const pos = table_core.calculateNextCell(current_row, self.active_col, num_cols, total_rows, wrap_to_start);
|
const pos = table_core.calculateNextCell(current_row, self.nav.active_col, num_cols, total_rows, wrap_to_start);
|
||||||
|
|
||||||
if (pos.result == .navigated) {
|
if (pos.result == .navigated) {
|
||||||
self.active_col = pos.col;
|
self.nav.active_col = pos.col;
|
||||||
// Navegar a la nueva fila si cambió
|
// Navegar a la nueva fila si cambió
|
||||||
if (pos.row != current_row) {
|
if (pos.row != current_row) {
|
||||||
if (pos.row == 0) {
|
if (pos.row == 0) {
|
||||||
|
|
@ -530,10 +518,10 @@ pub const VirtualAdvancedTableState = struct {
|
||||||
const current_row = self.getSelectedRow() orelse 0;
|
const current_row = self.getSelectedRow() orelse 0;
|
||||||
|
|
||||||
// Usar función de table_core
|
// Usar función de table_core
|
||||||
const pos = table_core.calculatePrevCell(current_row, self.active_col, num_cols, total_rows, wrap_to_end);
|
const pos = table_core.calculatePrevCell(current_row, self.nav.active_col, num_cols, total_rows, wrap_to_end);
|
||||||
|
|
||||||
if (pos.result == .navigated) {
|
if (pos.result == .navigated) {
|
||||||
self.active_col = pos.col;
|
self.nav.active_col = pos.col;
|
||||||
// Navegar a la nueva fila si cambió
|
// Navegar a la nueva fila si cambió
|
||||||
if (pos.row != current_row) {
|
if (pos.row != current_row) {
|
||||||
if (pos.row == total_rows - 1) {
|
if (pos.row == total_rows - 1) {
|
||||||
|
|
@ -561,7 +549,7 @@ pub const VirtualAdvancedTableState = struct {
|
||||||
/// Obtiene la celda activa (fila seleccionada + columna activa)
|
/// Obtiene la celda activa (fila seleccionada + columna activa)
|
||||||
pub fn getActiveCell(self: *const Self) ?CellId {
|
pub fn getActiveCell(self: *const Self) ?CellId {
|
||||||
if (self.getSelectedRow()) |row| {
|
if (self.getSelectedRow()) |row| {
|
||||||
return .{ .row = row, .col = self.active_col };
|
return .{ .row = row, .col = self.nav.active_col };
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -701,8 +689,8 @@ pub const VirtualAdvancedTableState = struct {
|
||||||
filter_bar_height: u32,
|
filter_bar_height: u32,
|
||||||
) ?CellGeometry {
|
) ?CellGeometry {
|
||||||
// Verificar si la fila está en la ventana visible
|
// Verificar si la fila está en la ventana visible
|
||||||
if (row < self.scroll_offset) return null;
|
if (row < self.nav.scroll_row) return null;
|
||||||
const visible_row = row - self.scroll_offset;
|
const visible_row = row - self.nav.scroll_row;
|
||||||
|
|
||||||
// Calcular Y (después de filter bar + header)
|
// Calcular Y (después de filter bar + header)
|
||||||
const content_start_y = bounds_y + @as(i32, @intCast(filter_bar_height)) + @as(i32, @intCast(header_height));
|
const content_start_y = bounds_y + @as(i32, @intCast(filter_bar_height)) + @as(i32, @intCast(header_height));
|
||||||
|
|
@ -712,7 +700,7 @@ pub const VirtualAdvancedTableState = struct {
|
||||||
if (col >= columns.len) return null;
|
if (col >= columns.len) return null;
|
||||||
|
|
||||||
// Calcular X (sumando anchos de columnas anteriores, menos scroll horizontal)
|
// Calcular X (sumando anchos de columnas anteriores, menos scroll horizontal)
|
||||||
var x: i32 = bounds_x - self.scroll_offset_x;
|
var x: i32 = bounds_x - self.nav.scroll_x;
|
||||||
for (columns[0..col]) |c| {
|
for (columns[0..col]) |c| {
|
||||||
x += @as(i32, @intCast(c.width));
|
x += @as(i32, @intCast(c.width));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -209,8 +209,8 @@ pub fn virtualAdvancedTableRect(
|
||||||
const max_scroll_x: i32 = @max(0, @as(i32, @intCast(total_columns_width)) - @as(i32, @intCast(available_width)));
|
const max_scroll_x: i32 = @max(0, @as(i32, @intCast(total_columns_width)) - @as(i32, @intCast(available_width)));
|
||||||
|
|
||||||
// Clamp current scroll_offset_x
|
// Clamp current scroll_offset_x
|
||||||
if (list_state.scroll_offset_x > max_scroll_x) {
|
if (list_state.nav.scroll_x > max_scroll_x) {
|
||||||
list_state.scroll_offset_x = max_scroll_x;
|
list_state.nav.scroll_x = max_scroll_x;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate FilterBar height
|
// Calculate FilterBar height
|
||||||
|
|
@ -228,9 +228,9 @@ pub fn virtualAdvancedTableRect(
|
||||||
|
|
||||||
// Fetch window if needed
|
// Fetch window if needed
|
||||||
if (needs_refetch) {
|
if (needs_refetch) {
|
||||||
if (provider.fetchWindow(list_state.scroll_offset, buffer_size)) |window| {
|
if (provider.fetchWindow(list_state.nav.scroll_row, buffer_size)) |window| {
|
||||||
list_state.current_window = window;
|
list_state.current_window = window;
|
||||||
list_state.window_start = list_state.scroll_offset;
|
list_state.window_start = list_state.nav.scroll_row;
|
||||||
} else |_| {
|
} else |_| {
|
||||||
// Error fetching - keep current window
|
// Error fetching - keep current window
|
||||||
}
|
}
|
||||||
|
|
@ -258,7 +258,7 @@ pub fn virtualAdvancedTableRect(
|
||||||
const header_y = bounds.y + @as(i32, @intCast(filter_bar_h));
|
const header_y = bounds.y + @as(i32, @intCast(filter_bar_h));
|
||||||
|
|
||||||
// Draw header (with horizontal scroll offset)
|
// Draw header (with horizontal scroll offset)
|
||||||
drawHeaderAt(ctx, bounds, header_y, config, &colors, list_state, &result, list_state.scroll_offset_x);
|
drawHeaderAt(ctx, bounds, header_y, config, &colors, list_state, &result, list_state.nav.scroll_x);
|
||||||
|
|
||||||
// Draw visible rows
|
// Draw visible rows
|
||||||
const content_bounds = Layout.Rect.init(
|
const content_bounds = Layout.Rect.init(
|
||||||
|
|
@ -277,7 +277,7 @@ pub fn virtualAdvancedTableRect(
|
||||||
colors.row_normal,
|
colors.row_normal,
|
||||||
));
|
));
|
||||||
|
|
||||||
drawRows(ctx, content_bounds, config, &colors, list_state, visible_rows, &result, list_state.scroll_offset_x);
|
drawRows(ctx, content_bounds, config, &colors, list_state, visible_rows, &result, list_state.nav.scroll_x);
|
||||||
|
|
||||||
// Draw CellEditor overlay if editing
|
// Draw CellEditor overlay if editing
|
||||||
if (list_state.isEditing()) {
|
if (list_state.isEditing()) {
|
||||||
|
|
@ -357,7 +357,7 @@ pub fn virtualAdvancedTableRect(
|
||||||
|
|
||||||
// Draw horizontal scrollbar if needed
|
// Draw horizontal scrollbar if needed
|
||||||
if (needs_h_scroll and config.show_scrollbar) {
|
if (needs_h_scroll and config.show_scrollbar) {
|
||||||
drawScrollbarH(ctx, bounds, footer_h, scrollbar_h_h, list_state.scroll_offset_x, max_scroll_x, available_width, &colors);
|
drawScrollbarH(ctx, bounds, footer_h, scrollbar_h_h, list_state.nav.scroll_x, max_scroll_x, available_width, &colors);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw border around the entire list (always visible)
|
// Draw border around the entire list (always visible)
|
||||||
|
|
@ -441,7 +441,7 @@ fn needsRefetch(list_state: *VirtualAdvancedTableState, visible_rows: usize, buf
|
||||||
if (list_state.current_window.len == 0) return true;
|
if (list_state.current_window.len == 0) return true;
|
||||||
|
|
||||||
// Check if scroll is outside current window
|
// Check if scroll is outside current window
|
||||||
const scroll = list_state.scroll_offset;
|
const scroll = list_state.nav.scroll_row;
|
||||||
const window_end = list_state.window_start + list_state.current_window.len;
|
const window_end = list_state.window_start + list_state.current_window.len;
|
||||||
|
|
||||||
// Refetch if scroll is near edges of window
|
// Refetch if scroll is near edges of window
|
||||||
|
|
@ -793,9 +793,9 @@ fn drawRows(
|
||||||
-1;
|
-1;
|
||||||
|
|
||||||
// Calcular rango de filas a dibujar
|
// Calcular rango de filas a dibujar
|
||||||
const first_row = list_state.scroll_offset;
|
const first_row = list_state.nav.scroll_row;
|
||||||
const last_row = @min(
|
const last_row = @min(
|
||||||
list_state.scroll_offset + visible_rows,
|
list_state.nav.scroll_row + visible_rows,
|
||||||
list_state.window_start + list_state.current_window.len,
|
list_state.window_start + list_state.current_window.len,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -841,7 +841,7 @@ fn drawRows(
|
||||||
.alternating_rows = true,
|
.alternating_rows = true,
|
||||||
.has_focus = list_state.has_focus,
|
.has_focus = list_state.has_focus,
|
||||||
.selected_row = selected_row,
|
.selected_row = selected_row,
|
||||||
.active_col = list_state.active_col,
|
.active_col = list_state.nav.active_col,
|
||||||
.colors = render_colors,
|
.colors = render_colors,
|
||||||
.columns = render_cols[0..num_cols],
|
.columns = render_cols[0..num_cols],
|
||||||
},
|
},
|
||||||
|
|
@ -923,7 +923,7 @@ fn drawScrollbar(
|
||||||
const visible_ratio = @as(f32, @floatFromInt(visible_rows)) / @as(f32, @floatFromInt(total_rows));
|
const visible_ratio = @as(f32, @floatFromInt(visible_rows)) / @as(f32, @floatFromInt(total_rows));
|
||||||
const thumb_h = @max(20, @as(u32, @intFromFloat(visible_ratio * @as(f32, @floatFromInt(content_h)))));
|
const thumb_h = @max(20, @as(u32, @intFromFloat(visible_ratio * @as(f32, @floatFromInt(content_h)))));
|
||||||
|
|
||||||
const scroll_ratio = @as(f32, @floatFromInt(list_state.scroll_offset)) /
|
const scroll_ratio = @as(f32, @floatFromInt(list_state.nav.scroll_row)) /
|
||||||
@as(f32, @floatFromInt(@max(1, total_rows - visible_rows)));
|
@as(f32, @floatFromInt(@max(1, total_rows - visible_rows)));
|
||||||
const thumb_y = track_y + @as(i32, @intFromFloat(scroll_ratio * @as(f32, @floatFromInt(content_h - thumb_h))));
|
const thumb_y = track_y + @as(i32, @intFromFloat(scroll_ratio * @as(f32, @floatFromInt(content_h - thumb_h))));
|
||||||
|
|
||||||
|
|
@ -1093,15 +1093,15 @@ fn handleMouseClick(
|
||||||
const screen_row = @as(usize, @intCast(relative_y)) / config.row_height;
|
const screen_row = @as(usize, @intCast(relative_y)) / config.row_height;
|
||||||
|
|
||||||
// Convert screen row to buffer index (accounting for scroll)
|
// Convert screen row to buffer index (accounting for scroll)
|
||||||
const window_offset = list_state.scroll_offset -| list_state.window_start;
|
const window_offset = list_state.nav.scroll_row -| list_state.window_start;
|
||||||
const data_idx = window_offset + screen_row;
|
const data_idx = window_offset + screen_row;
|
||||||
|
|
||||||
if (data_idx < list_state.current_window.len) {
|
if (data_idx < list_state.current_window.len) {
|
||||||
const global_row = list_state.scroll_offset + screen_row;
|
const global_row = list_state.nav.scroll_row + screen_row;
|
||||||
|
|
||||||
// Detect which column was clicked
|
// Detect which column was clicked
|
||||||
var clicked_col: usize = 0;
|
var clicked_col: usize = 0;
|
||||||
const relative_x = mouse.x - bounds.x + list_state.scroll_offset_x;
|
const relative_x = mouse.x - bounds.x + list_state.nav.scroll_x;
|
||||||
var col_start: i32 = 0;
|
var col_start: i32 = 0;
|
||||||
for (config.columns, 0..) |col, col_idx| {
|
for (config.columns, 0..) |col, col_idx| {
|
||||||
const col_end = col_start + @as(i32, @intCast(col.width));
|
const col_end = col_start + @as(i32, @intCast(col.width));
|
||||||
|
|
@ -1112,13 +1112,8 @@ fn handleMouseClick(
|
||||||
col_start = col_end;
|
col_start = col_end;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Double-click detection using table_core
|
// Double-click detection using embedded nav.double_click
|
||||||
var dc_state = table_core.DoubleClickState{
|
var dc_state = list_state.nav.double_click;
|
||||||
.last_click_time = list_state.last_click_time,
|
|
||||||
.last_click_row = list_state.last_click_row,
|
|
||||||
.last_click_col = list_state.last_click_col,
|
|
||||||
.threshold_ms = list_state.double_click_threshold_ms,
|
|
||||||
};
|
|
||||||
|
|
||||||
const is_double_click = table_core.detectDoubleClick(
|
const is_double_click = table_core.detectDoubleClick(
|
||||||
&dc_state,
|
&dc_state,
|
||||||
|
|
@ -1128,9 +1123,7 @@ fn handleMouseClick(
|
||||||
);
|
);
|
||||||
|
|
||||||
// Update state from detection
|
// Update state from detection
|
||||||
list_state.last_click_time = dc_state.last_click_time;
|
list_state.nav.double_click = dc_state;
|
||||||
list_state.last_click_row = dc_state.last_click_row;
|
|
||||||
list_state.last_click_col = dc_state.last_click_col;
|
|
||||||
|
|
||||||
if (is_double_click and !list_state.isEditing()) {
|
if (is_double_click and !list_state.isEditing()) {
|
||||||
// Double-click: start editing
|
// Double-click: start editing
|
||||||
|
|
@ -1143,7 +1136,7 @@ fn handleMouseClick(
|
||||||
} else {
|
} else {
|
||||||
// Single click: select
|
// Single click: select
|
||||||
list_state.selectById(list_state.current_window[data_idx].id);
|
list_state.selectById(list_state.current_window[data_idx].id);
|
||||||
list_state.active_col = clicked_col;
|
list_state.nav.active_col = clicked_col;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue