working events

This commit is contained in:
Daniel Bulant 2023-10-25 20:41:40 +02:00
parent ef22786b66
commit 7609ab3de3
7 changed files with 159 additions and 38 deletions

View file

@ -18,7 +18,8 @@ fn main() {
} }
}, },
fill: Paint::color(Color::rgb(255, 0, 0)), fill: Paint::color(Color::rgb(255, 0, 0)),
radius: 10. radius: 10.,
events: Default::default()
}))); })));
root.children.push(Arc::new(RwLock::new(Layout { root.children.push(Arc::new(RwLock::new(Layout {
style: Style { style: Style {
@ -48,7 +49,8 @@ fn main() {
} }
}, },
fill: Paint::color(Color::rgb(0, 255, 0)), fill: Paint::color(Color::rgb(0, 255, 0)),
radius: 5. radius: 5.,
events: Default::default()
})), })),
Arc::new(RwLock::new(nodes::primitives::Rectangle { Arc::new(RwLock::new(nodes::primitives::Rectangle {
style: Style { style: Style {
@ -62,12 +64,13 @@ fn main() {
} }
}, },
fill: Paint::color(Color::rgb(0, 255, 255)), fill: Paint::color(Color::rgb(0, 255, 255)),
radius: 5. radius: 5.,
events: Default::default()
})) }))
] ],
events: Default::default()
}))); })));
root.children.push(Arc::new(RwLock::new(nodes::primitives::Rectangle { let right_node = Arc::new(RwLock::new(nodes::primitives::Rectangle {
style: Style { style: Style {
overflow: nodes::Overflow::Visible, overflow: nodes::Overflow::Visible,
layout: TaffyStyle { layout: TaffyStyle {
@ -79,8 +82,21 @@ fn main() {
} }
}, },
fill: Paint::color(Color::rgb(0, 0, 255)), fill: Paint::color(Color::rgb(0, 0, 255)),
radius: 0. radius: 0.,
}))); events: Default::default()
}));
root.children.push(right_node.clone());
right_node.clone().write().unwrap().events.add_handler(Box::new(move |event| {
match event.event {
mangui::events::InnerEvent::MouseDown(_) => {
right_node.write().unwrap().fill = Paint::color(Color::rgb(255, 0, 255));
},
mangui::events::InnerEvent::MouseUp(_) => {
right_node.write().unwrap().fill = Paint::color(Color::rgb(0, 0, 255));
},
_ => {}
}
}));
let groot: SharedNode = Arc::new(RwLock::new(root)); let groot: SharedNode = Arc::new(RwLock::new(root));
mangui::run_event_loop(groot); mangui::run_event_loop(groot);

52
ui/src/events/handler.rs Normal file
View file

@ -0,0 +1,52 @@
use std::{collections::HashMap, fmt::Debug, sync::{Arc, Mutex}};
// use crate::nodes::Node;
use super::NodeEvent;
/// A node event handler
pub type EventHandler = dyn FnMut(&NodeEvent);
pub type InnerEventHandlerDataset = Arc<Mutex<HashMap<usize, Arc<Mutex<Box<EventHandler>>>>>>;
/// An event handler database that allows adding and removing event handlers and running them all.
/// **IMPORTANT**: handlers are locked during event execution, so you can't access handlers from within an event handler.
/// Debug output of EventHandlerDatabase is changed as to prevent deadlocks - handlers are not printed.
/// Although Arc<Mutex> is used, you may be able to delay changing handlers by using thread/some async runtime. I didn't check it though :)
#[derive(Default)]
pub struct EventHandlerDatabase {
pub handlers: InnerEventHandlerDataset,
next_token: usize,
}
impl Debug for EventHandlerDatabase {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("EventHandlerDatabase")
.field("handlers", &"Disabled as it's too easy to get a deadlock :(")
.field("next_token", &self.next_token)
.finish()
}
}
impl EventHandlerDatabase {
/// Creates a new event handler database with the given handlers.
/// If you don't need to add or remove handlers, you can use [EventHandlerDatabase::default] instead.
pub fn new(handlers: Vec<Box<EventHandler>>) -> Self {
let mut db = Self::default();
for handler in handlers {
db.add_handler(handler);
}
db
}
/// Adds an event handler to the database and returns a token that can be used to remove it.
pub fn add_handler(&mut self, handler: Box<EventHandler>) -> usize {
let token = self.next_token;
self.next_token += 1;
self.handlers.lock().unwrap().insert(token, Arc::new(Mutex::new(handler)));
token
}
/// Removes an event handler from the database using the token returned by [EventHandlerDatabase::add_handler].
pub fn remove_handler(&mut self, token: usize) {
self.handlers.lock().unwrap().remove(&token);
}
}

View file

@ -5,6 +5,7 @@ use winit::event::ElementState;
pub use winit::event::{TouchPhase, MouseScrollDelta, DeviceId, ModifiersState, VirtualKeyCode, ScanCode, MouseButton}; pub use winit::event::{TouchPhase, MouseScrollDelta, DeviceId, ModifiersState, VirtualKeyCode, ScanCode, MouseButton};
use crate::SharedNode; use crate::SharedNode;
pub mod handler;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct NodeEvent { pub struct NodeEvent {

View file

@ -9,7 +9,7 @@ use femtovg::{Canvas, Color};
use glutin::surface::Surface; use glutin::surface::Surface;
use glutin::{context::PossiblyCurrentContext, display::Display}; use glutin::{context::PossiblyCurrentContext, display::Display};
use glutin_winit::DisplayBuilder; use glutin_winit::DisplayBuilder;
use nodes::get_element_at; use nodes::{get_element_at, run_event_handlers, run_single_event_handlers};
use raw_window_handle::HasRawWindowHandle; use raw_window_handle::HasRawWindowHandle;
use winit::event::{Event, WindowEvent, ModifiersState, DeviceId}; use winit::event::{Event, WindowEvent, ModifiersState, DeviceId};
use winit::event_loop::{ControlFlow, EventLoop}; use winit::event_loop::{ControlFlow, EventLoop};
@ -126,9 +126,8 @@ pub fn run_event_loop(root_node: SharedNode) -> ! {
}) })
}; };
for node in path.iter().rev() { run_event_handlers(path, event);
node.write().unwrap().on_event(&event); window.request_redraw();
}
} }
}, },
WindowEvent::DroppedFile(path) => {}, WindowEvent::DroppedFile(path) => {},
@ -147,7 +146,8 @@ pub fn run_event_loop(root_node: SharedNode) -> ! {
path: strong_focus_path.clone(), path: strong_focus_path.clone(),
event: if focused { events::InnerEvent::Focus } else { events::InnerEvent::Blur } event: if focused { events::InnerEvent::Focus } else { events::InnerEvent::Blur }
}; };
strong_focus_path.last().unwrap().write().unwrap().on_event(&focus_event); // strong_focus_path.last().unwrap().write().unwrap().on_event(&focus_event);
run_single_event_handlers(strong_focus_path.last().unwrap().clone(), focus_event);
let focus_event = NodeEvent { let focus_event = NodeEvent {
target: strong_focus_path.last().unwrap().clone(), target: strong_focus_path.last().unwrap().clone(),
@ -155,9 +155,8 @@ pub fn run_event_loop(root_node: SharedNode) -> ! {
event: if focused { events::InnerEvent::FocusIn } else { events::InnerEvent::FocusOut } event: if focused { events::InnerEvent::FocusIn } else { events::InnerEvent::FocusOut }
}; };
for node in strong_focus_path.iter().rev() { run_event_handlers(strong_focus_path, focus_event);
node.write().unwrap().on_event(&focus_event); window.request_redraw();
}
}, },
None => {} None => {}
}; };
@ -201,9 +200,8 @@ pub fn run_event_loop(root_node: SharedNode) -> ! {
} }
}; };
for node in path.iter().rev() { window.request_redraw();
node.write().unwrap().on_event(&event); run_event_handlers(path, event);
}
}, },
None => {} None => {}
} }

View file

@ -1,19 +1,21 @@
use std::fmt::{Debug, Formatter}; use std::fmt::{Debug, Formatter};
use crate::nodes::{Node, NodeChildren, Style}; use crate::{nodes::{Node, NodeChildren, Style}, events::handler::EventHandlerDatabase};
use taffy::style::Dimension; use taffy::style::Dimension;
/// A simple layout node which contains children. /// A simple layout node which contains children.
#[derive(Clone, Default)] #[derive(Default)]
pub struct Layout { pub struct Layout {
pub style: Style, pub style: Style,
pub children: NodeChildren pub children: NodeChildren,
pub events: EventHandlerDatabase
} }
impl Layout { impl Layout {
pub fn new(children: NodeChildren) -> Layout { pub fn new(children: NodeChildren) -> Layout {
Layout { Layout {
style: Style::default(), style: Style::default(),
children children,
events: EventHandlerDatabase::default()
} }
} }
} }
@ -39,5 +41,11 @@ impl Node for Layout {
self.style.layout.size.height = Dimension::Points(height); self.style.layout.size.height = Dimension::Points(height);
} }
fn on_event(&mut self, event: &crate::events::NodeEvent) {} // fn on_event(&mut self, event: &crate::events::NodeEvent) {
// // dbg!("layout", self.children.len(), &event.event);
// }
fn event_handlers(&self) -> Option<crate::events::handler::InnerEventHandlerDataset> {
Some(self.events.handlers.clone())
}
} }

View file

@ -8,9 +8,8 @@ use taffy::layout::Layout;
pub use taffy::style::Style as TaffyStyle; pub use taffy::style::Style as TaffyStyle;
use taffy::Taffy; use taffy::Taffy;
use crate::events::Location; use crate::events::Location;
use crate::{NodeLayoutMap, NodePtr, CurrentRenderer}; use crate::events::handler::InnerEventHandlerDataset;
use crate::{NodeLayoutMap, NodePtr, CurrentRenderer, SharedNode};
type SharedTNode = Arc<RwLock<dyn Node>>;
pub struct RenderContext { pub struct RenderContext {
pub canvas: Canvas<CurrentRenderer>, pub canvas: Canvas<CurrentRenderer>,
@ -51,7 +50,7 @@ pub struct Style {
pub overflow: Overflow pub overflow: Overflow
} }
type NodeChildren = Vec<SharedTNode>; type NodeChildren = Vec<SharedNode>;
pub trait Node: Debug { pub trait Node: Debug {
/// Return style. Usually, you just want self.style. /// Return style. Usually, you just want self.style.
@ -67,8 +66,19 @@ pub trait Node: Debug {
/// Called when an event happens on the node. This is called after the children have been called. /// Called when an event happens on the node. This is called after the children have been called.
/// Beware! Events include a path and target with [Arc<RwLock<Node>>]s, but you already have a write lock for this node! /// Beware! Events include a path and target with [Arc<RwLock<Node>>]s, but you already have a write lock for this node!
/// Remember to check if the node is the same as self, and if it is, use self instead of the node in the path to prevent deadlocks! /// Remember to check if the node is the same as self, and if it is, use self instead of the node in the path to prevent deadlocks!
fn on_event(&mut self, _event: &crate::events::NodeEvent) {} // fn on_event(&mut self, _event: &crate::events::NodeEvent) {}
/// Returns the event handlers of the node. If the node has no event handlers, return None.
/// Use [EventHandlerDatabase] to manage this, and return it's handlers property.
/// Example code:
/// ```rust
/// fn event_handlers(&self) -> Option<InnerEventHandlerDataset> {
/// Some(self.events.handlers.clone())
/// }
/// ```
fn event_handlers(&self) -> Option<InnerEventHandlerDataset> {
None
}
/// Called when the size of window changes on the root node. Layouts do implement this. /// Called when the size of window changes on the root node. Layouts do implement this.
/// Is an optional function instead of another trait because of missing support for trait upcasting /// Is an optional function instead of another trait because of missing support for trait upcasting
@ -76,7 +86,32 @@ pub trait Node: Debug {
fn resize(&mut self, _width: f32, _height: f32) {} fn resize(&mut self, _width: f32, _height: f32) {}
} }
pub fn get_element_at(node: &SharedTNode, context: &RenderContext, location: Location) -> Option<Vec<SharedTNode>> { /// Runs event handlers for the given path
/// The target element should be the last one in path (event handlers are ran in reverse order)
pub fn run_event_handlers(path: Vec<SharedNode>, event: crate::events::NodeEvent) {
for node in path.iter().rev() {
let mut node = node.read().unwrap();
if let Some(handlers) = node.event_handlers() {
drop(node);
for handler in handlers.lock().unwrap().values_mut() {
handler.lock().unwrap()(&event);
}
}
}
}
pub fn run_single_event_handlers(node: SharedNode, event: crate::events::NodeEvent) {
let mut node = node.read().unwrap();
if let Some(handlers) = node.event_handlers() {
drop(node);
for handler in handlers.lock().unwrap().values_mut() {
handler.lock().unwrap()(&event);
}
}
}
/// Attempts to get path to the element at the target location. Assumes elements are always inside their parents.
pub fn get_element_at(node: &SharedNode, context: &RenderContext, location: Location) -> Option<Vec<SharedNode>> {
let node_borrowed = node.read().unwrap(); let node_borrowed = node.read().unwrap();
let children = node_borrowed.children(); let children = node_borrowed.children();
let taffy_node = context.node_layout.get(node); let taffy_node = context.node_layout.get(node);
@ -106,7 +141,7 @@ pub fn get_element_at(node: &SharedTNode, context: &RenderContext, location: Loc
} }
} }
pub fn layout_recursively(node: &SharedTNode, context: &mut RenderContext) -> taffy::node::Node { pub fn layout_recursively(node: &SharedNode, context: &mut RenderContext) -> taffy::node::Node {
let taffy_node = context.node_layout.get(node); let taffy_node = context.node_layout.get(node);
let taffy_node = match taffy_node { let taffy_node = match taffy_node {
Some(taffy_node) => taffy_node, Some(taffy_node) => taffy_node,
@ -133,7 +168,7 @@ pub fn layout_recursively(node: &SharedTNode, context: &mut RenderContext) -> ta
taffy_node taffy_node
} }
pub fn render_recursively(node: &SharedTNode, context: &mut RenderContext) { pub fn render_recursively(node: &SharedNode, context: &mut RenderContext) {
let read_node = node.read().unwrap(); let read_node = node.read().unwrap();
let styles = read_node.style(); let styles = read_node.style();
let taffy_node = context.node_layout.get(node).unwrap(); let taffy_node = context.node_layout.get(node).unwrap();

View file

@ -1,12 +1,13 @@
use femtovg::{Color, Paint, Path, Renderer}; use femtovg::{Color, Paint, Path};
use taffy::layout::Layout; use taffy::layout::Layout;
use crate::nodes::{Node, NodeChildren, RenderContext, Style}; use crate::{nodes::{Node, NodeChildren, RenderContext, Style}, events::handler::EventHandlerDatabase};
#[derive(Clone, Default, Debug)] #[derive(Default, Debug)]
pub struct Rectangle { pub struct Rectangle {
pub style: Style, pub style: Style,
pub fill: Paint, pub fill: Paint,
pub radius: f32 pub radius: f32,
pub events: EventHandlerDatabase
} }
impl Rectangle { impl Rectangle {
@ -14,7 +15,8 @@ impl Rectangle {
Rectangle { Rectangle {
style: Style::default(), style: Style::default(),
fill: Paint::color(Color::rgb(0, 0, 0)), fill: Paint::color(Color::rgb(0, 0, 0)),
radius: 0. radius: 0.,
events: EventHandlerDatabase::default()
} }
} }
} }
@ -41,5 +43,14 @@ impl Node for Rectangle {
); );
} }
fn on_event(&mut self, event: &crate::events::NodeEvent) {} // fn on_event(&mut self, event: &crate::events::NodeEvent) {
// // dbg!("rect", &self.fill, &event.event);
// self.events.handlers.lock().unwrap().values_mut().for_each(|handler| {
// handler.lock().unwrap()(event, self);
// });
// }
fn event_handlers(&self) -> Option<crate::events::handler::InnerEventHandlerDataset> {
Some(self.events.handlers.clone())
}
} }