add accepts_input flag as a way to track an input field's disabled state
This commit is contained in:
@@ -9,7 +9,6 @@ pub use state::*;
|
||||
pub use storage::*;
|
||||
|
||||
/* TODO list
|
||||
- disabled input/area
|
||||
- remove gpuikit based input
|
||||
- auto-scroll when cursor moves
|
||||
- cursor blinking
|
||||
|
||||
@@ -21,6 +21,7 @@ pub fn editable_text(id: impl Into<ElementId>) -> EditableTextElement {
|
||||
supports_multiline: true,
|
||||
init_storage: InitStorage::default(),
|
||||
placeholder: None,
|
||||
accepts_input: true,
|
||||
};
|
||||
this.interactivity.element_id = Some(id.into());
|
||||
|
||||
@@ -40,7 +41,6 @@ pub fn text_area(id: impl Into<ElementId>) -> EditableTextElement {
|
||||
editable_text(id).multiline(true)
|
||||
}
|
||||
|
||||
// TODO: Disabled flag/state?
|
||||
pub struct EditableTextElement {
|
||||
interactivity: Interactivity,
|
||||
// Populated on first render with an entity stored/attached to the view.
|
||||
@@ -50,6 +50,7 @@ pub struct EditableTextElement {
|
||||
init_storage: InitStorage,
|
||||
supports_multiline: bool,
|
||||
placeholder: Option<SharedString>,
|
||||
accepts_input: bool,
|
||||
}
|
||||
|
||||
impl EditableTextElement {
|
||||
@@ -78,6 +79,12 @@ impl EditableTextElement {
|
||||
self.init_storage = InitStorage::new_typed(move |_cx| storage.clone());
|
||||
self
|
||||
}
|
||||
|
||||
/// Configures whether the element can accept input (effectively is the element currently enabled).
|
||||
pub fn accepts_input(mut self, enabled: bool) -> Self {
|
||||
self.accepts_input = enabled;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl InteractiveElement for EditableTextElement {
|
||||
@@ -193,6 +200,7 @@ impl Element for EditableTextElement {
|
||||
let placeholder = self.placeholder.clone();
|
||||
let placeholder_color = Hsla::white().opacity(0.5); // TODO: as an element param
|
||||
let supports_multiline = self.supports_multiline;
|
||||
let accepts_input = self.accepts_input;
|
||||
let layout_id = self.interactivity().request_layout(
|
||||
global_id,
|
||||
inspector_id,
|
||||
@@ -317,6 +325,7 @@ impl Element for EditableTextElement {
|
||||
|
||||
let layout_data = TextInputLayoutData {
|
||||
supports_multiline,
|
||||
accepts_input,
|
||||
wrap_width,
|
||||
size: Some(size),
|
||||
last_seen_storage_version,
|
||||
@@ -552,6 +561,7 @@ impl Element for EditableTextElement {
|
||||
window.set_cursor_style(CursorStyle::IBeam, hitbox);
|
||||
}
|
||||
|
||||
let accepts_input = self.accepts_input;
|
||||
let inner_bounds = prepaint.inner_bounds;
|
||||
let to_local_position = -(bounds.origin + prepaint.scroll_offset);
|
||||
let perform_paint = |style: &Style, window: &mut Window, cx: &mut App| {
|
||||
@@ -559,9 +569,11 @@ impl Element for EditableTextElement {
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTE: Skip when disabled
|
||||
let ime_handler = ElementInputHandler::new(inner_bounds, request_layout.state.clone());
|
||||
window.handle_input(&prepaint.focus_handle, ime_handler, cx);
|
||||
if accepts_input {
|
||||
let ime_handler =
|
||||
ElementInputHandler::new(inner_bounds, request_layout.state.clone());
|
||||
window.handle_input(&prepaint.focus_handle, ime_handler, cx);
|
||||
}
|
||||
|
||||
window.on_mouse_event({
|
||||
let state = request_layout.state.clone();
|
||||
|
||||
@@ -38,6 +38,7 @@ pub struct EditableTextState {
|
||||
#[derive(Default)]
|
||||
pub(super) struct TextInputLayoutData {
|
||||
pub supports_multiline: bool,
|
||||
pub accepts_input: bool,
|
||||
/// The last known width at which the lines were wrapped.
|
||||
pub wrap_width: Option<Pixels>,
|
||||
/// The last known size of the text, as generated during layout.
|
||||
@@ -277,6 +278,10 @@ impl EditableTextState {
|
||||
boundary: TextBoundary,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
if !self.layout_data.accepts_input {
|
||||
return;
|
||||
}
|
||||
|
||||
let range = self.selected_range();
|
||||
let range = match range.is_empty() {
|
||||
false => range,
|
||||
@@ -555,13 +560,20 @@ impl<'app> EditableTextActionHandler<Context<'app, Self>> for EditableTextState
|
||||
}
|
||||
|
||||
fn insert_enter(&mut self, _: &Enter, window: &mut Window, cx: &mut Context<'app, Self>) {
|
||||
if self.layout_data.supports_multiline {
|
||||
// TODO: Why is the cursor appearing at the start of the entire field instead of on the new line?
|
||||
self.replace_text_in_range(None, "\n", window, cx);
|
||||
if !self.layout_data.supports_multiline {
|
||||
return;
|
||||
}
|
||||
if !self.layout_data.accepts_input {
|
||||
return;
|
||||
}
|
||||
// TODO: Why is the cursor appearing at the start of the entire field instead of on the new line?
|
||||
self.replace_text_in_range(None, "\n", window, cx);
|
||||
}
|
||||
|
||||
fn insert_tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Context<'app, Self>) {
|
||||
if !self.layout_data.accepts_input {
|
||||
return;
|
||||
}
|
||||
self.replace_text_in_range(None, "\t", window, cx);
|
||||
}
|
||||
|
||||
@@ -741,6 +753,10 @@ impl<'app> EditableTextActionHandler<Context<'app, Self>> for EditableTextState
|
||||
}
|
||||
|
||||
fn cut(&mut self, _: &Cut, _w: &mut Window, cx: &mut Context<'app, Self>) {
|
||||
if !self.layout_data.accepts_input {
|
||||
return;
|
||||
}
|
||||
|
||||
if !self.selected_range.is_empty() {
|
||||
// Cut selected text
|
||||
let slice = &self.storage.content_utf8()[self.selected_range.clone()];
|
||||
@@ -789,6 +805,10 @@ impl<'app> EditableTextActionHandler<Context<'app, Self>> for EditableTextState
|
||||
}
|
||||
|
||||
fn paste(&mut self, _: &Paste, _w: &mut Window, cx: &mut Context<'app, Self>) {
|
||||
if !self.layout_data.accepts_input {
|
||||
return;
|
||||
}
|
||||
|
||||
let Some(text) = cx.read_from_clipboard().and_then(|item| item.text()) else {
|
||||
return;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user