Compare commits
12 Commits
06dd015092
..
work
| Author | SHA1 | Date | |
|---|---|---|---|
| a648c62aa2 | |||
| c118bb446b | |||
| 1102dc7338 | |||
| 1aadef0e7e | |||
| 426ff0adfc | |||
| dab6cf298a | |||
| 38d896d44d | |||
| 7b54aaf3c4 | |||
| 17c436d944 | |||
| a592318a6f | |||
| 796bc41752 | |||
| 7bafb04a34 |
Generated
+406
-386
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
ActiveData, Event, EventCtx, EventFn, EventIdCtx, EventLike, HasEvents, IdLike, LayerId,
|
||||
WeakWidget, Widget, WidgetEventFn, WidgetId,
|
||||
WeakWidget, WidgetEventFn, WidgetId,
|
||||
util::{HashMap, HashSet, TypeMap},
|
||||
};
|
||||
use std::{any::TypeId, rc::Rc};
|
||||
@@ -24,15 +24,16 @@ impl<Rsc: HasEvents + 'static> EventManager<Rsc> {
|
||||
self.types.type_or_default()
|
||||
}
|
||||
|
||||
pub fn register<W: Widget + ?Sized, E: EventLike>(
|
||||
pub fn register<I: IdLike + 'static, E: EventLike>(
|
||||
&mut self,
|
||||
id: WeakWidget<W>,
|
||||
id: I,
|
||||
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.widget_to_types
|
||||
.entry(id.id())
|
||||
.entry(i)
|
||||
.or_default()
|
||||
.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> {
|
||||
fn register<W: Widget + ?Sized>(
|
||||
fn register<I: IdLike + 'static>(
|
||||
&mut self,
|
||||
widget: WeakWidget<W>,
|
||||
widget: I,
|
||||
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();
|
||||
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| {
|
||||
f(
|
||||
EventIdCtx {
|
||||
widget,
|
||||
widget: WeakWidget::new(widget.id()),
|
||||
state: ctx.state,
|
||||
data: ctx.data,
|
||||
},
|
||||
|
||||
@@ -2,10 +2,8 @@
|
||||
#![feature(const_ops)]
|
||||
#![feature(const_trait_impl)]
|
||||
#![feature(const_convert)]
|
||||
#![feature(map_try_insert)]
|
||||
#![feature(unboxed_closures)]
|
||||
#![feature(fn_traits)]
|
||||
#![feature(const_cmp)]
|
||||
#![feature(const_destruct)]
|
||||
#![feature(portable_simd)]
|
||||
#![feature(associated_type_defaults)]
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
+1
-3
@@ -2,11 +2,9 @@ use crate::{Mask, TextData, Textures, WeakWidget, WidgetId, Widgets, util::Track
|
||||
|
||||
mod active;
|
||||
mod cache;
|
||||
// mod draw_state;
|
||||
mod painter;
|
||||
mod render_state;
|
||||
mod size;
|
||||
mod state;
|
||||
|
||||
pub use active::*;
|
||||
pub use painter::Painter;
|
||||
@@ -30,7 +28,7 @@ pub trait UiRsc {
|
||||
#[allow(unused_variables)]
|
||||
fn on_remove(&mut self, id: WidgetId) {}
|
||||
#[allow(unused_variables)]
|
||||
fn on_draw(&mut self, active: &ActiveData) {}
|
||||
fn on_draw(&mut self, active: &ActiveData, redrawn: bool) {}
|
||||
#[allow(unused_variables)]
|
||||
fn on_undraw(&mut self, active: &ActiveData) {}
|
||||
|
||||
|
||||
@@ -81,10 +81,12 @@ impl UiRenderState {
|
||||
old_children: Option<Vec<WidgetId>>,
|
||||
rsc: &mut dyn UiRsc,
|
||||
) {
|
||||
let mut redrawn = old_children.is_some();
|
||||
let mut old_children = old_children.unwrap_or_default();
|
||||
if let Some(active) = self.active.get_mut(&id)
|
||||
&& !rsc.widgets().needs_redraw.contains(&id)
|
||||
{
|
||||
redrawn = true;
|
||||
// check to see if we can skip drawing first
|
||||
if active.region == region {
|
||||
return;
|
||||
@@ -149,7 +151,7 @@ impl UiRenderState {
|
||||
}
|
||||
}
|
||||
|
||||
rsc.on_draw(&active);
|
||||
rsc.on_draw(&active, redrawn);
|
||||
self.active.insert(id, active);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
// pub struct DynState {
|
||||
//
|
||||
// }
|
||||
@@ -34,7 +34,7 @@ pub trait HasRoot {
|
||||
|
||||
pub trait WidgetArrLike<Rsc, const LEN: usize, Tag> {
|
||||
#[track_caller]
|
||||
fn add(self, state: &mut Rsc) -> WidgetArr<LEN>;
|
||||
fn add(self, rsc: &mut Rsc) -> WidgetArr<LEN>;
|
||||
}
|
||||
|
||||
impl<Rsc, const LEN: usize> WidgetArrLike<Rsc, LEN, ArrTag> for WidgetArr<LEN> {
|
||||
@@ -58,6 +58,13 @@ macro_rules! impl_widget_arr {
|
||||
)
|
||||
}
|
||||
}
|
||||
impl<Rsc: UiRsc, $($W: WidgetLike<Rsc, $Tag>,$Tag,)*> IntoWidgetVec<Rsc, ($($Tag,)*), ArrTag> for ($($W,)*) {
|
||||
fn into_vec(self, rsc: &mut Rsc) -> Vec<StrongWidget> {
|
||||
#[allow(non_snake_case)]
|
||||
let ($($W,)*) = self;
|
||||
vec![$($W.add(rsc).upgrade(rsc),)*]
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+14
-1
@@ -1,4 +1,4 @@
|
||||
use crate::{Axis, AxisT, Len, Painter, SizeCtx};
|
||||
use crate::{Axis, AxisT, Len, Painter, SizeCtx, UiRsc};
|
||||
use std::any::Any;
|
||||
|
||||
mod data;
|
||||
@@ -85,3 +85,16 @@ impl<State, F: FnOnce(&mut State) -> Option<StrongWidget>> WidgetOption<State> f
|
||||
self(state)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait IntoWidgetVec<Rsc, WTag, GTag> {
|
||||
fn into_vec(self, rsc: &mut Rsc) -> Vec<StrongWidget>;
|
||||
}
|
||||
|
||||
impl<Rsc: UiRsc, I: IntoIterator, Tag> IntoWidgetVec<Rsc, Tag, IterTag> for I
|
||||
where
|
||||
I::Item: WidgetLike<Rsc, Tag>,
|
||||
{
|
||||
fn into_vec(self, rsc: &mut Rsc) -> Vec<StrongWidget> {
|
||||
self.into_iter().map(|w| w.add_strong(rsc).any()).collect()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,3 +62,4 @@ impl<Rsc: UiRsc, V: WidgetView> WidgetLike<Rsc, ViewTag> for V {
|
||||
}
|
||||
|
||||
pub struct ArrTag;
|
||||
pub struct IterTag;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::marker::Unsize;
|
||||
|
||||
use crate::{Widget, WeakWidget};
|
||||
use crate::{IdLike, WeakWidget, Widget};
|
||||
|
||||
pub trait WidgetView {
|
||||
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> {
|
||||
type Widget = W;
|
||||
}
|
||||
|
||||
impl<WV: WidgetView> IdLike for WV {
|
||||
type Widget = WV::Widget;
|
||||
|
||||
fn id(&self) -> super::WidgetId {
|
||||
self.root().id
|
||||
}
|
||||
}
|
||||
|
||||
+1
-5
@@ -10,11 +10,7 @@ struct State {
|
||||
}
|
||||
|
||||
impl DefaultAppState for State {
|
||||
fn new(
|
||||
mut ui_state: DefaultUiState,
|
||||
rsc: &mut DefaultRsc<Self>,
|
||||
_: Proxy<Self::Event>,
|
||||
) -> Self {
|
||||
fn new(mut ui_state: DefaultUiState, rsc: &mut DefaultRsc<Self>) -> Self {
|
||||
rect(Color::RED).set_root(rsc, &mut ui_state);
|
||||
Self { ui_state }
|
||||
}
|
||||
|
||||
@@ -16,11 +16,7 @@ pub struct Client {
|
||||
}
|
||||
|
||||
impl DefaultAppState for Client {
|
||||
fn new(
|
||||
mut ui_state: DefaultUiState,
|
||||
rsc: &mut DefaultRsc<Self>,
|
||||
_: Proxy<Self::Event>,
|
||||
) -> Self {
|
||||
fn new(mut ui_state: DefaultUiState, rsc: &mut DefaultRsc<Self>) -> Self {
|
||||
let rrect = rect(Color::WHITE).radius(20);
|
||||
let pad_test = (
|
||||
rrect.color(Color::BLUE),
|
||||
@@ -148,7 +144,7 @@ impl DefaultAppState for Client {
|
||||
.span(Dir::DOWN)
|
||||
.add(rsc);
|
||||
|
||||
let main = WidgetPtr::new().add(rsc);
|
||||
let main = WidgetPtr::empty().add(rsc);
|
||||
|
||||
let vals = Rc::new(RefCell::new((0, Vec::new())));
|
||||
let mut switch_button = |color, to: WeakWidget, label| {
|
||||
|
||||
+1
-5
@@ -11,11 +11,7 @@ struct State {
|
||||
}
|
||||
|
||||
impl DefaultAppState for State {
|
||||
fn new(
|
||||
mut ui_state: DefaultUiState,
|
||||
rsc: &mut DefaultRsc<Self>,
|
||||
_: Proxy<Self::Event>,
|
||||
) -> Self {
|
||||
fn new(mut ui_state: DefaultUiState, rsc: &mut DefaultRsc<Self>) -> Self {
|
||||
let rect = rect(Color::RED).add(rsc);
|
||||
rect.task_on(CursorSense::click(), async move |mut ctx| {
|
||||
tokio::time::sleep(Duration::from_secs(1)).await;
|
||||
|
||||
+9
-10
@@ -15,29 +15,28 @@ type Rsc = DefaultRsc<State>;
|
||||
struct Test {
|
||||
#[root]
|
||||
root: WeakWidget<Rect>,
|
||||
cur: WeakState<bool>,
|
||||
}
|
||||
|
||||
impl Test {
|
||||
pub fn new(rsc: &mut Rsc) -> Self {
|
||||
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) {
|
||||
let rect = (self.root)(rsc);
|
||||
if rect.color == Color::RED {
|
||||
rect.color = Color::BLUE;
|
||||
let cur = &mut rsc[self.cur];
|
||||
*cur = !*cur;
|
||||
if *cur {
|
||||
rsc[self.root].color = Color::BLUE;
|
||||
} else {
|
||||
rect.color = Color::RED;
|
||||
rsc[self.root].color = Color::RED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DefaultAppState for State {
|
||||
fn new(
|
||||
mut ui_state: DefaultUiState,
|
||||
rsc: &mut DefaultRsc<Self>,
|
||||
_: Proxy<Self::Event>,
|
||||
) -> Self {
|
||||
fn new(mut ui_state: DefaultUiState, rsc: &mut DefaultRsc<Self>) -> Self {
|
||||
let test = Test::new(rsc);
|
||||
|
||||
test.on(CursorSense::click(), move |_, rsc| {
|
||||
|
||||
@@ -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.
|
||||
|
||||
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:
|
||||
1. does what I want it to (text, images, video, animations)
|
||||
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
|
||||
|
||||
|
||||
@@ -7,3 +7,11 @@ impl Event for Submit {}
|
||||
#[derive(Eq, PartialEq, Hash, Clone)]
|
||||
pub struct Edited;
|
||||
impl Event for Edited {}
|
||||
|
||||
#[derive(Eq, PartialEq, Hash, Clone)]
|
||||
pub struct Draw;
|
||||
impl Event for Draw {}
|
||||
|
||||
#[derive(Eq, PartialEq, Hash, Clone)]
|
||||
pub struct Undraw;
|
||||
impl Event for Undraw {}
|
||||
|
||||
+205
-42
@@ -17,6 +17,7 @@ mod event;
|
||||
mod input;
|
||||
mod render;
|
||||
mod sense;
|
||||
mod state;
|
||||
mod task;
|
||||
|
||||
pub use app::*;
|
||||
@@ -25,9 +26,29 @@ pub use event::*;
|
||||
pub use input::*;
|
||||
pub use render::*;
|
||||
pub use sense::*;
|
||||
pub use state::*;
|
||||
pub use task::*;
|
||||
|
||||
pub type Proxy<Event> = EventLoopProxy<Event>;
|
||||
pub struct EventSender<State: DefaultAppState> {
|
||||
proxy: EventLoopProxy<UiMainEvent<State>>,
|
||||
}
|
||||
|
||||
impl<State: DefaultAppState> Clone for EventSender<State> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
proxy: self.proxy.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<State: DefaultAppState> EventSender<State> {
|
||||
pub fn send(&self, event: State::Event) {
|
||||
let _ = self.proxy.send_event(UiMainEvent::App(event));
|
||||
}
|
||||
pub fn run(&self, f: impl MainCallback<State>) {
|
||||
let _ = self.proxy.send_event(UiMainEvent::Callback(Box::new(f)));
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DefaultUiState {
|
||||
pub root: Option<StrongWidget>,
|
||||
@@ -68,9 +89,8 @@ pub trait HasDefaultUiState: Sized + 'static {
|
||||
}
|
||||
|
||||
pub trait DefaultAppState: HasDefaultUiState {
|
||||
type Event = ();
|
||||
fn new(ui_state: DefaultUiState, rsc: &mut DefaultRsc<Self>, proxy: Proxy<Self::Event>)
|
||||
-> Self;
|
||||
type Event: Send = ();
|
||||
fn new(ui_state: DefaultUiState, rsc: &mut DefaultRsc<Self>) -> Self;
|
||||
#[allow(unused_variables)]
|
||||
fn event(
|
||||
&mut self,
|
||||
@@ -94,29 +114,66 @@ pub trait DefaultAppState: HasDefaultUiState {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DefaultRsc<State: 'static> {
|
||||
pub struct DefaultRsc<State: 'static + DefaultAppState> {
|
||||
pub ui: UiData,
|
||||
pub events: EventManager<Self>,
|
||||
pub tasks: Tasks<Self>,
|
||||
pub state: WidgetState,
|
||||
pub widget_events: Vec<WidgetEvent>,
|
||||
pub window_event: EventSender<State>,
|
||||
_state: PhantomData<State>,
|
||||
}
|
||||
|
||||
impl<State> DefaultRsc<State> {
|
||||
fn init(window: Arc<Window>) -> (Self, TaskMsgReceiver<Self>) {
|
||||
let (tasks, recv) = Tasks::init(window);
|
||||
pub struct WidgetEvent {
|
||||
id: WidgetId,
|
||||
ty: WidgetEventType,
|
||||
}
|
||||
|
||||
pub enum WidgetEventType {
|
||||
Draw,
|
||||
Undraw,
|
||||
Remove,
|
||||
}
|
||||
|
||||
pub trait MainCallback<State>: FnOnce(&mut DefaultRsc<State>) + Sync + Send + 'static {}
|
||||
impl<F: FnOnce(&mut DefaultRsc<State>) + Sync + Send + 'static, State> MainCallback<State> for F {}
|
||||
|
||||
pub enum UiMainEvent<State: DefaultAppState> {
|
||||
RequestUpdate,
|
||||
Callback(Box<dyn MainCallback<State>>),
|
||||
App(State::Event),
|
||||
}
|
||||
|
||||
impl<State: DefaultAppState> DefaultRsc<State> {
|
||||
fn init(proxy: EventLoopProxy<UiMainEvent<State>>) -> (Self, TaskMsgReceiver<Self>) {
|
||||
let window_event = EventSender {
|
||||
proxy: proxy.clone(),
|
||||
};
|
||||
let (tasks, recv) = Tasks::init(move || {
|
||||
if proxy.send_event(UiMainEvent::RequestUpdate).is_err() {
|
||||
panic!("main thread blew up or smth");
|
||||
}
|
||||
});
|
||||
(
|
||||
Self {
|
||||
ui: Default::default(),
|
||||
events: Default::default(),
|
||||
tasks,
|
||||
widget_events: Default::default(),
|
||||
state: Default::default(),
|
||||
window_event,
|
||||
_state: Default::default(),
|
||||
},
|
||||
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: DefaultAppState> UiRsc for DefaultRsc<State> {
|
||||
fn ui(&self) -> &UiData {
|
||||
&self.ui
|
||||
}
|
||||
@@ -125,24 +182,39 @@ impl<State> UiRsc for DefaultRsc<State> {
|
||||
&mut self.ui
|
||||
}
|
||||
|
||||
fn on_draw(&mut self, active: &ActiveData) {
|
||||
fn on_draw(&mut self, active: &ActiveData, redrawn: bool) {
|
||||
self.events.draw(active);
|
||||
if !redrawn {
|
||||
self.widget_events.push(WidgetEvent {
|
||||
id: active.id,
|
||||
ty: WidgetEventType::Draw,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn on_undraw(&mut self, active: &ActiveData) {
|
||||
self.events.undraw(active);
|
||||
self.widget_events.push(WidgetEvent {
|
||||
id: active.id,
|
||||
ty: WidgetEventType::Undraw,
|
||||
});
|
||||
}
|
||||
|
||||
fn on_remove(&mut self, id: WidgetId) {
|
||||
self.events.remove(id);
|
||||
self.state.remove(id);
|
||||
self.widget_events.push(WidgetEvent {
|
||||
id,
|
||||
ty: WidgetEventType::Remove,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl<State: 'static> HasState for DefaultRsc<State> {
|
||||
impl<State: 'static + DefaultAppState> HasState for DefaultRsc<State> {
|
||||
type State = State;
|
||||
}
|
||||
|
||||
impl<State: 'static> HasEvents for DefaultRsc<State> {
|
||||
impl<State: 'static + DefaultAppState> HasEvents for DefaultRsc<State> {
|
||||
fn events(&self) -> &EventManager<Self> {
|
||||
&self.events
|
||||
}
|
||||
@@ -152,12 +224,22 @@ impl<State: 'static> HasEvents for DefaultRsc<State> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<State: 'static> HasTasks for DefaultRsc<State> {
|
||||
impl<State: 'static + DefaultAppState> HasTasks for DefaultRsc<State> {
|
||||
fn tasks_mut(&mut self) -> &mut Tasks<Self> {
|
||||
&mut self.tasks
|
||||
}
|
||||
}
|
||||
|
||||
impl<State: 'static + DefaultAppState> 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> {
|
||||
rsc: DefaultRsc<State>,
|
||||
render: UiRenderState,
|
||||
@@ -166,15 +248,15 @@ pub struct DefaultApp<State: DefaultAppState> {
|
||||
}
|
||||
|
||||
impl<State: DefaultAppState> AppState for DefaultApp<State> {
|
||||
type Event = State::Event;
|
||||
type Event = UiMainEvent<State>;
|
||||
|
||||
fn new(event_loop: &ActiveEventLoop, proxy: EventLoopProxy<Self::Event>) -> Self {
|
||||
let window = event_loop
|
||||
.create_window(State::window_attributes())
|
||||
.unwrap();
|
||||
let default_state = DefaultUiState::new(window);
|
||||
let (mut rsc, task_recv) = DefaultRsc::init(default_state.window.clone());
|
||||
let state = State::new(default_state, &mut rsc, proxy);
|
||||
let (mut rsc, task_recv) = DefaultRsc::init(proxy);
|
||||
let state = State::new(default_state, &mut rsc);
|
||||
let render = UiRenderState::new();
|
||||
Self {
|
||||
rsc,
|
||||
@@ -185,38 +267,39 @@ impl<State: DefaultAppState> AppState for DefaultApp<State> {
|
||||
}
|
||||
|
||||
fn event(&mut self, event: Self::Event, _: &ActiveEventLoop) {
|
||||
self.state.event(event, &mut self.rsc, &mut self.render);
|
||||
match event {
|
||||
UiMainEvent::RequestUpdate => {
|
||||
self.check_updates();
|
||||
}
|
||||
UiMainEvent::App(event) => {
|
||||
self.state.event(event, &mut self.rsc, &mut self.render);
|
||||
}
|
||||
UiMainEvent::Callback(f) => f(&mut self.rsc),
|
||||
}
|
||||
}
|
||||
|
||||
fn window_event(&mut self, event: WindowEvent, event_loop: &ActiveEventLoop) {
|
||||
let Self {
|
||||
rsc,
|
||||
render,
|
||||
state,
|
||||
task_recv,
|
||||
rsc, render, state, ..
|
||||
} = self;
|
||||
|
||||
for update in task_recv.try_iter() {
|
||||
update(state, rsc);
|
||||
}
|
||||
|
||||
// input handling
|
||||
let ui_state = state.default_state_mut();
|
||||
let input_changed = ui_state.input.event(&event);
|
||||
let cursor_state = ui_state.cursor_state().clone();
|
||||
let old = ui_state.focus;
|
||||
if cursor_state.buttons.left.is_start() {
|
||||
ui_state.focus = None;
|
||||
}
|
||||
if input_changed {
|
||||
if ui_state.input.event(&event) {
|
||||
let cursor_state = ui_state.cursor_state().clone();
|
||||
let old = ui_state.focus;
|
||||
if cursor_state.buttons.left.is_start() {
|
||||
ui_state.focus = None;
|
||||
}
|
||||
let window_size = ui_state.window_size();
|
||||
render.run_sensors(rsc, state, cursor_state, window_size);
|
||||
if old != state.default_state().focus
|
||||
&& let Some(old) = old
|
||||
{
|
||||
old.edit(rsc).deselect();
|
||||
}
|
||||
}
|
||||
let ui_state = state.default_state_mut();
|
||||
if old != ui_state.focus
|
||||
&& let Some(old) = old
|
||||
{
|
||||
old.edit(rsc).deselect();
|
||||
}
|
||||
match &event {
|
||||
WindowEvent::CloseRequested => event_loop.exit(),
|
||||
WindowEvent::RedrawRequested => {
|
||||
@@ -278,14 +361,94 @@ impl<State: DefaultAppState> AppState for DefaultApp<State> {
|
||||
_ => (),
|
||||
}
|
||||
state.window_event(event, rsc, render);
|
||||
let ui_state = self.state.default_state_mut();
|
||||
if render.needs_redraw(&ui_state.root, rsc.widgets()) {
|
||||
ui_state.renderer.window().request_redraw();
|
||||
}
|
||||
ui_state.input.end_frame();
|
||||
|
||||
self.check_updates();
|
||||
self.state.default_state_mut().input.end_frame();
|
||||
}
|
||||
|
||||
fn exit(&mut self) {
|
||||
self.state.exit(&mut self.rsc, &mut self.render);
|
||||
}
|
||||
}
|
||||
|
||||
impl<State: DefaultAppState> DefaultApp<State> {
|
||||
pub fn check_updates(&mut self) {
|
||||
let Self {
|
||||
rsc,
|
||||
render,
|
||||
state,
|
||||
task_recv,
|
||||
} = self;
|
||||
|
||||
for update in task_recv.try_iter() {
|
||||
update(state, rsc);
|
||||
}
|
||||
|
||||
let mut events = std::mem::take(&mut rsc.widget_events);
|
||||
for event in events.drain(..) {
|
||||
match event.ty {
|
||||
WidgetEventType::Draw => {
|
||||
rsc.run_event::<Draw>(event.id, (), state);
|
||||
}
|
||||
WidgetEventType::Undraw => {
|
||||
rsc.run_event::<Undraw>(event.id, (), state);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
rsc.widget_events = events;
|
||||
|
||||
let ui_state = state.default_state();
|
||||
if render.needs_redraw(&ui_state.root, rsc.widgets()) {
|
||||
ui_state.renderer.window().request_redraw();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 + DefaultAppState, 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 + DefaultAppState, 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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
+5
-6
@@ -13,7 +13,6 @@ use tokio::{
|
||||
unbounded_channel as async_channel,
|
||||
},
|
||||
};
|
||||
use winit::window::Window;
|
||||
|
||||
pub type TaskMsgSender<Rsc> = SyncSender<Box<dyn TaskUpdate<Rsc>>>;
|
||||
pub type TaskMsgReceiver<Rsc> = SyncReceiver<Box<dyn TaskUpdate<Rsc>>>;
|
||||
@@ -23,7 +22,7 @@ impl<F: FnOnce(&mut Rsc::State, &mut Rsc) + Send, Rsc: HasState> TaskUpdate<Rsc>
|
||||
|
||||
pub struct Tasks<Rsc: HasState> {
|
||||
start: AsyncSender<BoxTask>,
|
||||
window: Arc<Window>,
|
||||
request_update: Arc<dyn Fn() + Send + Sync>,
|
||||
msg_send: SyncSender<Box<dyn TaskUpdate<Rsc>>>,
|
||||
}
|
||||
|
||||
@@ -45,7 +44,7 @@ impl<Rsc: HasState + 'static> TaskCtx<Rsc> {
|
||||
type BoxTask = Pin<Box<dyn Future<Output = ()> + Send>>;
|
||||
|
||||
impl<Rsc: HasState> Tasks<Rsc> {
|
||||
pub fn init(window: Arc<Window>) -> (Self, TaskMsgReceiver<Rsc>) {
|
||||
pub fn init(request_update: impl Fn() + 'static + Send + Sync) -> (Self, TaskMsgReceiver<Rsc>) {
|
||||
let (start, start_recv) = async_channel();
|
||||
let (msgs, msgs_recv) = sync_channel();
|
||||
std::thread::spawn(|| {
|
||||
@@ -56,7 +55,7 @@ impl<Rsc: HasState> Tasks<Rsc> {
|
||||
Self {
|
||||
start,
|
||||
msg_send: msgs,
|
||||
window,
|
||||
request_update: Arc::new(request_update),
|
||||
},
|
||||
msgs_recv,
|
||||
)
|
||||
@@ -67,10 +66,10 @@ impl<Rsc: HasState> Tasks<Rsc> {
|
||||
F::CallOnceFuture: Send,
|
||||
{
|
||||
let send = self.msg_send.clone();
|
||||
let window = self.window.clone();
|
||||
let request_update = self.request_update.clone();
|
||||
let _ = self.start.send(Box::pin(async move {
|
||||
task(TaskCtx::new(send)).await;
|
||||
window.request_redraw();
|
||||
request_update();
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,6 +57,13 @@ widget_trait! {
|
||||
|
||||
pub trait HasTasks: Sized + HasState + HasEvents {
|
||||
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>:
|
||||
|
||||
@@ -5,6 +5,7 @@ mod ptr;
|
||||
mod rect;
|
||||
mod text;
|
||||
mod trait_fns;
|
||||
mod selector;
|
||||
|
||||
pub use image::*;
|
||||
pub use mask::*;
|
||||
@@ -13,3 +14,4 @@ pub use ptr::*;
|
||||
pub use rect::*;
|
||||
pub use text::*;
|
||||
pub use trait_fns::*;
|
||||
pub use selector::*;
|
||||
|
||||
@@ -152,32 +152,32 @@ impl Span {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SpanBuilder<State, const LEN: usize, Wa: WidgetArrLike<State, LEN, Tag>, Tag> {
|
||||
pub children: Wa,
|
||||
pub struct SpanBuilder<Children, Rsc, Tag, GTag> {
|
||||
pub children: Children,
|
||||
pub dir: Dir,
|
||||
pub gap: f32,
|
||||
_pd: PhantomData<(State, Tag)>,
|
||||
_pd: PhantomData<(Rsc, Tag, GTag)>,
|
||||
}
|
||||
|
||||
impl<Rsc, const LEN: usize, Wa: WidgetArrLike<Rsc, LEN, Tag>, Tag> WidgetFnTrait<Rsc>
|
||||
for SpanBuilder<Rsc, LEN, Wa, Tag>
|
||||
impl<Children: IntoWidgetVec<Rsc, Tag, GTag>, Rsc, Tag, GTag> WidgetFnTrait<Rsc>
|
||||
for SpanBuilder<Children, Rsc, Tag, GTag>
|
||||
{
|
||||
type Widget = Span;
|
||||
|
||||
#[track_caller]
|
||||
fn run(self, rsc: &mut Rsc) -> Self::Widget {
|
||||
Span {
|
||||
children: self.children.add(rsc).arr.into_iter().collect(),
|
||||
children: self.children.into_vec(rsc),
|
||||
dir: self.dir,
|
||||
gap: self.gap,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<State, const LEN: usize, Wa: WidgetArrLike<State, LEN, Tag>, Tag>
|
||||
SpanBuilder<State, LEN, Wa, Tag>
|
||||
impl<Children: IntoWidgetVec<Rsc, Tag, GTag>, Rsc, Tag, GTag>
|
||||
SpanBuilder<Children, Rsc, Tag, GTag>
|
||||
{
|
||||
pub fn new(children: Wa, dir: Dir) -> Self {
|
||||
pub fn new(children: Children, dir: Dir) -> Self {
|
||||
Self {
|
||||
children,
|
||||
dir,
|
||||
|
||||
@@ -42,30 +42,28 @@ pub enum StackSize {
|
||||
Child(usize),
|
||||
}
|
||||
|
||||
pub struct StackBuilder<State, const LEN: usize, Wa: WidgetArrLike<State, LEN, Tag>, Tag> {
|
||||
pub children: Wa,
|
||||
pub struct StackBuilder<Children, Rsc, Tag, GTag> {
|
||||
pub children: Children,
|
||||
pub size: StackSize,
|
||||
_pd: PhantomData<(State, Tag)>,
|
||||
_pd: PhantomData<(Rsc, Tag, GTag)>,
|
||||
}
|
||||
|
||||
impl<Rsc, const LEN: usize, Wa: WidgetArrLike<Rsc, LEN, Tag>, Tag> WidgetFnTrait<Rsc>
|
||||
for StackBuilder<Rsc, LEN, Wa, Tag>
|
||||
impl<Children: IntoWidgetVec<Rsc, Tag, GTag>, Rsc, Tag, GTag> WidgetFnTrait<Rsc>
|
||||
for StackBuilder<Children, Rsc, Tag, GTag>
|
||||
{
|
||||
type Widget = Stack;
|
||||
|
||||
#[track_caller]
|
||||
fn run(self, rsc: &mut Rsc) -> Self::Widget {
|
||||
Stack {
|
||||
children: self.children.add(rsc).arr.into_iter().collect(),
|
||||
children: self.children.into_vec(rsc),
|
||||
size: self.size,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<State, const LEN: usize, Wa: WidgetArrLike<State, LEN, Tag>, Tag>
|
||||
StackBuilder<State, LEN, Wa, Tag>
|
||||
{
|
||||
pub fn new(children: Wa) -> Self {
|
||||
impl<Children: IntoWidgetVec<Rsc, Tag, GTag>, Rsc, Tag, GTag> StackBuilder<Children, Rsc, Tag, GTag> {
|
||||
pub fn new(children: Children) -> Self {
|
||||
Self {
|
||||
children,
|
||||
size: StackSize::default(),
|
||||
|
||||
+4
-2
@@ -30,8 +30,10 @@ impl Widget for WidgetPtr {
|
||||
}
|
||||
|
||||
impl WidgetPtr {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
pub fn new(widget: StrongWidget) -> Self {
|
||||
Self {
|
||||
inner: Some(widget),
|
||||
}
|
||||
}
|
||||
pub fn empty() -> Self {
|
||||
Self {
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
use std::hash::Hash;
|
||||
|
||||
use iris_core::util::HashMap;
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
pub struct WidgetSelector<T> {
|
||||
current: (T, StrongWidget),
|
||||
map: HashMap<T, StrongWidget>,
|
||||
}
|
||||
|
||||
impl<T: Hash + Eq> WidgetSelector<T> {
|
||||
pub fn new(key: T, widget: StrongWidget) -> Self {
|
||||
Self {
|
||||
current: (key, widget),
|
||||
map: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set(&mut self, key: T, widget: StrongWidget) {
|
||||
self.map.insert(key, widget);
|
||||
}
|
||||
|
||||
pub fn select(&mut self, key: T) -> bool {
|
||||
if let Some(val) = self.map.remove(&key) {
|
||||
let mut new = (key, val);
|
||||
std::mem::swap(&mut new, &mut self.current);
|
||||
self.map.insert(new.0, new.1);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> Widget for WidgetSelector<T> {
|
||||
fn draw(&mut self, painter: &mut Painter) {
|
||||
painter.widget(&self.current.1);
|
||||
}
|
||||
|
||||
fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len {
|
||||
ctx.width(&self.current.1)
|
||||
}
|
||||
|
||||
fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len {
|
||||
ctx.height(&self.current.1)
|
||||
}
|
||||
}
|
||||
+50
-7
@@ -131,18 +131,61 @@ widget_trait! {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CoreWidgetArr<Rsc, const LEN: usize, Wa: WidgetArrLike<Rsc, LEN, Tag>, Tag> {
|
||||
fn span(self, dir: Dir) -> SpanBuilder<Rsc, LEN, Wa, Tag>;
|
||||
fn stack(self) -> StackBuilder<Rsc, LEN, Wa, Tag>;
|
||||
pub trait CoreWidgetArr<Children, Rsc, Tag, GTag> {
|
||||
fn span(self, dir: Dir) -> SpanBuilder<Children, Rsc, Tag, GTag>;
|
||||
fn stack(self) -> StackBuilder<Children, Rsc, Tag, GTag>;
|
||||
}
|
||||
|
||||
impl<State, const LEN: usize, Wa: WidgetArrLike<State, LEN, Tag>, Tag>
|
||||
CoreWidgetArr<State, LEN, Wa, Tag> for Wa
|
||||
impl<Children: IntoWidgetVec<Rsc, Tag, GTag>, Rsc, Tag, GTag>
|
||||
CoreWidgetArr<Children, Rsc, Tag, GTag> for Children
|
||||
{
|
||||
fn span(self, dir: Dir) -> SpanBuilder<State, LEN, Wa, Tag> {
|
||||
fn span(self, dir: Dir) -> SpanBuilder<Children, Rsc, Tag, GTag> {
|
||||
SpanBuilder::new(self, dir)
|
||||
}
|
||||
fn stack(self) -> StackBuilder<State, LEN, Wa, Tag> {
|
||||
fn stack(self) -> StackBuilder<Children, Rsc, Tag, GTag> {
|
||||
StackBuilder::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait RscFnMap<Rsc> {
|
||||
type Input;
|
||||
fn rsc_map<O>(
|
||||
self,
|
||||
f: impl Fn(Self::Input, &mut Rsc) -> O + Clone,
|
||||
) -> impl Iterator<Item = impl FnOnce(&mut Rsc) -> O>;
|
||||
}
|
||||
|
||||
impl<I: IntoIterator, Rsc> RscFnMap<Rsc> for I {
|
||||
type Input = I::Item;
|
||||
fn rsc_map<O>(
|
||||
self,
|
||||
f: impl Fn(Self::Input, &mut Rsc) -> O + Clone,
|
||||
) -> impl Iterator<Item = impl FnOnce(&mut Rsc) -> O> {
|
||||
self.into_iter().map(move |i| {
|
||||
let f = f.clone();
|
||||
move |rsc: &mut Rsc| f(i, rsc)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub trait WidgetFnMap<Rsc: UiRsc> {
|
||||
fn widget_map<O: WidgetLike<Rsc, Tag>, Tag>(
|
||||
self,
|
||||
f: impl Fn(WeakWidget) -> O + Clone,
|
||||
) -> impl Iterator<Item = impl FnOnce(&mut Rsc) -> WeakWidget>;
|
||||
}
|
||||
|
||||
impl<I: IntoIterator, Rsc: UiRsc> WidgetFnMap<Rsc> for I
|
||||
where
|
||||
I::Item: WidgetIdFn<Rsc>,
|
||||
{
|
||||
fn widget_map<O: WidgetLike<Rsc, Tag>, Tag>(
|
||||
self,
|
||||
f: impl Fn(WeakWidget) -> O + Clone,
|
||||
) -> impl Iterator<Item = impl FnOnce(&mut Rsc) -> WeakWidget> {
|
||||
self.into_iter().map(move |f2| {
|
||||
let f = f.clone();
|
||||
move |rsc: &mut Rsc| f(f2(rsc)).add(rsc) as WeakWidget
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user