diff --git a/crates/gpui_elements/src/editable_text/input_element.rs b/crates/gpui_elements/src/editable_text/input_element.rs index 52257aabec..61f3ec8a9e 100644 --- a/crates/gpui_elements/src/editable_text/input_element.rs +++ b/crates/gpui_elements/src/editable_text/input_element.rs @@ -1,11 +1,10 @@ use crate::editable_text::{ - EditableInputActionElement, StateBackedElement, TextInputState, UnicodeTextStorage, + EditableInputActionElement, InitStorage, StateBackedElement, TextInputState, }; use gpui::{ App, Element, ElementId, Entity, Hitbox, InteractiveElement, Interactivity, IntoElement, SharedString, StyleRefinement, Styled, TextStyle, Window, }; -use std::rc::Rc; #[track_caller] pub fn input(id: impl Into) -> TextInputElement { @@ -47,17 +46,6 @@ impl IntoElement for TextInputElement { } } -#[derive(Clone, Default)] -pub(super) struct InitStorage(Option Box>>); -impl InitStorage { - fn exec(&self, cx: &mut App) -> Box { - match &self.0 { - None => Box::new(String::new()), - Some(init) => (*init)(cx), - } - } -} - impl EditableInputActionElement for TextInputElement {} impl super::StateBackedElement for TextInputElement { type State = TextInputState; @@ -80,20 +68,24 @@ impl super::StateBackedElement for TextInputElement { } } -#[doc(hidden)] -pub struct LayoutState { - state: Entity, - text_style: TextStyle, -} +pub mod element { + use super::*; -#[doc(hidden)] -pub struct PrepaintState { - hitbox: Option, + #[doc(hidden)] + pub struct LayoutState { + pub state: Entity, + pub text_style: TextStyle, + } + + #[doc(hidden)] + pub struct PrepaintState { + pub hitbox: Option, + } } impl Element for TextInputElement { - type RequestLayoutState = LayoutState; - type PrepaintState = PrepaintState; + type RequestLayoutState = element::LayoutState; + type PrepaintState = element::PrepaintState; fn id(&self) -> Option { self.interactivity.element_id.clone() @@ -130,7 +122,7 @@ impl Element for TextInputElement { }, ); - let layout_state = LayoutState { + let layout_state = element::LayoutState { state, text_style: resolved_text_style.unwrap_or_else(|| window.text_style()), }; diff --git a/crates/gpui_elements/src/editable_text/storage.rs b/crates/gpui_elements/src/editable_text/storage.rs index 1cd990cff8..e5b2422e65 100644 --- a/crates/gpui_elements/src/editable_text/storage.rs +++ b/crates/gpui_elements/src/editable_text/storage.rs @@ -1,5 +1,5 @@ -use gpui::NavigationDirection; -use std::ops::Range; +use gpui::{App, NavigationDirection}; +use std::{ops::Range, rc::Rc}; use unicode_segmentation::UnicodeSegmentation; pub enum TextBoundary { @@ -13,6 +13,17 @@ pub enum TextBoundary { Document, } +#[derive(Clone, Default)] +pub(super) struct InitStorage(Option Box>>); +impl InitStorage { + pub fn exec(&self, cx: &mut App) -> Box { + match &self.0 { + None => Box::new(String::new()), + Some(init) => (*init)(cx), + } + } +} + pub trait UnicodeTextStorage { /// Returns a reference to the utf8 string. fn content_utf8(&self) -> &str; diff --git a/crates/gpui_elements/src/editable_text/text_area_element.rs b/crates/gpui_elements/src/editable_text/text_area_element.rs index cad2948606..02662e0bd9 100644 --- a/crates/gpui_elements/src/editable_text/text_area_element.rs +++ b/crates/gpui_elements/src/editable_text/text_area_element.rs @@ -1,3 +1,160 @@ -use gpui::ElementId; +use crate::editable_text::{ + EditableInputActionElement, InitStorage, StateBackedElement, TextAreaState, +}; +use gpui::{ + App, Element, ElementId, Entity, Hitbox, InteractiveElement, Interactivity, IntoElement, + SharedString, StyleRefinement, Styled, TextStyle, Window, +}; -pub fn text_area(id: impl Into) {} +pub fn text_area(id: impl Into) -> TextAreaElement { + let mut this = TextAreaElement { + id: id.into(), + placeholder: None, + interactivity: Interactivity::new(), + init_storage: InitStorage::default(), + }; + this = this.key_context(super::DEFAULT_INPUT_CONTEXT); + this.register_actions(); + this +} + +// TODO: Disabled flag/state? +pub struct TextAreaElement { + id: ElementId, + placeholder: Option, + interactivity: Interactivity, + init_storage: InitStorage, +} + +impl InteractiveElement for TextAreaElement { + fn interactivity(&mut self) -> &mut Interactivity { + &mut self.interactivity + } +} + +impl Styled for TextAreaElement { + fn style(&mut self) -> &mut StyleRefinement { + &mut self.interactivity.base_style + } +} + +impl IntoElement for TextAreaElement { + type Element = Self; + fn into_element(self) -> Self::Element { + self + } +} + +impl EditableInputActionElement for TextAreaElement {} +impl super::StateBackedElement for TextAreaElement { + type State = TextAreaState; + type InitProps = (ElementId, InitStorage); + + fn init_props(&self) -> Self::InitProps { + (self.id.clone(), self.init_storage.clone()) + } + + fn get_or_init_state( + init_props: &Self::InitProps, + window: &mut Window, + cx: &mut App, + ) -> Entity { + // Get the state from the app using the element's id as the key. + // If it doesnt exist, initialize a new state with the user's desired storage medium. + window.use_keyed_state(init_props.0.clone(), cx, |_window, cx| { + TextAreaState::new(init_props.1.exec(cx), cx) + }) + } +} + +pub mod element { + use super::*; + + #[doc(hidden)] + pub struct LayoutState { + pub state: Entity, + pub text_style: TextStyle, + } + + #[doc(hidden)] + pub struct PrepaintState { + pub hitbox: Option, + } +} + +impl Element for TextAreaElement { + type RequestLayoutState = element::LayoutState; + type PrepaintState = element::PrepaintState; + + fn id(&self) -> Option { + self.interactivity.element_id.clone() + } + + fn source_location(&self) -> Option<&'static std::panic::Location<'static>> { + self.interactivity.source_location() + } + + fn request_layout( + &mut self, + global_id: Option<&gpui::GlobalElementId>, + inspector_id: Option<&gpui::InspectorElementId>, + window: &mut gpui::Window, + cx: &mut gpui::App, + ) -> (gpui::LayoutId, Self::RequestLayoutState) { + let mut resolved_text_style = None; + + let state = self.get_state(window, cx); + + let layout_id = self.interactivity.request_layout( + global_id, + inspector_id, + window, + cx, + |element_style, window, cx| { + window.with_text_style(element_style.text_style().cloned(), |window| { + resolved_text_style = Some(window.text_style()); + + let mut style = element_style.clone(); + if let gpui::Length::Auto = style.size.width { + style.size.width = gpui::relative(1.).into(); + } + if let gpui::Length::Auto = style.size.height { + style.size.height = gpui::relative(1.).into(); + } + window.request_layout(style, None, cx) + }) + }, + ); + + let layout_state = element::LayoutState { + state, + text_style: resolved_text_style.unwrap_or_else(|| window.text_style()), + }; + (layout_id, layout_state) + } + + fn prepaint( + &mut self, + id: Option<&gpui::GlobalElementId>, + inspector_id: Option<&gpui::InspectorElementId>, + bounds: gpui::Bounds, + request_layout: &mut Self::RequestLayoutState, + window: &mut gpui::Window, + cx: &mut gpui::App, + ) -> Self::PrepaintState { + todo!() + } + + fn paint( + &mut self, + id: Option<&gpui::GlobalElementId>, + inspector_id: Option<&gpui::InspectorElementId>, + bounds: gpui::Bounds, + request_layout: &mut Self::RequestLayoutState, + prepaint: &mut Self::PrepaintState, + window: &mut gpui::Window, + cx: &mut gpui::App, + ) { + todo!() + } +} diff --git a/crates/gpui_elements/src/editable_text/text_area_state.rs b/crates/gpui_elements/src/editable_text/text_area_state.rs index 628e135e73..a69225e3ac 100644 --- a/crates/gpui_elements/src/editable_text/text_area_state.rs +++ b/crates/gpui_elements/src/editable_text/text_area_state.rs @@ -1,5 +1,6 @@ use crate::editable_text::{ EditableTextActionHandler, TextBoundary, TextInputStateBase, TextStateNotifier, + UnicodeTextStorage, notify::{TextChanged, TextHistoryPushed}, }; use gpui::{ @@ -15,6 +16,13 @@ pub struct TextAreaState { impl EventEmitter for TextAreaState {} impl EventEmitter for TextAreaState {} +impl TextAreaState { + pub fn new(storage: impl Into>, cx: &mut Context) -> Self { + let internal = TextInputStateBase::new(storage, cx); + Self { internal } + } +} + impl TextStateNotifier for Context<'_, TextAreaState> { fn notify_changed(&mut self) { self.notify();