Refactor Slider, Input, InputOtp to use EventEmiter to emit change (#45)
This commit is contained in:
parent
f6146220e4
commit
bac5392e0a
7 changed files with 108 additions and 107 deletions
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Reference in a new issue