diff --git a/src/core/context.zig b/src/core/context.zig index c6bad35..3ed2acd 100644 --- a/src/core/context.zig +++ b/src/core/context.zig @@ -127,6 +127,11 @@ pub const Context = struct { /// Main loop should check this and request another frame if true. needs_animation_frame: bool = false, + /// Flag set by widgets that have an active cursor (TextInput, CellEditor). + /// Reset each frame in beginFrame(). Only when true does needsCursorAnimation() return true. + /// This prevents unnecessary redraws when no text field is being edited. + requested_cursor_blink: bool = false, + /// Optional text measurement function (set by application with TTF font) /// Returns pixel width of text. If null, falls back to char_width * len. text_measure_fn: ?*const fn ([]const u8) u32 = null, @@ -244,6 +249,9 @@ pub const Context = struct { // Reset animation request (set by widgets during draw) self.needs_animation_frame = false; + // Reset cursor blink request (set by TextInput/CellEditor during draw) + self.requested_cursor_blink = false; + self.frame += 1; } @@ -393,14 +401,24 @@ pub const Context = struct { /// Check if cursor animation is needed (for event loop timeout decisions). /// Returns true if we're within the active period where cursor should blink. - /// The application should use a short timeout (e.g., 500ms) when this returns true, + /// Only returns true if a widget (TextInput/CellEditor) requested cursor blink this frame. + /// The application should use a short timeout (e.g., 600ms) when this returns true, /// and can use infinite timeout when false. pub fn needsCursorAnimation(self: Self) bool { if (self.current_time_ms == 0) return false; + // Smart cursor: only blink if a widget actually requested it this frame + if (!self.requested_cursor_blink) return false; const idle_time = self.current_time_ms -| self.last_input_time_ms; return idle_time < CURSOR_IDLE_TIMEOUT_MS; } + /// Request cursor blink animation for this frame. + /// Called by widgets with active text cursors (TextInput, CellEditor). + /// Must be called each frame while cursor is active (immediate-mode pattern). + pub fn requestCursorBlink(self: *Self) void { + self.requested_cursor_blink = true; + } + /// Get recommended event loop timeout in milliseconds. /// Returns the time until next cursor blink toggle, or null for infinite wait. pub fn getAnimationTimeout(self: Self) ?u32 { diff --git a/src/widgets/text_input.zig b/src/widgets/text_input.zig index 5ceae77..290be43 100644 --- a/src/widgets/text_input.zig +++ b/src/widgets/text_input.zig @@ -284,6 +284,11 @@ pub fn textInputRect( // Sync state.focused for backwards compatibility state.focused = has_focus; + // Smart cursor: request cursor blink only when we have an active editable cursor + if (has_focus and !config.readonly) { + ctx.requestCursorBlink(); + } + // Theme colors (Z-Design: usar theme dinĂ¡mico, con overrides del panel) const theme = Style.currentTheme().*; // Use override colors if provided, otherwise use theme defaults diff --git a/src/widgets/virtual_advanced_table/cell_editor.zig b/src/widgets/virtual_advanced_table/cell_editor.zig index 1ebc3ee..79e08ca 100644 --- a/src/widgets/virtual_advanced_table/cell_editor.zig +++ b/src/widgets/virtual_advanced_table/cell_editor.zig @@ -57,6 +57,9 @@ pub fn drawCellEditor( if (!state.isEditing()) return result; + // Smart cursor: request cursor blink while editing cell + ctx.requestCursorBlink(); + // Padding interno const padding: i32 = 2;