Refactor Slider, Input, InputOtp to use EventEmiter to emit change (#45)

This commit is contained in:
Jason Lee 2024-07-18 18:38:58 +08:00 committed by GitHub
parent f6146220e4
commit bac5392e0a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 108 additions and 107 deletions

View file

@ -1,5 +1,5 @@
use gpui::{
actions, AppContext, ClickEvent, FocusHandle, InteractiveElement, IntoElement, KeyBinding,
actions, AppContext, FocusHandle, InteractiveElement, IntoElement, KeyBinding,
ParentElement as _, Render, SharedString, Styled, View, ViewContext, VisualContext,
WindowContext,
};
@ -7,7 +7,7 @@ use gpui::{
use ui::{
button::Button,
h_flex,
input::{InputOtp, TextEvent, TextInput},
input::{InputEvent, InputOtp, TextInput},
prelude::FluentBuilder as _,
v_flex, Clickable, FocusableCycle, IconName, Size,
};
@ -86,16 +86,15 @@ impl InputStory {
.placeholder("This input have prefix and suffix.")
});
let view = cx.view().clone();
let otp_input = cx.new_view(|cx| {
InputOtp::new(6, cx)
.masked(true)
.on_change(move |value: &SharedString, cx| {
view.update(cx, |view, _| {
view.otp_value = Some(value.clone());
})
})
});
let otp_input = cx.new_view(|cx| InputOtp::new(6, cx).masked(true));
cx.subscribe(&otp_input, |this, _, ev: &InputEvent, cx| match ev {
InputEvent::Change(text) => {
this.otp_value = Some(text.clone());
cx.notify();
}
_ => {}
})
.detach();
Self {
input1,
@ -126,11 +125,6 @@ impl InputStory {
}
}
#[allow(unused)]
fn on_change(ev: &ClickEvent, cx: &mut WindowContext) {
println!("Input changed: {:?}", ev);
}
fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
self.cycle_focus(true, cx);
}
@ -142,14 +136,14 @@ impl InputStory {
fn on_input_event(
&mut self,
_: View<TextInput>,
event: &TextEvent,
event: &InputEvent,
_cx: &mut ViewContext<Self>,
) {
match event {
TextEvent::Input { text } => println!("Input: {}", text),
TextEvent::PressEnter => println!("PressEnter"),
TextEvent::Focus => println!("Focus"),
TextEvent::Blur => println!("Blur"),
InputEvent::Change(text) => println!("Change: {}", text),
InputEvent::PressEnter => println!("PressEnter"),
InputEvent::Focus => println!("Focus"),
InputEvent::Blur => println!("Blur"),
};
}
}
@ -183,25 +177,35 @@ impl Render for InputStory {
.justify_start()
.gap_3()
.child(
section("Normal Input", cx)
.child(self.input1.clone())
.child(self.input2.clone()),
h_flex()
.gap_3()
.items_start()
.child(
section("Normal Input", cx)
.child(self.input1.clone())
.child(self.input2.clone()),
)
.child(
section("Input State", cx)
.child(self.disabled_input.clone())
.child(self.mash_input.clone()),
),
)
.child(
section("Input State", cx)
.child(self.disabled_input.clone())
.child(self.mash_input.clone()),
)
.child(
section("Preifx and Suffix", cx)
.child(self.prefix_input1.clone())
.child(self.both_input1.clone())
.child(self.suffix_input1.clone()),
)
.child(
section("Input Size", cx)
.child(self.large_input.clone())
.child(self.small_input.clone()),
h_flex()
.gap_3()
.items_start()
.child(
section("Preifx and Suffix", cx)
.child(self.prefix_input1.clone())
.child(self.both_input1.clone())
.child(self.suffix_input1.clone()),
)
.child(
section("Input Size", cx)
.child(self.large_input.clone())
.child(self.small_input.clone()),
),
)
.child(
section("Input OTP", cx).child(

View file

@ -3,8 +3,13 @@ use gpui::{
WindowContext,
};
use ui::{
button::Button, divider::Divider, h_flex, indicator::Indicator, progress::Progress,
slider::Slider, v_flex, Clickable, IconName, Size,
button::Button,
divider::Divider,
h_flex,
indicator::Indicator,
progress::Progress,
slider::{Slider, SliderEvent},
v_flex, Clickable, IconName, Size,
};
pub struct ProgressStory {
@ -21,31 +26,36 @@ impl ProgressStory {
}
fn new(cx: &mut ViewContext<Self>) -> Self {
let slider1 = Slider::horizontal()
.min(-255.)
.max(255.)
.default_value(15.)
.step(15.)
.on_change(cx.listener(|this, value, cx| {
let slider1 = cx.new_view(|_| {
Slider::horizontal()
.min(-255.)
.max(255.)
.default_value(15.)
.step(15.)
});
cx.subscribe(&slider1, |this, _, event: &SliderEvent, cx| match event {
SliderEvent::Change(value) => {
this.slider1_value = *value;
cx.notify();
}));
}
})
.detach();
let slider2 = Slider::horizontal()
.min(0.)
.max(5.)
.step(1.0)
.on_change(cx.listener(|this, value, cx| {
let slider2 = cx.new_view(|_| Slider::horizontal().min(0.).max(5.).step(1.0));
cx.subscribe(&slider2, |this, _, event: &SliderEvent, cx| match event {
SliderEvent::Change(value) => {
this.slider2_value = *value;
cx.notify();
}));
}
})
.detach();
Self {
value: 50.,
slider1_value: 15.,
slider2_value: 1.,
slider1: cx.new_view(|_| slider1),
slider2: cx.new_view(|_| slider2),
slider1,
slider2,
}
}

View file

@ -5,7 +5,7 @@ use gpui::{
use ui::{
button::Button,
h_flex,
input::{TextEvent, TextInput},
input::{InputEvent, TextInput},
theme::ActiveTheme,
v_flex,
webview::WebView,
@ -44,8 +44,8 @@ impl WebViewStory {
cx.subscribe(
&address_input,
|this: &mut Self, input, event: &TextEvent, cx| match event {
TextEvent::PressEnter => {
|this: &mut Self, input, event: &InputEvent, cx| match event {
InputEvent::PressEnter => {
let url = input.read(cx).text();
this.webview.update(cx, |view, _| {
view.load_url(&url);

View file

@ -47,8 +47,8 @@ actions!(
]
);
pub enum TextEvent {
Input { text: SharedString },
pub enum InputEvent {
Change(SharedString),
PressEnter,
Focus,
Blur,
@ -113,7 +113,7 @@ pub struct TextInput {
size: Size,
}
impl EventEmitter<TextEvent> for TextInput {}
impl EventEmitter<InputEvent> for TextInput {}
impl TextInput {
pub fn new(cx: &mut ViewContext<Self>) -> Self {
@ -292,7 +292,7 @@ impl TextInput {
}
fn enter(&mut self, _: &Enter, cx: &mut ViewContext<Self>) {
cx.emit(TextEvent::PressEnter);
cx.emit(InputEvent::PressEnter);
}
fn clean(&mut self, _: &ClickEvent, cx: &mut ViewContext<Self>) {
@ -505,7 +505,7 @@ impl TextInput {
self.blink_cursor.update(cx, |cursor, cx| {
cursor.start(cx);
});
cx.emit(TextEvent::Focus);
cx.emit(InputEvent::Focus);
}
fn on_blur(&mut self, cx: &mut ViewContext<Self>) {
@ -513,7 +513,7 @@ impl TextInput {
self.blink_cursor.update(cx, |cursor, cx| {
cursor.stop(cx);
});
cx.emit(TextEvent::Blur);
cx.emit(InputEvent::Blur);
}
fn pause_blink_cursor(&mut self, cx: &mut ViewContext<Self>) {
@ -571,9 +571,7 @@ impl ViewInputHandler for TextInput {
(self.text[0..range.start].to_owned() + new_text + &self.text[range.end..]).into();
self.selected_range = range.start + new_text.len()..range.start + new_text.len();
self.marked_range.take();
cx.emit(TextEvent::Input {
text: self.text.clone(),
});
cx.emit(InputEvent::Change(self.text.clone()));
cx.notify();
}
@ -602,9 +600,7 @@ impl ViewInputHandler for TextInput {
.map(|range_utf16| self.range_from_utf16(range_utf16))
.map(|new_range| new_range.start + range.start..new_range.end + range.end)
.unwrap_or_else(|| range.start + new_text.len()..range.start + new_text.len());
cx.emit(TextEvent::Input {
text: self.text.clone(),
});
cx.emit(InputEvent::Change(self.text.clone()));
cx.notify();
}

View file

@ -1,12 +1,17 @@
use gpui::{
div, prelude::FluentBuilder as _, AnyElement, Context, FocusHandle, FocusableView,
InteractiveElement, IntoElement, KeyDownEvent, Model, MouseButton, MouseDownEvent,
ParentElement as _, Render, SharedString, Styled as _, ViewContext, WindowContext,
div, prelude::FluentBuilder as _, AnyElement, Context, EventEmitter, FocusHandle,
FocusableView, InteractiveElement, IntoElement, KeyDownEvent, Model, MouseButton,
MouseDownEvent, ParentElement as _, Render, SharedString, Styled as _, ViewContext,
};
use crate::{h_flex, theme::ActiveTheme, v_flex};
use super::blink_cursor::BlinkCursor;
use super::{blink_cursor::BlinkCursor, InputEvent};
pub enum InputOptEvent {
/// When all OTP input have filled, this event will be triggered.
Change(SharedString),
}
pub struct InputOtp {
focus_handle: FocusHandle,
@ -15,7 +20,6 @@ pub struct InputOtp {
masked: bool,
value: SharedString,
blink_cursor: Model<BlinkCursor>,
on_change: Option<Box<dyn Fn(&SharedString, &mut WindowContext) + 'static>>,
}
impl InputOtp {
@ -28,7 +32,6 @@ impl InputOtp {
number_of_groups: 2,
value: SharedString::default(),
masked: false,
on_change: None,
blink_cursor: blink_cursor.clone(),
};
@ -65,15 +68,6 @@ impl InputOtp {
self
}
/// Set callback to be called when the value of the OTP Input changes (All OTP input have filled).
pub fn on_change<F>(mut self, f: F) -> Self
where
F: Fn(&SharedString, &mut WindowContext) + 'static,
{
self.on_change = Some(Box::new(f));
self
}
fn on_input_mouse_down(&mut self, _: &MouseDownEvent, cx: &mut ViewContext<Self>) {
cx.focus(&self.focus_handle);
}
@ -108,9 +102,7 @@ impl InputOtp {
self.value = SharedString::from(chars.iter().collect::<String>());
if self.value.chars().count() == self.length {
if let Some(on_change) = &self.on_change {
on_change(&self.value, cx);
}
cx.emit(InputEvent::Change(self.value.clone()));
}
cx.notify()
}
@ -119,12 +111,14 @@ impl InputOtp {
self.blink_cursor.update(cx, |cursor, cx| {
cursor.start(cx);
});
cx.emit(InputEvent::Focus);
}
fn on_blur(&mut self, cx: &mut ViewContext<Self>) {
self.blink_cursor.update(cx, |cursor, cx| {
cursor.stop(cx);
});
cx.emit(InputEvent::Blur);
}
fn pause_blink_cursor(&mut self, cx: &mut ViewContext<Self>) {
@ -139,6 +133,7 @@ impl FocusableView for InputOtp {
self.focus_handle.clone()
}
}
impl EventEmitter<InputEvent> for InputOtp {}
impl Render for InputOtp {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {

View file

@ -2,7 +2,7 @@ use std::{cell::Cell, rc::Rc};
use gpui::prelude::FluentBuilder as _;
use crate::input::{TextEvent, TextInput};
use crate::input::{InputEvent, TextInput};
use crate::scroll::ScrollbarState;
use crate::theme::{ActiveTheme, Colorize as _};
use crate::{scroll::Scrollbar, v_flex};
@ -157,15 +157,15 @@ where
fn on_query_input_event(
&mut self,
_: View<TextInput>,
event: &TextEvent,
event: &InputEvent,
cx: &mut ViewContext<Self>,
) {
match event {
TextEvent::Input { text } => {
InputEvent::Change(text) => {
self.delegate.perform_search(&text.trim(), cx);
cx.notify()
}
TextEvent::PressEnter => self.action_confirm(&Confirm, cx),
InputEvent::PressEnter => self.action_confirm(&Confirm, cx),
_ => {}
}
}

View file

@ -3,14 +3,18 @@ use crate::{
tooltip::Tooltip,
};
use gpui::{
canvas, div, px, relative, Axis, Bounds, DragMoveEvent, EntityId, InteractiveElement,
IntoElement, MouseButton, MouseDownEvent, ParentElement as _, Pixels, Point, Render,
StatefulInteractiveElement as _, Styled, ViewContext, VisualContext as _, WindowContext,
canvas, div, px, relative, Axis, Bounds, DragMoveEvent, EntityId, EventEmitter,
InteractiveElement, IntoElement, MouseButton, MouseDownEvent, ParentElement as _, Pixels,
Point, Render, StatefulInteractiveElement as _, Styled, ViewContext, VisualContext as _,
};
#[derive(Clone, Render)]
pub struct DragThumb(EntityId);
pub enum SliderEvent {
Change(f32),
}
/// A slider component.
pub struct Slider {
axis: Axis,
@ -18,7 +22,6 @@ pub struct Slider {
max: f32,
step: f32,
value: f32,
on_change: Option<Box<dyn Fn(&f32, &mut WindowContext) + 'static>>,
bounds: Bounds<Pixels>,
}
@ -30,7 +33,6 @@ impl Slider {
max: 100.0,
step: 1.0,
value: 0.0,
on_change: None,
bounds: Bounds::default(),
}
}
@ -63,12 +65,6 @@ impl Slider {
self
}
/// Set the on_change callback of the slider.
pub fn on_change(mut self, on_change: impl Fn(&f32, &mut WindowContext) + 'static) -> Self {
self.on_change = Some(Box::new(on_change));
self
}
/// Set the value of the slider.
pub fn set_value(&mut self, value: f32, cx: &mut gpui::ViewContext<Self>) {
self.value = value;
@ -114,9 +110,7 @@ impl Slider {
let value = (value / step).round() * step;
self.value = value.clamp(self.min, self.max);
if let Some(on_change) = &self.on_change {
on_change(&self.value, cx);
}
cx.emit(SliderEvent::Change(self.value));
cx.notify();
}
@ -160,6 +154,8 @@ impl Slider {
}
}
impl EventEmitter<SliderEvent> for Slider {}
impl Render for Slider {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
div()