Add components example
Re-arrange generics on mouse event handler Add TypeTag struct for dynamically tagged components
This commit is contained in:
parent
5ce7ccac32
commit
e5eed29c72
49 changed files with 585 additions and 155 deletions
|
@ -3280,7 +3280,11 @@ impl<'a, 'b, V: View> ViewContext<'a, 'b, V> {
|
|||
}
|
||||
|
||||
pub fn mouse_state<Tag: 'static>(&self, region_id: usize) -> MouseState {
|
||||
let region_id = MouseRegionId::new::<Tag>(self.view_id, region_id);
|
||||
self.mouse_state_dynamic(TypeTag::new::<Tag>(), region_id)
|
||||
}
|
||||
|
||||
pub fn mouse_state_dynamic(&self, tag: TypeTag, region_id: usize) -> MouseState {
|
||||
let region_id = MouseRegionId::new(tag, self.view_id, region_id);
|
||||
MouseState {
|
||||
hovered: self.window.hovered_region_ids.contains(®ion_id),
|
||||
clicked: if let Some((clicked_region_id, button)) = self.window.clicked_region {
|
||||
|
@ -3321,6 +3325,36 @@ impl<'a, 'b, V: View> ViewContext<'a, 'b, V> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct TypeTag {
|
||||
tag: TypeId,
|
||||
#[cfg(debug_assertions)]
|
||||
tag_type_name: &'static str,
|
||||
}
|
||||
|
||||
impl TypeTag {
|
||||
pub fn new<Tag: 'static>() -> Self {
|
||||
Self {
|
||||
tag: TypeId::of::<Tag>(),
|
||||
#[cfg(debug_assertions)]
|
||||
tag_type_name: std::any::type_name::<Tag>(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dynamic(tag: TypeId, #[cfg(debug_assertions)] type_name: &'static str) -> Self {
|
||||
Self {
|
||||
tag,
|
||||
#[cfg(debug_assertions)]
|
||||
tag_type_name: type_name,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
pub(crate) fn type_name(&self) -> &'static str {
|
||||
self.tag_type_name
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> BorrowAppContext for ViewContext<'_, '_, V> {
|
||||
fn read_with<T, F: FnOnce(&AppContext) -> T>(&self, f: F) -> T {
|
||||
BorrowAppContext::read_with(&*self.window_context, f)
|
||||
|
@ -5171,7 +5205,7 @@ mod tests {
|
|||
fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
|
||||
enum Handler {}
|
||||
let mouse_down_count = self.mouse_down_count.clone();
|
||||
MouseEventHandler::<Handler, _>::new(0, cx, |_, _| Empty::new())
|
||||
MouseEventHandler::new::<Handler, _>(0, cx, |_, _| Empty::new())
|
||||
.on_down(MouseButton::Left, move |_, _, _| {
|
||||
mouse_down_count.fetch_add(1, SeqCst);
|
||||
})
|
||||
|
|
|
@ -193,11 +193,11 @@ pub trait Element<V: View>: 'static {
|
|||
Resizable::new(self.into_any(), side, size, on_resize)
|
||||
}
|
||||
|
||||
fn mouse<Tag>(self, region_id: usize) -> MouseEventHandler<Tag, V>
|
||||
fn mouse<Tag: 'static>(self, region_id: usize) -> MouseEventHandler<V>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
MouseEventHandler::for_child(self.into_any(), region_id)
|
||||
MouseEventHandler::for_child::<Tag>(self.into_any(), region_id)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -38,6 +38,15 @@ pub struct ContainerStyle {
|
|||
pub cursor: Option<CursorStyle>,
|
||||
}
|
||||
|
||||
impl ContainerStyle {
|
||||
pub fn fill(color: Color) -> Self {
|
||||
Self {
|
||||
background_color: Some(color),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Container<V: View> {
|
||||
child: AnyElement<V>,
|
||||
style: ContainerStyle,
|
||||
|
|
|
@ -11,12 +11,12 @@ use crate::{
|
|||
MouseHover, MouseMove, MouseMoveOut, MouseScrollWheel, MouseUp, MouseUpOut,
|
||||
},
|
||||
AnyElement, Element, EventContext, LayoutContext, MouseRegion, MouseState, PaintContext,
|
||||
SceneBuilder, SizeConstraint, View, ViewContext,
|
||||
SceneBuilder, SizeConstraint, TypeTag, View, ViewContext,
|
||||
};
|
||||
use serde_json::json;
|
||||
use std::{marker::PhantomData, ops::Range};
|
||||
use std::ops::Range;
|
||||
|
||||
pub struct MouseEventHandler<Tag: 'static, V: View> {
|
||||
pub struct MouseEventHandler<V: View> {
|
||||
child: AnyElement<V>,
|
||||
region_id: usize,
|
||||
cursor_style: Option<CursorStyle>,
|
||||
|
@ -26,13 +26,13 @@ pub struct MouseEventHandler<Tag: 'static, V: View> {
|
|||
notify_on_click: bool,
|
||||
above: bool,
|
||||
padding: Padding,
|
||||
_tag: PhantomData<Tag>,
|
||||
tag: TypeTag,
|
||||
}
|
||||
|
||||
/// Element which provides a render_child callback with a MouseState and paints a mouse
|
||||
/// region under (or above) it for easy mouse event handling.
|
||||
impl<Tag, V: View> MouseEventHandler<Tag, V> {
|
||||
pub fn for_child(child: impl Element<V>, region_id: usize) -> Self {
|
||||
impl<V: View> MouseEventHandler<V> {
|
||||
pub fn for_child<Tag: 'static>(child: impl Element<V>, region_id: usize) -> Self {
|
||||
Self {
|
||||
child: child.into_any(),
|
||||
region_id,
|
||||
|
@ -43,16 +43,19 @@ impl<Tag, V: View> MouseEventHandler<Tag, V> {
|
|||
hoverable: false,
|
||||
above: false,
|
||||
padding: Default::default(),
|
||||
_tag: PhantomData,
|
||||
tag: TypeTag::new::<Tag>(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new<E, F>(region_id: usize, cx: &mut ViewContext<V>, render_child: F) -> Self
|
||||
pub fn new<Tag: 'static, E>(
|
||||
region_id: usize,
|
||||
cx: &mut ViewContext<V>,
|
||||
render_child: impl FnOnce(&mut MouseState, &mut ViewContext<V>) -> E,
|
||||
) -> Self
|
||||
where
|
||||
E: Element<V>,
|
||||
F: FnOnce(&mut MouseState, &mut ViewContext<V>) -> E,
|
||||
{
|
||||
let mut mouse_state = cx.mouse_state::<Tag>(region_id);
|
||||
let mut mouse_state = cx.mouse_state_dynamic(TypeTag::new::<Tag>(), region_id);
|
||||
let child = render_child(&mut mouse_state, cx).into_any();
|
||||
let notify_on_hover = mouse_state.accessed_hovered();
|
||||
let notify_on_click = mouse_state.accessed_clicked();
|
||||
|
@ -66,19 +69,46 @@ impl<Tag, V: View> MouseEventHandler<Tag, V> {
|
|||
hoverable: true,
|
||||
above: false,
|
||||
padding: Default::default(),
|
||||
_tag: PhantomData,
|
||||
tag: TypeTag::new::<Tag>(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_dynamic(
|
||||
tag: TypeTag,
|
||||
region_id: usize,
|
||||
cx: &mut ViewContext<V>,
|
||||
render_child: impl FnOnce(&mut MouseState, &mut ViewContext<V>) -> AnyElement<V>,
|
||||
) -> Self {
|
||||
let mut mouse_state = cx.mouse_state_dynamic(tag, region_id);
|
||||
let child = render_child(&mut mouse_state, cx);
|
||||
let notify_on_hover = mouse_state.accessed_hovered();
|
||||
let notify_on_click = mouse_state.accessed_clicked();
|
||||
Self {
|
||||
child,
|
||||
region_id,
|
||||
cursor_style: None,
|
||||
handlers: Default::default(),
|
||||
notify_on_hover,
|
||||
notify_on_click,
|
||||
hoverable: true,
|
||||
above: false,
|
||||
padding: Default::default(),
|
||||
tag,
|
||||
}
|
||||
}
|
||||
|
||||
/// Modifies the MouseEventHandler to render the MouseRegion above the child element. Useful
|
||||
/// for drag and drop handling and similar events which should be captured before the child
|
||||
/// gets the opportunity
|
||||
pub fn above<D, F>(region_id: usize, cx: &mut ViewContext<V>, render_child: F) -> Self
|
||||
pub fn above<Tag: 'static, D>(
|
||||
region_id: usize,
|
||||
cx: &mut ViewContext<V>,
|
||||
render_child: impl FnOnce(&mut MouseState, &mut ViewContext<V>) -> D,
|
||||
) -> Self
|
||||
where
|
||||
D: Element<V>,
|
||||
F: FnOnce(&mut MouseState, &mut ViewContext<V>) -> D,
|
||||
{
|
||||
let mut handler = Self::new(region_id, cx, render_child);
|
||||
let mut handler = Self::new::<Tag, _>(region_id, cx, render_child);
|
||||
handler.above = true;
|
||||
handler
|
||||
}
|
||||
|
@ -223,7 +253,8 @@ impl<Tag, V: View> MouseEventHandler<Tag, V> {
|
|||
});
|
||||
}
|
||||
scene.push_mouse_region(
|
||||
MouseRegion::from_handlers::<Tag>(
|
||||
MouseRegion::from_handlers(
|
||||
self.tag,
|
||||
cx.view_id(),
|
||||
self.region_id,
|
||||
hit_bounds,
|
||||
|
@ -236,7 +267,7 @@ impl<Tag, V: View> MouseEventHandler<Tag, V> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<Tag, V: View> Element<V> for MouseEventHandler<Tag, V> {
|
||||
impl<V: View> Element<V> for MouseEventHandler<V> {
|
||||
type LayoutState = ();
|
||||
type PaintState = ();
|
||||
|
||||
|
|
|
@ -95,7 +95,7 @@ impl<V: View> Tooltip<V> {
|
|||
} else {
|
||||
None
|
||||
};
|
||||
let child = MouseEventHandler::<MouseEventHandlerState<Tag>, _>::new(id, cx, |_, _| child)
|
||||
let child = MouseEventHandler::new::<MouseEventHandlerState<Tag>, _>(id, cx, |_, _| child)
|
||||
.on_hover(move |e, _, cx| {
|
||||
let position = e.position;
|
||||
if e.started {
|
||||
|
|
|
@ -72,6 +72,13 @@ pub struct TextStyle {
|
|||
}
|
||||
|
||||
impl TextStyle {
|
||||
pub fn for_color(color: Color) -> Self {
|
||||
Self {
|
||||
color,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn refine(self, refinement: TextStyleRefinement) -> TextStyle {
|
||||
TextStyle {
|
||||
color: refinement.color.unwrap_or(self.color),
|
||||
|
|
|
@ -24,6 +24,7 @@ use crate::{
|
|||
use anyhow::{anyhow, bail, Result};
|
||||
use async_task::Runnable;
|
||||
pub use event::*;
|
||||
use pathfinder_geometry::vector::vec2f;
|
||||
use postage::oneshot;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
|
@ -180,6 +181,16 @@ pub struct WindowOptions<'a> {
|
|||
pub screen: Option<Rc<dyn Screen>>,
|
||||
}
|
||||
|
||||
impl<'a> WindowOptions<'a> {
|
||||
pub fn with_bounds(bounds: Vector2F) -> Self {
|
||||
Self {
|
||||
bounds: WindowBounds::Fixed(RectF::new(vec2f(0., 0.), bounds)),
|
||||
center: true,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TitlebarOptions<'a> {
|
||||
pub title: Option<&'a str>,
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
use crate::{platform::MouseButton, window::WindowContext, EventContext, View, ViewContext};
|
||||
use crate::{
|
||||
platform::MouseButton, window::WindowContext, EventContext, TypeTag, View, ViewContext,
|
||||
};
|
||||
use collections::HashMap;
|
||||
use pathfinder_geometry::rect::RectF;
|
||||
use smallvec::SmallVec;
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
fmt::Debug,
|
||||
mem::Discriminant,
|
||||
rc::Rc,
|
||||
};
|
||||
use std::{any::Any, fmt::Debug, mem::Discriminant, rc::Rc};
|
||||
|
||||
use super::{
|
||||
mouse_event::{
|
||||
|
@ -33,14 +30,27 @@ impl MouseRegion {
|
|||
/// should pass a different (consistent) region_id. If you have one big region that covers your
|
||||
/// whole component, just pass the view_id again.
|
||||
pub fn new<Tag: 'static>(view_id: usize, region_id: usize, bounds: RectF) -> Self {
|
||||
Self::from_handlers::<Tag>(view_id, region_id, bounds, Default::default())
|
||||
Self::from_handlers(
|
||||
TypeTag::new::<Tag>(),
|
||||
view_id,
|
||||
region_id,
|
||||
bounds,
|
||||
Default::default(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn handle_all<Tag: 'static>(view_id: usize, region_id: usize, bounds: RectF) -> Self {
|
||||
Self::from_handlers::<Tag>(view_id, region_id, bounds, HandlerSet::capture_all())
|
||||
Self::from_handlers(
|
||||
TypeTag::new::<Tag>(),
|
||||
view_id,
|
||||
region_id,
|
||||
bounds,
|
||||
HandlerSet::capture_all(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn from_handlers<Tag: 'static>(
|
||||
pub fn from_handlers(
|
||||
tag: TypeTag,
|
||||
view_id: usize,
|
||||
region_id: usize,
|
||||
bounds: RectF,
|
||||
|
@ -49,10 +59,8 @@ impl MouseRegion {
|
|||
Self {
|
||||
id: MouseRegionId {
|
||||
view_id,
|
||||
tag: TypeId::of::<Tag>(),
|
||||
tag,
|
||||
region_id,
|
||||
#[cfg(debug_assertions)]
|
||||
tag_type_name: std::any::type_name::<Tag>(),
|
||||
},
|
||||
bounds,
|
||||
handlers,
|
||||
|
@ -180,20 +188,16 @@ impl MouseRegion {
|
|||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, PartialOrd, Ord)]
|
||||
pub struct MouseRegionId {
|
||||
view_id: usize,
|
||||
tag: TypeId,
|
||||
tag: TypeTag,
|
||||
region_id: usize,
|
||||
#[cfg(debug_assertions)]
|
||||
tag_type_name: &'static str,
|
||||
}
|
||||
|
||||
impl MouseRegionId {
|
||||
pub(crate) fn new<Tag: 'static>(view_id: usize, region_id: usize) -> Self {
|
||||
pub(crate) fn new(tag: TypeTag, view_id: usize, region_id: usize) -> Self {
|
||||
MouseRegionId {
|
||||
view_id,
|
||||
region_id,
|
||||
tag: TypeId::of::<Tag>(),
|
||||
#[cfg(debug_assertions)]
|
||||
tag_type_name: std::any::type_name::<Tag>(),
|
||||
tag,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -203,7 +207,7 @@ impl MouseRegionId {
|
|||
|
||||
#[cfg(debug_assertions)]
|
||||
pub fn tag_type_name(&self) -> &'static str {
|
||||
self.tag_type_name
|
||||
self.tag.type_name()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -106,7 +106,7 @@ impl View for Select {
|
|||
Default::default()
|
||||
};
|
||||
let mut result = Flex::column().with_child(
|
||||
MouseEventHandler::<Header, _>::new(self.handle.id(), cx, |mouse_state, cx| {
|
||||
MouseEventHandler::new::<Header, _>(self.handle.id(), cx, |mouse_state, cx| {
|
||||
(self.render_item)(
|
||||
self.selected_item_ix,
|
||||
ItemType::Header,
|
||||
|
@ -130,7 +130,7 @@ impl View for Select {
|
|||
let selected_item_ix = this.selected_item_ix;
|
||||
range.end = range.end.min(this.item_count);
|
||||
items.extend(range.map(|ix| {
|
||||
MouseEventHandler::<Item, _>::new(ix, cx, |mouse_state, cx| {
|
||||
MouseEventHandler::new::<Item, _>(ix, cx, |mouse_state, cx| {
|
||||
(this.render_item)(
|
||||
ix,
|
||||
if ix == selected_item_ix {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue