Skip to content
Snippets Groups Projects
Commit 151ac062 authored by StarArawn's avatar StarArawn
Browse files

Fixed up focus states. Fixed up state management with multiple of the same type.

parent e01f83cd
No related branches found
No related tags found
No related merge requests found
use flo_binding::Changeable;
use morphorm::Hierarchy;
use std::collections::HashMap;
use crate::{widget_manager::WidgetManager, Event, EventType, Index, InputEvent};
use crate::{
multi_state::MultiState, widget_manager::WidgetManager, Event, EventType, Index, InputEvent,
};
pub struct KayakContext {
widget_states: HashMap<crate::Index, resources::Resources>,
global_bindings: HashMap<crate::Index, Vec<flo_binding::Uuid>>,
widget_state_lifetimes:
HashMap<crate::Index, HashMap<flo_binding::Uuid, Box<dyn crate::Releasable>>>,
current_id: Index,
pub current_id: Index,
pub widget_manager: WidgetManager,
last_mouse_position: (f32, f32),
pub global_state: resources::Resources,
previous_events: HashMap<Index, Option<EventType>>,
current_focus: Index,
last_focus: Index,
last_state_type_id: Option<std::any::TypeId>,
current_state_index: usize,
}
impl std::fmt::Debug for KayakContext {
......@@ -35,6 +42,10 @@ impl KayakContext {
last_mouse_position: (0.0, 0.0),
global_state: resources::Resources::default(),
previous_events: HashMap::new(),
current_focus: Index::default(),
last_focus: Index::default(),
last_state_type_id: None,
current_state_index: 0,
}
}
......@@ -90,15 +101,43 @@ impl KayakContext {
pub fn set_current_id(&mut self, id: crate::Index) {
self.current_id = id;
self.current_state_index = 0;
self.last_state_type_id = None;
}
pub fn create_state<T: resources::Resource + Clone + PartialEq>(
&mut self,
initial_state: T,
) -> Option<crate::Binding<T>> {
let state_type_id = initial_state.type_id();
if let Some(last_state_type_id) = self.last_state_type_id {
if state_type_id != last_state_type_id {
self.current_state_index = 0;
}
}
if self.widget_states.contains_key(&self.current_id) {
let states = self.widget_states.get_mut(&self.current_id).unwrap();
if !states.contains::<crate::Binding<T>>() {
if !states.contains::<MultiState<crate::Binding<T>>>() {
let state = crate::bind(initial_state);
let dirty_nodes = self.widget_manager.dirty_nodes.clone();
let cloned_id = self.current_id;
let lifetime = state.when_changed(crate::notify(move || {
if let Ok(mut dirty_nodes) = dirty_nodes.lock() {
dirty_nodes.insert(cloned_id);
}
}));
Self::insert_state_lifetime(
&mut self.widget_state_lifetimes,
self.current_id,
state.id,
lifetime,
);
states.insert(MultiState::new(state));
self.last_state_type_id = Some(state_type_id);
self.current_state_index += 1;
} else {
// Add new value to the multi-state.
let state = crate::bind(initial_state);
let dirty_nodes = self.widget_manager.dirty_nodes.clone();
let cloned_id = self.current_id;
......@@ -113,7 +152,10 @@ impl KayakContext {
state.id,
lifetime,
);
states.insert(state);
let mut multi_state = states.remove::<MultiState<crate::Binding<T>>>().unwrap();
multi_state.get_or_add(state, &mut self.current_state_index);
states.insert(multi_state);
self.last_state_type_id = Some(state_type_id);
}
} else {
let mut states = resources::Resources::default();
......@@ -131,12 +173,24 @@ impl KayakContext {
state.id,
lifetime,
);
states.insert(state);
states.insert(MultiState::new(state));
self.widget_states.insert(self.current_id, states);
self.current_state_index += 1;
self.last_state_type_id = Some(state_type_id);
}
return self.get_state();
}
fn get_state<T: resources::Resource + Clone + PartialEq>(&self) -> Option<T> {
if self.widget_states.contains_key(&self.current_id) {
let states = self.widget_states.get(&self.current_id).unwrap();
if let Ok(state) = states.get::<MultiState<T>>() {
return Some(state.get(self.current_state_index - 1).clone());
}
}
return None;
}
fn insert_state_lifetime(
lifetimes: &mut HashMap<
crate::Index,
......@@ -177,16 +231,6 @@ impl KayakContext {
}
}
fn get_state<T: resources::Resource + Clone + PartialEq>(&self) -> Option<T> {
if self.widget_states.contains_key(&self.current_id) {
let states = self.widget_states.get(&self.current_id).unwrap();
if let Ok(state) = states.get::<T>() {
return Some(state.clone());
}
}
return None;
}
pub fn set_global_state<T: resources::Resource>(&mut self, state: T) {
self.global_state.insert(state);
}
......@@ -222,7 +266,11 @@ impl KayakContext {
pub fn process_events(&mut self, input_events: Vec<InputEvent>) {
let mut events_stream = Vec::new();
for (index, _) in self.widget_manager.nodes.iter() {
let mut was_click_event = false;
let mut was_focus_event = false;
for index in self.widget_manager.node_tree.down_iter() {
if let Some(layout) = self.widget_manager.layout_cache.rect.get(&index) {
for input_event in input_events.iter() {
match input_event {
......@@ -278,6 +326,7 @@ impl KayakContext {
self.last_mouse_position = *point;
}
InputEvent::MouseLeftClick => {
was_click_event = true;
if layout.contains(&self.last_mouse_position) {
let click_event = Event {
target: index,
......@@ -285,6 +334,28 @@ impl KayakContext {
..Event::default()
};
events_stream.push(click_event);
if let Some(widget) =
self.widget_manager.current_widgets.get(index).unwrap()
{
if widget.focusable() {
was_focus_event = true;
if let Some(widget) =
self.widget_manager.current_widgets.get(index).unwrap()
{
dbg!(index);
dbg!(widget.get_name());
}
let focus_event = Event {
target: index,
event_type: EventType::Focus,
..Event::default()
};
events_stream.push(focus_event);
self.last_focus = self.current_focus;
self.current_focus = index;
}
}
}
}
InputEvent::CharEvent { c } => events_stream.push(Event {
......@@ -302,6 +373,25 @@ impl KayakContext {
}
}
if was_click_event && !was_focus_event && self.current_focus != Index::default() {
let focus_event = Event {
target: self.current_focus,
event_type: EventType::Blur,
..Event::default()
};
events_stream.push(focus_event);
self.current_focus = Index::default();
}
if was_click_event && was_focus_event && self.current_focus != self.last_focus {
let focus_event = Event {
target: self.last_focus,
event_type: EventType::Blur,
..Event::default()
};
events_stream.push(focus_event);
}
// Propagate Events
for event in events_stream.iter_mut() {
let mut parents: Vec<Index> = Vec::new();
......@@ -312,6 +402,14 @@ impl KayakContext {
target_widget.on_event(self, event);
self.widget_manager.repossess(target_widget);
// Event debugging
// if matches!(event.event_type, EventType::Click) {
// dbg!("Click event!");
// let widget = self.widget_manager.take(event.target);
// dbg!(widget.get_name());
// self.widget_manager.repossess(widget);
// }
// TODO: Restore propagation.
// for parent in parents {
// if event.should_propagate {
......
......@@ -23,6 +23,8 @@ pub enum EventType {
Hover,
MouseIn,
MouseOut,
Focus,
Blur,
CharInput { c: char },
KeyboardInput { key: KeyCode },
}
......@@ -18,6 +18,10 @@ impl Widget for Fragment {
self.id
}
fn focusable(&self) -> bool {
false
}
fn set_id(&mut self, id: Index) {
self.id = id;
}
......
......@@ -15,6 +15,7 @@ pub mod tree;
mod vec;
pub mod widget;
pub mod widget_manager;
mod multi_state;
use std::sync::{Arc, RwLock};
......
// Handles storing more than one item in state..
pub struct MultiState<T> {
pub data: Vec<T>,
}
impl<T> MultiState<T> {
pub fn new(first_item: T) -> Self {
Self {
data: vec![first_item],
}
}
pub fn get_or_add(&mut self, initial_value: T, index: &mut usize) -> &T {
if !self.data.get(*index).is_some() {
self.data.push(initial_value);
}
let item = &self.data[*index];
*index += 1;
item
}
pub fn get(&self, index: usize) -> &T {
let item = &self.data[index];
item
}
}
......@@ -4,6 +4,8 @@ use crate::layout_cache::Space;
pub enum RenderCommand {
Empty,
Window,
/// Represents a node that has no renderable object but contributes to the layout.
Layout,
Clip,
Quad,
Text {
......
......@@ -60,6 +60,7 @@ impl From<&Style> for RenderPrimitive {
match render_command {
RenderCommand::Window => Self::Empty,
RenderCommand::Empty => Self::Empty,
RenderCommand::Layout => Self::Empty,
RenderCommand::Clip => Self::Clip {
layout: Rect::default(),
},
......
......@@ -43,6 +43,10 @@ where
self.id
}
fn focusable(&self) -> bool {
false
}
fn set_id(&mut self, id: Index) {
self.id = id;
}
......
......@@ -3,6 +3,7 @@ use as_any::AsAny;
use crate::{context::KayakContext, styles::Style, Event, Index};
pub trait Widget: std::fmt::Debug + AsAny + Send + Sync {
fn focusable(&self) -> bool;
fn get_id(&self) -> Index;
fn set_id(&mut self, id: Index);
fn get_styles(&self) -> Option<Style>;
......
......@@ -199,10 +199,6 @@ impl WidgetManager {
.unwrap_or(vec![]);
let styles = styles.unwrap_or(default_styles.clone());
if matches!(styles.render_command.resolve(), RenderCommand::Empty) {
continue;
}
let mut node = NodeBuilder::empty()
.with_id(dirty_node_index)
.with_styles(styles)
......
......@@ -20,6 +20,10 @@ impl Widget for Test {
todo!()
}
fn focusable(&self) -> bool {
false
}
fn set_id(&mut self, _id: Index) {
todo!()
}
......
......@@ -3,7 +3,17 @@ use proc_macro_error::emit_error;
use quote::{quote, ToTokens};
use syn::spanned::Spanned;
pub fn create_function_widget(f: syn::ItemFn) -> TokenStream {
pub struct WidgetArguments {
pub focusable: bool,
}
impl Default for WidgetArguments {
fn default() -> Self {
Self { focusable: false }
}
}
pub fn create_function_widget(f: syn::ItemFn, widget_arguments: WidgetArguments) -> TokenStream {
let struct_name = f.sig.ident;
let (impl_generics, ty_generics, where_clause) = f.sig.generics.split_for_impl();
let inputs = f.sig.inputs;
......@@ -15,6 +25,8 @@ pub fn create_function_widget(f: syn::ItemFn) -> TokenStream {
#[cfg(not(feature = "internal"))]
let kayak_core = quote! { kayak_ui::core };
let focusable = widget_arguments.focusable;
let mut input_names: Vec<_> = inputs
.iter()
.filter_map(|argument| match argument {
......@@ -147,6 +159,10 @@ pub fn create_function_widget(f: syn::ItemFn) -> TokenStream {
self.id
}
fn focusable(&self) -> bool {
#focusable
}
fn set_id(&mut self, id: #kayak_core::Index) {
self.id = id;
}
......
......@@ -11,6 +11,7 @@ mod partial_eq;
mod widget;
mod widget_attributes;
use function_component::WidgetArguments;
use partial_eq::impl_dyn_partial_eq;
use proc_macro::TokenStream;
use proc_macro_error::proc_macro_error;
......@@ -60,9 +61,16 @@ pub fn constructor(input: TokenStream) -> TokenStream {
#[proc_macro_attribute]
#[proc_macro_error]
pub fn widget(_attr: TokenStream, item: TokenStream) -> TokenStream {
pub fn widget(args: TokenStream, item: TokenStream) -> TokenStream {
let mut widget_args = WidgetArguments::default();
if !args.is_empty() {
// Parse stuff..
let parsed = args.to_string();
widget_args.focusable = parsed.contains("focusable");
}
let f = parse_macro_input!(item as syn::ItemFn);
function_component::create_function_widget(f)
function_component::create_function_widget(f, widget_args)
}
#[proc_macro_derive(DynPartialEq)]
......
use kayak_ui::core::{
render_command::RenderCommand,
rsx,
styles::{Style, StyleProp, Units},
widget, Bound, Color, EventType, MutableBound, OnEvent,
......@@ -33,8 +34,16 @@ impl std::fmt::Debug for OnChange {
}
}
#[widget]
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Focus(pub bool);
#[widget(focusable)]
pub fn TextBox(value: String, on_change: Option<OnChange>) {
*styles = Some(Style {
render_command: StyleProp::Value(RenderCommand::Layout),
..styles.clone().unwrap_or_default()
});
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)),
......@@ -45,10 +54,15 @@ pub fn TextBox(value: String, on_change: Option<OnChange>) {
};
let internal_value = context.create_state("".to_string()).unwrap();
let has_focus = context.create_state(Focus(false)).unwrap();
let cloned_on_change = on_change.clone();
let cloned_has_focus = has_focus.clone();
self.on_event = Some(OnEvent::new(move |_, event| match event.event_type {
EventType::CharInput { c } => {
if !cloned_has_focus.get().0 {
return;
}
let mut current_value = internal_value.get();
if c == '\u{8}' {
if current_value.len() > 0 {
......@@ -66,9 +80,26 @@ pub fn TextBox(value: String, on_change: Option<OnChange>) {
}
internal_value.set(current_value);
}
EventType::Focus => {
dbg!("Has focus!");
cloned_has_focus.set(Focus(true))
}
EventType::Blur => {
dbg!("Lost focus!");
cloned_has_focus.set(Focus(false))
}
_ => {}
}));
// let cloned_has_focus = has_focus.clone();
// let on_event = Some(OnEvent::new(move |_, event| match event.event_type {
// EventType::Focus => {
// dbg!("Has focus!");
// cloned_has_focus.set(Focus(true))
// }
// _ => {}
// }));
let value = value.clone();
rsx! {
<Background styles={Some(background_styles)}>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment