add external api for access to caret by routing it through keyed element data
This commit is contained in:
@@ -115,6 +115,28 @@
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! To use a blinking caret, 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 {
|
||||
//! use gpui_elements::editable_text::{text_input, EditableTextState, TextChanged, Caret};
|
||||
//! let id = ElementId::from("my_input");
|
||||
//!
|
||||
//! let state = EditableTextState::use_keyed(id.clone(), window, cx);
|
||||
//!
|
||||
//! // 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();
|
||||
//! // ensures the caret receives events from the input state during typing & other actions
|
||||
//! caret.subscribe_to(&state, cx);
|
||||
//! caret
|
||||
//! });
|
||||
//!
|
||||
//! text_input(id).state(state.downgrade())
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! You can view more complex examples in the gpui crate examples.
|
||||
//! TODO: there is no example with editable text yet, and we should link it here when there is.
|
||||
//!
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::editable_text::{
|
||||
EditableTextState,
|
||||
Caret, EditableTextState,
|
||||
actions::{DEFAULT_INPUT_CONTEXT, EditableTextActionElement, EditableTextActionHandler},
|
||||
layout::{EditableTextLayoutResult, EditableTextLayoutState, TextLineSegment},
|
||||
};
|
||||
@@ -216,6 +216,7 @@ struct PrelayoutState {
|
||||
#[doc(hidden)]
|
||||
pub struct LayoutState {
|
||||
state: Entity<EditableTextState>,
|
||||
caret: Entity<Caret>,
|
||||
}
|
||||
|
||||
struct InteractivityPrepaint {
|
||||
@@ -253,6 +254,7 @@ impl Element for EditableTextElement {
|
||||
cx: &mut App,
|
||||
) -> (gpui::LayoutId, Self::RequestLayoutState) {
|
||||
let entity = self.find_or_create_state(window, cx);
|
||||
let caret = self.find_or_create_caret(&entity, window, cx);
|
||||
|
||||
// Read new state information from the underlying entity.
|
||||
// Block-wrapped so that the state being read is dropped before continuing.
|
||||
@@ -302,7 +304,13 @@ impl Element for EditableTextElement {
|
||||
},
|
||||
);
|
||||
|
||||
(layout_id, LayoutState { state: entity })
|
||||
(
|
||||
layout_id,
|
||||
LayoutState {
|
||||
state: entity,
|
||||
caret,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn prepaint(
|
||||
@@ -316,16 +324,17 @@ impl Element for EditableTextElement {
|
||||
) -> Self::PrepaintState {
|
||||
// should reflect the text content layout size of the stored text,
|
||||
// so that scrolling can take it into account during prepaint.
|
||||
let (content_size, caret_entity, focus_handle) = {
|
||||
let (content_size, focus_handle) = {
|
||||
let state = request_layout.state.read(cx);
|
||||
let content_size = state.layout_data.state.size.unwrap_or_else(|| bounds.size);
|
||||
let caret = state.caret_entity().clone();
|
||||
let focus_handle = state.focus_handle(cx);
|
||||
(content_size, caret, focus_handle)
|
||||
(content_size, focus_handle)
|
||||
};
|
||||
|
||||
let is_focused = focus_handle.is_focused(window);
|
||||
let caret_visible = caret_entity.update(cx, |caret, cx| caret.update_focus(is_focused, cx));
|
||||
let caret_visible = request_layout
|
||||
.caret
|
||||
.update(cx, |caret, cx| caret.update_focus(is_focused, cx));
|
||||
window.set_focus_handle(&focus_handle, cx);
|
||||
|
||||
let prepaint = self.interactivity.prepaint(
|
||||
@@ -443,6 +452,23 @@ impl EditableTextElement {
|
||||
state
|
||||
}
|
||||
|
||||
fn find_or_create_caret(
|
||||
&self,
|
||||
state: &Entity<EditableTextState>,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> Entity<Caret> {
|
||||
let Some(element_id) = self.interactivity.element_id.clone() else {
|
||||
unimplemented!("all input elements must be assigned an id")
|
||||
};
|
||||
|
||||
window.use_keyed_state(element_id, cx, |_window, cx| {
|
||||
let mut caret = Caret::default();
|
||||
caret.subscribe_to(state, cx);
|
||||
caret
|
||||
})
|
||||
}
|
||||
|
||||
fn process_frame_events(
|
||||
prepaint: &PrepaintState,
|
||||
bounds: Bounds<Pixels>,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::editable_text::{
|
||||
StringStorage, TextBoundary, UnicodeTextStorage,
|
||||
actions::EditableTextActionHandler,
|
||||
caret::{Caret, CaretNotify},
|
||||
caret::CaretNotify,
|
||||
history::EditableTextHistory,
|
||||
layout::{EditableTextLayoutResult, TextLineSegment},
|
||||
};
|
||||
@@ -63,8 +63,6 @@ pub struct EditableTextState {
|
||||
/// The storage medium backing this element-state. Hypothetically supports both
|
||||
/// std String and other crates (e.g. long document text).
|
||||
storage: Box<dyn UnicodeTextStorage>,
|
||||
/// The caret entity which has internal state for features like blinking
|
||||
caret: Entity<Caret>,
|
||||
|
||||
/// The utf-8 character range that is currently selected by the user.
|
||||
/// Valid both when start < end and start > end (which dictates the direction of the selection).
|
||||
@@ -163,18 +161,8 @@ impl EditableTextState {
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn new(storage: impl UnicodeTextStorage + 'static, cx: &mut Context<Self>) -> Self {
|
||||
use gpui::AppContext;
|
||||
let caret = cx.new({
|
||||
let state_entity = cx.entity();
|
||||
move |cx| {
|
||||
let mut caret = Caret::default().blink_interval_default();
|
||||
caret.subscribe_to(&state_entity, cx);
|
||||
caret
|
||||
}
|
||||
});
|
||||
Self {
|
||||
storage: Box::new(storage),
|
||||
caret,
|
||||
|
||||
selected_range: 0.into(),
|
||||
marked_range: None,
|
||||
@@ -222,11 +210,6 @@ impl EditableTextState {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a reference to the entity owning the state of the [`Caret`] (e.g. its blinking state).
|
||||
pub(super) fn caret_entity(&self) -> &Entity<Caret> {
|
||||
&self.caret
|
||||
}
|
||||
|
||||
/// Returns the position of the caret in utf8 character space.
|
||||
pub(super) fn caret_pos(&self) -> usize {
|
||||
self.selected_range.start
|
||||
|
||||
Reference in New Issue
Block a user