diff --git a/crates/ui/src/story/input_story.rs b/crates/ui/src/story/input_story.rs index 0dce6afa..e0af1d11 100644 --- a/crates/ui/src/story/input_story.rs +++ b/crates/ui/src/story/input_story.rs @@ -8,27 +8,31 @@ use crate::text_field::TextField; use super::story_case; pub struct InputStory { - input1: TextField, - input2: TextField, - mash_input: TextField, - disabled_input: TextField, + input1: View, + input2: View, + mash_input: View, + disabled_input: View, } impl InputStory { pub(crate) fn new(cx: &mut WindowContext) -> Self { - let input1 = TextField::new(cx).set_text("Hello 世界", cx); + let input1 = cx.new_view(|cx| TextField::new(cx).set_text("Hello 世界", cx)); - let mask_input = TextField::new(cx) - .set_masked(true, cx) - .set_text("this-is-password", cx); + let mask_input = cx.new_view(|cx| { + TextField::new(cx) + .set_masked(true, cx) + .set_text("this-is-password", cx) + }); Self { input1, - input2: TextField::new(cx).set_placeholder("Enter text here...", cx), + input2: cx.new_view(|cx| TextField::new(cx).set_placeholder("Enter text here...", cx)), mash_input: mask_input, - disabled_input: TextField::new(cx) - .set_text("This is disabled input", cx) - .set_disabled(true, cx), + disabled_input: cx.new_view(|cx| { + TextField::new(cx) + .set_text("This is disabled input", cx) + .set_disabled(true, cx) + }), } } @@ -39,7 +43,7 @@ impl InputStory { } impl Render for InputStory { - fn render(&mut self, cx: &mut ViewContext) -> impl IntoElement { + fn render(&mut self, _cx: &mut ViewContext) -> impl IntoElement { story_case("Input", "A text input field.").child( div() .flex() diff --git a/crates/ui/src/text_field/mod.rs b/crates/ui/src/text_field/mod.rs index f481957f..4240a12e 100644 --- a/crates/ui/src/text_field/mod.rs +++ b/crates/ui/src/text_field/mod.rs @@ -4,15 +4,16 @@ mod text_view; use crate::theme::ActiveTheme; use gpui::{ - div, prelude::FluentBuilder as _, ClipboardItem, EventEmitter, FocusHandle, InteractiveElement, - IntoElement, KeyDownEvent, MouseButton, ParentElement, RenderOnce, Styled, View, WindowContext, + div, prelude::FluentBuilder as _, ClipboardItem, EventEmitter, FocusHandle, FocusableView, + InteractiveElement, IntoElement, KeyDownEvent, MouseButton, ParentElement, Render, RenderOnce, + Styled, View, WindowContext, }; use std::time::Duration; use text_view::TextView; const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500); -#[derive(Clone, IntoElement)] +#[derive(Clone)] pub struct TextField { focus_handle: FocusHandle, pub view: View, @@ -56,8 +57,14 @@ impl TextField { } } -impl RenderOnce for TextField { - fn render(self, cx: &mut WindowContext) -> impl IntoElement { +impl FocusableView for TextField { + fn focus_handle(&self, _cx: &gpui::AppContext) -> FocusHandle { + self.focus_handle.clone() + } +} + +impl Render for TextField { + fn render(&mut self, cx: &mut gpui::ViewContext) -> impl IntoElement { let focus_handle = self.focus_handle.clone(); let theme = cx.theme(); @@ -70,14 +77,17 @@ impl RenderOnce for TextField { div() .track_focus(&focus_handle) .when(!text_view.disabled, |this| { - this.on_mouse_down(MouseButton::Left, move |_, cx| { - cx.prevent_default(); - self.focus_handle.focus(cx) - }) + this.on_mouse_down( + MouseButton::Left, + cx.listener(move |view, _, cx| { + cx.prevent_default(); + view.focus_handle.focus(cx) + }), + ) }) .when(!disabled, |this| { - this.on_key_down(move |ev, cx| { - self.view.update(cx, |text_view, cx| { + this.on_key_down(cx.listener(move |this, ev: &KeyDownEvent, cx| { + this.view.update(cx, |text_view, cx| { let prev = text_view.text.clone(); cx.emit(TextEvent::KeyDown(ev.clone())); let keystroke = ev.keystroke.key.as_str(); @@ -219,7 +229,7 @@ impl RenderOnce for TextField { } cx.notify(); }); - }) + })) }) .border_color(if focused { theme.ring } else { theme.input }) .border_1()