Update input
This commit is contained in:
parent
ae8c0ab657
commit
ae5e4f6149
13 changed files with 376 additions and 289 deletions
|
|
@ -26,7 +26,7 @@ fn main() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
workspace::open_new(app_state.clone(), cx, |workspace, cx| {
|
workspace::open_new(app_state.clone(), cx, |_workspace, _cx| {
|
||||||
// do something
|
// do something
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ use crate::{
|
||||||
colors::Color,
|
colors::Color,
|
||||||
disableable::{Clickable, Disableable, Selectable},
|
disableable::{Clickable, Disableable, Selectable},
|
||||||
label::Label,
|
label::Label,
|
||||||
|
theme::Theme,
|
||||||
HlsaExt as _,
|
HlsaExt as _,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -117,26 +118,17 @@ impl Clickable for Button {
|
||||||
|
|
||||||
impl RenderOnce for Button {
|
impl RenderOnce for Button {
|
||||||
fn render(self, cx: &mut WindowContext) -> impl IntoElement {
|
fn render(self, cx: &mut WindowContext) -> impl IntoElement {
|
||||||
|
let theme = cx.global::<Theme>();
|
||||||
let style: ButtonStyle = self.style;
|
let style: ButtonStyle = self.style;
|
||||||
let normal_style = style.normal(cx);
|
|
||||||
|
|
||||||
self.base
|
self.base
|
||||||
.id(self.id)
|
.id(self.id)
|
||||||
.group("")
|
|
||||||
.flex()
|
.flex()
|
||||||
.items_center()
|
.items_center()
|
||||||
.justify_center()
|
.justify_center()
|
||||||
.child(
|
|
||||||
Label::new(self.label)
|
|
||||||
.color(style.text_color())
|
|
||||||
.map(|this| match self.size {
|
|
||||||
ButtonSize::Small => this.text_sm(),
|
|
||||||
ButtonSize::Medium => this.text_base(),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.map(|this| match self.size {
|
.map(|this| match self.size {
|
||||||
ButtonSize::Small => this.px_3().py_2().h_7(),
|
ButtonSize::Small => this.px_3().py_2().h_5(),
|
||||||
ButtonSize::Medium => this.px_4().py_2().h_10(),
|
ButtonSize::Medium => this.px_4().py_2().h_8(),
|
||||||
})
|
})
|
||||||
.map(|this| match self.rounded {
|
.map(|this| match self.rounded {
|
||||||
ButtonRounded::Small => this.rounded_sm(),
|
ButtonRounded::Small => this.rounded_sm(),
|
||||||
|
|
@ -145,22 +137,13 @@ impl RenderOnce for Button {
|
||||||
ButtonRounded::None => this.rounded_none(),
|
ButtonRounded::None => this.rounded_none(),
|
||||||
})
|
})
|
||||||
.when(!self.disabled, |this| {
|
.when(!self.disabled, |this| {
|
||||||
this.cursor_pointer()
|
this.hover(|this| this.border_color(theme.blue))
|
||||||
.hover(|this| {
|
|
||||||
let hover_style = style.hovered(cx);
|
|
||||||
this.bg(hover_style.bg).border_color(hover_style.border)
|
|
||||||
})
|
|
||||||
.active(|this| {
|
|
||||||
let active_style = style.active(cx);
|
|
||||||
this.bg(active_style.bg).border_color(active_style.border)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
.when_some(
|
.when_some(
|
||||||
self.on_click.filter(|_| !self.disabled),
|
self.on_click.filter(|_| !self.disabled),
|
||||||
|this, on_click| {
|
|this, on_click| {
|
||||||
this.on_mouse_down(MouseButton::Left, |_, cx| cx.prevent_default())
|
this.on_mouse_down(MouseButton::Left, |_, cx| cx.prevent_default())
|
||||||
.on_click(move |event, cx| {
|
.on_click(move |event, cx| {
|
||||||
dbg!("---------- button click");
|
|
||||||
cx.stop_propagation();
|
cx.stop_propagation();
|
||||||
(on_click)(event, cx)
|
(on_click)(event, cx)
|
||||||
})
|
})
|
||||||
|
|
@ -173,8 +156,22 @@ impl RenderOnce for Button {
|
||||||
.border_color(disabled_style.border)
|
.border_color(disabled_style.border)
|
||||||
})
|
})
|
||||||
.border_1()
|
.border_1()
|
||||||
.border_color(normal_style.border)
|
.border_color(theme.crust)
|
||||||
.bg(normal_style.bg)
|
.bg(theme.base)
|
||||||
|
.child({
|
||||||
|
let text_color = if self.disabled {
|
||||||
|
theme.text_disabled
|
||||||
|
} else {
|
||||||
|
theme.text
|
||||||
|
};
|
||||||
|
|
||||||
|
Label::new(self.label)
|
||||||
|
.color(text_color)
|
||||||
|
.map(|this| match self.size {
|
||||||
|
ButtonSize::Small => this.text_sm(),
|
||||||
|
ButtonSize::Medium => this.text_base(),
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
use gpui::{
|
use gpui::{
|
||||||
div, prelude::FluentBuilder as _, AbsoluteLength, DefiniteLength, Div, IntoElement,
|
div, prelude::FluentBuilder as _, AbsoluteLength, DefiniteLength, Div, Half, Hsla, IntoElement,
|
||||||
ParentElement, RenderOnce, SharedString, Style, StyleRefinement, Styled, WindowContext,
|
ParentElement, RenderOnce, SharedString, Styled, WindowContext,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::colors::Color;
|
use crate::{hls, theme::Theme};
|
||||||
|
|
||||||
#[derive(IntoElement)]
|
#[derive(IntoElement)]
|
||||||
pub struct Label {
|
pub struct Label {
|
||||||
base: Div,
|
base: Div,
|
||||||
label: SharedString,
|
label: SharedString,
|
||||||
color: Color,
|
color: Hsla,
|
||||||
multiple_lines: bool,
|
multiple_lines: bool,
|
||||||
line_height: Option<DefiniteLength>,
|
line_height: Option<DefiniteLength>,
|
||||||
text_size: Option<AbsoluteLength>,
|
text_size: Option<AbsoluteLength>,
|
||||||
|
|
@ -21,7 +21,7 @@ impl Label {
|
||||||
base: div(),
|
base: div(),
|
||||||
label: label.into(),
|
label: label.into(),
|
||||||
multiple_lines: false,
|
multiple_lines: false,
|
||||||
color: Color::Foreground,
|
color: hls(0., 0., 0.),
|
||||||
line_height: None,
|
line_height: None,
|
||||||
text_size: None,
|
text_size: None,
|
||||||
}
|
}
|
||||||
|
|
@ -32,7 +32,7 @@ impl Label {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn color(mut self, color: Color) -> Self {
|
pub fn color(mut self, color: Hsla) -> Self {
|
||||||
self.color = color;
|
self.color = color;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
@ -46,6 +46,8 @@ impl Styled for Label {
|
||||||
|
|
||||||
impl RenderOnce for Label {
|
impl RenderOnce for Label {
|
||||||
fn render(self, cx: &mut WindowContext) -> impl IntoElement {
|
fn render(self, cx: &mut WindowContext) -> impl IntoElement {
|
||||||
|
let theme = cx.global::<Theme>();
|
||||||
|
|
||||||
let label_text = if !self.multiple_lines {
|
let label_text = if !self.multiple_lines {
|
||||||
SharedString::from(self.label.replace('\n', ""))
|
SharedString::from(self.label.replace('\n', ""))
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -54,7 +56,7 @@ impl RenderOnce for Label {
|
||||||
|
|
||||||
self.base
|
self.base
|
||||||
.child(label_text)
|
.child(label_text)
|
||||||
.text_color(self.color.color(cx))
|
.text_color(theme.text)
|
||||||
.map(|this| {
|
.map(|this| {
|
||||||
if let Some(text_size) = self.text_size {
|
if let Some(text_size) = self.text_size {
|
||||||
this.text_size(text_size)
|
this.text_size(text_size)
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use gpui::{
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
button::{Button, ButtonSize, ButtonStyle},
|
button::{Button, ButtonSize, ButtonStyle},
|
||||||
disableable::{Clickable as _, Disableable as _},
|
disableable::{Clickable, Disableable as _},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::story_case;
|
use super::story_case;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use gpui::{
|
use gpui::{
|
||||||
div, prelude::FluentBuilder as _, ClickEvent, IntoElement, ParentElement as _, Render,
|
div, ClickEvent, IntoElement, ParentElement as _, Render, Styled as _, ViewContext,
|
||||||
Styled as _, ViewContext, VisualContext, WindowContext,
|
WindowContext,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::text_field::TextField;
|
use crate::text_field::TextField;
|
||||||
|
|
@ -10,6 +10,7 @@ use super::story_case;
|
||||||
pub struct InputStory;
|
pub struct InputStory;
|
||||||
|
|
||||||
impl InputStory {
|
impl InputStory {
|
||||||
|
#[allow(unused)]
|
||||||
fn on_change(ev: &ClickEvent, cx: &mut WindowContext) {
|
fn on_change(ev: &ClickEvent, cx: &mut WindowContext) {
|
||||||
println!("Input changed: {:?}", ev);
|
println!("Input changed: {:?}", ev);
|
||||||
}
|
}
|
||||||
|
|
@ -23,13 +24,8 @@ impl Render for InputStory {
|
||||||
.flex_col()
|
.flex_col()
|
||||||
.justify_start()
|
.justify_start()
|
||||||
.gap_3()
|
.gap_3()
|
||||||
.child({
|
.child(TextField::new(cx, "Enter text here...", false))
|
||||||
TextField::new(cx, "Enter text here...", false)
|
.child(TextField::new(cx, "Enter text here...", false)),
|
||||||
})
|
|
||||||
.child({
|
|
||||||
let input = TextField::new(cx, "Enter text here...", false);
|
|
||||||
input
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,143 @@
|
||||||
|
use core::fmt;
|
||||||
|
use std::fmt::{Display, Formatter};
|
||||||
|
|
||||||
|
use gpui::{
|
||||||
|
div, prelude::FluentBuilder as _, px, AnyElement, ElementId, IntoElement, ParentElement,
|
||||||
|
Render, RenderOnce, SharedString, Styled as _, View, ViewContext, VisualContext, WindowContext,
|
||||||
|
};
|
||||||
|
|
||||||
mod button_story;
|
mod button_story;
|
||||||
mod input_story;
|
mod input_story;
|
||||||
mod story;
|
|
||||||
|
|
||||||
pub use story::Stories;
|
use crate::{button::Button, disableable::Clickable as _, label::Label};
|
||||||
use story::StoryContainer;
|
|
||||||
|
use button_story::ButtonStory;
|
||||||
|
use input_story::InputStory;
|
||||||
|
|
||||||
pub fn story_case(name: &'static str, description: &'static str) -> StoryContainer {
|
pub fn story_case(name: &'static str, description: &'static str) -> StoryContainer {
|
||||||
StoryContainer::new(name, description)
|
StoryContainer::new(name, description)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(IntoElement)]
|
||||||
|
pub struct StoryContainer {
|
||||||
|
name: SharedString,
|
||||||
|
description: SharedString,
|
||||||
|
children: Vec<AnyElement>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ParentElement for StoryContainer {
|
||||||
|
fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
|
||||||
|
self.children.extend(elements)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StoryContainer {
|
||||||
|
pub fn new(name: impl Into<SharedString>, description: impl Into<SharedString>) -> Self {
|
||||||
|
Self {
|
||||||
|
name: name.into(),
|
||||||
|
description: description.into(),
|
||||||
|
children: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RenderOnce for StoryContainer {
|
||||||
|
fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
|
||||||
|
div()
|
||||||
|
.flex()
|
||||||
|
.flex_col()
|
||||||
|
.gap_4()
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.flex()
|
||||||
|
.flex_col()
|
||||||
|
.gap_2()
|
||||||
|
.child(Label::new(self.name).text_size(px(24.0)))
|
||||||
|
.child(Label::new(self.description).text_size(px(16.0))),
|
||||||
|
)
|
||||||
|
.children(self.children)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||||
|
enum StoryType {
|
||||||
|
Button,
|
||||||
|
Input,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for StoryType {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Button => write!(f, "Button"),
|
||||||
|
Self::Input => write!(f, "Input"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Stories {
|
||||||
|
active: StoryType,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Stories {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
active: StoryType::Button,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn view(cx: &mut WindowContext) -> View<Self> {
|
||||||
|
cx.new_view(|_cx| Self::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_active(&mut self, ty: StoryType, cx: &mut ViewContext<Self>) {
|
||||||
|
self.active = ty;
|
||||||
|
dbg!("--------------------- set_active: {}", ty);
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_story_buttons(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||||
|
div()
|
||||||
|
.flex()
|
||||||
|
.items_center()
|
||||||
|
.gap_4()
|
||||||
|
.child(self.swith_button("story-button", StoryType::Button, cx))
|
||||||
|
.child(self.swith_button("story-input", StoryType::Input, cx))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn swith_button(
|
||||||
|
&self,
|
||||||
|
id: &str,
|
||||||
|
ty: StoryType,
|
||||||
|
cx: &mut ViewContext<Self>,
|
||||||
|
) -> impl IntoElement {
|
||||||
|
let name = format!("{}", ty);
|
||||||
|
Button::new(SharedString::from(id.to_string()), name)
|
||||||
|
.on_click(move |_e, cx| {
|
||||||
|
dbg!("--------------------- on_click: {}", ty);
|
||||||
|
// cx.update_view(self, |this| {
|
||||||
|
// this.set_active(ty, cx);
|
||||||
|
// });
|
||||||
|
})
|
||||||
|
.style(crate::button::ButtonStyle::Secondary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Stories {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Render for Stories {
|
||||||
|
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||||
|
div()
|
||||||
|
.flex()
|
||||||
|
.flex_col()
|
||||||
|
.gap_4()
|
||||||
|
.child(self.render_story_buttons(cx))
|
||||||
|
.map(|this| match self.active {
|
||||||
|
StoryType::Button => this.child(cx.new_view(|_cx| ButtonStory {})),
|
||||||
|
StoryType::Input => this.child(cx.new_view(|_cx| InputStory {})),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,127 +0,0 @@
|
||||||
use core::fmt;
|
|
||||||
use std::fmt::{Display, Formatter};
|
|
||||||
|
|
||||||
use gpui::{
|
|
||||||
div, prelude::FluentBuilder as _, px, AnyElement, ElementId, InputHandler, IntoElement,
|
|
||||||
ParentElement, Render, RenderOnce, SharedString, Styled as _, ViewContext, VisualContext,
|
|
||||||
WindowContext,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{button::Button, disableable::Clickable as _, label::Label};
|
|
||||||
|
|
||||||
use super::{button_story::ButtonStory, input_story::InputStory};
|
|
||||||
|
|
||||||
#[derive(IntoElement)]
|
|
||||||
pub struct StoryContainer {
|
|
||||||
name: SharedString,
|
|
||||||
description: SharedString,
|
|
||||||
children: Vec<AnyElement>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ParentElement for StoryContainer {
|
|
||||||
fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
|
|
||||||
self.children.extend(elements)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StoryContainer {
|
|
||||||
pub fn new(name: impl Into<SharedString>, description: impl Into<SharedString>) -> Self {
|
|
||||||
Self {
|
|
||||||
name: name.into(),
|
|
||||||
description: description.into(),
|
|
||||||
children: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RenderOnce for StoryContainer {
|
|
||||||
fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.flex_col()
|
|
||||||
.gap_4()
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.flex_col()
|
|
||||||
.gap_2()
|
|
||||||
.child(Label::new(self.name).text_size(px(24.0)))
|
|
||||||
.child(Label::new(self.description).text_size(px(16.0))),
|
|
||||||
)
|
|
||||||
.children(self.children)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
|
||||||
enum StoryType {
|
|
||||||
Button,
|
|
||||||
Input,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for StoryType {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Self::Button => write!(f, "Button"),
|
|
||||||
Self::Input => write!(f, "Input"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Stories {
|
|
||||||
active: StoryType,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Stories {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
active: StoryType::Input,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_active(&mut self, ty: StoryType, cx: &mut ViewContext<Self>) {
|
|
||||||
self.active = ty;
|
|
||||||
dbg!("--------------------- set_active: {}", ty);
|
|
||||||
cx.notify();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_story_buttons(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.items_center()
|
|
||||||
.gap_4()
|
|
||||||
.child(self.swith_button(StoryType::Button, cx))
|
|
||||||
.child(self.swith_button(StoryType::Input, cx))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn swith_button(&self, ty: StoryType, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
|
||||||
let name = format!("{}", ty);
|
|
||||||
Button::new(ElementId::Name(SharedString::from(name.clone())), name)
|
|
||||||
.on_click(cx.listener(move |this, _, cx| {
|
|
||||||
dbg!("--------------------- on_click: {}", ty);
|
|
||||||
this.set_active(ty, cx);
|
|
||||||
}))
|
|
||||||
.style(crate::button::ButtonStyle::Secondary)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Stories {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Render for Stories {
|
|
||||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
|
||||||
let nav_buttons = self.render_story_buttons(cx);
|
|
||||||
|
|
||||||
div()
|
|
||||||
.flex()
|
|
||||||
.flex_col()
|
|
||||||
.gap_4()
|
|
||||||
.child(nav_buttons)
|
|
||||||
.map(|this| match self.active {
|
|
||||||
StoryType::Button => this.child(cx.new_view(|cx| ButtonStory {})),
|
|
||||||
StoryType::Input => this.child(cx.new_view(|cx| InputStory {})),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
pub mod blink_manager;
|
mod blink_manager;
|
||||||
pub mod cursor_layout;
|
mod cursor_layout;
|
||||||
pub mod text_field;
|
mod text_field;
|
||||||
|
|
||||||
pub use text_field::*;
|
pub use text_field::*;
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ use gpui::ModelContext;
|
||||||
|
|
||||||
pub struct BlinkManager {
|
pub struct BlinkManager {
|
||||||
blink_interval: Duration,
|
blink_interval: Duration,
|
||||||
|
|
||||||
blink_epoch: usize,
|
blink_epoch: usize,
|
||||||
blinking_paused: bool,
|
blinking_paused: bool,
|
||||||
visible: bool,
|
visible: bool,
|
||||||
|
|
@ -14,7 +13,7 @@ pub struct BlinkManager {
|
||||||
impl BlinkManager {
|
impl BlinkManager {
|
||||||
pub fn new(blink_interval: Duration) -> Self {
|
pub fn new(blink_interval: Duration) -> Self {
|
||||||
Self {
|
Self {
|
||||||
blink_interval: Duration::from_millis(500),
|
blink_interval,
|
||||||
blink_epoch: 0,
|
blink_epoch: 0,
|
||||||
blinking_paused: false,
|
blinking_paused: false,
|
||||||
visible: true,
|
visible: true,
|
||||||
|
|
@ -22,11 +21,17 @@ impl BlinkManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn show_cursor(&self, cx: &mut ModelContext<'_, Self>) -> bool {
|
pub fn show_cursor(&self, _cx: &mut ModelContext<'_, Self>) -> bool {
|
||||||
self.enabled && (!self.blinking_paused || self.visible)
|
self.enabled && (!self.blinking_paused || self.visible)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn blink_cursor(&mut self, epoch: usize, cx: &mut ModelContext<Self>) {}
|
pub fn blink_cursor(&mut self, epoch: usize, cx: &mut ModelContext<Self>) {
|
||||||
|
if self.blink_epoch != epoch {
|
||||||
|
self.blink_epoch = epoch;
|
||||||
|
self.visible = !self.visible;
|
||||||
|
cx.refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn disable(&mut self, _cx: &mut ModelContext<Self>) {
|
pub fn disable(&mut self, _cx: &mut ModelContext<Self>) {
|
||||||
self.enabled = false;
|
self.enabled = false;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
use gpui::{outline, px, AppContext, Bounds, Hsla, Pixels, ShapedLine, Size, ViewContext};
|
use gpui::{outline, px, Bounds, Hsla, Pixels, ShapedLine, Size, ViewContext};
|
||||||
|
|
||||||
pub struct CursorLayout {
|
pub struct CursorLayout {
|
||||||
origin: gpui::Point<Pixels>,
|
origin: gpui::Point<Pixels>,
|
||||||
|
#[allow(unused)]
|
||||||
block_width: Pixels,
|
block_width: Pixels,
|
||||||
line_height: Pixels,
|
line_height: Pixels,
|
||||||
color: Hsla,
|
color: Hsla,
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
use std::{ops::Range, time::Duration};
|
use std::{ops::Range, time::Duration};
|
||||||
|
|
||||||
use gpui::{
|
use gpui::{
|
||||||
div, Context, EventEmitter, FocusHandle, HighlightStyle, InteractiveElement, InteractiveText,
|
div, ClipboardItem, Context, EventEmitter, FocusHandle, HighlightStyle, InteractiveElement,
|
||||||
IntoElement, KeyDownEvent, Model, ParentElement, Render, RenderOnce, Styled, StyledText,
|
InteractiveText, IntoElement, KeyDownEvent, Model, ParentElement, Render, RenderOnce, Styled,
|
||||||
TextStyle, View, ViewContext, VisualContext, WindowContext,
|
StyledText, TextStyle, View, ViewContext, VisualContext, WindowContext,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{theme::Theme, disableable::Disableable};
|
use crate::{disableable::Disableable, theme::Theme};
|
||||||
|
|
||||||
use super::{blink_manager::BlinkManager, cursor_layout::CursorLayout};
|
use super::{blink_manager::BlinkManager, cursor_layout::CursorLayout};
|
||||||
|
|
||||||
|
|
@ -43,107 +43,144 @@ impl Disableable for TextField {
|
||||||
|
|
||||||
impl RenderOnce for TextField {
|
impl RenderOnce for TextField {
|
||||||
fn render(self, cx: &mut WindowContext) -> impl IntoElement {
|
fn render(self, cx: &mut WindowContext) -> impl IntoElement {
|
||||||
// cx.focus(&self.focus_handle);
|
cx.focus(&self.focus_handle);
|
||||||
let theme = cx.global::<Theme>();
|
let theme = cx.global::<Theme>();
|
||||||
|
|
||||||
let clone = self.view.clone();
|
let clone = self.view.clone();
|
||||||
|
|
||||||
div()
|
div()
|
||||||
.border_color(
|
.border_color(if self.focus_handle.is_focused(cx) {
|
||||||
self.focus_handle
|
theme.blue
|
||||||
.is_focused(cx)
|
} else {
|
||||||
.then(|| theme.blue)
|
theme.crust
|
||||||
.unwrap_or(theme.crust),
|
})
|
||||||
)
|
|
||||||
.border_1()
|
.border_1()
|
||||||
.track_focus(&self.focus_handle)
|
.track_focus(&self.focus_handle)
|
||||||
.on_key_down(move |event, cx| {
|
.on_key_down(move |ev, cx| {
|
||||||
if self.disable {
|
self.view.update(cx, |editor, cx| {
|
||||||
return;
|
let prev = editor.text.clone();
|
||||||
}
|
cx.emit(TextEvent::KeyDown(ev.clone()));
|
||||||
|
let keystroke = &ev.keystroke.key;
|
||||||
|
let chars = editor.text.chars().collect::<Vec<char>>();
|
||||||
|
let m = ev.keystroke.modifiers.secondary();
|
||||||
|
|
||||||
self.view.update(cx, |text_view, vc| {
|
dbg!("---------------- {:?}", ev);
|
||||||
let prev = text_view.text.clone();
|
|
||||||
vc.emit(TextEvent::KeyDown(event.clone()));
|
|
||||||
let keystroke = &event.keystroke.key;
|
|
||||||
let chars = text_view.text.chars().collect::<Vec<char>>();
|
|
||||||
|
|
||||||
let m = event.keystroke.modifiers.platform;
|
|
||||||
|
|
||||||
if m {
|
if m {
|
||||||
match keystroke.as_str() {
|
match keystroke.as_str() {
|
||||||
|
"a" => {
|
||||||
|
editor.selection = 0..chars.len();
|
||||||
|
}
|
||||||
|
"c" => {
|
||||||
|
// if !editor.masked {
|
||||||
|
let selected_text =
|
||||||
|
chars[editor.selection.clone()].iter().collect();
|
||||||
|
cx.write_to_clipboard(ClipboardItem::new(selected_text));
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
"v" => {
|
||||||
|
let clipboard = cx.read_from_clipboard();
|
||||||
|
if let Some(clipboard) = clipboard {
|
||||||
|
let text = clipboard.text();
|
||||||
|
editor.text.replace_range(
|
||||||
|
editor.char_range_to_text_range(&editor.text),
|
||||||
|
text,
|
||||||
|
);
|
||||||
|
let i = editor.selection.start + text.chars().count();
|
||||||
|
editor.selection = i..i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"x" => {
|
||||||
|
let selected_text =
|
||||||
|
chars[editor.selection.clone()].iter().collect();
|
||||||
|
cx.write_to_clipboard(ClipboardItem::new(selected_text));
|
||||||
|
editor.text.replace_range(
|
||||||
|
editor.char_range_to_text_range(&editor.text),
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
editor.selection.end = editor.selection.start;
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
} else if !event
|
} else if !ev.keystroke.ime_key.clone().unwrap_or_default().is_empty() {
|
||||||
.keystroke
|
let ime_key = &ev.keystroke.ime_key.clone().unwrap_or_default();
|
||||||
.ime_key
|
editor
|
||||||
.clone()
|
.text
|
||||||
.unwrap_or_default()
|
.replace_range(editor.char_range_to_text_range(&editor.text), ime_key);
|
||||||
.is_empty()
|
let i = editor.selection.start + ime_key.chars().count();
|
||||||
{
|
editor.selection = i..i;
|
||||||
let ime_key = &event.keystroke.ime_key.clone().unwrap_or_default();
|
|
||||||
text_view.text.replace_range(
|
|
||||||
text_view.char_range_to_text_range(&text_view.text),
|
|
||||||
ime_key,
|
|
||||||
);
|
|
||||||
let i = text_view.selection.start + ime_key.chars().count();
|
|
||||||
text_view.selection = i..i;
|
|
||||||
} else {
|
} else {
|
||||||
match keystroke.as_str() {
|
match keystroke.as_str() {
|
||||||
"left" => {
|
"left" => {
|
||||||
if text_view.selection.start > 0 {
|
if editor.selection.start > 0 {
|
||||||
text_view.selection =
|
let i = if editor.selection.start == editor.selection.end {
|
||||||
text_view.selection.start - 1..text_view.selection.end;
|
editor.selection.start - 1
|
||||||
|
} else {
|
||||||
|
editor.selection.start
|
||||||
|
};
|
||||||
|
editor.selection = i..i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"right" => {
|
"right" => {
|
||||||
if text_view.selection.end < text_view.text.len() {
|
if editor.selection.end < editor.text.len() {
|
||||||
text_view.selection =
|
let i = if editor.selection.start == editor.selection.end {
|
||||||
text_view.selection.start + 1..text_view.selection.end + 1;
|
editor.selection.end + 1
|
||||||
} else {
|
} else {
|
||||||
text_view.selection =
|
editor.selection.end
|
||||||
text_view.selection.start + 1..text_view.selection.end;
|
};
|
||||||
|
editor.selection = i..i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"backspace" => {
|
"backspace" => {
|
||||||
if text_view.text.is_empty() {
|
if editor.text.is_empty() && !ev.is_held {
|
||||||
return;
|
// cx.emit(TextEvent::Back);
|
||||||
}
|
} else if editor.selection.start == editor.selection.end
|
||||||
|
&& editor.selection.start > 0
|
||||||
if text_view.selection.start == text_view.selection.end {
|
{
|
||||||
let i = (text_view.selection.start - 1).min(chars.len());
|
let i = (editor.selection.start - 1).min(chars.len());
|
||||||
text_view.text = chars[0..i].iter().collect::<String>()
|
editor.text = chars[0..i].iter().collect::<String>()
|
||||||
+ &(chars[text_view.selection.end.min(chars.len())..]
|
+ &(chars[editor.selection.end.min(chars.len())..]
|
||||||
.iter()
|
.iter()
|
||||||
.collect::<String>());
|
.collect::<String>());
|
||||||
text_view.selection = i..i;
|
editor.selection = i..i;
|
||||||
|
} else {
|
||||||
|
editor.text.replace_range(
|
||||||
|
editor.char_range_to_text_range(&editor.text),
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
editor.selection.end = editor.selection.start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"enter" => {
|
||||||
|
if ev.keystroke.modifiers.shift {
|
||||||
|
editor.text.insert(
|
||||||
|
editor.char_range_to_text_range(&editor.text).start,
|
||||||
|
'\n',
|
||||||
|
);
|
||||||
|
let i = editor.selection.start + 1;
|
||||||
|
editor.selection = i..i;
|
||||||
}
|
}
|
||||||
|
|
||||||
text_view.text.replace_range(
|
|
||||||
text_view.char_range_to_text_range(&text_view.text),
|
|
||||||
"",
|
|
||||||
);
|
|
||||||
|
|
||||||
text_view.selection.end = text_view.selection.start;
|
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
if prev != editor.text {
|
||||||
if prev != text_view.text {
|
cx.emit(TextEvent::Input {
|
||||||
vc.emit(TextEvent::Input {
|
text: editor.text.clone(),
|
||||||
text: text_view.text.clone(),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
cx.notify();
|
||||||
vc.notify();
|
});
|
||||||
})
|
|
||||||
})
|
})
|
||||||
.rounded_lg()
|
.rounded_sm()
|
||||||
.py_1p5()
|
.py_1p5()
|
||||||
.px_3()
|
.px_3()
|
||||||
.min_w_20()
|
.min_w_20()
|
||||||
.bg(self.disable.then(|| theme.crust).unwrap_or(theme.base))
|
.bg(if self.disable {
|
||||||
|
theme.crust
|
||||||
|
} else {
|
||||||
|
theme.base
|
||||||
|
})
|
||||||
.child(clone)
|
.child(clone)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -175,7 +212,7 @@ impl TextView {
|
||||||
placeholder: &str,
|
placeholder: &str,
|
||||||
disable: bool,
|
disable: bool,
|
||||||
) -> View<Self> {
|
) -> View<Self> {
|
||||||
let blink_manager = cx.new_model(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL));
|
let blink_manager = cx.new_model(|_cx| BlinkManager::new(CURSOR_BLINK_INTERVAL));
|
||||||
|
|
||||||
let cursor = CursorLayout::new(
|
let cursor = CursorLayout::new(
|
||||||
gpui::Point::new(gpui::px(0.0), gpui::px(0.0)),
|
gpui::Point::new(gpui::px(0.0), gpui::px(0.0)),
|
||||||
|
|
@ -203,15 +240,17 @@ impl TextView {
|
||||||
let view = cx.new_view(|cx| {
|
let view = cx.new_view(|cx| {
|
||||||
cx.on_blur(
|
cx.on_blur(
|
||||||
focus_handle,
|
focus_handle,
|
||||||
|editor: &mut TextView, cx: &mut ViewContext<'_, TextView>| {
|
|view: &mut TextView, cx: &mut ViewContext<'_, TextView>| {
|
||||||
editor.blink_manager.update(cx, BlinkManager::disable);
|
view.blink_manager.update(cx, BlinkManager::disable);
|
||||||
cx.emit(TextEvent::Blur);
|
cx.emit(TextEvent::Blur);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.detach();
|
.detach();
|
||||||
|
|
||||||
cx.on_focus(focus_handle, |view, cx| {
|
cx.on_focus(focus_handle, |view, cx| {
|
||||||
view.select_all(cx);
|
view.blink_manager.update(cx, |bm, cx| {
|
||||||
|
bm.blink_cursor(0, cx);
|
||||||
|
});
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
m
|
m
|
||||||
|
|
@ -285,6 +324,15 @@ impl TextView {
|
||||||
.len();
|
.len();
|
||||||
start..end
|
start..end
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_text(&mut self, text: impl ToString, cx: &mut ViewContext<Self>) {
|
||||||
|
self.text = text.to_string();
|
||||||
|
self.selection = self.text.len()..self.text.len();
|
||||||
|
cx.notify();
|
||||||
|
cx.emit(TextEvent::Input {
|
||||||
|
text: self.text.clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Render for TextView {
|
impl Render for TextView {
|
||||||
|
|
@ -292,9 +340,11 @@ impl Render for TextView {
|
||||||
let theme = cx.global::<Theme>();
|
let theme = cx.global::<Theme>();
|
||||||
let mut text = self.text.clone();
|
let mut text = self.text.clone();
|
||||||
|
|
||||||
let mut style = TextStyle::default();
|
let mut style = TextStyle {
|
||||||
style.color = theme.text;
|
color: theme.text,
|
||||||
style.font_family = theme.font_sans.clone();
|
font_family: theme.font_sans.clone(),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
let mut selection_style = HighlightStyle::default();
|
let mut selection_style = HighlightStyle::default();
|
||||||
let mut color = theme.lavender;
|
let mut color = theme.lavender;
|
||||||
|
|
@ -303,10 +353,10 @@ impl Render for TextView {
|
||||||
|
|
||||||
let highlights = vec![(self.char_range_to_text_range(&text), selection_style)];
|
let highlights = vec![(self.char_range_to_text_range(&text), selection_style)];
|
||||||
|
|
||||||
let styled_text: StyledText = if text.len() == 0 {
|
let styled_text: StyledText = if text.is_empty() {
|
||||||
text = self.placeholder.to_string();
|
text = self.placeholder.to_string();
|
||||||
style.color = theme.subtext0;
|
style.color = theme.subtext0;
|
||||||
StyledText::new(text)
|
StyledText::new(text).with_highlights(&style, highlights)
|
||||||
} else {
|
} else {
|
||||||
StyledText::new(text).with_highlights(&style, highlights)
|
StyledText::new(text).with_highlights(&style, highlights)
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -64,16 +64,26 @@ impl From<FlavorColors> for Theme {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum ThemeMode {
|
||||||
|
Light,
|
||||||
|
Dark,
|
||||||
|
}
|
||||||
|
|
||||||
impl Theme {
|
impl Theme {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
Self::from(catppuccin::PALETTE.mocha.colors)
|
Self::from(catppuccin::PALETTE.latte.colors)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(cx: &mut AppContext) {
|
pub fn init(cx: &mut AppContext) {
|
||||||
cx.set_global(Theme::new())
|
cx.set_global(Theme::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn change(flavour: Flavor, cx: &mut AppContext) {
|
pub fn change(mode: ThemeMode, cx: &mut AppContext) {
|
||||||
|
let flavour = match mode {
|
||||||
|
ThemeMode::Light => catppuccin::PALETTE.latte,
|
||||||
|
ThemeMode::Dark => catppuccin::PALETTE.mocha,
|
||||||
|
};
|
||||||
|
|
||||||
cx.set_global(Self::from(flavour.colors));
|
cx.set_global(Self::from(flavour.colors));
|
||||||
cx.refresh();
|
cx.refresh();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,11 @@ use gpui::{prelude::FluentBuilder, *};
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use ui::{
|
use ui::{
|
||||||
button::{Button, ButtonStyle}, disableable::Clickable as _, theme::Theme, Color
|
button::{Button, ButtonStyle},
|
||||||
|
disableable::Clickable as _,
|
||||||
|
text_field::TextField,
|
||||||
|
theme::Theme,
|
||||||
|
Color,
|
||||||
};
|
};
|
||||||
use util::ResultExt as _;
|
use util::ResultExt as _;
|
||||||
|
|
||||||
|
|
@ -17,17 +21,15 @@ pub struct Workspace {
|
||||||
|
|
||||||
impl Workspace {
|
impl Workspace {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
app_state: Arc<AppState>,
|
_app_state: Arc<AppState>,
|
||||||
parent: Option<WeakView<Self>>,
|
_parent: Option<WeakView<Self>>,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let weak_handle = cx.view().downgrade();
|
let weak_handle = cx.view().downgrade();
|
||||||
|
|
||||||
let workspace = Workspace {
|
Workspace {
|
||||||
weak_self: weak_handle.clone(),
|
weak_self: weak_handle.clone(),
|
||||||
};
|
}
|
||||||
|
|
||||||
workspace
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_local(
|
pub fn new_local(
|
||||||
|
|
@ -42,15 +44,19 @@ impl Workspace {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let window = cx.open_window(options, {
|
let window = cx.open_window(options, |cx| {
|
||||||
let app_state = app_state.clone();
|
cx.new_view(|cx| Workspace::new(app_state.clone(), None, cx))
|
||||||
move |cx| cx.new_view(|cx| Workspace::new(app_state.clone(), None, cx))
|
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
window
|
window
|
||||||
.update(&mut cx, |_, cx| {
|
.update(&mut cx, |_, cx| {
|
||||||
cx.activate_window();
|
cx.activate_window();
|
||||||
cx.set_window_title("GPUI App");
|
cx.set_window_title("GPUI App");
|
||||||
|
cx.on_release(|_, _, _cx| {
|
||||||
|
// exit app
|
||||||
|
std::process::exit(0);
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
})
|
})
|
||||||
.log_err();
|
.log_err();
|
||||||
|
|
||||||
|
|
@ -61,11 +67,8 @@ impl Workspace {
|
||||||
|
|
||||||
actions!(workspace, [Open]);
|
actions!(workspace, [Open]);
|
||||||
|
|
||||||
pub fn init(app_state: Arc<AppState>, cx: &mut AppContext) {
|
pub fn init(_app_state: Arc<AppState>, cx: &mut AppContext) {
|
||||||
cx.on_action({
|
cx.on_action(|_action: &Open, _cx: &mut AppContext| {});
|
||||||
let app_state = app_state.clone();
|
|
||||||
move |action: &Open, cx: &mut AppContext| {}
|
|
||||||
});
|
|
||||||
|
|
||||||
Theme::init(cx);
|
Theme::init(cx);
|
||||||
}
|
}
|
||||||
|
|
@ -85,18 +88,11 @@ pub fn open_new(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Workspace {
|
impl Workspace {}
|
||||||
pub fn render_ok_button(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
|
||||||
Button::new("ok-button", "OK")
|
|
||||||
.style(ButtonStyle::Primary)
|
|
||||||
.on_click(|_, cx| {
|
|
||||||
// todo
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Render for Workspace {
|
impl Render for Workspace {
|
||||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||||
|
let theme = cx.global::<Theme>();
|
||||||
div()
|
div()
|
||||||
.relative()
|
.relative()
|
||||||
.flex()
|
.flex()
|
||||||
|
|
@ -104,13 +100,37 @@ impl Render for Workspace {
|
||||||
.flex_col()
|
.flex_col()
|
||||||
.size_full()
|
.size_full()
|
||||||
.p_4()
|
.p_4()
|
||||||
.bg(Color::Background.color(cx))
|
.bg(theme.base)
|
||||||
|
.gap_4()
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.flex()
|
||||||
|
.items_center()
|
||||||
|
.justify_end()
|
||||||
|
.gap_2()
|
||||||
|
.child(
|
||||||
|
Button::new("btn-light", "Light")
|
||||||
|
.size(ui::button::ButtonSize::Small)
|
||||||
|
.on_click(|_e, cx| Theme::change(ui::theme::ThemeMode::Light, cx)),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
Button::new("btn-dark", "Dark")
|
||||||
|
.size(ui::button::ButtonSize::Small)
|
||||||
|
.on_click(|_e, cx| Theme::change(ui::theme::ThemeMode::Dark, cx)),
|
||||||
|
),
|
||||||
|
)
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
.flex()
|
.flex()
|
||||||
.py_3()
|
.py_3()
|
||||||
.gap_2()
|
.gap_2()
|
||||||
.child(cx.new_view(|_| ui::story::Stories::new())),
|
.child(ui::story::Stories::view(cx)),
|
||||||
)
|
)
|
||||||
|
.child({
|
||||||
|
let txt = TextField::new(cx, "Enter text here...", false);
|
||||||
|
txt.view
|
||||||
|
.update(cx, |this, cx| this.set_text("This is default text.", cx));
|
||||||
|
txt
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue