Add components example

Re-arrange generics on mouse event handler
Add TypeTag struct for dynamically tagged components
This commit is contained in:
Mikayla 2023-08-15 03:06:43 -07:00
parent 5ce7ccac32
commit e5eed29c72
No known key found for this signature in database
49 changed files with 585 additions and 155 deletions

View file

@ -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(&region_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);
})

View file

@ -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)
}
}

View file

@ -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,

View file

@ -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 = ();

View file

@ -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 {

View file

@ -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),

View file

@ -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>,

View file

@ -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()
}
}

View file

@ -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 {