Compare commits
5 Commits
06dd015092
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 7b54aaf3c4 | |||
| 17c436d944 | |||
| a592318a6f | |||
| 796bc41752 | |||
| 7bafb04a34 |
@@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
ActiveData, Event, EventCtx, EventFn, EventIdCtx, EventLike, HasEvents, IdLike, LayerId,
|
ActiveData, Event, EventCtx, EventFn, EventIdCtx, EventLike, HasEvents, IdLike, LayerId,
|
||||||
WeakWidget, Widget, WidgetEventFn, WidgetId,
|
WeakWidget, WidgetEventFn, WidgetId,
|
||||||
util::{HashMap, HashSet, TypeMap},
|
util::{HashMap, HashSet, TypeMap},
|
||||||
};
|
};
|
||||||
use std::{any::TypeId, rc::Rc};
|
use std::{any::TypeId, rc::Rc};
|
||||||
@@ -24,15 +24,16 @@ impl<Rsc: HasEvents + 'static> EventManager<Rsc> {
|
|||||||
self.types.type_or_default()
|
self.types.type_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register<W: Widget + ?Sized, E: EventLike>(
|
pub fn register<I: IdLike + 'static, E: EventLike>(
|
||||||
&mut self,
|
&mut self,
|
||||||
id: WeakWidget<W>,
|
id: I,
|
||||||
event: E,
|
event: E,
|
||||||
f: impl for<'a> WidgetEventFn<Rsc, <E::Event as Event>::Data<'a>, W>,
|
f: impl for<'a> WidgetEventFn<Rsc, <E::Event as Event>::Data<'a>, I::Widget>,
|
||||||
) {
|
) {
|
||||||
|
let i = id.id();
|
||||||
self.get_type::<E>().register(id, event, f);
|
self.get_type::<E>().register(id, event, f);
|
||||||
self.widget_to_types
|
self.widget_to_types
|
||||||
.entry(id.id())
|
.entry(i)
|
||||||
.or_default()
|
.or_default()
|
||||||
.insert(Self::type_key::<E>());
|
.insert(Self::type_key::<E>());
|
||||||
}
|
}
|
||||||
@@ -112,11 +113,11 @@ impl<Rsc: HasEvents, E: Event> Default for TypeEventManager<Rsc, E> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<Rsc: HasEvents + 'static, E: Event> TypeEventManager<Rsc, E> {
|
impl<Rsc: HasEvents + 'static, E: Event> TypeEventManager<Rsc, E> {
|
||||||
fn register<W: Widget + ?Sized>(
|
fn register<I: IdLike + 'static>(
|
||||||
&mut self,
|
&mut self,
|
||||||
widget: WeakWidget<W>,
|
widget: I,
|
||||||
event: impl EventLike<Event = E>,
|
event: impl EventLike<Event = E>,
|
||||||
f: impl for<'a> WidgetEventFn<Rsc, E::Data<'a>, W>,
|
f: impl for<'a> WidgetEventFn<Rsc, E::Data<'a>, I::Widget>,
|
||||||
) {
|
) {
|
||||||
let event = event.into_event();
|
let event = event.into_event();
|
||||||
self.map.entry(widget.id()).or_default().push((
|
self.map.entry(widget.id()).or_default().push((
|
||||||
@@ -124,7 +125,7 @@ impl<Rsc: HasEvents + 'static, E: Event> TypeEventManager<Rsc, E> {
|
|||||||
Rc::new(move |ctx, rsc| {
|
Rc::new(move |ctx, rsc| {
|
||||||
f(
|
f(
|
||||||
EventIdCtx {
|
EventIdCtx {
|
||||||
widget,
|
widget: WeakWidget::new(widget.id()),
|
||||||
state: ctx.state,
|
state: ctx.state,
|
||||||
data: ctx.data,
|
data: ctx.data,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,196 +0,0 @@
|
|||||||
use crate::{
|
|
||||||
ActiveData, Axis, EventsLike, Painter, SizeCtx, StrongWidget, UiRegion, UiRenderState, UiVec2,
|
|
||||||
WidgetId, Widgets,
|
|
||||||
render::MaskIdx,
|
|
||||||
util::{HashSet, forget_ref},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// state maintained between widgets during painting
|
|
||||||
pub struct Drawer<'a> {
|
|
||||||
pub(super) widgets: &'a mut Widgets,
|
|
||||||
pub(super) data: &'a mut PainterData,
|
|
||||||
pub(super) events: &'a mut dyn EventsLike,
|
|
||||||
pub(super) render: &'a mut UiRenderState,
|
|
||||||
root: Option<&'a StrongWidget>,
|
|
||||||
draw_started: HashSet<WidgetId>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Drawer<'a> {
|
|
||||||
/// redraws a widget that's currently active (drawn)
|
|
||||||
pub fn redraw(&mut self, id: WidgetId) {
|
|
||||||
self.widgets.needs_redraw.remove(&id);
|
|
||||||
self.draw_started.remove(&id);
|
|
||||||
// check if parent depends on the desired size of this, if so then redraw it first
|
|
||||||
for axis in [Axis::X, Axis::Y] {
|
|
||||||
if let Some(&(outer, old)) = self.render.cache.size.axis_dyn(axis).get(&id)
|
|
||||||
&& let Some(current) = self.render.active.get(&id)
|
|
||||||
&& let Some(pid) = current.parent
|
|
||||||
{
|
|
||||||
self.render.cache.size.axis_dyn(axis).remove(&id);
|
|
||||||
let new = self.size_ctx(id, outer).len_axis(id, axis);
|
|
||||||
self.render
|
|
||||||
.cache
|
|
||||||
.size
|
|
||||||
.axis_dyn(axis)
|
|
||||||
.insert(id, (outer, new));
|
|
||||||
if new != old {
|
|
||||||
self.redraw(pid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.draw_started.contains(&id) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let Some(active) = self.remove(id, false) else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
self.draw_inner(
|
|
||||||
active.layer,
|
|
||||||
id,
|
|
||||||
active.region,
|
|
||||||
active.parent,
|
|
||||||
active.mask,
|
|
||||||
Some(active.children),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn size_ctx<'b>(&'b mut self, source: WidgetId, outer: UiVec2) -> SizeCtx<'b> {
|
|
||||||
SizeCtx {
|
|
||||||
source,
|
|
||||||
cache: &mut self.render.cache,
|
|
||||||
text: &mut self.data.text,
|
|
||||||
textures: &mut self.data.textures,
|
|
||||||
widgets: &self.widgets,
|
|
||||||
outer,
|
|
||||||
output_size: self.render.output_size,
|
|
||||||
id: source,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn draw_inner(
|
|
||||||
&mut self,
|
|
||||||
layer: usize,
|
|
||||||
id: WidgetId,
|
|
||||||
region: UiRegion,
|
|
||||||
parent: Option<WidgetId>,
|
|
||||||
mask: MaskIdx,
|
|
||||||
old_children: Option<Vec<WidgetId>>,
|
|
||||||
) {
|
|
||||||
let mut old_children = old_children.unwrap_or_default();
|
|
||||||
if let Some(active) = self.render.active.get_mut(&id)
|
|
||||||
&& !self.widgets.needs_redraw.contains(&id)
|
|
||||||
{
|
|
||||||
// check to see if we can skip drawing first
|
|
||||||
if active.region == region {
|
|
||||||
return;
|
|
||||||
} else if active.region.size() == region.size() {
|
|
||||||
// TODO: epsilon?
|
|
||||||
let from = active.region;
|
|
||||||
self.mov(id, from, region);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// if not, then maintain resize and track old children to remove unneeded
|
|
||||||
let active = self.remove(id, false).unwrap();
|
|
||||||
old_children = active.children;
|
|
||||||
}
|
|
||||||
|
|
||||||
// draw widget
|
|
||||||
self.draw_started.insert(id);
|
|
||||||
|
|
||||||
let mut painter = Painter {
|
|
||||||
drawer: self,
|
|
||||||
region,
|
|
||||||
mask,
|
|
||||||
layer,
|
|
||||||
id,
|
|
||||||
textures: Vec::new(),
|
|
||||||
primitives: Vec::new(),
|
|
||||||
children: Vec::new(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut widget = painter.drawer.widgets.get_dyn_dynamic(id);
|
|
||||||
widget.draw(&mut painter);
|
|
||||||
drop(widget);
|
|
||||||
|
|
||||||
let Painter {
|
|
||||||
drawer: _,
|
|
||||||
region,
|
|
||||||
mask,
|
|
||||||
textures,
|
|
||||||
primitives,
|
|
||||||
children,
|
|
||||||
layer,
|
|
||||||
id,
|
|
||||||
} = painter;
|
|
||||||
|
|
||||||
// add to active
|
|
||||||
let active = ActiveData {
|
|
||||||
id,
|
|
||||||
region,
|
|
||||||
parent,
|
|
||||||
textures,
|
|
||||||
primitives,
|
|
||||||
children,
|
|
||||||
mask,
|
|
||||||
layer,
|
|
||||||
};
|
|
||||||
|
|
||||||
// remove old children that weren't kept
|
|
||||||
for c in &old_children {
|
|
||||||
if !active.children.contains(c) {
|
|
||||||
self.remove_rec(*c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// update modules
|
|
||||||
self.events.draw(&active);
|
|
||||||
self.render.active.insert(id, active);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mov(&mut self, id: WidgetId, from: UiRegion, to: UiRegion) {
|
|
||||||
let active = self.render.active.get_mut(&id).unwrap();
|
|
||||||
for h in &active.primitives {
|
|
||||||
let region = self.render.layers[h.layer].region_mut(h);
|
|
||||||
*region = region.outside(&from).within(&to);
|
|
||||||
}
|
|
||||||
active.region = active.region.outside(&from).within(&to);
|
|
||||||
// SAFETY: children cannot be recursive
|
|
||||||
let children = unsafe { forget_ref(&active.children) };
|
|
||||||
for child in children {
|
|
||||||
self.mov(*child, from, to);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// NOTE: instance textures are cleared and self.textures freed
|
|
||||||
fn remove(&mut self, id: WidgetId, undraw: bool) -> Option<ActiveData> {
|
|
||||||
let mut active = self.render.active.remove(&id);
|
|
||||||
if let Some(active) = &mut active {
|
|
||||||
for h in &active.primitives {
|
|
||||||
let mask = self.render.layers.free(h);
|
|
||||||
if mask != MaskIdx::NONE {
|
|
||||||
self.data.masks.remove(mask);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
active.textures.clear();
|
|
||||||
self.data.textures.free();
|
|
||||||
if undraw {
|
|
||||||
self.events.undraw(active);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
active
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remove_rec(&mut self, id: WidgetId) -> Option<ActiveData> {
|
|
||||||
self.render.cache.remove(id);
|
|
||||||
let inst = self.remove(id, true);
|
|
||||||
if let Some(inst) = &inst {
|
|
||||||
for c in &inst.children {
|
|
||||||
self.remove_rec(*c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
inst
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,11 +2,9 @@ use crate::{Mask, TextData, Textures, WeakWidget, WidgetId, Widgets, util::Track
|
|||||||
|
|
||||||
mod active;
|
mod active;
|
||||||
mod cache;
|
mod cache;
|
||||||
// mod draw_state;
|
|
||||||
mod painter;
|
mod painter;
|
||||||
mod render_state;
|
mod render_state;
|
||||||
mod size;
|
mod size;
|
||||||
mod state;
|
|
||||||
|
|
||||||
pub use active::*;
|
pub use active::*;
|
||||||
pub use painter::Painter;
|
pub use painter::Painter;
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
// pub struct DynState {
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
use std::marker::Unsize;
|
use std::marker::Unsize;
|
||||||
|
|
||||||
use crate::{Widget, WeakWidget};
|
use crate::{IdLike, WeakWidget, Widget};
|
||||||
|
|
||||||
pub trait WidgetView {
|
pub trait WidgetView {
|
||||||
type Widget: Widget + ?Sized + Unsize<dyn Widget>;
|
type Widget: Widget + ?Sized + Unsize<dyn Widget>;
|
||||||
@@ -14,3 +14,11 @@ pub trait HasWidget {
|
|||||||
impl<W: Widget + Unsize<dyn Widget> + ?Sized> HasWidget for WeakWidget<W> {
|
impl<W: Widget + Unsize<dyn Widget> + ?Sized> HasWidget for WeakWidget<W> {
|
||||||
type Widget = W;
|
type Widget = W;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<WV: WidgetView> IdLike for WV {
|
||||||
|
type Widget = WV::Widget;
|
||||||
|
|
||||||
|
fn id(&self) -> super::WidgetId {
|
||||||
|
self.root().id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -15,19 +15,22 @@ type Rsc = DefaultRsc<State>;
|
|||||||
struct Test {
|
struct Test {
|
||||||
#[root]
|
#[root]
|
||||||
root: WeakWidget<Rect>,
|
root: WeakWidget<Rect>,
|
||||||
|
cur: WeakState<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Test {
|
impl Test {
|
||||||
pub fn new(rsc: &mut Rsc) -> Self {
|
pub fn new(rsc: &mut Rsc) -> Self {
|
||||||
let root = rect(Color::RED).add(rsc);
|
let root = rect(Color::RED).add(rsc);
|
||||||
Self { root }
|
let cur = rsc.create_state(root, false);
|
||||||
|
Self { root, cur }
|
||||||
}
|
}
|
||||||
pub fn toggle(&self, rsc: &mut Rsc) {
|
pub fn toggle(&self, rsc: &mut Rsc) {
|
||||||
let rect = (self.root)(rsc);
|
let cur = &mut rsc[self.cur];
|
||||||
if rect.color == Color::RED {
|
*cur = !*cur;
|
||||||
rect.color = Color::BLUE;
|
if *cur {
|
||||||
|
rsc[self.root].color = Color::BLUE;
|
||||||
} else {
|
} else {
|
||||||
rect.color = Color::RED;
|
rsc[self.root].color = Color::RED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ My experimental attempt at a rust ui library (also my first ui library).
|
|||||||
|
|
||||||
It's currently designed around using retained data structures (widgets), rather than diffing generated trees from data like xilem or iced. This is an experiment and I'm not sure if it's a good idea or not.
|
It's currently designed around using retained data structures (widgets), rather than diffing generated trees from data like xilem or iced. This is an experiment and I'm not sure if it's a good idea or not.
|
||||||
|
|
||||||
There's a `main.rs` that runs a testing window, so you can just `cargo run` to see it working.
|
Examples are in `examples`, eg. `cargo run --example tabs`.
|
||||||
|
|
||||||
Goals, in general order:
|
Goals, in general order:
|
||||||
1. does what I want it to (text, images, video, animations)
|
1. does what I want it to (text, images, video, animations)
|
||||||
2. very easy to use ignoring ergonomic ref counting
|
2. very easy to use ignoring ergonomic ref counting
|
||||||
3. reasonably fast / efficient (a lot faster than electron, save battery life)
|
3. reasonably fast / efficient (a lot faster than electron, save battery life, try to beat iced and xilem)
|
||||||
|
|
||||||
## dev details
|
## dev details
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ mod event;
|
|||||||
mod input;
|
mod input;
|
||||||
mod render;
|
mod render;
|
||||||
mod sense;
|
mod sense;
|
||||||
|
mod state;
|
||||||
mod task;
|
mod task;
|
||||||
|
|
||||||
pub use app::*;
|
pub use app::*;
|
||||||
@@ -25,6 +26,7 @@ pub use event::*;
|
|||||||
pub use input::*;
|
pub use input::*;
|
||||||
pub use render::*;
|
pub use render::*;
|
||||||
pub use sense::*;
|
pub use sense::*;
|
||||||
|
pub use state::*;
|
||||||
pub use task::*;
|
pub use task::*;
|
||||||
|
|
||||||
pub type Proxy<Event> = EventLoopProxy<Event>;
|
pub type Proxy<Event> = EventLoopProxy<Event>;
|
||||||
@@ -98,6 +100,7 @@ pub struct DefaultRsc<State: 'static> {
|
|||||||
pub ui: UiData,
|
pub ui: UiData,
|
||||||
pub events: EventManager<Self>,
|
pub events: EventManager<Self>,
|
||||||
pub tasks: Tasks<Self>,
|
pub tasks: Tasks<Self>,
|
||||||
|
pub state: WidgetState,
|
||||||
_state: PhantomData<State>,
|
_state: PhantomData<State>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,11 +112,16 @@ impl<State> DefaultRsc<State> {
|
|||||||
ui: Default::default(),
|
ui: Default::default(),
|
||||||
events: Default::default(),
|
events: Default::default(),
|
||||||
tasks,
|
tasks,
|
||||||
|
state: Default::default(),
|
||||||
_state: Default::default(),
|
_state: Default::default(),
|
||||||
},
|
},
|
||||||
recv,
|
recv,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn create_state<T: 'static>(&mut self, id: impl IdLike, data: T) -> WeakState<T> {
|
||||||
|
self.state.add(id.id(), data)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<State> UiRsc for DefaultRsc<State> {
|
impl<State> UiRsc for DefaultRsc<State> {
|
||||||
@@ -135,6 +143,7 @@ impl<State> UiRsc for DefaultRsc<State> {
|
|||||||
|
|
||||||
fn on_remove(&mut self, id: WidgetId) {
|
fn on_remove(&mut self, id: WidgetId) {
|
||||||
self.events.remove(id);
|
self.events.remove(id);
|
||||||
|
self.state.remove(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,6 +167,16 @@ impl<State: 'static> HasTasks for DefaultRsc<State> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<State: 'static> HasWidgetState for DefaultRsc<State> {
|
||||||
|
fn widget_state(&self) -> &WidgetState {
|
||||||
|
&self.state
|
||||||
|
}
|
||||||
|
|
||||||
|
fn widget_state_mut(&mut self) -> &mut WidgetState {
|
||||||
|
&mut self.state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct DefaultApp<State: DefaultAppState> {
|
pub struct DefaultApp<State: DefaultAppState> {
|
||||||
rsc: DefaultRsc<State>,
|
rsc: DefaultRsc<State>,
|
||||||
render: UiRenderState,
|
render: UiRenderState,
|
||||||
@@ -289,3 +308,47 @@ impl<State: DefaultAppState> AppState for DefaultApp<State> {
|
|||||||
self.state.exit(&mut self.rsc, &mut self.render);
|
self.state.exit(&mut self.rsc, &mut self.render);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait RscIdx<Rsc> {
|
||||||
|
type Output;
|
||||||
|
fn get(self, rsc: &Rsc) -> &Self::Output;
|
||||||
|
fn get_mut(self, rsc: &mut Rsc) -> &mut Self::Output;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<State: 'static, I: RscIdx<DefaultRsc<State>>> std::ops::Index<I> for DefaultRsc<State> {
|
||||||
|
type Output = I::Output;
|
||||||
|
|
||||||
|
fn index(&self, index: I) -> &Self::Output {
|
||||||
|
index.get(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<State: 'static, I: RscIdx<DefaultRsc<State>>> std::ops::IndexMut<I> for DefaultRsc<State> {
|
||||||
|
fn index_mut(&mut self, index: I) -> &mut Self::Output {
|
||||||
|
index.get_mut(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W: Widget, Rsc: UiRsc> RscIdx<Rsc> for WeakWidget<W> {
|
||||||
|
type Output = W;
|
||||||
|
|
||||||
|
fn get(self, rsc: &Rsc) -> &Self::Output {
|
||||||
|
&rsc.ui().widgets[self]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_mut(self, rsc: &mut Rsc) -> &mut Self::Output {
|
||||||
|
&mut rsc.ui_mut().widgets[self]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: 'static, Rsc: HasWidgetState> RscIdx<Rsc> for WeakState<T> {
|
||||||
|
type Output = T;
|
||||||
|
|
||||||
|
fn get(self, rsc: &Rsc) -> &Self::Output {
|
||||||
|
rsc.widget_state().get(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_mut(self, rsc: &mut Rsc) -> &mut Self::Output {
|
||||||
|
rsc.widget_state_mut().get_mut(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
75
src/default/state.rs
Normal file
75
src/default/state.rs
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
use iris_core::{
|
||||||
|
WidgetId,
|
||||||
|
util::{HashMap, HashSet},
|
||||||
|
};
|
||||||
|
use std::{
|
||||||
|
any::{Any, TypeId},
|
||||||
|
marker::PhantomData,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
struct Key {
|
||||||
|
id: WidgetId,
|
||||||
|
ty: TypeId,
|
||||||
|
i: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct WidgetState {
|
||||||
|
widgets: HashMap<WidgetId, HashSet<(TypeId, usize)>>,
|
||||||
|
counts: HashMap<(WidgetId, TypeId), usize>,
|
||||||
|
map: HashMap<Key, Box<dyn Any>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WidgetState {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
pub fn add<T: 'static>(&mut self, id: WidgetId, data: T) -> WeakState<T> {
|
||||||
|
let ty = TypeId::of::<T>();
|
||||||
|
let count = self.counts.entry((id, ty)).or_default();
|
||||||
|
let i = *count;
|
||||||
|
let key = Key { ty, i, id };
|
||||||
|
self.map.insert(key, Box::new(data));
|
||||||
|
self.widgets.entry(id).or_default().insert((ty, i));
|
||||||
|
*count += 1;
|
||||||
|
WeakState {
|
||||||
|
key,
|
||||||
|
_pd: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn remove(&mut self, id: WidgetId) {
|
||||||
|
for &(ty, i) in self.widgets.get(&id).into_iter().flatten() {
|
||||||
|
self.map.remove(&Key { id, ty, i });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn get<T: 'static>(&self, state: WeakState<T>) -> &T {
|
||||||
|
self.map.get(&state.key).unwrap().downcast_ref().unwrap()
|
||||||
|
}
|
||||||
|
pub fn get_mut<T: 'static>(&mut self, state: WeakState<T>) -> &mut T {
|
||||||
|
self.map
|
||||||
|
.get_mut(&state.key)
|
||||||
|
.unwrap()
|
||||||
|
.downcast_mut()
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct WeakState<T> {
|
||||||
|
key: Key,
|
||||||
|
_pd: PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait HasWidgetState {
|
||||||
|
fn widget_state(&self) -> &WidgetState;
|
||||||
|
fn widget_state_mut(&mut self) -> &mut WidgetState;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: 'static> FnOnce<(&'a mut WidgetState,)> for WeakState<T> {
|
||||||
|
type Output = &'a mut T;
|
||||||
|
|
||||||
|
extern "rust-call" fn call_once(self, (state,): (&'a mut WidgetState,)) -> Self::Output {
|
||||||
|
state.get_mut(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -57,6 +57,13 @@ widget_trait! {
|
|||||||
|
|
||||||
pub trait HasTasks: Sized + HasState + HasEvents {
|
pub trait HasTasks: Sized + HasState + HasEvents {
|
||||||
fn tasks_mut(&mut self) -> &mut Tasks<Self>;
|
fn tasks_mut(&mut self) -> &mut Tasks<Self>;
|
||||||
|
|
||||||
|
fn spawn_task<F: AsyncFnOnce(TaskCtx<Self>) + 'static + std::marker::Send>(&mut self, task: F)
|
||||||
|
where
|
||||||
|
F::CallOnceFuture: Send,
|
||||||
|
{
|
||||||
|
self.tasks_mut().spawn(task);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait AsyncWidgetEventFn<Rsc: HasEvents, W: ?Sized>:
|
pub trait AsyncWidgetEventFn<Rsc: HasEvents, W: ?Sized>:
|
||||||
|
|||||||
Reference in New Issue
Block a user