refactor project structure (start of redoing atomic branch without atomics)

This commit is contained in:
iris committed 2025-12-11 05:25:58 -05:00
1 parent 38266debb6
commit 2dc5b0f62c
76 files changed
+540 -442

No files matched your search

Generated
+24 -4
View File
@@ -1042,16 +1042,36 @@ name = "iris"
version = "0.1.0"
dependencies = [
"arboard",
"bytemuck",
"cosmic-text",
"fxhash",
"image",
"iris-core",
"iris-macro",
"pollster",
"unicode-segmentation",
"wgpu",
"winit",
]
[[package]]
name = "iris-core"
version = "0.1.0"
dependencies = [
"bytemuck",
"cosmic-text",
"fxhash",
"image",
"wgpu",
"winit",
]
[[package]]
name = "iris-macro"
version = "0.1.0"
dependencies = [
"quote",
"syn",
]
[[package]]
name = "itertools"
version = "0.12.1"
@@ -2401,9 +2421,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.110"
version = "2.0.111"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea"
checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87"
dependencies = [
"proc-macro2",
"quote",
+23 -4
View File
@@ -1,12 +1,30 @@
[package]
name = "iris"
version = "0.1.0"
edition = "2024"
default-run = "test"
version.workspace = true
edition.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
iris-core = { workspace = true }
iris-macro = { workspace = true }
cosmic-text = { workspace = true }
unicode-segmentation = { workspace = true }
winit = { workspace = true }
arboard = { workspace = true, features = ["wayland-data-control"] }
pollster = { workspace = true }
wgpu = { workspace = true }
image = { workspace = true }
[workspace]
members = ["core", "macro"]
[workspace.package]
version = "0.1.0"
edition = "2024"
[workspace.dependencies]
pollster = "0.4.0"
winit = "0.30.12"
wgpu = "27.0.1"
@@ -15,5 +33,6 @@ image = "0.25.6"
cosmic-text = "0.15.0"
unicode-segmentation = "1.12.0"
fxhash = "0.2.1"
arboard = { version = "3.6.1", features = ["wayland-data-control"] }
arboard = "3.6.1"
iris-core = { path = "core" }
iris-macro = { path = "macro" }
+12
View File
@@ -0,0 +1,12 @@
[package]
name = "iris-core"
version.workspace = true
edition.workspace = true
[dependencies]
winit = { workspace = true }
wgpu = { workspace = true }
bytemuck ={ workspace = true }
image = { workspace = true }
cosmic-text = { workspace = true }
fxhash = { workspace = true }
File renamed without changes.
File renamed without changes.
@@ -1,7 +1,7 @@
use std::{hash::Hash, rc::Rc};
use crate::{
layout::{IdFnTag, Ui, UiModule, WidgetId, WidgetIdFn, WidgetLike},
layout::{Ui, UiModule, WeakWidgetId, WidgetId},
util::{HashMap, Id},
};
@@ -18,7 +18,7 @@ pub struct EventCtx<'a, Ctx, Data> {
pub type ECtx<'a, Ctx, Data, W> = EventIdCtx<'a, Ctx, Data, W>;
pub struct EventIdCtx<'a, Ctx, Data, W> {
pub id: &'a WidgetId<W>,
pub id: &'a WeakWidgetId<W>,
pub ui: &'a mut Ui,
pub state: &'a mut Ctx,
pub data: Data,
@@ -30,74 +30,8 @@ impl<F: Fn(EventCtx<Ctx, Data>) + 'static, Ctx, Data> EventFn<Ctx, Data> for F {
pub trait WidgetEventFn<Ctx, Data, W>: Fn(EventIdCtx<Ctx, Data, W>) + 'static {}
impl<F: Fn(EventIdCtx<Ctx, Data, W>) + 'static, Ctx, Data, W> WidgetEventFn<Ctx, Data, W> for F {}
// TODO: naming in here is a bit weird like eventable
#[macro_export]
macro_rules! event_ctx {
($ty: ty) => {
mod local_event_trait {
use super::*;
#[allow(unused_imports)]
use $crate::prelude::*;
pub trait EventableCtx<W, Tag, Ctx: 'static> {
fn on<E: Event>(
self,
event: E,
f: impl WidgetEventFn<Ctx, E::Data, W>,
) -> impl WidgetIdFn<W> + EventableCtx<W, IdFnTag, Ctx>;
}
impl<WL: WidgetLike<Tag>, Tag> EventableCtx<WL::Widget, Tag, $ty> for WL {
fn on<E: Event>(
self,
event: E,
f: impl WidgetEventFn<$ty, E::Data, WL::Widget>,
) -> impl WidgetIdFn<WL::Widget> + EventableCtx<WL::Widget, IdFnTag, $ty> {
eventable::Eventable::on(self, event, f)
}
}
}
use local_event_trait::*;
};
}
pub use event_ctx;
pub mod eventable {
use super::*;
pub trait Eventable<W, Tag> {
fn on<E: Event, Ctx: 'static>(
self,
event: E,
f: impl WidgetEventFn<Ctx, E::Data, W>,
) -> impl WidgetIdFn<W> + Eventable<W, IdFnTag>;
}
impl<WL: WidgetLike<Tag>, Tag> Eventable<WL::Widget, Tag> for WL {
fn on<E: Event, Ctx: 'static>(
self,
event: E,
f: impl WidgetEventFn<Ctx, E::Data, WL::Widget>,
) -> impl WidgetIdFn<WL::Widget> {
move |ui| {
let id = self.add(ui);
let id_ = id.weak();
ui.register_event(&id, event, move |ctx| {
f(EventIdCtx {
id: &id_.strong(),
state: ctx.state,
data: ctx.data,
ui: ctx.ui,
});
});
id
}
}
}
}
pub trait DefaultEvent: Hash + Eq + 'static {
type Data: Clone;
type Data: Clone = ();
}
impl<E: DefaultEvent> Event for E {
@@ -203,7 +137,7 @@ impl Ui {
.data
.modules
.get_mut::<E::Module<Ctx>>()
.run(&id.id, event)
.run(&id.id(), event)
{
f(EventCtx {
ui: self,
+4 -16
View File
@@ -1,33 +1,21 @@
mod color;
mod attr;
mod event;
mod id;
mod layer;
mod module;
mod num;
mod orientation;
mod painter;
mod text;
mod texture;
mod primitive;
mod ui;
mod attr;
mod vec2;
mod widget;
mod widgets;
pub use color::*;
pub use attr::*;
pub use event::*;
pub use id::*;
pub use layer::*;
pub use module::*;
pub use num::*;
pub use orientation::*;
pub use painter::*;
pub use text::*;
pub use texture::*;
pub use primitive::*;
pub use ui::*;
pub use attr::*;
pub use vec2::*;
pub use widget::*;
pub use widgets::*;
pub type UiColor = Color<u8>;
File renamed without changes.
+49
View File
@@ -0,0 +1,49 @@
use crate::util::Vec2;
use std::marker::Destruct;
pub const trait UiNum {
fn to_f32(self) -> f32;
}
impl const UiNum for f32 {
fn to_f32(self) -> f32 {
self
}
}
impl const UiNum for u32 {
fn to_f32(self) -> f32 {
self as f32
}
}
impl const UiNum for i32 {
fn to_f32(self) -> f32 {
self as f32
}
}
pub const fn vec2(x: impl const UiNum, y: impl const UiNum) -> Vec2 {
Vec2::new(x.to_f32(), y.to_f32())
}
impl<T: const UiNum + Copy> const From<T> for Vec2 {
fn from(v: T) -> Self {
Self {
x: v.to_f32(),
y: v.to_f32(),
}
}
}
impl<T: const UiNum, U: const UiNum> const From<(T, U)> for Vec2
where
(T, U): const Destruct,
{
fn from((x, y): (T, U)) -> Self {
Self {
x: x.to_f32(),
y: y.to_f32(),
}
}
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -3,7 +3,7 @@ mod axis;
mod len;
mod pos;
use super::vec2::*;
use crate::util::Vec2;
pub use align::*;
pub use axis::*;
File renamed without changes.
@@ -1,10 +1,10 @@
use crate::{
layout::{
Axis, Len, Modules, PrimitiveLayers, RenderedText, Size, TextAttrs, TextBuffer, TextData,
TextureHandle, Textures, UiRegion, UiVec2, Vec2, WidgetId, Widgets,
TextureHandle, Textures, UiRegion, UiVec2, WidgetId, Widgets,
},
render::{Mask, MaskIdx, Primitive, PrimitiveHandle, PrimitiveInst},
util::{HashMap, HashSet, Id, TrackedArena},
util::{HashMap, HashSet, Id, TrackedArena, Vec2},
};
/// makes your surfaces look pretty
@@ -33,7 +33,7 @@ struct PainterCtx<'a> {
pub modules: &'a mut Modules,
pub cache_width: HashMap<Id, (UiVec2, Len)>,
pub cache_height: HashMap<Id, (UiVec2, Len)>,
pub needs_redraw: HashSet<Id>,
pub needs_redraw: &'a mut HashSet<Id>,
draw_started: HashSet<Id>,
}
@@ -323,7 +323,7 @@ impl<'a> PainterCtx<'a> {
}
impl PainterData {
fn ctx(&mut self, needs_redraw: HashSet<Id>) -> PainterCtx<'_> {
fn ctx<'a>(&'a mut self, needs_redraw: &'a mut HashSet<Id>) -> PainterCtx<'a> {
PainterCtx {
widgets: &self.widgets,
active: &mut self.active,
@@ -341,13 +341,14 @@ impl PainterData {
}
pub fn draw(&mut self, id: Id) {
let mut ctx = self.ctx(Default::default());
let mut need_redraw = HashSet::default();
let mut ctx = self.ctx(&mut need_redraw);
ctx.draw_started.clear();
ctx.layers.clear();
ctx.draw_inner(0, id, UiRegion::FULL, None, MaskIdx::NONE, None);
}
pub fn redraw(&mut self, ids: HashSet<Id>) {
pub fn redraw(&mut self, ids: &mut HashSet<Id>) {
let mut ctx = self.ctx(ids);
while let Some(&id) = ctx.needs_redraw.iter().next() {
ctx.redraw(id);
@@ -399,9 +400,9 @@ impl<'a, 'c> Painter<'a, 'c> {
}
fn widget_at<W>(&mut self, id: &WidgetId<W>, region: UiRegion) {
self.children.push(id.id);
self.children.push(id.id());
self.ctx
.draw_inner(self.layer, id.id, region, Some(self.id), self.mask, None);
.draw_inner(self.layer, id.id(), region, Some(self.id), self.mask, None);
}
pub fn texture_within(&mut self, handle: &TextureHandle, region: UiRegion) {
@@ -552,11 +553,11 @@ impl SizeCtx<'_> {
}
pub fn width<W>(&mut self, id: &WidgetId<W>) -> Len {
self.width_inner(id.id)
self.width_inner(id.id())
}
pub fn height<W>(&mut self, id: &WidgetId<W>) -> Len {
self.height_inner(id.id)
self.height_inner(id.id())
}
pub fn len_axis<W>(&mut self, id: &WidgetId<W>, axis: Axis) -> Len {
File renamed without changes.
File renamed without changes.
+9
View File
@@ -0,0 +1,9 @@
mod color;
mod layer;
mod text;
mod texture;
pub use color::*;
pub use layer::*;
pub use text::*;
pub use texture::*;
@@ -1,11 +1,13 @@
use std::simd::{Simd, num::SimdUint};
use crate::layout::{Align, RegionAlign, TextureHandle, Textures, UiColor, Vec2};
use crate::{
layout::{Align, RegionAlign, TextureHandle, Textures, UiColor},
util::Vec2,
};
use cosmic_text::{
Attrs, AttrsList, Buffer, CacheKey, Color, Family, FontSystem, Metrics, Placement, SwashCache,
SwashContent,
};
use image::{GenericImageView, RgbaImage};
use std::simd::{Simd, num::SimdUint};
/// TODO: properly wrap this
pub mod text_lib {
@@ -1,12 +1,13 @@
use crate::{
render::TexturePrimitive,
util::{RefCounter, Vec2},
};
use image::{DynamicImage, GenericImageView};
use std::{
ops::Index,
sync::mpsc::{Receiver, Sender, channel},
};
use image::{DynamicImage, GenericImageView};
use crate::{layout::Vec2, render::TexturePrimitive, util::RefCounter};
#[derive(Debug, Clone)]
pub struct TextureHandle {
inner: TexturePrimitive,
+23 -43
View File
@@ -1,13 +1,11 @@
use image::DynamicImage;
use crate::{
core::{TextEdit, TextEditCtx},
layout::{
Event, EventFn, EventModule, IdLike, PainterData, PixelRegion, TextureHandle, Vec2, Widget,
Event, EventFn, EventModule, IdLike, PainterData, PixelRegion, TextureHandle, Widget,
WidgetId, WidgetInstance, WidgetLike,
},
util::{HashSet, Id},
util::{Id, Vec2},
};
use image::DynamicImage;
use std::{
any::{Any, TypeId},
ops::{Index, IndexMut},
@@ -16,9 +14,8 @@ use std::{
pub struct Ui {
// TODO: make this at least pub(super)
pub(crate) data: PainterData,
pub data: PainterData,
root: Option<WidgetId>,
updates: HashSet<Id>,
recv: Receiver<Id>,
pub(super) send: Sender<Id>,
full_redraw: bool,
@@ -32,11 +29,11 @@ impl Ui {
/// useful for debugging
pub fn set_label<W>(&mut self, id: &WidgetId<W>, label: String) {
self.data.widgets.data_mut(&id.id).unwrap().label = label;
self.data.widgets.data_mut(&id.id()).unwrap().label = label;
}
pub fn label<W>(&self, id: &WidgetId<W>) -> &String {
&self.data.widgets.data(&id.id).unwrap().label
&self.data.widgets.data(&id.id()).unwrap().label
}
pub fn add_widget<W: Widget>(&mut self, w: W) -> WidgetId<W> {
@@ -45,7 +42,7 @@ impl Ui {
pub fn push<W: Widget>(&mut self, w: W) -> WidgetId<W> {
let id = self.new_id();
self.data.widgets.insert(id.id, w);
self.data.widgets.insert(id.id(), w);
id
}
@@ -58,11 +55,11 @@ impl Ui {
Self::default()
}
pub fn get<W: Widget>(&self, id: &impl IdLike<W>) -> Option<&W> {
pub fn get<I: IdLike>(&self, id: &I) -> Option<&I::Widget> {
self.data.widgets.get(id)
}
pub fn get_mut<W: Widget>(&mut self, id: &impl IdLike<W>) -> Option<&mut W> {
pub fn get_mut<I: IdLike>(&mut self, id: &I) -> Option<&mut I::Widget> {
self.data.widgets.get_mut(id)
}
@@ -87,7 +84,7 @@ impl Ui {
self.data
.modules
.get_mut::<E::Module<Ctx>>()
.register(id.id, event, f);
.register(id.id(), event, f);
}
pub fn resize(&mut self, size: impl Into<Vec2>) {
@@ -104,7 +101,7 @@ impl Ui {
// free before bc nothing should exist
self.free();
if let Some(root) = &self.root {
self.data.draw(root.id);
self.data.draw(root.id());
}
}
@@ -112,26 +109,19 @@ impl Ui {
if self.full_redraw {
self.redraw_all();
self.full_redraw = false;
} else if !self.updates.is_empty() {
} else if self.data.widgets.has_updates() {
self.redraw_updates();
}
if self.resized {
self.resized = false;
self.redraw_size();
self.redraw_all();
}
}
fn redraw_size(&mut self) {
// let mut ctx = PainterCtx::new(&mut self.data);
// let dep = ctx.px_dependent.clone();
// for id in dep {
// ctx.redraw(id);
// }
self.redraw_all();
}
fn redraw_updates(&mut self) {
self.data.redraw(std::mem::take(&mut self.updates));
let mut updates = std::mem::take(&mut self.data.widgets.updates);
self.data.redraw(&mut updates);
self.data.widgets.updates = updates;
self.free();
}
@@ -147,7 +137,7 @@ impl Ui {
}
pub fn needs_redraw(&self) -> bool {
self.full_redraw || !self.updates.is_empty()
self.full_redraw || self.data.widgets.has_updates()
}
pub fn num_widgets(&self) -> usize {
@@ -158,14 +148,6 @@ impl Ui {
self.data.active.len()
}
pub fn text(&mut self, id: &impl IdLike<TextEdit>) -> TextEditCtx<'_> {
self.updates.insert(id.id());
TextEditCtx {
text: self.data.widgets.get_mut(id).unwrap(),
font_system: &mut self.data.text.font_system,
}
}
pub fn debug_layers(&self) {
for ((idx, depth), primitives) in self.data.layers.iter_depth() {
let indent = " ".repeat(depth * 2);
@@ -178,7 +160,7 @@ impl Ui {
}
}
pub fn window_region<W>(&self, id: &impl IdLike<W>) -> Option<PixelRegion> {
pub fn window_region(&self, id: &impl IdLike) -> Option<PixelRegion> {
let region = self.data.active.get(&id.id())?.region;
Some(region.to_px(self.data.output_size))
}
@@ -191,17 +173,16 @@ impl Ui {
}
}
impl<W: Widget> Index<&WidgetId<W>> for Ui {
type Output = W;
impl<I: IdLike> Index<&I> for Ui {
type Output = I::Widget;
fn index(&self, id: &WidgetId<W>) -> &Self::Output {
fn index(&self, id: &I) -> &Self::Output {
self.get(id).unwrap()
}
}
impl<W: Widget> IndexMut<&WidgetId<W>> for Ui {
fn index_mut(&mut self, id: &WidgetId<W>) -> &mut Self::Output {
self.updates.insert(id.id);
impl<I: IdLike> IndexMut<&I> for Ui {
fn index_mut(&mut self, id: &I) -> &mut Self::Output {
self.get_mut(id).unwrap()
}
}
@@ -222,7 +203,6 @@ impl Default for Ui {
Self {
data: PainterData::default(),
root: Default::default(),
updates: Default::default(),
full_redraw: false,
send,
recv,
@@ -1,7 +1,7 @@
use std::{any::TypeId, marker::PhantomData, sync::mpsc::Sender};
use crate::{
layout::{Ui, WidgetLike},
layout::{Ui, Widget},
util::{Id, RefCounter},
};
@@ -58,7 +58,7 @@ impl<W> Clone for WidgetId<W> {
}
impl<W> WidgetId<W> {
pub(super) fn new(id: Id, ty: TypeId, send: Sender<Id>) -> Self {
pub(crate) fn new(id: Id, ty: TypeId, send: Sender<Id>) -> Self {
Self {
ty,
id,
@@ -77,7 +77,7 @@ impl<W> WidgetId<W> {
unsafe { std::mem::transmute(self) }
}
pub fn key(&self) -> Id {
pub fn id(&self) -> Id {
self.id
}
@@ -108,26 +108,6 @@ impl<W> WidgetId<W> {
}
}
impl<W> WeakWidgetId<W> {
/// should guarantee that widget is still valid to prevent indexing failures
pub(crate) fn strong(&self) -> WidgetId<W> {
let Self {
ty,
id,
ref counter,
ref send,
_pd,
} = *self;
WidgetId {
ty,
id,
counter: counter.clone(),
send: send.clone(),
_pd,
}
}
}
impl<W> Drop for WidgetId<W> {
fn drop(&mut self) {
if self.counter.drop() {
@@ -136,29 +116,12 @@ impl<W> Drop for WidgetId<W> {
}
}
pub struct IdTag;
pub struct IdFnTag;
pub trait WidgetIdFn<W>: FnOnce(&mut Ui) -> WidgetId<W> {}
impl<W, F: FnOnce(&mut Ui) -> WidgetId<W>> WidgetIdFn<W> for F {}
pub trait WidgetRet: FnOnce(&mut Ui) -> WidgetId<AnyWidget> {}
impl<F: FnOnce(&mut Ui) -> WidgetId<AnyWidget>> WidgetRet for F {}
impl<W: 'static> WidgetLike<IdTag> for WidgetId<W> {
type Widget = W;
fn add(self, _: &mut Ui) -> WidgetId<W> {
self
}
}
impl<W: 'static, F: FnOnce(&mut Ui) -> WidgetId<W>> WidgetLike<IdFnTag> for F {
type Widget = W;
fn add(self, ui: &mut Ui) -> WidgetId<W> {
self(ui)
}
}
pub trait WidgetIdLike<W> {
fn id(self, send: &Sender<Id>) -> WidgetId<W>;
}
@@ -169,11 +132,20 @@ impl<W> WidgetIdLike<W> for &WidgetId<W> {
}
}
pub trait IdLike<W> {
pub trait IdLike {
type Widget: Widget + 'static;
fn id(&self) -> Id;
}
impl<W> IdLike<W> for WidgetId<W> {
impl<W: Widget> IdLike for WidgetId<W> {
type Widget = W;
fn id(&self) -> Id {
self.id
}
}
impl<W: Widget> IdLike for WeakWidgetId<W> {
type Widget = W;
fn id(&self) -> Id {
self.id
}
@@ -1,28 +1,4 @@
use crate::{
core::WidgetPtr,
layout::{Len, Painter, SizeCtx, Ui, WidgetId, WidgetIdFn},
};
use std::{any::Any, marker::PhantomData};
pub trait Widget: Any {
fn draw(&mut self, painter: &mut Painter);
fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len;
fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len;
}
impl Widget for () {
fn draw(&mut self, _: &mut Painter) {}
fn desired_width(&mut self, _: &mut SizeCtx) -> Len {
Len::ZERO
}
fn desired_height(&mut self, _: &mut SizeCtx) -> Len {
Len::ZERO
}
}
pub struct WidgetTag;
pub struct FnTag;
use super::*;
pub trait WidgetLike<Tag> {
type Widget: 'static;
@@ -48,50 +24,8 @@ pub trait WidgetLike<Tag> {
{
ui.set_root(self);
}
fn set_ptr(self, ptr: &WidgetId<WidgetPtr>, ui: &mut Ui)
where
Self: Sized,
{
ui[ptr].inner = Some(self.add(ui).any());
}
}
/// A function that returns a widget given a UI.
/// Useful for defining trait functions on widgets that create a parent widget so that the children
/// don't need to be IDs yet
pub trait WidgetFn<W: Widget>: FnOnce(&mut Ui) -> W {}
impl<W: Widget, F: FnOnce(&mut Ui) -> W> WidgetFn<W> for F {}
impl<W: Widget, F: FnOnce(&mut Ui) -> W> WidgetLike<FnTag> for F {
type Widget = W;
fn add(self, ui: &mut Ui) -> WidgetId<W> {
self(ui).add(ui)
}
}
impl<W: Widget> WidgetLike<WidgetTag> for W {
type Widget = W;
fn add(self, ui: &mut Ui) -> WidgetId<W> {
ui.add_widget(self)
}
}
pub struct WidgetArr<const LEN: usize, Ws> {
pub arr: [WidgetId; LEN],
_pd: PhantomData<Ws>,
}
impl<const LEN: usize, Ws> WidgetArr<LEN, Ws> {
pub fn new(arr: [WidgetId; LEN]) -> Self {
Self {
arr,
_pd: PhantomData,
}
}
}
pub struct ArrTag;
pub trait WidgetArrLike<const LEN: usize, Tag> {
type Ws;
fn ui(self, ui: &mut Ui) -> WidgetArr<LEN, Self::Ws>;
@@ -142,19 +76,3 @@ impl_widget_arr!(9;A B C D E F G H I);
impl_widget_arr!(10;A B C D E F G H I J);
impl_widget_arr!(11;A B C D E F G H I J K);
impl_widget_arr!(12;A B C D E F G H I J K L);
pub trait WidgetOption {
fn get(self, ui: &mut Ui) -> Option<WidgetId>;
}
impl WidgetOption for () {
fn get(self, _: &mut Ui) -> Option<WidgetId> {
None
}
}
impl<F: FnOnce(&mut Ui) -> Option<WidgetId>> WidgetOption for F {
fn get(self, ui: &mut Ui) -> Option<WidgetId> {
self(ui)
}
}
+64
View File
@@ -0,0 +1,64 @@
use crate::layout::{Len, Painter, SizeCtx, Ui};
use std::{any::Any, marker::PhantomData};
mod id;
mod like;
mod tag;
mod widgets;
pub use id::*;
pub use like::*;
pub use tag::*;
pub use widgets::*;
pub trait Widget: Any {
fn draw(&mut self, painter: &mut Painter);
fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len;
fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len;
}
impl Widget for () {
fn draw(&mut self, _: &mut Painter) {}
fn desired_width(&mut self, _: &mut SizeCtx) -> Len {
Len::ZERO
}
fn desired_height(&mut self, _: &mut SizeCtx) -> Len {
Len::ZERO
}
}
/// A function that returns a widget given a UI.
/// Useful for defining trait functions on widgets that create a parent widget so that the children
/// don't need to be IDs yet
pub trait WidgetFn<W: Widget>: FnOnce(&mut Ui) -> W {}
impl<W: Widget, F: FnOnce(&mut Ui) -> W> WidgetFn<W> for F {}
pub struct WidgetArr<const LEN: usize, Ws> {
pub arr: [WidgetId; LEN],
_pd: PhantomData<Ws>,
}
impl<const LEN: usize, Ws> WidgetArr<LEN, Ws> {
pub fn new(arr: [WidgetId; LEN]) -> Self {
Self {
arr,
_pd: PhantomData,
}
}
}
pub trait WidgetOption {
fn get(self, ui: &mut Ui) -> Option<WidgetId>;
}
impl WidgetOption for () {
fn get(self, _: &mut Ui) -> Option<WidgetId> {
None
}
}
impl<F: FnOnce(&mut Ui) -> Option<WidgetId>> WidgetOption for F {
fn get(self, ui: &mut Ui) -> Option<WidgetId> {
self(ui)
}
}
+36
View File
@@ -0,0 +1,36 @@
use super::*;
pub struct ArrTag;
pub struct WidgetTag;
impl<W: Widget> WidgetLike<WidgetTag> for W {
type Widget = W;
fn add(self, ui: &mut Ui) -> WidgetId<W> {
ui.add_widget(self)
}
}
pub struct FnTag;
impl<W: Widget, F: FnOnce(&mut Ui) -> W> WidgetLike<FnTag> for F {
type Widget = W;
fn add(self, ui: &mut Ui) -> WidgetId<W> {
self(ui).add(ui)
}
}
pub struct IdTag;
impl<W: 'static> WidgetLike<IdTag> for WidgetId<W> {
type Widget = W;
fn add(self, _: &mut Ui) -> WidgetId<W> {
self
}
}
pub struct IdFnTag;
impl<W: 'static, F: FnOnce(&mut Ui) -> WidgetId<W>> WidgetLike<IdFnTag> for F {
type Widget = W;
fn add(self, ui: &mut Ui) -> WidgetId<W> {
self(ui)
}
}
@@ -1,10 +1,11 @@
use crate::{
layout::{IdLike, Widget},
util::{DynBorrower, HashMap, Id, IdTracker},
util::{DynBorrower, HashMap, HashSet, Id, IdTracker},
};
#[derive(Default)]
pub struct Widgets {
pub updates: HashSet<Id>,
ids: IdTracker,
map: HashMap<Id, WidgetData>,
}
@@ -17,11 +18,8 @@ pub struct WidgetData {
}
impl Widgets {
pub fn new() -> Self {
Self {
ids: IdTracker::default(),
map: HashMap::default(),
}
pub fn has_updates(&self) -> bool {
!self.updates.is_empty()
}
pub fn get_dyn(&self, id: Id) -> Option<&dyn Widget> {
@@ -29,6 +27,7 @@ impl Widgets {
}
pub fn get_dyn_mut(&mut self, id: Id) -> Option<&mut dyn Widget> {
self.updates.insert(id);
Some(self.map.get_mut(&id)?.widget.as_mut())
}
@@ -47,11 +46,11 @@ impl Widgets {
WidgetWrapper::new(data.widget.as_mut(), &mut data.borrowed)
}
pub fn get<W: Widget>(&self, id: &impl IdLike<W>) -> Option<&W> {
pub fn get<I: IdLike>(&self, id: &I) -> Option<&I::Widget> {
self.get_dyn(id.id())?.as_any().downcast_ref()
}
pub fn get_mut<W: Widget>(&mut self, id: &impl IdLike<W>) -> Option<&mut W> {
pub fn get_mut<I: IdLike>(&mut self, id: &I) -> Option<&mut I::Widget> {
self.get_dyn_mut(id.id())?.as_any_mut().downcast_mut()
}
+17
View File
@@ -0,0 +1,17 @@
#![feature(macro_metavar_expr_concat)]
#![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)]
#![feature(unsize)]
#![feature(coerce_unsized)]
pub mod layout;
pub mod render;
pub mod util;
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+7 -5
View File
@@ -4,13 +4,15 @@ mod change;
mod id;
mod math;
mod refcount;
mod vec2;
pub(crate) use arena::*;
pub(crate) use borrow::*;
pub use arena::*;
pub use borrow::*;
pub use change::*;
pub(crate) use id::*;
pub(crate) use math::*;
pub(crate) use refcount::*;
pub use id::*;
pub use math::*;
pub use refcount::*;
pub use vec2::*;
pub type HashMap<K, V> = fxhash::FxHashMap<K, V>;
pub type HashSet<K> = fxhash::FxHashSet<K>;
@@ -22,6 +22,12 @@ impl RefCounter {
}
}
impl Default for RefCounter {
fn default() -> Self {
Self::new()
}
}
impl Clone for RefCounter {
fn clone(&self) -> Self {
self.0.fetch_add(1, Ordering::Release);
+2 -30
View File
@@ -1,8 +1,5 @@
use crate::{
layout::UiNum,
util::{DivOr, impl_op},
};
use std::{hash::Hash, marker::Destruct, ops::*};
use crate::util::{DivOr, impl_op};
use std::{hash::Hash, ops::*};
#[repr(C)]
#[derive(Clone, Copy, PartialEq, Default, bytemuck::Pod, bytemuck::Zeroable)]
@@ -20,10 +17,6 @@ impl Hash for Vec2 {
}
}
pub const fn vec2(x: impl const UiNum, y: impl const UiNum) -> Vec2 {
Vec2::new(x.to_f32(), y.to_f32())
}
impl Vec2 {
pub const ZERO: Self = Self::new(0.0, 0.0);
pub const ONE: Self = Self::new(1.0, 1.0);
@@ -68,15 +61,6 @@ impl Vec2 {
}
}
impl<T: const UiNum + Copy> const From<T> for Vec2 {
fn from(v: T) -> Self {
Self {
x: v.to_f32(),
y: v.to_f32(),
}
}
}
// this version looks kinda cool... is it more readable? more annoying to copy and change though
impl_op!(impl Add for Vec2: add x y);
impl_op!(Vec2 Sub sub; x y);
@@ -102,18 +86,6 @@ impl Neg for Vec2 {
}
}
impl<T: const UiNum, U: const UiNum> const From<(T, U)> for Vec2
where
(T, U): const Destruct,
{
fn from((x, y): (T, U)) -> Self {
Self {
x: x.to_f32(),
y: y.to_f32(),
}
}
}
impl std::fmt::Debug for Vec2 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "({}, {})", self.x, self.y)
+1 -1
View File
@@ -1,4 +1,4 @@
use iris::{prelude::*, winit::*};
use iris::prelude::*;
fn main() {
DefaultApp::<State>::run();
+11
View File
@@ -0,0 +1,11 @@
[package]
name = "iris-macro"
version.workspace = true
edition.workspace = true
[dependencies]
quote = "1.0.42"
syn = { version = "2.0.111", features = ["full"] }
[lib]
proc-macro = true
+77
View File
@@ -0,0 +1,77 @@
extern crate proc_macro;
use proc_macro::TokenStream;
use quote::quote;
use syn::{
Attribute, Block, Ident, ItemTrait, Signature, Token, Visibility,
parse::{Parse, ParseStream, Result},
parse_macro_input, parse_quote,
};
struct Input {
attrs: Vec<Attribute>,
vis: Visibility,
name: Ident,
fns: Vec<InputFn>,
}
struct InputFn {
sig: Signature,
body: Block,
}
impl Parse for Input {
fn parse(input: ParseStream) -> Result<Self> {
let attrs = input.call(Attribute::parse_outer)?;
let vis = input.parse()?;
input.parse::<Token![trait]>()?;
let name = input.parse()?;
input.parse::<Token![;]>()?;
let mut fns = Vec::new();
while !input.is_empty() {
let sig = input.parse()?;
let body = input.parse()?;
fns.push(InputFn { sig, body })
}
if !input.is_empty() {
input.error("function expected");
}
Ok(Input {
attrs,
vis,
name,
fns,
})
}
}
#[proc_macro]
pub fn widget_trait(input: TokenStream) -> TokenStream {
let Input {
attrs,
vis,
name,
fns,
} = parse_macro_input!(input as Input);
let sigs: Vec<_> = fns.iter().map(|f| f.sig.clone()).collect();
let impls: Vec<_> = fns
.iter()
.map(|InputFn { sig, body }| quote! { #sig #body })
.collect();
let mut trai: ItemTrait = parse_quote!(
#vis trait #name<WL: WidgetLike<Tag>, Tag> {
#(#sigs;)*
}
);
trai.attrs = attrs;
TokenStream::from(quote! {
#trai
impl<WL: WidgetLike<Tag>, Tag> #name<WL, Tag> for WL {
#(#impls)*
}
})
}
+2 -5
View File
@@ -1,8 +1,5 @@
use cosmic_text::Family;
use iris::{
prelude::*,
winit::{attr::Selectable, event::Submit, *},
};
use iris::prelude::*;
use len_fns::*;
use winit::event::WindowEvent;
@@ -110,7 +107,7 @@ impl DefaultAppState for Client {
.size(30)
.attr::<Selectable>(())
.on(Submit, move |ctx| {
let content = ctx.ui.text(ctx.id).take();
let content = ctx.id.edit(ctx.ui).take();
let text = wtext(content)
.editable(false)
.size(30)
File renamed without changes.
+3 -3
View File
@@ -1,10 +1,10 @@
use crate::{prelude::*, winit::UiState};
use crate::{default::UiState, prelude::*};
use std::time::{Duration, Instant};
use winit::dpi::{LogicalPosition, LogicalSize};
pub struct Selector;
impl<W: 'static> WidgetAttr<W> for Selector {
impl<W: 'static + Widget> WidgetAttr<W> for Selector {
type Input = WidgetId<TextEdit>;
fn run(ui: &mut Ui, container: &WidgetId<W>, id: Self::Input) {
@@ -42,7 +42,7 @@ fn select(ui: &mut Ui, id: WidgetId<TextEdit>, state: &mut UiState, data: Cursor
let now = Instant::now();
let recent = (now - state.last_click) < Duration::from_millis(300);
state.last_click = now;
ui.text(&id)
id.edit(ui)
.select(data.cursor, data.size, data.sense.is_dragging(), recent);
if let Some(region) = ui.window_region(&id) {
state.window.set_ime_allowed(true);
+2 -8
View File
@@ -2,14 +2,8 @@ use crate::layout::DefaultEvent;
#[derive(Eq, PartialEq, Hash, Clone)]
pub struct Submit;
impl DefaultEvent for Submit {}
#[derive(Eq, PartialEq, Hash, Clone)]
pub struct Edited;
impl DefaultEvent for Submit {
type Data = ();
}
impl DefaultEvent for Edited {
type Data = ();
}
impl DefaultEvent for Edited {}
+1 -5
View File
@@ -1,8 +1,4 @@
use crate::{
core::{CursorState, Modifiers},
layout::Vec2,
winit::UiState,
};
use crate::prelude::*;
use winit::{
event::{MouseButton, MouseScrollDelta, WindowEvent},
keyboard::{Key, NamedKey},
+11 -9
View File
@@ -1,6 +1,4 @@
use crate::prelude::*;
use crate::winit::event::{Edited, Submit};
use crate::winit::{input::Input, render::UiRenderer};
use arboard::Clipboard;
use std::sync::Arc;
use std::time::Instant;
@@ -9,12 +7,16 @@ use winit::event_loop::{ActiveEventLoop, EventLoopProxy};
use winit::window::{Window, WindowAttributes};
mod app;
pub mod attr;
pub mod event;
pub mod input;
pub mod render;
mod attr;
mod event;
mod input;
mod render;
pub use app::*;
pub use attr::*;
pub use event::*;
pub use input::*;
pub use render::*;
pub type Proxy<Event> = EventLoopProxy<Event>;
pub type DefaultApp<Data> = App<DefaultState<Data>>;
@@ -105,7 +107,7 @@ impl<State: DefaultAppState> AppState for DefaultState<State> {
if old != ui_state.focus
&& let Some(old) = old
{
ui.text(&old).deselect();
old.edit(ui).deselect();
}
match &event {
WindowEvent::CloseRequested => event_loop.exit(),
@@ -123,7 +125,7 @@ impl<State: DefaultAppState> AppState for DefaultState<State> {
&& event.state.is_pressed()
{
let sel = &sel.clone();
let mut text = ui.text(sel);
let mut text = sel.edit(ui);
match text.apply_event(event, &ui_state.input.modifiers) {
TextInputResult::Unfocus => {
ui_state.focus = None;
@@ -152,7 +154,7 @@ impl<State: DefaultAppState> AppState for DefaultState<State> {
}
WindowEvent::Ime(ime) => {
if let Some(sel) = &ui_state.focus {
let mut text = ui.text(sel);
let mut text = sel.edit(ui);
match ime {
Ime::Enabled | Ime::Disabled => (),
Ime::Preedit(content, _pos) => {
File renamed without changes.
+67
View File
@@ -0,0 +1,67 @@
use crate::prelude::*;
// TODO: naming in here is a bit weird like eventable
#[macro_export]
macro_rules! event_ctx {
($ty: ty) => {
mod local_event_trait {
use super::*;
#[allow(unused_imports)]
use $crate::prelude::*;
pub trait EventableCtx<W, Tag, Ctx: 'static> {
fn on<E: Event>(
self,
event: E,
f: impl WidgetEventFn<Ctx, E::Data, W>,
) -> impl WidgetIdFn<W> + EventableCtx<W, IdFnTag, Ctx>;
}
impl<WL: WidgetLike<Tag>, Tag> EventableCtx<WL::Widget, Tag, $ty> for WL {
fn on<E: Event>(
self,
event: E,
f: impl WidgetEventFn<$ty, E::Data, WL::Widget>,
) -> impl WidgetIdFn<WL::Widget> + EventableCtx<WL::Widget, IdFnTag, $ty> {
eventable::Eventable::on(self, event, f)
}
}
}
use local_event_trait::*;
};
}
pub use event_ctx;
pub mod eventable {
use super::*;
pub trait Eventable<W, Tag> {
fn on<E: Event, Ctx: 'static>(
self,
event: E,
f: impl WidgetEventFn<Ctx, E::Data, W>,
) -> impl WidgetIdFn<W> + Eventable<W, IdFnTag>;
}
impl<WL: WidgetLike<Tag>, Tag> Eventable<WL::Widget, Tag> for WL {
fn on<E: Event, Ctx: 'static>(
self,
event: E,
f: impl WidgetEventFn<Ctx, E::Data, WL::Widget>,
) -> impl WidgetIdFn<WL::Widget> {
move |ui| {
let id = self.add(ui);
let id_ = id.weak();
ui.register_event(&id, event, move |ctx| {
f(EventIdCtx {
id: &id_,
state: ctx.state,
data: ctx.data,
ui: ctx.ui,
});
});
id
}
}
}
}
-46
View File
@@ -1,46 +0,0 @@
//! tree structure for masking
use crate::layout::UiRegion;
pub struct Masks {
data: Vec<MaskNode>,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct MaskPtr(u32);
#[repr(C)]
pub struct MaskNode {
/// TODO: this is just a rect for now,
/// but would like to support arbitrary masks
/// at some point; custom shader
/// would probably handle that case
/// bc you'd need to render to a special target
/// anyways
region: UiRegion,
prev: MaskPtr,
}
impl MaskPtr {
const NONE: Self = Self(u32::MAX);
}
impl Masks {
pub fn push(&mut self, parent: MaskPtr, region: UiRegion) -> MaskPtr {
match parent.0 {
_ => {
}
u32::MAX => {
let i = self.data.len();
self.data.push(MaskNode {
region,
prev: parent,
});
MaskPtr(i as u32)
}
}
}
pub fn pop(&mut self, i: usize) {}
}
-21
View File
@@ -1,21 +0,0 @@
pub const trait UiNum {
fn to_f32(self) -> f32;
}
impl const UiNum for f32 {
fn to_f32(self) -> f32 {
self
}
}
impl const UiNum for u32 {
fn to_f32(self) -> f32 {
self as f32
}
}
impl const UiNum for i32 {
fn to_f32(self) -> f32 {
self as f32
}
}
+15 -16
View File
@@ -1,24 +1,23 @@
#![feature(macro_metavar_expr_concat)]
#![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(gen_blocks)]
#![feature(associated_type_defaults)]
pub mod core;
pub mod layout;
pub mod render;
pub mod util;
pub mod winit;
mod default;
mod event;
mod widget;
pub use iris_core::*;
pub use iris_macro::*;
pub mod prelude {
pub use crate::core::*;
pub use crate::layout::*;
pub use crate::render::*;
use super::*;
pub use default::*;
pub use event::*;
pub use iris_macro::*;
pub use layout::*;
pub use render::*;
pub use widget::*;
pub use super::util::Vec2;
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+13 -5
View File
@@ -6,8 +6,8 @@ use std::{
};
use crate::{
layout::{UiModule, UiRegion, Vec2},
util::{HashMap, Id},
layout::{UiModule, UiRegion},
util::{HashMap, Id, Vec2},
};
#[derive(Clone, Copy, PartialEq)]
@@ -166,8 +166,12 @@ impl<Ctx> CursorModule<Ctx> {
}
}
impl Ui {
pub fn run_sensors<Ctx: 'static>(
pub trait SensorUi {
fn run_sensors<Ctx: 'static>(&mut self, ctx: &mut Ctx, cursor: &CursorState, window_size: Vec2);
}
impl SensorUi for Ui {
fn run_sensors<Ctx: 'static>(
&mut self,
ctx: &mut Ctx,
cursor: &CursorState,
@@ -205,7 +209,11 @@ impl<Ctx: 'static> CursorModule<Ctx> {
scroll_delta: cursor.scroll_delta,
sense,
};
(sensor.f)(EventCtx { ui, state: ctx, data });
(sensor.f)(EventCtx {
ui,
state: ctx,
data,
});
}
}
}
File renamed without changes.
@@ -610,3 +610,16 @@ impl DerefMut for TextEdit {
&mut self.view
}
}
pub trait TextEditable {
fn edit<'a>(&self, ui: &'a mut Ui) -> TextEditCtx<'a>;
}
impl<I: IdLike<Widget = TextEdit>> TextEditable for I {
fn edit<'a>(&self, ui: &'a mut Ui) -> TextEditCtx<'a> {
TextEditCtx {
text: ui.data.widgets.get_mut(self).unwrap(),
font_system: &mut ui.data.text.font_system,
}
}
}
File renamed without changes.
File renamed without changes.