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)),
radius: 10.
radius: 10.,
events: Default::default()
})));
root.children.push(Arc::new(RwLock::new(Layout {
style: Style {
@ -48,7 +49,8 @@ fn main() {
}
},
fill: Paint::color(Color::rgb(0, 255, 0)),
radius: 5.
radius: 5.,
events: Default::default()
})),
Arc::new(RwLock::new(nodes::primitives::Rectangle {
style: Style {
@ -62,12 +64,13 @@ fn main() {
}
},
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 {
overflow: nodes::Overflow::Visible,
layout: TaffyStyle {
@ -79,8 +82,21 @@ fn main() {
}
},
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));
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};
use crate::SharedNode;
pub mod handler;
#[derive(Clone, Debug)]
pub struct NodeEvent {

View file

@ -9,7 +9,7 @@ use femtovg::{Canvas, Color};
use glutin::surface::Surface;
use glutin::{context::PossiblyCurrentContext, display::Display};
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 winit::event::{Event, WindowEvent, ModifiersState, DeviceId};
use winit::event_loop::{ControlFlow, EventLoop};
@ -126,9 +126,8 @@ pub fn run_event_loop(root_node: SharedNode) -> ! {
})
};
for node in path.iter().rev() {
node.write().unwrap().on_event(&event);
}
run_event_handlers(path, event);
window.request_redraw();
}
},
WindowEvent::DroppedFile(path) => {},
@ -147,7 +146,8 @@ pub fn run_event_loop(root_node: SharedNode) -> ! {
path: strong_focus_path.clone(),
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 {
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 }
};
for node in strong_focus_path.iter().rev() {
node.write().unwrap().on_event(&focus_event);
}
run_event_handlers(strong_focus_path, focus_event);
window.request_redraw();
},
None => {}
};
@ -201,9 +200,8 @@ pub fn run_event_loop(root_node: SharedNode) -> ! {
}
};
for node in path.iter().rev() {
node.write().unwrap().on_event(&event);
}
window.request_redraw();
run_event_handlers(path, event);
},
None => {}
}

View file

@ -1,19 +1,21 @@
use std::fmt::{Debug, Formatter};
use crate::nodes::{Node, NodeChildren, Style};
use crate::{nodes::{Node, NodeChildren, Style}, events::handler::EventHandlerDatabase};
use taffy::style::Dimension;
/// A simple layout node which contains children.
#[derive(Clone, Default)]
#[derive(Default)]
pub struct Layout {
pub style: Style,
pub children: NodeChildren
pub children: NodeChildren,
pub events: EventHandlerDatabase
}
impl Layout {
pub fn new(children: NodeChildren) -> Layout {
Layout {
style: Style::default(),
children
children,
events: EventHandlerDatabase::default()
}
}
}
@ -39,5 +41,11 @@ impl Node for Layout {
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;
use taffy::Taffy;
use crate::events::Location;
use crate::{NodeLayoutMap, NodePtr, CurrentRenderer};
type SharedTNode = Arc<RwLock<dyn Node>>;
use crate::events::handler::InnerEventHandlerDataset;
use crate::{NodeLayoutMap, NodePtr, CurrentRenderer, SharedNode};
pub struct RenderContext {
pub canvas: Canvas<CurrentRenderer>,
@ -51,7 +50,7 @@ pub struct Style {
pub overflow: Overflow
}
type NodeChildren = Vec<SharedTNode>;
type NodeChildren = Vec<SharedNode>;
pub trait Node: Debug {
/// 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.
/// 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!
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.
/// 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) {}
}
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 children = node_borrowed.children();
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 = match taffy_node {
Some(taffy_node) => taffy_node,
@ -133,7 +168,7 @@ pub fn layout_recursively(node: &SharedTNode, context: &mut RenderContext) -> ta
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 styles = read_node.style();
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 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 style: Style,
pub fill: Paint,
pub radius: f32
pub radius: f32,
pub events: EventHandlerDatabase
}
impl Rectangle {
@ -14,7 +15,8 @@ impl Rectangle {
Rectangle {
style: Style::default(),
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())
}
}