mirror of
https://github.com/danbulant/mangui
synced 2026-06-19 22:31:03 +00:00
working events
This commit is contained in:
parent
ef22786b66
commit
7609ab3de3
7 changed files with 159 additions and 38 deletions
|
|
@ -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
52
ui/src/events/handler.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 => {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue