add simpler api for configuring the caret blink interval via element

This commit is contained in:
temportalflux
2026-07-11 09:31:07 -04:00
parent d6d35ab410
commit d5b3191738
3 changed files with 53 additions and 12 deletions
+15 -2
View File
@@ -115,7 +115,20 @@
//! # }
//! ```
//!
//! To use a blinking caret, construct a caret entity with a blinking interval when constructing the state:
//! To use a blinking caret, you can use one of the templated functions:
//! ```
//! # use gpui::{prelude::*, App, Entity, Window, AppContext, ElementId};
//! # fn test(window: &mut Window, cx: &mut App) -> gpui_elements::editable_text::EditableTextElement {
//! use gpui_elements::editable_text::{text_input};
//! let id = ElementId::from("my_input");
//! text_input(id)
//! .caret_blink_interval_500ms()
//! // or use the parameterized one, e.g. 200ms
//! .caret_blink_interval(std::time::Duration::from_millis(200))
//! # }
//! ```
//!
//! or construct a caret entity with a blinking interval when constructing the state:
//! ```
//! # use gpui::{prelude::*, App, Entity, Window, AppContext, ElementId};
//! # fn test(window: &mut Window, cx: &mut App) -> gpui_elements::editable_text::EditableTextElement {
@@ -127,7 +140,7 @@
//! // Ensure the caret exists, linked to the input element by id.
//! window.use_keyed_state(id.clone(), cx, |window, cx| {
//! // using the default interval of 500ms
//! let mut caret = Caret::default().blink_interval_default();
//! let mut caret = Caret::default().with_blink_interval_500ms();
//! // ensures the caret receives events from the input state during typing & other actions
//! caret.subscribe_to(&state, cx);
//! caret
@@ -3,7 +3,7 @@ use smallvec::SmallVec;
use std::time::Duration;
/// Default interval for caret blinking (500ms).
pub const DEFAULT_BLINK_INTERVAL: Duration = Duration::from_millis(500);
pub const BLINK_INTERVAL_500MS: Duration = Duration::from_millis(500);
/// Events emitted that the [`Caret`] listens to.
pub enum CaretNotify {
@@ -44,19 +44,28 @@ impl Default for Caret {
}
impl Caret {
/// Sets the blinking interval of the caret to the global "default".
/// The true default of the caret is "do not blink".
pub fn blink_interval_default(mut self) -> Self {
self.interval = DEFAULT_BLINK_INTERVAL;
self
/// Returns the duration of the current blink interval
pub fn blink_interval(&self) -> Duration {
self.interval
}
/// Sets the blinking interval of the caret.
pub fn blink_interval(mut self, interval: Duration) -> Self {
pub fn set_blink_interval(&mut self, interval: Duration) {
self.interval = interval;
}
/// Sets the blinking interval of the caret.
pub fn with_blink_interval(mut self, interval: Duration) -> Self {
self.set_blink_interval(interval);
self
}
/// Sets the blinking interval of the caret to the global "default".
/// The true default of the caret is "do not blink".
pub fn with_blink_interval_500ms(self) -> Self {
self.with_blink_interval(BLINK_INTERVAL_500MS)
}
/// Listens for CaretNotify events on an entity (e.g. [`EditableTextState`]).
pub fn subscribe_to<E>(&mut self, emitter: &Entity<E>, cx: &mut Context<Self>)
where
@@ -88,7 +97,7 @@ impl Caret {
// Caret has no blinking interval, it is always visible
if self.interval.is_zero() {
return true;
return is_focused;
}
match (is_focused, was_focused) {
@@ -1,5 +1,5 @@
use crate::editable_text::{
Caret, EditableTextState,
BLINK_INTERVAL_500MS, Caret, EditableTextState,
actions::{DEFAULT_INPUT_CONTEXT, EditableTextActionElement, EditableTextActionHandler},
layout::{EditableTextLayoutResult, EditableTextLayoutState, TextLineSegment},
};
@@ -12,7 +12,7 @@ use gpui::{
px, size,
};
use smallvec::SmallVec;
use std::{cell::RefCell, ops::Range, rc::Rc, sync::Arc};
use std::{cell::RefCell, ops::Range, rc::Rc, sync::Arc, time::Duration};
const CARET_RENDER_WIDTH: f32 = 2.0;
@@ -29,6 +29,7 @@ pub fn editable_text(id: impl Into<ElementId>) -> EditableTextElement {
placeholder: None,
accepts_input: true,
colors: EditableTextColors::default(),
caret_blink_interval: None,
};
this.interactivity.element_id = Some(id.into());
@@ -63,6 +64,7 @@ pub struct EditableTextElement {
placeholder: Option<SharedString>,
accepts_input: bool,
colors: EditableTextColors,
caret_blink_interval: Option<Duration>,
}
/// EditableText styling that goes beyond what Style/StyleRefinement supports
@@ -140,6 +142,17 @@ impl EditableTextElement {
self
}
/// Sets the blinking interval of the caret.
pub fn caret_blink_interval(mut self, duration: Duration) -> Self {
self.caret_blink_interval = Some(duration);
self
}
/// Sets the blinking interval of the caret to 500ms
pub fn caret_blink_interval_500ms(self) -> Self {
self.caret_blink_interval(BLINK_INTERVAL_500MS)
}
/// Sets the color of the placeholder text which is rendered when the element's stored text is empty.
///
/// Cannot be refined via [`StyleRefinement`](gpui::StyleRefinement) due to limitations in the fields of [`Style`](gpui::Style).
@@ -256,6 +269,12 @@ impl Element for EditableTextElement {
let entity = self.find_or_create_state(window, cx);
let caret = self.find_or_create_caret(&entity, window, cx);
if let Some(duration) = self.caret_blink_interval.take()
&& caret.read(cx).blink_interval() != duration
{
caret.update(cx, |caret, _cx| caret.set_blink_interval(duration));
}
// Read new state information from the underlying entity.
// Block-wrapped so that the state being read is dropped before continuing.
let (prelayout, next_scroll_offset) = {