From e01f83cdf800047c430e82ee3c3f601bad222462 Mon Sep 17 00:00:00 2001 From: StarArawn <toasterthegamer@gmail.com> Date: Fri, 17 Dec 2021 12:08:48 -0500 Subject: [PATCH] Added text box.. --- bevy_kayak_ui/src/key.rs | 169 +++++++++++++++ bevy_kayak_ui/src/lib.rs | 20 +- examples/text_box.rs | 78 +++++++ kayak_core/src/binding.rs | 28 +++ kayak_core/src/context.rs | 25 ++- kayak_core/src/event.rs | 4 +- kayak_core/src/input_event.rs | 4 + kayak_core/src/keys.rs | 203 ++++++++++++++++++ kayak_core/src/lib.rs | 3 + kayak_core/src/widget_manager.rs | 1 + kayak_render_macros/src/function_component.rs | 16 +- kayak_widgets/src/lib.rs | 2 + kayak_widgets/src/text_box.rs | 80 +++++++ 13 files changed, 618 insertions(+), 15 deletions(-) create mode 100644 bevy_kayak_ui/src/key.rs create mode 100644 examples/text_box.rs create mode 100644 kayak_core/src/keys.rs create mode 100644 kayak_widgets/src/text_box.rs diff --git a/bevy_kayak_ui/src/key.rs b/bevy_kayak_ui/src/key.rs new file mode 100644 index 0000000..9da2ba4 --- /dev/null +++ b/bevy_kayak_ui/src/key.rs @@ -0,0 +1,169 @@ +use kayak_core::KeyCode; + +pub fn convert_virtual_key_code(virtual_key_code: bevy::prelude::KeyCode) -> KeyCode { + match virtual_key_code { + bevy::prelude::KeyCode::Key1 => KeyCode::Key1, + bevy::prelude::KeyCode::Key2 => KeyCode::Key2, + bevy::prelude::KeyCode::Key3 => KeyCode::Key3, + bevy::prelude::KeyCode::Key4 => KeyCode::Key4, + bevy::prelude::KeyCode::Key5 => KeyCode::Key5, + bevy::prelude::KeyCode::Key6 => KeyCode::Key6, + bevy::prelude::KeyCode::Key7 => KeyCode::Key7, + bevy::prelude::KeyCode::Key8 => KeyCode::Key8, + bevy::prelude::KeyCode::Key9 => KeyCode::Key9, + bevy::prelude::KeyCode::Key0 => KeyCode::Key0, + bevy::prelude::KeyCode::A => KeyCode::A, + bevy::prelude::KeyCode::B => KeyCode::B, + bevy::prelude::KeyCode::C => KeyCode::C, + bevy::prelude::KeyCode::D => KeyCode::D, + bevy::prelude::KeyCode::E => KeyCode::E, + bevy::prelude::KeyCode::F => KeyCode::F, + bevy::prelude::KeyCode::G => KeyCode::G, + bevy::prelude::KeyCode::H => KeyCode::H, + bevy::prelude::KeyCode::I => KeyCode::I, + bevy::prelude::KeyCode::J => KeyCode::J, + bevy::prelude::KeyCode::K => KeyCode::K, + bevy::prelude::KeyCode::L => KeyCode::L, + bevy::prelude::KeyCode::M => KeyCode::M, + bevy::prelude::KeyCode::N => KeyCode::N, + bevy::prelude::KeyCode::O => KeyCode::O, + bevy::prelude::KeyCode::P => KeyCode::P, + bevy::prelude::KeyCode::Q => KeyCode::Q, + bevy::prelude::KeyCode::R => KeyCode::R, + bevy::prelude::KeyCode::S => KeyCode::S, + bevy::prelude::KeyCode::T => KeyCode::T, + bevy::prelude::KeyCode::U => KeyCode::U, + bevy::prelude::KeyCode::V => KeyCode::V, + bevy::prelude::KeyCode::W => KeyCode::W, + bevy::prelude::KeyCode::X => KeyCode::X, + bevy::prelude::KeyCode::Y => KeyCode::Y, + bevy::prelude::KeyCode::Z => KeyCode::Z, + bevy::prelude::KeyCode::Escape => KeyCode::Escape, + bevy::prelude::KeyCode::F1 => KeyCode::F1, + bevy::prelude::KeyCode::F2 => KeyCode::F2, + bevy::prelude::KeyCode::F3 => KeyCode::F3, + bevy::prelude::KeyCode::F4 => KeyCode::F4, + bevy::prelude::KeyCode::F5 => KeyCode::F5, + bevy::prelude::KeyCode::F6 => KeyCode::F6, + bevy::prelude::KeyCode::F7 => KeyCode::F7, + bevy::prelude::KeyCode::F8 => KeyCode::F8, + bevy::prelude::KeyCode::F9 => KeyCode::F9, + bevy::prelude::KeyCode::F10 => KeyCode::F10, + bevy::prelude::KeyCode::F11 => KeyCode::F11, + bevy::prelude::KeyCode::F12 => KeyCode::F12, + bevy::prelude::KeyCode::F13 => KeyCode::F13, + bevy::prelude::KeyCode::F14 => KeyCode::F14, + bevy::prelude::KeyCode::F15 => KeyCode::F15, + bevy::prelude::KeyCode::F16 => KeyCode::F16, + bevy::prelude::KeyCode::F17 => KeyCode::F17, + bevy::prelude::KeyCode::F18 => KeyCode::F18, + bevy::prelude::KeyCode::F19 => KeyCode::F19, + bevy::prelude::KeyCode::F20 => KeyCode::F20, + bevy::prelude::KeyCode::F21 => KeyCode::F21, + bevy::prelude::KeyCode::F22 => KeyCode::F22, + bevy::prelude::KeyCode::F23 => KeyCode::F23, + bevy::prelude::KeyCode::F24 => KeyCode::F24, + bevy::prelude::KeyCode::Snapshot => KeyCode::Snapshot, + bevy::prelude::KeyCode::Scroll => KeyCode::Scroll, + bevy::prelude::KeyCode::Pause => KeyCode::Pause, + bevy::prelude::KeyCode::Insert => KeyCode::Insert, + bevy::prelude::KeyCode::Home => KeyCode::Home, + bevy::prelude::KeyCode::Delete => KeyCode::Delete, + bevy::prelude::KeyCode::End => KeyCode::End, + bevy::prelude::KeyCode::PageDown => KeyCode::PageDown, + bevy::prelude::KeyCode::PageUp => KeyCode::PageUp, + bevy::prelude::KeyCode::Left => KeyCode::Left, + bevy::prelude::KeyCode::Up => KeyCode::Up, + bevy::prelude::KeyCode::Right => KeyCode::Right, + bevy::prelude::KeyCode::Down => KeyCode::Down, + bevy::prelude::KeyCode::Back => KeyCode::Back, + bevy::prelude::KeyCode::Return => KeyCode::Return, + bevy::prelude::KeyCode::Space => KeyCode::Space, + bevy::prelude::KeyCode::Compose => KeyCode::Compose, + bevy::prelude::KeyCode::Caret => KeyCode::Caret, + bevy::prelude::KeyCode::Numlock => KeyCode::Numlock, + bevy::prelude::KeyCode::Numpad0 => KeyCode::Numpad0, + bevy::prelude::KeyCode::Numpad1 => KeyCode::Numpad1, + bevy::prelude::KeyCode::Numpad2 => KeyCode::Numpad2, + bevy::prelude::KeyCode::Numpad3 => KeyCode::Numpad3, + bevy::prelude::KeyCode::Numpad4 => KeyCode::Numpad4, + bevy::prelude::KeyCode::Numpad5 => KeyCode::Numpad5, + bevy::prelude::KeyCode::Numpad6 => KeyCode::Numpad6, + bevy::prelude::KeyCode::Numpad7 => KeyCode::Numpad7, + bevy::prelude::KeyCode::Numpad8 => KeyCode::Numpad8, + bevy::prelude::KeyCode::Numpad9 => KeyCode::Numpad9, + bevy::prelude::KeyCode::AbntC1 => KeyCode::AbntC1, + bevy::prelude::KeyCode::AbntC2 => KeyCode::AbntC2, + bevy::prelude::KeyCode::NumpadAdd => KeyCode::NumpadAdd, + bevy::prelude::KeyCode::Apostrophe => KeyCode::Apostrophe, + bevy::prelude::KeyCode::Apps => KeyCode::Apps, + bevy::prelude::KeyCode::Asterisk => KeyCode::Asterisk, + bevy::prelude::KeyCode::Plus => KeyCode::Plus, + bevy::prelude::KeyCode::At => KeyCode::At, + bevy::prelude::KeyCode::Ax => KeyCode::Ax, + bevy::prelude::KeyCode::Backslash => KeyCode::Backslash, + bevy::prelude::KeyCode::Calculator => KeyCode::Calculator, + bevy::prelude::KeyCode::Capital => KeyCode::Capital, + bevy::prelude::KeyCode::Colon => KeyCode::Colon, + bevy::prelude::KeyCode::Comma => KeyCode::Comma, + bevy::prelude::KeyCode::Convert => KeyCode::Convert, + bevy::prelude::KeyCode::NumpadDecimal => KeyCode::NumpadDecimal, + bevy::prelude::KeyCode::NumpadDivide => KeyCode::NumpadDivide, + bevy::prelude::KeyCode::Equals => KeyCode::Equals, + bevy::prelude::KeyCode::Grave => KeyCode::Grave, + bevy::prelude::KeyCode::Kana => KeyCode::Kana, + bevy::prelude::KeyCode::Kanji => KeyCode::Kanji, + bevy::prelude::KeyCode::LAlt => KeyCode::LAlt, + bevy::prelude::KeyCode::LBracket => KeyCode::LBracket, + bevy::prelude::KeyCode::LControl => KeyCode::LControl, + bevy::prelude::KeyCode::LShift => KeyCode::LShift, + bevy::prelude::KeyCode::LWin => KeyCode::LWin, + bevy::prelude::KeyCode::Mail => KeyCode::Mail, + bevy::prelude::KeyCode::MediaSelect => KeyCode::MediaSelect, + bevy::prelude::KeyCode::MediaStop => KeyCode::MediaStop, + bevy::prelude::KeyCode::Minus => KeyCode::Minus, + bevy::prelude::KeyCode::NumpadMultiply => KeyCode::NumpadMultiply, + bevy::prelude::KeyCode::Mute => KeyCode::Mute, + bevy::prelude::KeyCode::MyComputer => KeyCode::MyComputer, + bevy::prelude::KeyCode::NavigateForward => KeyCode::NavigateForward, + bevy::prelude::KeyCode::NavigateBackward => KeyCode::NavigateBackward, + bevy::prelude::KeyCode::NextTrack => KeyCode::NextTrack, + bevy::prelude::KeyCode::NoConvert => KeyCode::NoConvert, + bevy::prelude::KeyCode::NumpadComma => KeyCode::NumpadComma, + bevy::prelude::KeyCode::NumpadEnter => KeyCode::NumpadEnter, + bevy::prelude::KeyCode::NumpadEquals => KeyCode::NumpadEquals, + bevy::prelude::KeyCode::Oem102 => KeyCode::Oem102, + bevy::prelude::KeyCode::Period => KeyCode::Period, + bevy::prelude::KeyCode::PlayPause => KeyCode::PlayPause, + bevy::prelude::KeyCode::Power => KeyCode::Power, + bevy::prelude::KeyCode::PrevTrack => KeyCode::PrevTrack, + bevy::prelude::KeyCode::RAlt => KeyCode::RAlt, + bevy::prelude::KeyCode::RBracket => KeyCode::RBracket, + bevy::prelude::KeyCode::RControl => KeyCode::RControl, + bevy::prelude::KeyCode::RShift => KeyCode::RShift, + bevy::prelude::KeyCode::RWin => KeyCode::RWin, + bevy::prelude::KeyCode::Semicolon => KeyCode::Semicolon, + bevy::prelude::KeyCode::Slash => KeyCode::Slash, + bevy::prelude::KeyCode::Sleep => KeyCode::Sleep, + bevy::prelude::KeyCode::Stop => KeyCode::Stop, + bevy::prelude::KeyCode::NumpadSubtract => KeyCode::NumpadSubtract, + bevy::prelude::KeyCode::Sysrq => KeyCode::Sysrq, + bevy::prelude::KeyCode::Tab => KeyCode::Tab, + bevy::prelude::KeyCode::Underline => KeyCode::Underline, + bevy::prelude::KeyCode::Unlabeled => KeyCode::Unlabeled, + bevy::prelude::KeyCode::VolumeDown => KeyCode::VolumeDown, + bevy::prelude::KeyCode::VolumeUp => KeyCode::VolumeUp, + bevy::prelude::KeyCode::Wake => KeyCode::Wake, + bevy::prelude::KeyCode::WebBack => KeyCode::WebBack, + bevy::prelude::KeyCode::WebFavorites => KeyCode::WebFavorites, + bevy::prelude::KeyCode::WebForward => KeyCode::WebForward, + bevy::prelude::KeyCode::WebHome => KeyCode::WebHome, + bevy::prelude::KeyCode::WebRefresh => KeyCode::WebRefresh, + bevy::prelude::KeyCode::WebSearch => KeyCode::WebSearch, + bevy::prelude::KeyCode::WebStop => KeyCode::WebStop, + bevy::prelude::KeyCode::Yen => KeyCode::Yen, + bevy::prelude::KeyCode::Copy => KeyCode::Copy, + bevy::prelude::KeyCode::Paste => KeyCode::Paste, + bevy::prelude::KeyCode::Cut => KeyCode::Cut, + } +} diff --git a/bevy_kayak_ui/src/lib.rs b/bevy_kayak_ui/src/lib.rs index 16511b3..44e37cb 100644 --- a/bevy_kayak_ui/src/lib.rs +++ b/bevy_kayak_ui/src/lib.rs @@ -1,13 +1,14 @@ use bevy::{ - input::{mouse::MouseButtonInput, ElementState}, + input::{keyboard::KeyboardInput, mouse::MouseButtonInput, ElementState}, math::Vec2, prelude::{EventReader, IntoExclusiveSystem, MouseButton, Plugin, Res, World}, render::color::Color, - window::{CursorMoved, WindowCreated, WindowResized, Windows}, + window::{CursorMoved, ReceivedCharacter, WindowCreated, WindowResized, Windows}, }; mod bevy_context; mod camera; +mod key; mod render; pub use bevy_context::BevyContext; @@ -50,6 +51,8 @@ pub fn process_events( windows: Res<Windows>, mut cursor_moved_events: EventReader<CursorMoved>, mut mouse_button_input_events: EventReader<MouseButtonInput>, + mut char_input_events: EventReader<ReceivedCharacter>, + mut keyboard_input_events: EventReader<KeyboardInput>, ) { let window_size = if let Some(window) = windows.get_primary() { Vec2::new(window.width(), window.height()) @@ -78,6 +81,19 @@ pub fn process_events( } } + for event in char_input_events.iter() { + input_events.push(InputEvent::CharEvent { c: event.char }); + } + + for event in keyboard_input_events.iter() { + if let Some(key_code) = event.key_code { + let kayak_key_code = key::convert_virtual_key_code(key_code); + input_events.push(InputEvent::Keyboard { + key: kayak_key_code, + }); + } + } + context.process_events(input_events); } } diff --git a/examples/text_box.rs b/examples/text_box.rs new file mode 100644 index 0000000..6cd17e6 --- /dev/null +++ b/examples/text_box.rs @@ -0,0 +1,78 @@ +use bevy::{ + prelude::{App as BevyApp, AssetServer, Commands, Res, ResMut}, + window::WindowDescriptor, + DefaultPlugins, +}; +use kayak_ui::bevy::{BevyContext, BevyKayakUIPlugin, FontMapping, UICameraBundle}; +use kayak_ui::core::{ + render, rsx, + styles::{Style, StyleProp, Units}, + widget, Bound, Index, MutableBound, +}; +use kayak_widgets::{App, OnChange, TextBox, Window}; + +#[widget] +fn TextBoxExample(context: &mut KayakContext) { + let value = context.create_state("".to_string()).unwrap(); + let value2 = context.create_state("".to_string()).unwrap(); + + let input_styles = Style { + top: StyleProp::Value(Units::Pixels(10.0)), + ..Default::default() + }; + + let cloned_value = value.clone(); + let on_change = OnChange::new(move |event| { + cloned_value.set(event.value); + }); + + let cloned_value2 = value2.clone(); + let on_change2 = OnChange::new(move |event| { + cloned_value2.set(event.value); + }); + + let current_value = value.get(); + let current_value2 = value2.get(); + rsx! { + <> + <Window position={(50.0, 50.0)} size={(300.0, 300.0)} title={"TextBox Example".to_string()}> + <TextBox styles={Some(input_styles)} value={current_value} on_change={Some(on_change)} /> + <TextBox styles={Some(input_styles)} value={current_value2} on_change={Some(on_change2)} /> + </Window> + </> + } +} + +fn startup( + mut commands: Commands, + mut font_mapping: ResMut<FontMapping>, + asset_server: Res<AssetServer>, +) { + commands.spawn_bundle(UICameraBundle::new()); + + font_mapping.add(asset_server.load("roboto.kayak_font")); + + let context = BevyContext::new(|context| { + render! { + <App> + <TextBoxExample /> + </App> + } + }); + + commands.insert_resource(context); +} + +fn main() { + BevyApp::new() + .insert_resource(WindowDescriptor { + width: 1270.0, + height: 720.0, + title: String::from("UI Example"), + ..Default::default() + }) + .add_plugins(DefaultPlugins) + .add_plugin(BevyKayakUIPlugin) + .add_startup_system(startup) + .run(); +} diff --git a/kayak_core/src/binding.rs b/kayak_core/src/binding.rs index f1221a4..fbd4a31 100644 --- a/kayak_core/src/binding.rs +++ b/kayak_core/src/binding.rs @@ -1 +1,29 @@ +use std::time::Instant; + pub use flo_binding::{bind, notify, Binding, Bound, Changeable, MutableBound, Releasable}; + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct Debouncer { + last_updated: Instant, + time: f32, +} + +impl Debouncer { + pub fn new(time: f32) -> Self { + Self { + time, + last_updated: Instant::now(), + } + } + + pub fn should_update(&mut self) -> bool { + let elapsed_time = self.last_updated.elapsed().as_secs_f32(); + if elapsed_time > self.time { + self.last_updated = Instant::now(); + + return true; + } + + return false; + } +} diff --git a/kayak_core/src/context.rs b/kayak_core/src/context.rs index 19300e2..6014126 100644 --- a/kayak_core/src/context.rs +++ b/kayak_core/src/context.rs @@ -287,6 +287,16 @@ impl KayakContext { events_stream.push(click_event); } } + InputEvent::CharEvent { c } => events_stream.push(Event { + target: index, + event_type: EventType::CharInput { c: *c }, + ..Event::default() + }), + InputEvent::Keyboard { key } => events_stream.push(Event { + target: index, + event_type: EventType::KeyboardInput { key: *key }, + ..Event::default() + }), } } } @@ -302,13 +312,14 @@ impl KayakContext { target_widget.on_event(self, event); self.widget_manager.repossess(target_widget); - for parent in parents { - if event.should_propagate { - let mut parent_widget = self.widget_manager.take(parent); - parent_widget.on_event(self, event); - self.widget_manager.repossess(parent_widget); - } - } + // TODO: Restore propagation. + // for parent in parents { + // if event.should_propagate { + // let mut parent_widget = self.widget_manager.take(parent); + // parent_widget.on_event(self, event); + // self.widget_manager.repossess(parent_widget); + // } + // } } } diff --git a/kayak_core/src/event.rs b/kayak_core/src/event.rs index 1b1f3ec..7b56d25 100644 --- a/kayak_core/src/event.rs +++ b/kayak_core/src/event.rs @@ -1,4 +1,4 @@ -use crate::Index; +use crate::{Index, KeyCode}; #[derive(Debug, Clone, Copy, PartialEq)] pub struct Event { @@ -23,4 +23,6 @@ pub enum EventType { Hover, MouseIn, MouseOut, + CharInput { c: char }, + KeyboardInput { key: KeyCode }, } diff --git a/kayak_core/src/input_event.rs b/kayak_core/src/input_event.rs index 18856f7..5472702 100644 --- a/kayak_core/src/input_event.rs +++ b/kayak_core/src/input_event.rs @@ -1,4 +1,8 @@ +use crate::KeyCode; + pub enum InputEvent { MouseMoved((f32, f32)), MouseLeftClick, + CharEvent { c: char }, + Keyboard { key: KeyCode }, } diff --git a/kayak_core/src/keys.rs b/kayak_core/src/keys.rs new file mode 100644 index 0000000..1510a65 --- /dev/null +++ b/kayak_core/src/keys.rs @@ -0,0 +1,203 @@ +/// The key code of a keyboard input. +#[derive(Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] +#[repr(u32)] +pub enum KeyCode { + /// The '1' key over the letters. + Key1, + /// The '2' key over the letters. + Key2, + /// The '3' key over the letters. + Key3, + /// The '4' key over the letters. + Key4, + /// The '5' key over the letters. + Key5, + /// The '6' key over the letters. + Key6, + /// The '7' key over the letters. + Key7, + /// The '8' key over the letters. + Key8, + /// The '9' key over the letters. + Key9, + /// The '0' key over the 'O' and 'P' keys. + Key0, + + A, + B, + C, + D, + E, + F, + G, + H, + I, + J, + K, + L, + M, + N, + O, + P, + Q, + R, + S, + T, + U, + V, + W, + X, + Y, + Z, + + /// The Escape key, next to F1. + Escape, + + F1, + F2, + F3, + F4, + F5, + F6, + F7, + F8, + F9, + F10, + F11, + F12, + F13, + F14, + F15, + F16, + F17, + F18, + F19, + F20, + F21, + F22, + F23, + F24, + + /// Print Screen/SysRq. + Snapshot, + /// Scroll Lock. + Scroll, + /// Pause/Break key, next to Scroll lock. + Pause, + + /// `Insert`, next to Backspace. + Insert, + Home, + Delete, + End, + PageDown, + PageUp, + + Left, + Up, + Right, + Down, + + /// The Backspace key, right over Enter. + Back, + /// The Enter key. + Return, + /// The space bar. + Space, + + /// The "Compose" key on Linux. + Compose, + + Caret, + + Numlock, + Numpad0, + Numpad1, + Numpad2, + Numpad3, + Numpad4, + Numpad5, + Numpad6, + Numpad7, + Numpad8, + Numpad9, + + AbntC1, + AbntC2, + NumpadAdd, + Apostrophe, + Apps, + Asterisk, + Plus, + At, + Ax, + Backslash, + Calculator, + Capital, + Colon, + Comma, + Convert, + NumpadDecimal, + NumpadDivide, + Equals, + Grave, + Kana, + Kanji, + /// The left alt key. Maps to left option on Mac. + LAlt, + LBracket, + LControl, + LShift, + /// The left Windows key. Maps to left Command on Mac. + LWin, + Mail, + MediaSelect, + MediaStop, + Minus, + NumpadMultiply, + Mute, + MyComputer, + NavigateForward, // also called "Prior" + NavigateBackward, // also called "Next" + NextTrack, + NoConvert, + NumpadComma, + NumpadEnter, + NumpadEquals, + Oem102, + Period, + PlayPause, + Power, + PrevTrack, + /// The right alt key. Maps to right option on Mac. + RAlt, + RBracket, + RControl, + RShift, + /// The right Windows key. Maps to right Command on Mac. + RWin, + Semicolon, + Slash, + Sleep, + Stop, + NumpadSubtract, + Sysrq, + Tab, + Underline, + Unlabeled, + VolumeDown, + VolumeUp, + Wake, + WebBack, + WebFavorites, + WebForward, + WebHome, + WebRefresh, + WebSearch, + WebStop, + Yen, + Copy, + Paste, + Cut, +} diff --git a/kayak_core/src/lib.rs b/kayak_core/src/lib.rs index 7b2ee32..54edfee 100644 --- a/kayak_core/src/lib.rs +++ b/kayak_core/src/lib.rs @@ -5,6 +5,7 @@ pub mod event; pub mod fragment; pub(crate) mod generational_arena; mod input_event; +mod keys; pub mod layout_cache; pub mod node; pub mod render_command; @@ -18,12 +19,14 @@ pub mod widget_manager; use std::sync::{Arc, RwLock}; pub use binding::*; +pub use color::Color; pub use context::*; pub use event::*; pub use fragment::Fragment; pub use generational_arena::{Arena, Index}; pub use input_event::*; pub use kayak_render_macros::{constructor, render, rsx, widget}; +pub use keys::KeyCode; pub use resources::Resources; pub use tree::{Tree, WidgetTree}; pub use vec::VecTracker; diff --git a/kayak_core/src/widget_manager.rs b/kayak_core/src/widget_manager.rs index 3d9e1c3..1f78f7f 100644 --- a/kayak_core/src/widget_manager.rs +++ b/kayak_core/src/widget_manager.rs @@ -202,6 +202,7 @@ impl WidgetManager { if matches!(styles.render_command.resolve(), RenderCommand::Empty) { continue; } + let mut node = NodeBuilder::empty() .with_id(dirty_node_index) .with_styles(styles) diff --git a/kayak_render_macros/src/function_component.rs b/kayak_render_macros/src/function_component.rs index bc543e6..fbcdaa0 100644 --- a/kayak_render_macros/src/function_component.rs +++ b/kayak_render_macros/src/function_component.rs @@ -53,8 +53,13 @@ pub fn create_function_widget(f: syn::ItemFn) -> TokenStream { let input_string = (quote! { #input }).to_string(); if input_string.contains("children : Children") { *input = quote! { - #[derivative(Debug = "ignore", PartialEq = "ignore")] - pub children: Children + #[derivative(Debug = "ignore", PartialEq = "ignore")] + pub children: Children + }; + } else if input_string.contains("on_event : Option < OnEvent >") { + *input = quote! { + #[derivative(Debug = "ignore", PartialEq = "ignore")] + pub on_event: Option<#kayak_core::OnEvent> }; } else { *input = quote! { @@ -67,7 +72,7 @@ pub fn create_function_widget(f: syn::ItemFn) -> TokenStream { ( vec![ "styles : Option < Style >", - "styles : Option< kayak_core :: styles :: Style >", + "styles : Option< kayak_ui :: core :: styles :: Style >", ], quote! { pub styles: Option<#kayak_core::styles::Style> @@ -82,8 +87,9 @@ pub fn create_function_widget(f: syn::ItemFn) -> TokenStream { ), ( vec![ - "on_event: Option<OnEvent>", - "on_event : Option<kayak_core::OnEvent>", + "on_event : Option < OnEvent >", + "on_event : Option < kayak_ui :: core :: OnEvent >", + "on_event : Option <\nkayak_ui :: core :: OnEvent >", ], quote! { #[derivative(Debug = "ignore", PartialEq = "ignore")] diff --git a/kayak_widgets/src/lib.rs b/kayak_widgets/src/lib.rs index 9a0f309..fb09fe6 100644 --- a/kayak_widgets/src/lib.rs +++ b/kayak_widgets/src/lib.rs @@ -5,6 +5,7 @@ mod clip; mod element; mod if_element; mod image; +mod text_box; mod nine_patch; mod text; mod window; @@ -16,6 +17,7 @@ pub use clip::*; pub use element::*; pub use if_element::*; pub use image::*; +pub use text_box::*; pub use nine_patch::*; pub use text::*; pub use window::*; diff --git a/kayak_widgets/src/text_box.rs b/kayak_widgets/src/text_box.rs new file mode 100644 index 0000000..1f0121f --- /dev/null +++ b/kayak_widgets/src/text_box.rs @@ -0,0 +1,80 @@ +use kayak_ui::core::{ + rsx, + styles::{Style, StyleProp, Units}, + widget, Bound, Color, EventType, MutableBound, OnEvent, +}; +use std::sync::{Arc, RwLock}; + +use crate::{Background, Clip, Text}; + +#[derive(Debug, Clone, PartialEq)] +pub struct ChangeEvent { + pub value: String, +} + +#[derive(Clone)] +pub struct OnChange(pub Arc<RwLock<dyn FnMut(ChangeEvent) + Send + Sync + 'static>>); + +impl OnChange { + pub fn new<F: FnMut(ChangeEvent) + Send + Sync + 'static>(f: F) -> OnChange { + OnChange(Arc::new(RwLock::new(f))) + } +} + +impl PartialEq for OnChange { + fn eq(&self, _other: &Self) -> bool { + true + } +} + +impl std::fmt::Debug for OnChange { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("OnChange").finish() + } +} + +#[widget] +pub fn TextBox(value: String, on_change: Option<OnChange>) { + let background_styles = Style { + background_color: StyleProp::Value(Color::new(0.176, 0.196, 0.215, 1.0)), + border_radius: StyleProp::Value((5.0, 5.0, 5.0, 5.0)), + height: StyleProp::Value(Units::Pixels(26.0)), + padding_left: StyleProp::Value(Units::Pixels(5.0)), + padding_right: StyleProp::Value(Units::Pixels(5.0)), + ..styles.clone().unwrap_or_default() + }; + + let internal_value = context.create_state("".to_string()).unwrap(); + + let cloned_on_change = on_change.clone(); + self.on_event = Some(OnEvent::new(move |_, event| match event.event_type { + EventType::CharInput { c } => { + let mut current_value = internal_value.get(); + if c == '\u{8}' { + if current_value.len() > 0 { + current_value.truncate(current_value.len() - 1); + } + } else if !c.is_control() { + current_value.push(c); + } + if let Some(on_change) = cloned_on_change.as_ref() { + if let Ok(mut on_change) = on_change.0.write() { + on_change(ChangeEvent { + value: current_value.clone(), + }); + } + } + internal_value.set(current_value); + } + _ => {} + })); + + let value = value.clone(); + rsx! { + <Background styles={Some(background_styles)}> + <Clip> + <Text content={value} size={14.0} /> + </Clip> + </Background> + } +} -- GitLab