macro goodness

This commit is contained in:
2025-12-15 23:11:32 -05:00
parent 0b8a93c5ce
commit 8d1a810483
9 changed files with 131 additions and 61 deletions

View File

@@ -19,6 +19,7 @@ mod orientation;
mod painter;
mod primitive;
mod render;
mod state;
mod ui;
mod widget;

8
core/src/state.rs Normal file
View File

@@ -0,0 +1,8 @@
#[macro_export]
macro_rules! core_state {
($vis: vis $state: ty) => {
$vis type WidgetHandle<W = dyn Widget<$state>> = $crate::WidgetHandle<$state, W>;
$vis type WidgetRef<W = dyn Widget<$state>> = $crate::WidgetRef<$state, W>;
$vis type Ui = $crate::Ui<$state>;
};
}

View File

@@ -5,7 +5,7 @@ fn main() {
App::<State>::run();
}
#[derive(HasUi, HasUiState)]
#[derive(DefaultUiState)]
struct State {
ui: Ui<Self>,
ui_state: UiState<Self>,

View File

@@ -145,9 +145,16 @@ pub fn derive_global_state(input: TokenStream) -> TokenStream {
#[proc_macro_derive(HasUi)]
pub fn derive_has_ui(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as ItemStruct);
let name = input.ident;
let Some(field) = input.fields.iter().find(|f| f.ty == parse_quote!(Ui<Self>)) else {
has_ui(&parse_macro_input!(input))
}
fn has_ui(input: &ItemStruct) -> TokenStream {
let name = &input.ident;
let Some(field) = input
.fields
.iter()
.find(|f| f.ty == parse_quote!(Ui<Self>) || f.ty == parse_quote!(Ui))
else {
return Error::new(name.span(), "could not find a Ui<Self> field for HasUi")
.into_compile_error()
.into();
@@ -155,11 +162,11 @@ pub fn derive_has_ui(input: TokenStream) -> TokenStream {
let field = &field.ident;
quote! {
impl HasUi for #name {
fn ui_ref(&self) -> &Ui<Self> {
fn ui_ref(&self) -> &iris::iris_core::Ui<Self> {
&self.#field
}
fn ui(&mut self) -> &mut Ui<Self> {
fn ui(&mut self) -> &mut iris::iris_core::Ui<Self> {
&mut self.#field
}
}
@@ -167,14 +174,15 @@ pub fn derive_has_ui(input: TokenStream) -> TokenStream {
.into()
}
#[proc_macro_derive(HasUiState)]
pub fn derive_has_ui_state(input: TokenStream) -> TokenStream {
#[proc_macro_derive(DefaultUiState)]
pub fn derive_default_ui_state(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as ItemStruct);
let mut output = has_ui(&input);
let name = input.ident;
let Some(field) = input
.fields
.iter()
.find(|f| f.ty == parse_quote!(UiState<Self>))
.find(|f| f.ty == parse_quote!(UiState<Self>) || f.ty == parse_quote!(UiState))
else {
return Error::new(
name.span(),
@@ -184,12 +192,15 @@ pub fn derive_has_ui_state(input: TokenStream) -> TokenStream {
.into();
};
let field = &field.ident;
quote! {
impl HasUiState for #name {
fn ui_state(&mut self) -> &mut UiState<Self> {
&mut self.#field
output.extend::<TokenStream>(
quote! {
impl HasUiState for #name {
fn ui_state(&mut self) -> &mut iris::default::UiState<Self> {
&mut self.#field
}
}
}
}
.into()
.into(),
);
output
}

View File

@@ -1,23 +1,20 @@
use std::{cell::RefCell, rc::Rc};
use cosmic_text::Family;
use iris::prelude::*;
use len_fns::*;
use std::{cell::RefCell, rc::Rc};
use winit::{event::WindowEvent, event_loop::ActiveEventLoop, window::WindowAttributes};
iris::state_prelude!(Client);
fn main() {
App::<Client>::run();
}
#[derive(HasUi, HasUiState)]
#[derive(DefaultUiState)]
pub struct Client {
ui: Ui<Self>,
ui_state: UiState<Self>,
info: WidgetRef<Self, Text<Self>>,
ui: Ui,
ui_state: UiState,
info: WidgetRef<Text>,
}
event_ctx!(Client);
impl DefaultAppState for Client {
fn new(event_loop: &ActiveEventLoop, _proxy: Proxy<Self::Event>) -> Self {
let mut ui = Ui::new();
@@ -153,7 +150,7 @@ impl DefaultAppState for Client {
let main = WidgetPtr::new().handles(ui);
let vals = Rc::new(RefCell::new((0, Vec::new())));
let mut switch_button = |color, to: WidgetHandle<Self>, label| {
let mut switch_button = |color, to: WidgetHandle, label| {
let vec = &mut vals.borrow_mut().1;
let i = vec.len();
if vec.is_empty() {

View File

@@ -52,7 +52,10 @@ pub trait HasUiState: Sized + 'static + HasUi {
fn ui_state(&mut self) -> &mut UiState<Self>;
fn ui_with_ui_state(&mut self) -> (&mut Ui<Self>, &mut UiState<Self>) {
// as long as you're not doing anything actually unhinged this should always work safely
(unsafe { std::mem::transmute(self.ui()) }, self.ui_state())
(
unsafe { std::mem::transmute::<&mut Ui<Self>, &mut Ui<Self>>(self.ui()) },
self.ui_state(),
)
}
}

View File

@@ -23,34 +23,3 @@ pub mod eventable {
}
}
}
// 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<WL: WidgetLike<$ty, Tag>, Tag> {
fn on<E: EventLike>(
self,
event: E,
f: impl for<'a> WidgetEventFn<$ty, <E::Event as Event>::Data<'a>, WL::Widget>,
) -> impl WidgetIdFn<$ty, WL::Widget>;
}
impl<WL: WidgetLike<$ty, Tag>, Tag> EventableCtx<WL, Tag> for WL {
fn on<E: EventLike>(
self,
event: E,
f: impl for<'a> WidgetEventFn<Client, <E::Event as Event>::Data<'a>, WL::Widget>,
) -> impl WidgetIdFn<Client, WL::Widget> {
eventable::Eventable::on(self, event, f)
}
}
}
use local_event_trait::*;
};
}
pub use event_ctx;

View File

@@ -4,9 +4,29 @@
#![feature(associated_type_defaults)]
#![feature(unsize)]
mod default;
mod event;
mod widget;
pub mod default;
pub mod event;
pub mod state;
pub mod widget;
pub use iris_core;
pub use iris_macro;
#[macro_export]
macro_rules! state_prelude {
($state:ty) => {
iris::event_state!($state);
iris::iris_core::core_state!($state);
iris::default_state!($state);
iris::widget_state!($state);
pub use iris::{
default::*,
iris_core::{len_fns::*, util::Vec2, *},
iris_macro::*,
widget::*,
};
};
}
pub mod prelude {
use super::*;
@@ -17,4 +37,5 @@ pub mod prelude {
pub use widget::*;
pub use iris_core::util::Vec2;
pub use state::*;
}

60
src/state.rs Normal file
View File

@@ -0,0 +1,60 @@
#[macro_export]
macro_rules! default_state {
($vis:vis $state:ty) => {
$vis type UiState = $crate::default::UiState<$state>;
};
}
#[macro_export]
macro_rules! widget_state {
($vis:vis $state:ty) => {
$crate::widget_state!(
$vis $state;
Aligned,
LayerOffset,
MaxSize,
Offset,
Scroll,
Sized,
Span,
Stack,
Text,
TextEdit,
Masked,
WidgetPtr,
);
};
($vis:vis $state:ty; $($ty:ident,)*) => {
$($vis type $ty = $crate::widget::$ty<$state>;)*
}
}
#[macro_export]
macro_rules! event_state {
($state:ty) => {
mod local_event_trait {
use super::*;
#[allow(unused_imports)]
use $crate::prelude::*;
pub trait EventableCtx<WL: WidgetLike<$state, Tag>, Tag> {
fn on<E: EventLike>(
self,
event: E,
f: impl for<'a> WidgetEventFn<$state, <E::Event as Event>::Data<'a>, WL::Widget>,
) -> impl WidgetIdFn<$state, WL::Widget>;
}
impl<WL: WidgetLike<$state, Tag>, Tag> EventableCtx<WL, Tag> for WL {
fn on<E: EventLike>(
self,
event: E,
f: impl for<'a> WidgetEventFn<Client, <E::Event as Event>::Data<'a>, WL::Widget>,
) -> impl WidgetIdFn<Client, WL::Widget> {
eventable::Eventable::on(self, event, f)
}
}
}
use local_event_trait::*;
};
}
pub use event_state;