Compare commits

...
Author SHA1 Message Date
iris fa771a99eb update stuff 2026-08-03 20:33:59 -04:00
iris e817bc83af const trait keyword order 2026-06-23 21:30:09 -04:00
iris a648c62aa2 update 2026-04-15 20:31:52 -04:00
iris c118bb446b some potentially nice trait stuff 2026-03-15 21:07:06 -04:00
iris 1102dc7338 work 2026-02-26 19:18:27 -05:00
iris 1aadef0e7e fix Draw (redraw) 2026-02-21 00:19:39 -05:00
iris 426ff0adfc oop 2026-02-18 16:49:59 -05:00
iris dab6cf298a Merge branch 'work' of git.arirex.me:shadowcat/iris into work 2026-02-17 18:14:38 -05:00
iris 38d896d44d selector 2026-02-17 18:14:19 -05:00
32 changed files with 1046 additions and 746 deletions

No files matched your search

Generated
+698 -615
View File
File diff suppressed because it is too large. Load diff
+2 -2
View File
@@ -28,9 +28,9 @@ version = "0.1.0"
edition = "2024" edition = "2024"
[workspace.dependencies] [workspace.dependencies]
pollster = "0.4.0" pollster = "1.0.1"
winit = "0.30.12" winit = "0.30.12"
wgpu = "28.0.0" wgpu = "30.0.0"
bytemuck = "1.23.1" bytemuck = "1.23.1"
image = "0.25.6" image = "0.25.6"
cosmic-text = "0.16.0" cosmic-text = "0.16.0"
-2
View File
@@ -2,10 +2,8 @@
#![feature(const_ops)] #![feature(const_ops)]
#![feature(const_trait_impl)] #![feature(const_trait_impl)]
#![feature(const_convert)] #![feature(const_convert)]
#![feature(map_try_insert)]
#![feature(unboxed_closures)] #![feature(unboxed_closures)]
#![feature(fn_traits)] #![feature(fn_traits)]
#![feature(const_cmp)]
#![feature(const_destruct)] #![feature(const_destruct)]
#![feature(portable_simd)] #![feature(portable_simd)]
#![feature(associated_type_defaults)] #![feature(associated_type_defaults)]
+5 -5
View File
@@ -5,19 +5,19 @@ pub const trait UiNum {
fn to_f32(self) -> f32; fn to_f32(self) -> f32;
} }
impl const UiNum for f32 { const impl UiNum for f32 {
fn to_f32(self) -> f32 { fn to_f32(self) -> f32 {
self self
} }
} }
impl const UiNum for u32 { const impl UiNum for u32 {
fn to_f32(self) -> f32 { fn to_f32(self) -> f32 {
self as f32 self as f32
} }
} }
impl const UiNum for i32 { const impl UiNum for i32 {
fn to_f32(self) -> f32 { fn to_f32(self) -> f32 {
self as f32 self as f32
} }
@@ -27,7 +27,7 @@ pub const fn vec2(x: impl const UiNum, y: impl const UiNum) -> Vec2 {
Vec2::new(x.to_f32(), y.to_f32()) Vec2::new(x.to_f32(), y.to_f32())
} }
impl<T: const UiNum + Copy> const From<T> for Vec2 { const impl<T: const UiNum + Copy> From<T> for Vec2 {
fn from(v: T) -> Self { fn from(v: T) -> Self {
Self { Self {
x: v.to_f32(), x: v.to_f32(),
@@ -36,7 +36,7 @@ impl<T: const UiNum + Copy> const From<T> for Vec2 {
} }
} }
impl<T: const UiNum, U: const UiNum> const From<(T, U)> for Vec2 const impl<T: const UiNum, U: const UiNum> From<(T, U)> for Vec2
where where
(T, U): const Destruct, (T, U): const Destruct,
{ {
+1 -1
View File
@@ -187,7 +187,7 @@ impl From<CardinalAlign> for Align {
} }
} }
impl const From<RegionAlign> for UiVec2 { const impl From<RegionAlign> for UiVec2 {
fn from(align: RegionAlign) -> Self { fn from(align: RegionAlign) -> Self {
Self::rel(align.rel()) Self::rel(align.rel())
} }
+2 -2
View File
@@ -74,14 +74,14 @@ pub const trait AxisT {
} }
pub struct XAxis; pub struct XAxis;
impl const AxisT for XAxis { const impl AxisT for XAxis {
fn get() -> Axis { fn get() -> Axis {
Axis::X Axis::X
} }
} }
pub struct YAxis; pub struct YAxis;
impl const AxisT for YAxis { const impl AxisT for YAxis {
fn get() -> Axis { fn get() -> Axis {
Axis::Y Axis::Y
} }
+2 -2
View File
@@ -124,13 +124,13 @@ impl Display for UiVec2 {
impl_op!(UiVec2 Add add; x y); impl_op!(UiVec2 Add add; x y);
impl_op!(UiVec2 Sub sub; x y); impl_op!(UiVec2 Sub sub; x y);
impl const From<Vec2> for UiVec2 { const impl From<Vec2> for UiVec2 {
fn from(abs: Vec2) -> Self { fn from(abs: Vec2) -> Self {
Self::abs(abs) Self::abs(abs)
} }
} }
impl<T: const UiNum, U: const UiNum> const From<(T, U)> for UiVec2 const impl<T: const UiNum, U: const UiNum> From<(T, U)> for UiVec2
where where
(T, U): const Destruct, (T, U): const Destruct,
{ {
+2 -2
View File
@@ -144,7 +144,7 @@ impl ColorNum for f32 {
unsafe impl bytemuck::Pod for Color<u8> {} unsafe impl bytemuck::Pod for Color<u8> {}
impl const F32Conversion for f32 { const impl F32Conversion for f32 {
fn to(self) -> f32 { fn to(self) -> f32 {
self self
} }
@@ -153,7 +153,7 @@ impl const F32Conversion for f32 {
} }
} }
impl const F32Conversion for u8 { const impl F32Conversion for u8 {
fn to(self) -> f32 { fn to(self) -> f32 {
self as f32 / 255.0 self as f32 / 255.0
} }
+2 -2
View File
@@ -188,7 +188,7 @@ impl UiRenderNode {
let pipeline_layout = device.create_pipeline_layout(&PipelineLayoutDescriptor { let pipeline_layout = device.create_pipeline_layout(&PipelineLayoutDescriptor {
label: Some("UI Shape Pipeline Layout"), label: Some("UI Shape Pipeline Layout"),
bind_group_layouts: &[&uniform_layout, &primitive_layout, &rsc_layout], bind_group_layouts: &[&uniform_layout, &primitive_layout, &rsc_layout].map(Some),
immediate_size: 0, immediate_size: 0,
}); });
let pipeline = device.create_render_pipeline(&RenderPipelineDescriptor { let pipeline = device.create_render_pipeline(&RenderPipelineDescriptor {
@@ -197,7 +197,7 @@ impl UiRenderNode {
vertex: VertexState { vertex: VertexState {
module: &shader, module: &shader,
entry_point: Some("vs_main"), entry_point: Some("vs_main"),
buffers: &[PrimitiveInstance::desc()], buffers: &[Some(PrimitiveInstance::desc())],
compilation_options: Default::default(), compilation_options: Default::default(),
}, },
fragment: Some(FragmentState { fragment: Some(FragmentState {
+5 -3
View File
@@ -1,3 +1,5 @@
enable wgpu_binding_array;
const RECT: u32 = 0u; const RECT: u32 = 0u;
const TEXTURE: u32 = 1u; const TEXTURE: u32 = 1u;
@@ -65,9 +67,9 @@ struct VertexOutput {
@location(0) top_left: vec2<f32>, @location(0) top_left: vec2<f32>,
@location(1) bot_right: vec2<f32>, @location(1) bot_right: vec2<f32>,
@location(2) uv: vec2<f32>, @location(2) uv: vec2<f32>,
@location(3) binding: u32, @location(3) @interpolate(flat) binding: u32,
@location(4) idx: u32, @location(4) @interpolate(flat) idx: u32,
@location(5) mask_idx: u32, @location(5) @interpolate(flat) mask_idx: u32,
@builtin(position) clip_position: vec4<f32>, @builtin(position) clip_position: vec4<f32>,
}; };
+1 -1
View File
@@ -28,7 +28,7 @@ pub trait UiRsc {
#[allow(unused_variables)] #[allow(unused_variables)]
fn on_remove(&mut self, id: WidgetId) {} fn on_remove(&mut self, id: WidgetId) {}
#[allow(unused_variables)] #[allow(unused_variables)]
fn on_draw(&mut self, active: &ActiveData) {} fn on_draw(&mut self, active: &ActiveData, redrawn: bool) {}
#[allow(unused_variables)] #[allow(unused_variables)]
fn on_undraw(&mut self, active: &ActiveData) {} fn on_undraw(&mut self, active: &ActiveData) {}
+3 -1
View File
@@ -81,10 +81,12 @@ impl UiRenderState {
old_children: Option<Vec<WidgetId>>, old_children: Option<Vec<WidgetId>>,
rsc: &mut dyn UiRsc, rsc: &mut dyn UiRsc,
) { ) {
let mut redrawn = old_children.is_some();
let mut old_children = old_children.unwrap_or_default(); let mut old_children = old_children.unwrap_or_default();
if let Some(active) = self.active.get_mut(&id) if let Some(active) = self.active.get_mut(&id)
&& !rsc.widgets().needs_redraw.contains(&id) && !rsc.widgets().needs_redraw.contains(&id)
{ {
redrawn = true;
// check to see if we can skip drawing first // check to see if we can skip drawing first
if active.region == region { if active.region == region {
return; return;
@@ -149,7 +151,7 @@ impl UiRenderState {
} }
} }
rsc.on_draw(&active); rsc.on_draw(&active, redrawn);
self.active.insert(id, active); self.active.insert(id, active);
} }
+9 -8
View File
@@ -9,15 +9,16 @@ pub const trait DivOr {
fn div_or(self, rhs: Self, other: Self) -> Self; fn div_or(self, rhs: Self, other: Self) -> Self;
} }
impl const DivOr for f32 { const impl DivOr for f32 {
fn div_or(self, rhs: Self, other: Self) -> Self { fn div_or(self, rhs: Self, other: Self) -> Self {
let res = self / rhs; let res = self / rhs;
if res.is_nan() { other } else { res } if res.is_nan() { other } else { res }
} }
} }
impl<T: const Add<Output = T> + const Sub<Output = T> + const Mul<Output = T> + const DivOr + Copy> const const impl<
LerpUtil for T T: const Add<Output = T> + const Sub<Output = T> + const Mul<Output = T> + const DivOr + Copy,
> LerpUtil for T
{ {
/// linear interpolation /// linear interpolation
/// from * (1.0 - self) + to * self /// from * (1.0 - self) + to * self
@@ -37,7 +38,7 @@ macro_rules! impl_op {
use super::*; use super::*;
#[allow(unused_imports)] #[allow(unused_imports)]
use std::ops::*; use std::ops::*;
impl const $op for $T { const impl $op for $T {
type Output = Self; type Output = Self;
fn $fn(self, rhs: Self) -> Self::Output { fn $fn(self, rhs: Self) -> Self::Output {
@@ -46,12 +47,12 @@ macro_rules! impl_op {
} }
} }
} }
impl const $opa for $T { const impl $opa for $T {
fn $fna(&mut self, rhs: Self) { fn $fna(&mut self, rhs: Self) {
*self = self.$fn(rhs); *self = self.$fn(rhs);
} }
} }
impl const $op<f32> for $T { const impl $op<f32> for $T {
type Output = Self; type Output = Self;
fn $fn(self, rhs: f32) -> Self::Output { fn $fn(self, rhs: f32) -> Self::Output {
@@ -60,7 +61,7 @@ macro_rules! impl_op {
} }
} }
} }
impl const $op<$T> for f32 { const impl $op<$T> for f32 {
type Output = $T; type Output = $T;
fn $fn(self, rhs: $T) -> Self::Output { fn $fn(self, rhs: $T) -> Self::Output {
@@ -69,7 +70,7 @@ macro_rules! impl_op {
} }
} }
} }
impl const $opa<f32> for $T { const impl $opa<f32> for $T {
fn $fna(&mut self, rhs: f32) { fn $fna(&mut self, rhs: f32) {
*self = self.$fn(rhs); *self = self.$fn(rhs);
} }
+1 -1
View File
@@ -67,7 +67,7 @@ impl_op!(Vec2 Sub sub; x y);
impl_op!(Vec2 Mul mul; x y); impl_op!(Vec2 Mul mul; x y);
impl_op!(Vec2 Div div; x y); impl_op!(Vec2 Div div; x y);
impl const DivOr for Vec2 { const impl DivOr for Vec2 {
fn div_or(self, rhs: Self, other: Self) -> Self { fn div_or(self, rhs: Self, other: Self) -> Self {
Self { Self {
x: self.x.div_or(rhs.x, other.x), x: self.x.div_or(rhs.x, other.x),
+8 -1
View File
@@ -34,7 +34,7 @@ pub trait HasRoot {
pub trait WidgetArrLike<Rsc, const LEN: usize, Tag> { pub trait WidgetArrLike<Rsc, const LEN: usize, Tag> {
#[track_caller] #[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> { 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
View File
@@ -1,4 +1,4 @@
use crate::{Axis, AxisT, Len, Painter, SizeCtx}; use crate::{Axis, AxisT, Len, Painter, SizeCtx, UiRsc};
use std::any::Any; use std::any::Any;
mod data; mod data;
@@ -85,3 +85,16 @@ impl<State, F: FnOnce(&mut State) -> Option<StrongWidget>> WidgetOption<State> f
self(state) 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()
}
}
+1
View File
@@ -62,3 +62,4 @@ impl<Rsc: UiRsc, V: WidgetView> WidgetLike<Rsc, ViewTag> for V {
} }
pub struct ArrTag; pub struct ArrTag;
pub struct IterTag;
+1 -5
View File
@@ -10,11 +10,7 @@ struct State {
} }
impl DefaultAppState for State { impl DefaultAppState for State {
fn new( fn new(mut ui_state: DefaultUiState, rsc: &mut DefaultRsc<Self>) -> Self {
mut ui_state: DefaultUiState,
rsc: &mut DefaultRsc<Self>,
_: Proxy<Self::Event>,
) -> Self {
rect(Color::RED).set_root(rsc, &mut ui_state); rect(Color::RED).set_root(rsc, &mut ui_state);
Self { ui_state } Self { ui_state }
} }
+2 -6
View File
@@ -16,11 +16,7 @@ pub struct Client {
} }
impl DefaultAppState for Client { impl DefaultAppState for Client {
fn new( fn new(mut ui_state: DefaultUiState, rsc: &mut DefaultRsc<Self>) -> Self {
mut ui_state: DefaultUiState,
rsc: &mut DefaultRsc<Self>,
_: Proxy<Self::Event>,
) -> Self {
let rrect = rect(Color::WHITE).radius(20); let rrect = rect(Color::WHITE).radius(20);
let pad_test = ( let pad_test = (
rrect.color(Color::BLUE), rrect.color(Color::BLUE),
@@ -148,7 +144,7 @@ impl DefaultAppState for Client {
.span(Dir::DOWN) .span(Dir::DOWN)
.add(rsc); .add(rsc);
let main = WidgetPtr::new().add(rsc); let main = WidgetPtr::empty().add(rsc);
let vals = Rc::new(RefCell::new((0, Vec::new()))); let vals = Rc::new(RefCell::new((0, Vec::new())));
let mut switch_button = |color, to: WeakWidget, label| { let mut switch_button = |color, to: WeakWidget, label| {
+1 -5
View File
@@ -11,11 +11,7 @@ struct State {
} }
impl DefaultAppState for State { impl DefaultAppState for State {
fn new( fn new(mut ui_state: DefaultUiState, rsc: &mut DefaultRsc<Self>) -> Self {
mut ui_state: DefaultUiState,
rsc: &mut DefaultRsc<Self>,
_: Proxy<Self::Event>,
) -> Self {
let rect = rect(Color::RED).add(rsc); let rect = rect(Color::RED).add(rsc);
rect.task_on(CursorSense::click(), async move |mut ctx| { rect.task_on(CursorSense::click(), async move |mut ctx| {
tokio::time::sleep(Duration::from_secs(1)).await; tokio::time::sleep(Duration::from_secs(1)).await;
+1 -5
View File
@@ -36,11 +36,7 @@ impl Test {
} }
impl DefaultAppState for State { impl DefaultAppState for State {
fn new( fn new(mut ui_state: DefaultUiState, rsc: &mut DefaultRsc<Self>) -> Self {
mut ui_state: DefaultUiState,
rsc: &mut DefaultRsc<Self>,
_: Proxy<Self::Event>,
) -> Self {
let test = Test::new(rsc); let test = Test::new(rsc);
test.on(CursorSense::click(), move |_, rsc| { test.on(CursorSense::click(), move |_, rsc| {
+1 -1
View File
@@ -6,7 +6,7 @@ edition.workspace = true
[dependencies] [dependencies]
proc-macro2 = "1.0.103" proc-macro2 = "1.0.103"
quote = "1.0.42" quote = "1.0.42"
syn = { version = "2.0.111", features = ["full"] } syn = { version = "3.0.3", features = ["full"] }
[lib] [lib]
proc-macro = true proc-macro = true
+8
View File
@@ -7,3 +7,11 @@ impl Event for Submit {}
#[derive(Eq, PartialEq, Hash, Clone)] #[derive(Eq, PartialEq, Hash, Clone)]
pub struct Edited; pub struct Edited;
impl Event for 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 {}
+137 -37
View File
@@ -29,7 +29,26 @@ pub use sense::*;
pub use state::*; pub use state::*;
pub use task::*; 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 struct DefaultUiState {
pub root: Option<StrongWidget>, pub root: Option<StrongWidget>,
@@ -70,9 +89,8 @@ pub trait HasDefaultUiState: Sized + 'static {
} }
pub trait DefaultAppState: HasDefaultUiState { pub trait DefaultAppState: HasDefaultUiState {
type Event = (); type Event: Send = ();
fn new(ui_state: DefaultUiState, rsc: &mut DefaultRsc<Self>, proxy: Proxy<Self::Event>) fn new(ui_state: DefaultUiState, rsc: &mut DefaultRsc<Self>) -> Self;
-> Self;
#[allow(unused_variables)] #[allow(unused_variables)]
fn event( fn event(
&mut self, &mut self,
@@ -96,23 +114,54 @@ pub trait DefaultAppState: HasDefaultUiState {
} }
} }
pub struct DefaultRsc<State: 'static> { pub struct DefaultRsc<State: 'static + DefaultAppState> {
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, pub state: WidgetState,
pub widget_events: Vec<WidgetEvent>,
pub window_event: EventSender<State>,
_state: PhantomData<State>, _state: PhantomData<State>,
} }
impl<State> DefaultRsc<State> { pub struct WidgetEvent {
fn init(window: Arc<Window>) -> (Self, TaskMsgReceiver<Self>) { id: WidgetId,
let (tasks, recv) = Tasks::init(window); 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 { Self {
ui: Default::default(), ui: Default::default(),
events: Default::default(), events: Default::default(),
tasks, tasks,
widget_events: Default::default(),
state: Default::default(), state: Default::default(),
window_event,
_state: Default::default(), _state: Default::default(),
}, },
recv, recv,
@@ -124,7 +173,7 @@ impl<State> DefaultRsc<State> {
} }
} }
impl<State> UiRsc for DefaultRsc<State> { impl<State: DefaultAppState> UiRsc for DefaultRsc<State> {
fn ui(&self) -> &UiData { fn ui(&self) -> &UiData {
&self.ui &self.ui
} }
@@ -133,25 +182,39 @@ impl<State> UiRsc for DefaultRsc<State> {
&mut self.ui &mut self.ui
} }
fn on_draw(&mut self, active: &ActiveData) { fn on_draw(&mut self, active: &ActiveData, redrawn: bool) {
self.events.draw(active); self.events.draw(active);
if !redrawn {
self.widget_events.push(WidgetEvent {
id: active.id,
ty: WidgetEventType::Draw,
});
}
} }
fn on_undraw(&mut self, active: &ActiveData) { fn on_undraw(&mut self, active: &ActiveData) {
self.events.undraw(active); self.events.undraw(active);
self.widget_events.push(WidgetEvent {
id: active.id,
ty: WidgetEventType::Undraw,
});
} }
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); 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; type State = State;
} }
impl<State: 'static> HasEvents for DefaultRsc<State> { impl<State: 'static + DefaultAppState> HasEvents for DefaultRsc<State> {
fn events(&self) -> &EventManager<Self> { fn events(&self) -> &EventManager<Self> {
&self.events &self.events
} }
@@ -161,13 +224,13 @@ 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> { fn tasks_mut(&mut self) -> &mut Tasks<Self> {
&mut self.tasks &mut self.tasks
} }
} }
impl<State: 'static> HasWidgetState for DefaultRsc<State> { impl<State: 'static + DefaultAppState> HasWidgetState for DefaultRsc<State> {
fn widget_state(&self) -> &WidgetState { fn widget_state(&self) -> &WidgetState {
&self.state &self.state
} }
@@ -185,15 +248,15 @@ pub struct DefaultApp<State: DefaultAppState> {
} }
impl<State: DefaultAppState> AppState for DefaultApp<State> { 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 { fn new(event_loop: &ActiveEventLoop, proxy: EventLoopProxy<Self::Event>) -> Self {
let window = event_loop let window = event_loop
.create_window(State::window_attributes()) .create_window(State::window_attributes())
.unwrap(); .unwrap();
let default_state = DefaultUiState::new(window); let default_state = DefaultUiState::new(window);
let (mut rsc, task_recv) = DefaultRsc::init(default_state.window.clone()); let (mut rsc, task_recv) = DefaultRsc::init(proxy);
let state = State::new(default_state, &mut rsc, proxy); let state = State::new(default_state, &mut rsc);
let render = UiRenderState::new(); let render = UiRenderState::new();
Self { Self {
rsc, rsc,
@@ -204,38 +267,39 @@ impl<State: DefaultAppState> AppState for DefaultApp<State> {
} }
fn event(&mut self, event: Self::Event, _: &ActiveEventLoop) { fn event(&mut self, event: Self::Event, _: &ActiveEventLoop) {
match event {
UiMainEvent::RequestUpdate => {
self.check_updates();
}
UiMainEvent::App(event) => {
self.state.event(event, &mut self.rsc, &mut self.render); 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) { fn window_event(&mut self, event: WindowEvent, event_loop: &ActiveEventLoop) {
let Self { let Self {
rsc, rsc, render, state, ..
render,
state,
task_recv,
} = self; } = self;
for update in task_recv.try_iter() { // input handling
update(state, rsc);
}
let ui_state = state.default_state_mut(); let ui_state = state.default_state_mut();
let input_changed = ui_state.input.event(&event); if ui_state.input.event(&event) {
let cursor_state = ui_state.cursor_state().clone(); let cursor_state = ui_state.cursor_state().clone();
let old = ui_state.focus; let old = ui_state.focus;
if cursor_state.buttons.left.is_start() { if cursor_state.buttons.left.is_start() {
ui_state.focus = None; ui_state.focus = None;
} }
if input_changed {
let window_size = ui_state.window_size(); let window_size = ui_state.window_size();
render.run_sensors(rsc, state, cursor_state, window_size); render.run_sensors(rsc, state, cursor_state, window_size);
} if old != state.default_state().focus
let ui_state = state.default_state_mut();
if old != ui_state.focus
&& let Some(old) = old && let Some(old) = old
{ {
old.edit(rsc).deselect(); old.edit(rsc).deselect();
} }
}
let ui_state = state.default_state_mut();
match &event { match &event {
WindowEvent::CloseRequested => event_loop.exit(), WindowEvent::CloseRequested => event_loop.exit(),
WindowEvent::RedrawRequested => { WindowEvent::RedrawRequested => {
@@ -297,11 +361,9 @@ impl<State: DefaultAppState> AppState for DefaultApp<State> {
_ => (), _ => (),
} }
state.window_event(event, rsc, render); state.window_event(event, rsc, render);
let ui_state = self.state.default_state_mut();
if render.needs_redraw(&ui_state.root, rsc.widgets()) { self.check_updates();
ui_state.renderer.window().request_redraw(); self.state.default_state_mut().input.end_frame();
}
ui_state.input.end_frame();
} }
fn exit(&mut self) { fn exit(&mut self) {
@@ -309,13 +371,49 @@ impl<State: DefaultAppState> AppState for DefaultApp<State> {
} }
} }
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> { pub trait RscIdx<Rsc> {
type Output; type Output;
fn get(self, rsc: &Rsc) -> &Self::Output; fn get(self, rsc: &Rsc) -> &Self::Output;
fn get_mut(self, rsc: &mut Rsc) -> &mut 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> { impl<State: 'static + DefaultAppState, I: RscIdx<DefaultRsc<State>>> std::ops::Index<I>
for DefaultRsc<State>
{
type Output = I::Output; type Output = I::Output;
fn index(&self, index: I) -> &Self::Output { fn index(&self, index: I) -> &Self::Output {
@@ -323,7 +421,9 @@ impl<State: 'static, I: RscIdx<DefaultRsc<State>>> std::ops::Index<I> for Defaul
} }
} }
impl<State: 'static, I: RscIdx<DefaultRsc<State>>> std::ops::IndexMut<I> for DefaultRsc<State> { 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 { fn index_mut(&mut self, index: I) -> &mut Self::Output {
index.get_mut(self) index.get_mut(self)
} }
+13 -4
View File
@@ -22,7 +22,11 @@ impl UiRenderer {
} }
pub fn draw(&mut self) { pub fn draw(&mut self) {
let output = self.surface.get_current_texture().unwrap(); let output = match self.surface.get_current_texture() {
CurrentSurfaceTexture::Success(v) => v,
CurrentSurfaceTexture::Suboptimal(v) => v,
_ => panic!("failed"),
};
let view = output let view = output
.texture .texture
.create_view(&TextureViewDescriptor::default()); .create_view(&TextureViewDescriptor::default());
@@ -45,7 +49,7 @@ impl UiRenderer {
} }
self.queue.submit(std::iter::once(encoder.finish())); self.queue.submit(std::iter::once(encoder.finish()));
output.present(); self.queue.present(output);
} }
pub fn resize(&mut self, size: &PhysicalSize<u32>) { pub fn resize(&mut self, size: &PhysicalSize<u32>) {
@@ -64,9 +68,12 @@ impl UiRenderer {
pub fn new(window: Arc<Window>) -> Self { pub fn new(window: Arc<Window>) -> Self {
let size = window.inner_size(); let size = window.inner_size();
let instance = Instance::new(&InstanceDescriptor { let instance = Instance::new(InstanceDescriptor {
backends: Backends::PRIMARY, backends: Backends::PRIMARY,
..Default::default() flags: Default::default(),
memory_budget_thresholds: Default::default(),
backend_options: Default::default(),
display: None,
}); });
let surface = instance let surface = instance
@@ -78,6 +85,7 @@ impl UiRenderer {
power_preference: PowerPreference::default(), power_preference: PowerPreference::default(),
compatible_surface: Some(&surface), compatible_surface: Some(&surface),
force_fallback_adapter: false, force_fallback_adapter: false,
apply_limit_buckets: false,
}) })
.block_on() .block_on()
.expect("Could not get adapter!"); .expect("Could not get adapter!");
@@ -119,6 +127,7 @@ impl UiRenderer {
alpha_mode: surface_caps.alpha_modes[0], alpha_mode: surface_caps.alpha_modes[0],
desired_maximum_frame_latency: 2, desired_maximum_frame_latency: 2,
view_formats: vec![], view_formats: vec![],
color_space: Default::default(),
}; };
surface.configure(&device, &config); surface.configure(&device, &config);
+5 -6
View File
@@ -13,7 +13,6 @@ use tokio::{
unbounded_channel as async_channel, unbounded_channel as async_channel,
}, },
}; };
use winit::window::Window;
pub type TaskMsgSender<Rsc> = SyncSender<Box<dyn TaskUpdate<Rsc>>>; pub type TaskMsgSender<Rsc> = SyncSender<Box<dyn TaskUpdate<Rsc>>>;
pub type TaskMsgReceiver<Rsc> = SyncReceiver<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> { pub struct Tasks<Rsc: HasState> {
start: AsyncSender<BoxTask>, start: AsyncSender<BoxTask>,
window: Arc<Window>, request_update: Arc<dyn Fn() + Send + Sync>,
msg_send: SyncSender<Box<dyn TaskUpdate<Rsc>>>, 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>>; type BoxTask = Pin<Box<dyn Future<Output = ()> + Send>>;
impl<Rsc: HasState> Tasks<Rsc> { 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 (start, start_recv) = async_channel();
let (msgs, msgs_recv) = sync_channel(); let (msgs, msgs_recv) = sync_channel();
std::thread::spawn(|| { std::thread::spawn(|| {
@@ -56,7 +55,7 @@ impl<Rsc: HasState> Tasks<Rsc> {
Self { Self {
start, start,
msg_send: msgs, msg_send: msgs,
window, request_update: Arc::new(request_update),
}, },
msgs_recv, msgs_recv,
) )
@@ -67,10 +66,10 @@ impl<Rsc: HasState> Tasks<Rsc> {
F::CallOnceFuture: Send, F::CallOnceFuture: Send,
{ {
let send = self.msg_send.clone(); 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 { let _ = self.start.send(Box::pin(async move {
task(TaskCtx::new(send)).await; task(TaskCtx::new(send)).await;
window.request_redraw(); request_update();
})); }));
} }
} }
+2
View File
@@ -5,6 +5,7 @@ mod ptr;
mod rect; mod rect;
mod text; mod text;
mod trait_fns; mod trait_fns;
mod selector;
pub use image::*; pub use image::*;
pub use mask::*; pub use mask::*;
@@ -13,3 +14,4 @@ pub use ptr::*;
pub use rect::*; pub use rect::*;
pub use text::*; pub use text::*;
pub use trait_fns::*; pub use trait_fns::*;
pub use selector::*;
+9 -9
View File
@@ -152,32 +152,32 @@ impl Span {
} }
} }
pub struct SpanBuilder<State, const LEN: usize, Wa: WidgetArrLike<State, LEN, Tag>, Tag> { pub struct SpanBuilder<Children, Rsc, Tag, GTag> {
pub children: Wa, pub children: Children,
pub dir: Dir, pub dir: Dir,
pub gap: f32, 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> impl<Children: IntoWidgetVec<Rsc, Tag, GTag>, Rsc, Tag, GTag> WidgetFnTrait<Rsc>
for SpanBuilder<Rsc, LEN, Wa, Tag> for SpanBuilder<Children, Rsc, Tag, GTag>
{ {
type Widget = Span; type Widget = Span;
#[track_caller] #[track_caller]
fn run(self, rsc: &mut Rsc) -> Self::Widget { fn run(self, rsc: &mut Rsc) -> Self::Widget {
Span { Span {
children: self.children.add(rsc).arr.into_iter().collect(), children: self.children.into_vec(rsc),
dir: self.dir, dir: self.dir,
gap: self.gap, gap: self.gap,
} }
} }
} }
impl<State, const LEN: usize, Wa: WidgetArrLike<State, LEN, Tag>, Tag> impl<Children: IntoWidgetVec<Rsc, Tag, GTag>, Rsc, Tag, GTag>
SpanBuilder<State, LEN, Wa, Tag> SpanBuilder<Children, Rsc, Tag, GTag>
{ {
pub fn new(children: Wa, dir: Dir) -> Self { pub fn new(children: Children, dir: Dir) -> Self {
Self { Self {
children, children,
dir, dir,
+8 -10
View File
@@ -42,30 +42,28 @@ pub enum StackSize {
Child(usize), Child(usize),
} }
pub struct StackBuilder<State, const LEN: usize, Wa: WidgetArrLike<State, LEN, Tag>, Tag> { pub struct StackBuilder<Children, Rsc, Tag, GTag> {
pub children: Wa, pub children: Children,
pub size: StackSize, 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> impl<Children: IntoWidgetVec<Rsc, Tag, GTag>, Rsc, Tag, GTag> WidgetFnTrait<Rsc>
for StackBuilder<Rsc, LEN, Wa, Tag> for StackBuilder<Children, Rsc, Tag, GTag>
{ {
type Widget = Stack; type Widget = Stack;
#[track_caller] #[track_caller]
fn run(self, rsc: &mut Rsc) -> Self::Widget { fn run(self, rsc: &mut Rsc) -> Self::Widget {
Stack { Stack {
children: self.children.add(rsc).arr.into_iter().collect(), children: self.children.into_vec(rsc),
size: self.size, size: self.size,
} }
} }
} }
impl<State, const LEN: usize, Wa: WidgetArrLike<State, LEN, Tag>, Tag> impl<Children: IntoWidgetVec<Rsc, Tag, GTag>, Rsc, Tag, GTag> StackBuilder<Children, Rsc, Tag, GTag> {
StackBuilder<State, LEN, Wa, Tag> pub fn new(children: Children) -> Self {
{
pub fn new(children: Wa) -> Self {
Self { Self {
children, children,
size: StackSize::default(), size: StackSize::default(),
+4 -2
View File
@@ -30,8 +30,10 @@ impl Widget for WidgetPtr {
} }
impl WidgetPtr { impl WidgetPtr {
pub fn new() -> Self { pub fn new(widget: StrongWidget) -> Self {
Self::default() Self {
inner: Some(widget),
}
} }
pub fn empty() -> Self { pub fn empty() -> Self {
Self { Self {
+48
View File
@@ -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
View File
@@ -131,18 +131,61 @@ widget_trait! {
} }
} }
pub trait CoreWidgetArr<Rsc, const LEN: usize, Wa: WidgetArrLike<Rsc, LEN, Tag>, Tag> { pub trait CoreWidgetArr<Children, Rsc, Tag, GTag> {
fn span(self, dir: Dir) -> SpanBuilder<Rsc, LEN, Wa, Tag>; fn span(self, dir: Dir) -> SpanBuilder<Children, Rsc, Tag, GTag>;
fn stack(self) -> StackBuilder<Rsc, LEN, Wa, Tag>; fn stack(self) -> StackBuilder<Children, Rsc, Tag, GTag>;
} }
impl<State, const LEN: usize, Wa: WidgetArrLike<State, LEN, Tag>, Tag> impl<Children: IntoWidgetVec<Rsc, Tag, GTag>, Rsc, Tag, GTag>
CoreWidgetArr<State, LEN, Wa, Tag> for Wa 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) SpanBuilder::new(self, dir)
} }
fn stack(self) -> StackBuilder<State, LEN, Wa, Tag> { fn stack(self) -> StackBuilder<Children, Rsc, Tag, GTag> {
StackBuilder::new(self) 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
})
}
}