diff --git a/Cargo.lock b/Cargo.lock index 25baa906..b693aaf4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3564,6 +3564,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "input" +version = "0.1.0" +dependencies = [ + "anyhow", + "gpui", + "gpui-component", +] + [[package]] name = "instant" version = "0.1.13" diff --git a/Cargo.toml b/Cargo.toml index dcada295..e514f660 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["examples/hello_world", "examples/window_title", "crates/macros", "crates/story", "crates/ui", "examples/window_title", "examples/app_assets", "examples/app_assets"] +members = ["examples/hello_world", "examples/window_title", "crates/macros", "crates/story", "crates/ui", "examples/window_title", "examples/app_assets", "examples/app_assets", "examples/input"] default-members = ["crates/story"] resolver = "2" diff --git a/examples/hello_world/src/main.rs b/examples/hello_world/src/main.rs index 75f16a2a..bbce8d0e 100644 --- a/examples/hello_world/src/main.rs +++ b/examples/hello_world/src/main.rs @@ -1,12 +1,8 @@ use gpui::*; -use gpui_component::{ - button::{Button, ButtonVariants}, - Root, StyledExt, -}; +use gpui_component::{button::*, *}; -pub struct HelloWorld; - -impl Render for HelloWorld { +pub struct Example; +impl Render for Example { fn render(&mut self, _: &mut Window, _: &mut Context) -> impl IntoElement { div() .v_flex() @@ -14,7 +10,6 @@ impl Render for HelloWorld { .size_full() .items_center() .justify_center() - .text_center() .child("Hello, World!") .child( Button::new("ok") @@ -29,38 +24,13 @@ fn main() { let app = Application::new(); app.run(move |cx| { - // We must initialize gpui_component before using it. + // This must be called before using any GPUI Component features. gpui_component::init(cx); - cx.activate(true); - - let mut window_size = size(px(640.), px(480.)); - if let Some(display) = cx.primary_display() { - let display_size = display.bounds().size; - window_size.width = window_size.width.min(display_size.width * 0.85); - window_size.height = window_size.height.min(display_size.height * 0.85); - } - let window_bounds = Bounds::centered(None, window_size, cx); - cx.spawn(async move |cx| { - let options = WindowOptions { - window_bounds: Some(WindowBounds::Windowed(window_bounds)), - titlebar: None, - window_min_size: Some(gpui::Size { - width: px(640.), - height: px(480.), - }), - kind: WindowKind::Normal, - #[cfg(target_os = "linux")] - window_background: gpui::WindowBackgroundAppearance::Transparent, - #[cfg(target_os = "linux")] - window_decorations: Some(gpui::WindowDecorations::Client), - ..Default::default() - }; - - cx.open_window(options, |window, cx| { - let view = cx.new(|_| HelloWorld); - // The first level on the window must be Root. + cx.open_window(WindowOptions::default(), |window, cx| { + let view = cx.new(|_| Example); + // This first level on the window, should be a Root. cx.new(|cx| Root::new(view.into(), window, cx)) })?; diff --git a/examples/input/Cargo.toml b/examples/input/Cargo.toml new file mode 100644 index 00000000..4f2d8c0b --- /dev/null +++ b/examples/input/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "input" +version = "0.1.0" +edition = "2024" + +[dependencies] +anyhow.workspace = true +gpui.workspace = true +gpui-component = { workspace = true } + +[lints] +workspace = true diff --git a/examples/input/src/main.rs b/examples/input/src/main.rs new file mode 100644 index 00000000..42fd4178 --- /dev/null +++ b/examples/input/src/main.rs @@ -0,0 +1,78 @@ +use gpui::*; +use gpui_component::{ + input::{InputEvent, InputState, TextInput}, + *, +}; + +pub struct Example { + input_state: Entity, + display_text: SharedString, + + /// We need to keep the subscriptions alive with the Example entity. + /// + /// So if the Example entity is dropped, the subscriptions are also dropped. + /// This is important to avoid memory leaks. + _subscriptions: Vec, +} + +impl Example { + fn new(window: &mut Window, cx: &mut Context) -> Self { + let input_state = cx.new(|cx| InputState::new(window, cx).placeholder("Enter your name")); + + let _subscriptions = vec![cx.subscribe_in(&input_state, window, { + let input_state = input_state.clone(); + move |this, _, ev: &InputEvent, _window, cx| match ev { + InputEvent::Change => { + let value = input_state.read(cx).value(); + this.display_text = format!("Hello, {}!", value).into(); + cx.notify() + } + _ => {} + } + })]; + + Self { + input_state, + display_text: SharedString::default(), + _subscriptions, + } + } +} + +impl Render for Example { + fn render(&mut self, _: &mut Window, _: &mut Context) -> impl IntoElement { + v_flex() + .p_5() + .gap_2() + .size_full() + .items_center() + .justify_center() + .child(TextInput::new(&self.input_state)) + .child(self.display_text.clone()) + } +} + +fn main() { + let app = Application::new(); + + app.run(move |cx| { + // This must be called before using any GPUI Component features. + gpui_component::init(cx); + + let window_options = WindowOptions { + window_bounds: Some(WindowBounds::centered(size(px(800.), px(600.)), cx)), + ..Default::default() + }; + + cx.spawn(async move |cx| { + cx.open_window(window_options, |window, cx| { + let view = cx.new(|cx| Example::new(window, cx)); + // This first level on the window, should be a Root. + cx.new(|cx| Root::new(view.into(), window, cx)) + })?; + + Ok::<_, anyhow::Error>(()) + }) + .detach(); + }); +}