learn how workspaces + proc macros work & restructure everything
This commit is contained in:
1 parent
434e3c3af7
commit
e44bb8eca4
76 files changed
+610
-576
No files matched your search
+10
-11
@@ -56,10 +56,9 @@ impl DefaultAppState for Client {
|
||||
let add_button = rect(Color::LIME)
|
||||
.radius(30)
|
||||
.on(CursorSense::click(), move |ctx| {
|
||||
let child = ctx
|
||||
.ui
|
||||
.add(image(include_bytes!("assets/sungals.png")).center())
|
||||
.any();
|
||||
let child = image(include_bytes!("assets/sungals.png"))
|
||||
.center()
|
||||
.add(ctx.ui);
|
||||
span_add_.get_mut().children.push(child);
|
||||
})
|
||||
.sized((150, 150))
|
||||
@@ -106,7 +105,7 @@ impl DefaultAppState for Client {
|
||||
.text_align(Align::LEFT)
|
||||
.size(30)
|
||||
.attr::<Selectable>(())
|
||||
.on(iris::winit::Submit, move |ctx| {
|
||||
.on(Submit, move |ctx| {
|
||||
let content = ctx.widget.get_mut().take();
|
||||
let text = wtext(content)
|
||||
.editable(false)
|
||||
@@ -115,7 +114,7 @@ impl DefaultAppState for Client {
|
||||
.wrap(true)
|
||||
.attr::<Selectable>(());
|
||||
let msg_box = text.background(rect(Color::WHITE.darker(0.5))).add(ctx.ui);
|
||||
texts.get_mut().children.push(msg_box.any());
|
||||
texts.get_mut().children.push(msg_box);
|
||||
})
|
||||
.add(ui);
|
||||
let text_edit_scroll = (
|
||||
@@ -163,13 +162,13 @@ impl DefaultAppState for Client {
|
||||
};
|
||||
|
||||
let tabs = (
|
||||
switch_button(Color::RED, pad_test.any(), "pad"),
|
||||
switch_button(Color::GREEN, span_test.any(), "span"),
|
||||
switch_button(Color::BLUE, span_add_test.any(), "image span"),
|
||||
switch_button(Color::MAGENTA, text_test.any(), "text layout"),
|
||||
switch_button(Color::RED, pad_test, "pad"),
|
||||
switch_button(Color::GREEN, span_test, "span"),
|
||||
switch_button(Color::BLUE, span_add_test, "image span"),
|
||||
switch_button(Color::MAGENTA, text_test, "text layout"),
|
||||
switch_button(
|
||||
Color::YELLOW.mul_rgb(0.5),
|
||||
text_edit_scroll.any(),
|
||||
text_edit_scroll,
|
||||
"text edit scroll",
|
||||
),
|
||||
)
|
||||
|
||||
File renamed without changes.
@@ -1,4 +1,4 @@
|
||||
use crate::{prelude::*, winit::UiState};
|
||||
use crate::{prelude::*, default::UiState};
|
||||
use std::time::{Duration, Instant};
|
||||
use winit::dpi::{LogicalPosition, LogicalSize};
|
||||
|
||||
File renamed without changes.
@@ -1,7 +1,7 @@
|
||||
use crate::{
|
||||
core::{CursorState, Modifiers},
|
||||
widget::{CursorState, Modifiers},
|
||||
layout::Vec2,
|
||||
winit::UiState,
|
||||
default::UiState,
|
||||
};
|
||||
use winit::{
|
||||
event::{MouseButton, MouseScrollDelta, WindowEvent},
|
||||
File renamed without changes.
File renamed without changes.
@@ -1,20 +0,0 @@
|
||||
use crate::layout::{Ui, WidgetRef, WidgetIdFn, WidgetLike};
|
||||
|
||||
pub trait WidgetAttr<W: ?Sized> {
|
||||
type Input;
|
||||
fn run(ui: &mut Ui, id: &WidgetRef<W>, input: Self::Input);
|
||||
}
|
||||
|
||||
pub trait Attrable<W: ?Sized, Tag> {
|
||||
fn attr<A: WidgetAttr<W>>(self, input: A::Input) -> impl WidgetIdFn<W>;
|
||||
}
|
||||
|
||||
impl<WL: WidgetLike<Tag>, Tag> Attrable<WL::Widget, Tag> for WL {
|
||||
fn attr<A: WidgetAttr<WL::Widget>>(self, input: A::Input) -> impl WidgetIdFn<WL::Widget> {
|
||||
|ui| {
|
||||
let id = self.add(ui);
|
||||
A::run(ui, &id, input);
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,165 +0,0 @@
|
||||
#![allow(clippy::multiple_bound_locations)]
|
||||
|
||||
use std::marker::Destruct;
|
||||
|
||||
/// stored in linear for sane manipulation
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Hash, PartialEq, Eq, bytemuck::Zeroable, Debug)]
|
||||
pub struct Color<T: ColorNum> {
|
||||
pub r: T,
|
||||
pub g: T,
|
||||
pub b: T,
|
||||
pub a: T,
|
||||
}
|
||||
|
||||
impl<T: ColorNum> Color<T> {
|
||||
pub const BLACK: Self = Self::rgb(T::MIN, T::MIN, T::MIN);
|
||||
pub const WHITE: Self = Self::rgb(T::MAX, T::MAX, T::MAX);
|
||||
pub const GRAY: Self = Self::rgb(T::MID, T::MID, T::MID);
|
||||
|
||||
pub const RED: Self = Self::rgb(T::MAX, T::MIN, T::MIN);
|
||||
pub const ORANGE: Self = Self::rgb(T::MAX, T::MID, T::MIN);
|
||||
pub const YELLOW: Self = Self::rgb(T::MAX, T::MAX, T::MIN);
|
||||
pub const LIME: Self = Self::rgb(T::MID, T::MAX, T::MIN);
|
||||
pub const GREEN: Self = Self::rgb(T::MIN, T::MAX, T::MIN);
|
||||
pub const TURQUOISE: Self = Self::rgb(T::MIN, T::MAX, T::MID);
|
||||
pub const CYAN: Self = Self::rgb(T::MIN, T::MAX, T::MAX);
|
||||
pub const SKY: Self = Self::rgb(T::MIN, T::MID, T::MAX);
|
||||
pub const BLUE: Self = Self::rgb(T::MIN, T::MIN, T::MAX);
|
||||
pub const PURPLE: Self = Self::rgb(T::MID, T::MIN, T::MAX);
|
||||
pub const MAGENTA: Self = Self::rgb(T::MAX, T::MIN, T::MAX);
|
||||
|
||||
pub const NONE: Self = Self::new(T::MIN, T::MIN, T::MIN, T::MIN);
|
||||
|
||||
pub const fn new(r: T, g: T, b: T, a: T) -> Self {
|
||||
Self { r, g, b, a }
|
||||
}
|
||||
pub const fn rgb(r: T, g: T, b: T) -> Self {
|
||||
Self { r, g, b, a: T::MAX }
|
||||
}
|
||||
pub fn alpha(mut self, a: T) -> Self {
|
||||
self.a = a;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn as_arr(self) -> [T; 4] {
|
||||
[self.r, self.g, self.b, self.a]
|
||||
}
|
||||
}
|
||||
|
||||
pub const trait F32Conversion {
|
||||
fn to(self) -> f32;
|
||||
fn from(x: f32) -> Self;
|
||||
}
|
||||
|
||||
pub trait ColorNum {
|
||||
const MIN: Self;
|
||||
const MID: Self;
|
||||
const MAX: Self;
|
||||
}
|
||||
|
||||
macro_rules! map_rgb {
|
||||
($x:ident,$self:ident, $e:tt) => {
|
||||
#[allow(unused_braces)]
|
||||
Self {
|
||||
r: {
|
||||
let $x = $self.r;
|
||||
$e
|
||||
},
|
||||
g: {
|
||||
let $x = $self.g;
|
||||
$e
|
||||
},
|
||||
b: {
|
||||
let $x = $self.b;
|
||||
$e
|
||||
},
|
||||
a: $self.a,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl<T: ColorNum + const F32Conversion> Color<T>
|
||||
where
|
||||
Self: const Destruct,
|
||||
{
|
||||
pub const fn mul_rgb(self, amt: impl const F32Conversion) -> Self {
|
||||
let amt = amt.to();
|
||||
map_rgb!(x, self, { T::from(x.to() * amt) })
|
||||
}
|
||||
|
||||
pub const fn add_rgb(self, amt: impl const F32Conversion) -> Self {
|
||||
let amt = amt.to();
|
||||
map_rgb!(x, self, { T::from(x.to() + amt) })
|
||||
}
|
||||
|
||||
pub const fn darker(self, amt: f32) -> Self {
|
||||
self.mul_rgb(1.0 - amt)
|
||||
}
|
||||
|
||||
pub const fn brighter(self, amt: f32) -> Self {
|
||||
map_rgb!(x, self, {
|
||||
let x = x.to();
|
||||
T::from(x + (1.0 - x) * amt)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn map_rgb(self, f: impl Fn(T) -> T) -> Self {
|
||||
Self {
|
||||
r: f(self.r),
|
||||
g: f(self.g),
|
||||
b: f(self.b),
|
||||
a: self.a,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn srgb(r: T, g: T, b: T) -> Self {
|
||||
Self {
|
||||
r: s_to_l(r),
|
||||
g: s_to_l(g),
|
||||
b: s_to_l(b),
|
||||
a: T::MAX,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn s_to_l<T: F32Conversion>(x: T) -> T {
|
||||
let x = x.to();
|
||||
T::from(if x <= 0.0405 {
|
||||
x / 12.92
|
||||
} else {
|
||||
((x + 0.055) / 1.055).powf(2.4)
|
||||
})
|
||||
}
|
||||
|
||||
impl ColorNum for u8 {
|
||||
const MIN: Self = u8::MIN;
|
||||
const MID: Self = u8::MAX / 2;
|
||||
const MAX: Self = u8::MAX;
|
||||
}
|
||||
|
||||
impl ColorNum for f32 {
|
||||
const MIN: Self = 0.0;
|
||||
const MID: Self = 0.5;
|
||||
const MAX: Self = 1.0;
|
||||
}
|
||||
|
||||
unsafe impl bytemuck::Pod for Color<u8> {}
|
||||
|
||||
impl const F32Conversion for f32 {
|
||||
fn to(self) -> f32 {
|
||||
self
|
||||
}
|
||||
fn from(x: f32) -> Self {
|
||||
x
|
||||
}
|
||||
}
|
||||
|
||||
impl const F32Conversion for u8 {
|
||||
fn to(self) -> f32 {
|
||||
self as f32 / 255.0
|
||||
}
|
||||
fn from(x: f32) -> Self {
|
||||
(x * 255.0).clamp(0.0, 255.0) as Self
|
||||
}
|
||||
}
|
||||
@@ -1,273 +0,0 @@
|
||||
use std::{hash::Hash, rc::Rc};
|
||||
|
||||
use crate::{
|
||||
layout::{IdFnTag, Ui, UiModule, WidgetIdFn, WidgetLike, WidgetRef},
|
||||
util::{HashMap, Id},
|
||||
};
|
||||
|
||||
pub trait Event: Sized {
|
||||
type Module<Ctx: 'static>: EventModule<Self, Ctx>;
|
||||
type Data: Clone;
|
||||
}
|
||||
|
||||
pub struct EventCtx<'a, Ctx, Data> {
|
||||
pub ui: &'a mut Ui,
|
||||
pub state: &'a mut Ctx,
|
||||
pub data: Data,
|
||||
}
|
||||
|
||||
pub type ECtx<'a, Ctx, Data, W> = EventIdCtx<'a, Ctx, Data, W>;
|
||||
pub struct EventIdCtx<'a, Ctx, Data, W: ?Sized> {
|
||||
pub widget: &'a WidgetRef<W>,
|
||||
pub ui: &'a mut Ui,
|
||||
pub state: &'a mut Ctx,
|
||||
pub data: Data,
|
||||
}
|
||||
|
||||
pub trait EventFn<Ctx, Data>: Fn(EventCtx<Ctx, Data>) + 'static {}
|
||||
impl<F: Fn(EventCtx<Ctx, Data>) + 'static, Ctx, Data> EventFn<Ctx, Data> for F {}
|
||||
|
||||
pub trait WidgetEventFn<Ctx, Data, W: ?Sized>: Fn(EventIdCtx<Ctx, Data, W>) + 'static {}
|
||||
impl<F: Fn(EventIdCtx<Ctx, Data, W>) + 'static, Ctx, Data, W: ?Sized> 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::*;
|
||||
use std::marker::Sized;
|
||||
#[allow(unused_imports)]
|
||||
use $crate::prelude::*;
|
||||
|
||||
#[allow(unused)]
|
||||
pub trait EventableCtx<W: ?Sized, 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)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub trait EventableCtxUi<W: ?Sized, Tag, Ctx: 'static>
|
||||
where
|
||||
WidgetRef<W>: EventableCtx<W, Tag, Ctx>,
|
||||
{
|
||||
fn on<E: Event>(
|
||||
&mut self,
|
||||
widget: &WidgetRef<W>,
|
||||
event: E,
|
||||
f: impl WidgetEventFn<Ctx, E::Data, W>,
|
||||
);
|
||||
}
|
||||
|
||||
impl<W: ?Sized + 'static, Tag> EventableCtxUi<W, Tag, $ty> for Ui
|
||||
where
|
||||
WidgetRef<W>: EventableCtx<W, Tag, $ty>,
|
||||
{
|
||||
fn on<E: Event>(
|
||||
&mut self,
|
||||
widget: &WidgetRef<W>,
|
||||
event: E,
|
||||
f: impl WidgetEventFn<$ty, E::Data, W>,
|
||||
) {
|
||||
self.register_widget_event(&widget, event, f);
|
||||
}
|
||||
}
|
||||
}
|
||||
use local_event_trait::*;
|
||||
};
|
||||
}
|
||||
pub use event_ctx;
|
||||
|
||||
pub mod eventable {
|
||||
use super::*;
|
||||
|
||||
pub trait Eventable<W: ?Sized, 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);
|
||||
ui.register_widget_event(&id, event, f);
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Ui {
|
||||
pub fn register_event<W: ?Sized, E: Event, Ctx: 'static>(
|
||||
&mut self,
|
||||
id: &WidgetRef<W>,
|
||||
event: E,
|
||||
f: impl EventFn<Ctx, E::Data>,
|
||||
) {
|
||||
self.data
|
||||
.modules
|
||||
.get_mut::<E::Module<Ctx>>()
|
||||
.register(id.id(), event, f);
|
||||
}
|
||||
|
||||
pub fn register_widget_event<W: ?Sized + 'static, E: Event, Ctx: 'static>(
|
||||
&mut self,
|
||||
id: &WidgetRef<W>,
|
||||
event: E,
|
||||
f: impl WidgetEventFn<Ctx, E::Data, W>,
|
||||
) {
|
||||
let id_ = id.weak();
|
||||
self.data
|
||||
.modules
|
||||
.get_mut::<E::Module<Ctx>>()
|
||||
.register(id.id(), event, move |ctx| {
|
||||
f(EventIdCtx {
|
||||
widget: &id_.expect_strong(),
|
||||
ui: ctx.ui,
|
||||
state: ctx.state,
|
||||
data: ctx.data,
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub trait DefaultEvent: Hash + Eq + 'static {
|
||||
type Data: Clone;
|
||||
}
|
||||
|
||||
impl<E: DefaultEvent> Event for E {
|
||||
type Module<Ctx: 'static> = DefaultEventModule<E, Ctx>;
|
||||
type Data = E::Data;
|
||||
}
|
||||
|
||||
pub trait EventModule<E: Event, Ctx>: UiModule + Default {
|
||||
fn register(&mut self, id: Id, event: E, f: impl EventFn<Ctx, E::Data>);
|
||||
fn run<'a>(
|
||||
&self,
|
||||
id: &Id,
|
||||
event: E,
|
||||
) -> Option<impl Fn(EventCtx<Ctx, E::Data>) + use<'a, Self, E, Ctx>>;
|
||||
}
|
||||
|
||||
type EventFnMap<Ctx, Data> = HashMap<Id, Vec<Rc<dyn EventFn<Ctx, Data>>>>;
|
||||
pub struct DefaultEventModule<E: Event, Ctx> {
|
||||
map: HashMap<E, EventFnMap<Ctx, <E as Event>::Data>>,
|
||||
}
|
||||
|
||||
impl<E: Event + 'static, Ctx: 'static> UiModule for DefaultEventModule<E, Ctx> {
|
||||
fn on_remove(&mut self, id: &Id) {
|
||||
for map in self.map.values_mut() {
|
||||
map.remove(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait HashableEvent: Event + Hash + Eq + 'static {}
|
||||
impl<E: Event + Hash + Eq + 'static> HashableEvent for E {}
|
||||
|
||||
impl<E: HashableEvent, Ctx: 'static> EventModule<E, Ctx> for DefaultEventModule<E, Ctx> {
|
||||
fn register(&mut self, id: Id, event: E, f: impl EventFn<Ctx, <E as Event>::Data>) {
|
||||
self.map
|
||||
.entry(event)
|
||||
.or_default()
|
||||
.entry(id)
|
||||
.or_default()
|
||||
.push(Rc::new(f));
|
||||
}
|
||||
|
||||
fn run<'a>(
|
||||
&self,
|
||||
id: &Id,
|
||||
event: E,
|
||||
) -> Option<impl Fn(EventCtx<Ctx, E::Data>) + use<'a, E, Ctx>> {
|
||||
if let Some(map) = self.map.get(&event)
|
||||
&& let Some(fs) = map.get(id)
|
||||
{
|
||||
let fs = fs.clone();
|
||||
Some(move |ctx: EventCtx<Ctx, <E as Event>::Data>| {
|
||||
for f in &fs {
|
||||
f(EventCtx {
|
||||
ui: ctx.ui,
|
||||
state: ctx.state,
|
||||
data: ctx.data.clone(),
|
||||
})
|
||||
}
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: HashableEvent, Ctx: 'static> DefaultEventModule<E, Ctx> {
|
||||
pub fn run_all(&self, event: E, ctx: EventCtx<Ctx, E::Data>)
|
||||
where
|
||||
E::Data: Clone,
|
||||
{
|
||||
if let Some(map) = self.map.get(&event) {
|
||||
for fs in map.values() {
|
||||
for f in fs {
|
||||
f(EventCtx {
|
||||
ui: ctx.ui,
|
||||
state: ctx.state,
|
||||
data: ctx.data.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Event + 'static, Ctx: 'static> Default for DefaultEventModule<E, Ctx> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
map: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Ui {
|
||||
pub fn run_event<E: Event, Ctx: 'static, W: ?Sized>(
|
||||
&mut self,
|
||||
ctx: &mut Ctx,
|
||||
id: &WidgetRef<W>,
|
||||
event: E,
|
||||
data: E::Data,
|
||||
) {
|
||||
if let Some(f) = self
|
||||
.data
|
||||
.modules
|
||||
.get_mut::<E::Module<Ctx>>()
|
||||
.run(&id.id(), event)
|
||||
{
|
||||
f(EventCtx {
|
||||
ui: self,
|
||||
state: ctx,
|
||||
data,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,268 +0,0 @@
|
||||
use std::ops::{Index, IndexMut};
|
||||
|
||||
use crate::render::{MaskIdx, Primitive, PrimitiveHandle, PrimitiveInst, Primitives};
|
||||
|
||||
struct LayerNode<T> {
|
||||
next: Ptr,
|
||||
prev: Ptr,
|
||||
child: Option<Child>,
|
||||
depth: usize,
|
||||
data: T,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
enum Ptr {
|
||||
/// continue on same level
|
||||
Next(usize),
|
||||
/// go back to parent
|
||||
Parent(usize),
|
||||
/// end
|
||||
None,
|
||||
}
|
||||
|
||||
/// TODO: currently this does not ever free layers
|
||||
/// is that realistically desired?
|
||||
pub struct Layers<T> {
|
||||
vec: Vec<LayerNode<T>>,
|
||||
/// index of last layer at top level (start at first = 0)
|
||||
last: usize,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct Child {
|
||||
head: usize,
|
||||
tail: usize,
|
||||
}
|
||||
|
||||
pub type PrimitiveLayers = Layers<Primitives>;
|
||||
|
||||
impl<T: Default> Layers<T> {
|
||||
pub fn new() -> Layers<T> {
|
||||
Self {
|
||||
vec: vec![LayerNode::head()],
|
||||
last: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.vec.clear();
|
||||
self.vec.push(LayerNode::head());
|
||||
}
|
||||
|
||||
fn push(&mut self, node: LayerNode<T>) -> usize {
|
||||
let i = self.vec.len();
|
||||
self.vec.push(node);
|
||||
i
|
||||
}
|
||||
|
||||
pub fn next(&mut self, i: usize) -> usize {
|
||||
if let Ptr::Next(i) = self.vec[i].next {
|
||||
return i;
|
||||
}
|
||||
let i_new = self.push(LayerNode::new(
|
||||
T::default(),
|
||||
self.vec[i].next,
|
||||
Ptr::Next(i),
|
||||
self.vec[i].depth,
|
||||
));
|
||||
self.vec[i].next = Ptr::Next(i_new);
|
||||
self.vec[i_new].prev = Ptr::Next(i);
|
||||
match self.vec[i_new].next {
|
||||
Ptr::Next(i) => self.vec[i].prev = Ptr::Next(i_new),
|
||||
Ptr::Parent(i) => self.vec[i].child.as_mut().unwrap().tail = i_new,
|
||||
Ptr::None => self.last = i_new,
|
||||
}
|
||||
i_new
|
||||
}
|
||||
|
||||
pub fn child(&mut self, i: usize) -> usize {
|
||||
if let Some(c) = self.vec[i].child {
|
||||
return c.head;
|
||||
}
|
||||
let i_child = self.push(LayerNode::new(
|
||||
T::default(),
|
||||
Ptr::Parent(i),
|
||||
Ptr::Parent(i),
|
||||
self.vec[i].depth + 1,
|
||||
));
|
||||
self.vec[i].child = Some(Child {
|
||||
head: i_child,
|
||||
tail: i_child,
|
||||
});
|
||||
i_child
|
||||
}
|
||||
|
||||
pub fn iter_mut(&mut self) -> LayerIteratorMut<'_, T> {
|
||||
LayerIteratorMut::new(&mut self.vec, self.last)
|
||||
}
|
||||
|
||||
pub fn iter_orderless_mut(&mut self) -> impl Iterator<Item = (usize, &mut T)> {
|
||||
self.vec.iter_mut().map(|n| &mut n.data).enumerate()
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = (usize, &T)> {
|
||||
self.indices().map(|i| (i, &self.vec[i].data))
|
||||
}
|
||||
|
||||
pub fn iter_depth(&self) -> impl Iterator<Item = ((usize, usize), &T)> {
|
||||
self.indices()
|
||||
.map(|i| ((i, self.vec[i].depth), &self.vec[i].data))
|
||||
}
|
||||
|
||||
pub fn indices(&self) -> LayerIndexIterator<'_, T> {
|
||||
LayerIndexIterator::new(&self.vec, self.last)
|
||||
}
|
||||
}
|
||||
|
||||
impl PrimitiveLayers {
|
||||
pub fn write<P: Primitive>(&mut self, layer: usize, info: PrimitiveInst<P>) -> PrimitiveHandle {
|
||||
self[layer].write(layer, info)
|
||||
}
|
||||
|
||||
pub fn free(&mut self, h: &PrimitiveHandle) -> MaskIdx {
|
||||
self[h.layer].free(h)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Default> Default for Layers<T> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Index<usize> for Layers<T> {
|
||||
type Output = T;
|
||||
|
||||
fn index(&self, index: usize) -> &Self::Output {
|
||||
&self.vec[index].data
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IndexMut<usize> for Layers<T> {
|
||||
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||
&mut self.vec[index].data
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Default> LayerNode<T> {
|
||||
pub fn new(data: T, next: Ptr, prev: Ptr, depth: usize) -> Self {
|
||||
Self {
|
||||
next,
|
||||
prev,
|
||||
child: None,
|
||||
data,
|
||||
depth,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn head() -> Self {
|
||||
Self::new(T::default(), Ptr::None, Ptr::None, 0)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LayerIteratorMut<'a, T> {
|
||||
inner: LayerIndexIterator<'a, T>,
|
||||
}
|
||||
|
||||
impl<'a, T> Iterator for LayerIteratorMut<'a, T> {
|
||||
type Item = (usize, &'a mut T);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let i = self.inner.next()?;
|
||||
// SAFETY: requires index iterator to work properly
|
||||
#[allow(mutable_transmutes)]
|
||||
let layer = unsafe { std::mem::transmute::<&T, &mut T>(&self.inner.vec[i].data) };
|
||||
Some((i, layer))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> DoubleEndedIterator for LayerIteratorMut<'a, T> {
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
let i = self.inner.next_back()?;
|
||||
// SAFETY: requires index iterator to work properly
|
||||
#[allow(mutable_transmutes)]
|
||||
let layer = unsafe { std::mem::transmute::<&T, &mut T>(&self.inner.vec[i].data) };
|
||||
Some((i, layer))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> LayerIteratorMut<'a, T> {
|
||||
fn new(vec: &'a mut Vec<LayerNode<T>>, last: usize) -> Self {
|
||||
Self {
|
||||
inner: LayerIndexIterator::new(vec, last),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LayerIndexIterator<'a, T> {
|
||||
next: Option<usize>,
|
||||
next_back: Option<usize>,
|
||||
vec: &'a Vec<LayerNode<T>>,
|
||||
}
|
||||
|
||||
impl<'a, T> Iterator for LayerIndexIterator<'a, T> {
|
||||
type Item = usize;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let ret_i = self.next?;
|
||||
let node = &self.vec[ret_i];
|
||||
self.next = if let Some(c) = node.child {
|
||||
Some(c.head)
|
||||
} else if let Ptr::Next(i) = node.next {
|
||||
Some(i)
|
||||
} else if let Ptr::Parent(i) = node.next {
|
||||
let mut node = &self.vec[i];
|
||||
while let Ptr::Parent(i) = node.next {
|
||||
node = &self.vec[i];
|
||||
}
|
||||
if let Ptr::Next(i) = node.next {
|
||||
Some(i)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if self.next_back.unwrap() == ret_i {
|
||||
self.next = None;
|
||||
self.next_back = None;
|
||||
}
|
||||
Some(ret_i)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> DoubleEndedIterator for LayerIndexIterator<'a, T> {
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
let ret_i = self.next_back?;
|
||||
let node = &self.vec[ret_i];
|
||||
self.next_back = if let Ptr::Next(mut i) = node.prev {
|
||||
while let Some(c) = self.vec[i].child {
|
||||
i = c.tail
|
||||
}
|
||||
Some(i)
|
||||
} else if let Ptr::Parent(i) = node.prev {
|
||||
Some(i)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if self.next.unwrap() == ret_i {
|
||||
self.next = None;
|
||||
self.next_back = None;
|
||||
}
|
||||
Some(ret_i)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> LayerIndexIterator<'a, T> {
|
||||
fn new(vec: &'a Vec<LayerNode<T>>, last: usize) -> Self {
|
||||
let mut last = last;
|
||||
while let Some(c) = vec[last].child {
|
||||
last = c.tail;
|
||||
}
|
||||
Self {
|
||||
next: Some(0),
|
||||
next_back: Some(last),
|
||||
vec,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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) {}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
mod attr;
|
||||
mod color;
|
||||
mod event;
|
||||
mod layer;
|
||||
mod module;
|
||||
mod num;
|
||||
mod orientation;
|
||||
mod painter;
|
||||
mod text;
|
||||
mod texture;
|
||||
mod ui;
|
||||
mod view;
|
||||
mod widget;
|
||||
mod widget_ref;
|
||||
mod widgets;
|
||||
|
||||
pub use attr::*;
|
||||
pub use color::*;
|
||||
pub use event::*;
|
||||
pub use layer::*;
|
||||
pub use module::*;
|
||||
pub use num::*;
|
||||
pub use orientation::*;
|
||||
pub use painter::*;
|
||||
pub use text::*;
|
||||
pub use texture::*;
|
||||
pub use ui::*;
|
||||
pub use view::*;
|
||||
pub use widget::*;
|
||||
pub use widget_ref::*;
|
||||
pub use widgets::*;
|
||||
|
||||
pub use crate::util::Vec2;
|
||||
pub type UiColor = Color<u8>;
|
||||
@@ -1,35 +0,0 @@
|
||||
use std::any::{Any, TypeId};
|
||||
|
||||
use crate::{
|
||||
layout::WidgetInstance,
|
||||
util::{HashMap, Id},
|
||||
};
|
||||
|
||||
#[allow(unused_variables)]
|
||||
pub trait UiModule: Any {
|
||||
fn on_draw(&mut self, inst: &WidgetInstance) {}
|
||||
fn on_undraw(&mut self, inst: &WidgetInstance) {}
|
||||
fn on_remove(&mut self, id: &Id) {}
|
||||
fn on_move(&mut self, inst: &WidgetInstance) {}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Modules {
|
||||
map: HashMap<TypeId, Box<dyn UiModule>>,
|
||||
}
|
||||
|
||||
impl Modules {
|
||||
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut (dyn UiModule + 'static)> {
|
||||
self.map.values_mut().map(|m| m.as_mut())
|
||||
}
|
||||
|
||||
pub fn get_mut<M: UiModule + Default>(&mut self) -> &mut M {
|
||||
let rf = self
|
||||
.map
|
||||
.entry(TypeId::of::<M>())
|
||||
.or_insert_with(|| Box::new(M::default()))
|
||||
.as_mut();
|
||||
let any: &mut dyn Any = &mut *rf;
|
||||
any.downcast_mut().unwrap()
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
use std::marker::Destruct;
|
||||
|
||||
use crate::util::Vec2;
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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<T: const UiNum + Copy> const From<T> for Vec2 {
|
||||
fn from(v: T) -> Self {
|
||||
Self {
|
||||
x: v.to_f32(),
|
||||
y: v.to_f32(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn vec2(x: impl const UiNum, y: impl const UiNum) -> Vec2 {
|
||||
Vec2::new(x.to_f32(), y.to_f32())
|
||||
}
|
||||
@@ -1,200 +0,0 @@
|
||||
use crate::layout::vec2;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub struct Align {
|
||||
pub x: Option<AxisAlign>,
|
||||
pub y: Option<AxisAlign>,
|
||||
}
|
||||
|
||||
impl Align {
|
||||
pub const TOP_LEFT: RegionAlign = RegionAlign::TOP_LEFT;
|
||||
pub const TOP_CENTER: RegionAlign = RegionAlign::TOP_CENTER;
|
||||
pub const TOP_RIGHT: RegionAlign = RegionAlign::TOP_RIGHT;
|
||||
pub const CENTER_LEFT: RegionAlign = RegionAlign::CENTER_LEFT;
|
||||
pub const CENTER: RegionAlign = RegionAlign::CENTER;
|
||||
pub const CENTER_RIGHT: RegionAlign = RegionAlign::CENTER_RIGHT;
|
||||
pub const BOT_LEFT: RegionAlign = RegionAlign::BOT_LEFT;
|
||||
pub const BOT_CENTER: RegionAlign = RegionAlign::BOT_CENTER;
|
||||
pub const BOT_RIGHT: RegionAlign = RegionAlign::BOT_RIGHT;
|
||||
pub const LEFT: CardinalAlign = CardinalAlign::LEFT;
|
||||
pub const H_CENTER: CardinalAlign = CardinalAlign::H_CENTER;
|
||||
pub const RIGHT: CardinalAlign = CardinalAlign::RIGHT;
|
||||
pub const TOP: CardinalAlign = CardinalAlign::TOP;
|
||||
pub const V_CENTER: CardinalAlign = CardinalAlign::V_CENTER;
|
||||
pub const BOT: CardinalAlign = CardinalAlign::BOT;
|
||||
|
||||
pub fn tuple(&self) -> (Option<AxisAlign>, Option<AxisAlign>) {
|
||||
(self.x, self.y)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub enum AxisAlign {
|
||||
Neg,
|
||||
Center,
|
||||
Pos,
|
||||
}
|
||||
|
||||
impl AxisAlign {
|
||||
pub const fn rel(&self) -> f32 {
|
||||
match self {
|
||||
Self::Neg => 0.0,
|
||||
Self::Center => 0.5,
|
||||
Self::Pos => 1.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CardinalAlign {
|
||||
axis: Axis,
|
||||
align: AxisAlign,
|
||||
}
|
||||
|
||||
impl CardinalAlign {
|
||||
pub const LEFT: Self = Self::new(Axis::X, AxisAlign::Neg);
|
||||
pub const H_CENTER: Self = Self::new(Axis::X, AxisAlign::Center);
|
||||
pub const RIGHT: Self = Self::new(Axis::X, AxisAlign::Pos);
|
||||
pub const TOP: Self = Self::new(Axis::Y, AxisAlign::Neg);
|
||||
pub const V_CENTER: Self = Self::new(Axis::Y, AxisAlign::Center);
|
||||
pub const BOT: Self = Self::new(Axis::Y, AxisAlign::Pos);
|
||||
|
||||
pub const fn new(axis: Axis, align: AxisAlign) -> Self {
|
||||
Self { axis, align }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub struct RegionAlign {
|
||||
pub x: AxisAlign,
|
||||
pub y: AxisAlign,
|
||||
}
|
||||
|
||||
impl RegionAlign {
|
||||
pub const TOP_LEFT: Self = Self::new(AxisAlign::Neg, AxisAlign::Neg);
|
||||
pub const TOP_CENTER: Self = Self::new(AxisAlign::Center, AxisAlign::Neg);
|
||||
pub const TOP_RIGHT: Self = Self::new(AxisAlign::Pos, AxisAlign::Neg);
|
||||
pub const CENTER_LEFT: Self = Self::new(AxisAlign::Neg, AxisAlign::Center);
|
||||
pub const CENTER: Self = Self::new(AxisAlign::Center, AxisAlign::Center);
|
||||
pub const CENTER_RIGHT: Self = Self::new(AxisAlign::Pos, AxisAlign::Center);
|
||||
pub const BOT_LEFT: Self = Self::new(AxisAlign::Neg, AxisAlign::Pos);
|
||||
pub const BOT_CENTER: Self = Self::new(AxisAlign::Center, AxisAlign::Pos);
|
||||
pub const BOT_RIGHT: Self = Self::new(AxisAlign::Pos, AxisAlign::Pos);
|
||||
|
||||
pub const fn new(x: AxisAlign, y: AxisAlign) -> Self {
|
||||
Self { x, y }
|
||||
}
|
||||
pub const fn rel(&self) -> Vec2 {
|
||||
vec2(self.x.rel(), self.y.rel())
|
||||
}
|
||||
}
|
||||
|
||||
impl UiVec2 {
|
||||
pub fn partial_align(&self, align: Align) -> UiRegion {
|
||||
UiRegion {
|
||||
x: if let Some(align) = align.x {
|
||||
self.x.align(align)
|
||||
} else {
|
||||
UiSpan::FULL
|
||||
},
|
||||
y: if let Some(align) = align.y {
|
||||
self.y.align(align)
|
||||
} else {
|
||||
UiSpan::FULL
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn align(&self, align: RegionAlign) -> UiRegion {
|
||||
UiRegion {
|
||||
x: self.x.align(align.x),
|
||||
y: self.y.align(align.y),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Vec2 {
|
||||
pub fn partial_align(&self, align: Align) -> UiRegion {
|
||||
let s = UiVec2::from(*self);
|
||||
UiRegion {
|
||||
x: if let Some(align) = align.x {
|
||||
s.x.align(align)
|
||||
} else {
|
||||
UiSpan::FULL
|
||||
},
|
||||
y: if let Some(align) = align.y {
|
||||
s.y.align(align)
|
||||
} else {
|
||||
UiSpan::FULL
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn align(&self, align: RegionAlign) -> UiRegion {
|
||||
let s = UiVec2::from(*self);
|
||||
UiRegion {
|
||||
x: s.x.align(align.x),
|
||||
y: s.y.align(align.y),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UiScalar {
|
||||
pub const fn align(&self, align: AxisAlign) -> UiSpan {
|
||||
let rel = align.rel();
|
||||
let mut start = UiScalar::rel(rel);
|
||||
start.abs -= self.abs * rel;
|
||||
start.rel -= self.rel * rel;
|
||||
let mut end = UiScalar::rel(rel);
|
||||
end.abs += self.abs * (1.0 - rel);
|
||||
end.rel += self.rel * (1.0 - rel);
|
||||
UiSpan { start, end }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RegionAlign> for Align {
|
||||
fn from(region: RegionAlign) -> Self {
|
||||
Self {
|
||||
x: Some(region.x),
|
||||
y: Some(region.y),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Align> for RegionAlign {
|
||||
fn from(align: Align) -> Self {
|
||||
Self {
|
||||
x: align.x.unwrap_or(AxisAlign::Center),
|
||||
y: align.y.unwrap_or(AxisAlign::Center),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CardinalAlign> for RegionAlign {
|
||||
fn from(align: CardinalAlign) -> Self {
|
||||
Align::from(align).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CardinalAlign> for Align {
|
||||
fn from(cardinal: CardinalAlign) -> Self {
|
||||
let align = Some(cardinal.align);
|
||||
match cardinal.axis {
|
||||
Axis::X => Self { x: align, y: None },
|
||||
Axis::Y => Self { x: None, y: align },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl const From<RegionAlign> for UiVec2 {
|
||||
fn from(align: RegionAlign) -> Self {
|
||||
Self::rel(align.rel())
|
||||
}
|
||||
}
|
||||
|
||||
impl RegionAlign {
|
||||
pub const fn pos(self) -> UiVec2 {
|
||||
UiVec2::from(self)
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
use super::*;
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
pub enum Axis {
|
||||
X,
|
||||
Y,
|
||||
}
|
||||
|
||||
impl std::ops::Not for Axis {
|
||||
type Output = Self;
|
||||
|
||||
fn not(self) -> Self::Output {
|
||||
match self {
|
||||
Self::X => Self::Y,
|
||||
Self::Y => Self::X,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||
pub struct Dir {
|
||||
pub axis: Axis,
|
||||
pub sign: Sign,
|
||||
}
|
||||
|
||||
impl Dir {
|
||||
pub const fn new(axis: Axis, dir: Sign) -> Self {
|
||||
Self { axis, sign: dir }
|
||||
}
|
||||
|
||||
pub const LEFT: Self = Self::new(Axis::X, Sign::Neg);
|
||||
pub const RIGHT: Self = Self::new(Axis::X, Sign::Pos);
|
||||
pub const UP: Self = Self::new(Axis::Y, Sign::Neg);
|
||||
pub const DOWN: Self = Self::new(Axis::Y, Sign::Pos);
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||
pub enum Sign {
|
||||
Neg,
|
||||
Pos,
|
||||
}
|
||||
|
||||
impl Vec2 {
|
||||
pub fn axis(&self, axis: Axis) -> f32 {
|
||||
match axis {
|
||||
Axis::X => self.x,
|
||||
Axis::Y => self.y,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn axis_mut(&mut self, axis: Axis) -> &mut f32 {
|
||||
match axis {
|
||||
Axis::X => &mut self.x,
|
||||
Axis::Y => &mut self.y,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn from_axis(axis: Axis, aligned: f32, ortho: f32) -> Self {
|
||||
Self {
|
||||
x: match axis {
|
||||
Axis::X => aligned,
|
||||
Axis::Y => ortho,
|
||||
},
|
||||
y: match axis {
|
||||
Axis::Y => aligned,
|
||||
Axis::X => ortho,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,198 +0,0 @@
|
||||
use super::*;
|
||||
use crate::{layout::UiNum, util::impl_op};
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq)]
|
||||
pub struct Size {
|
||||
pub x: Len,
|
||||
pub y: Len,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct Len {
|
||||
pub abs: f32,
|
||||
pub rel: f32,
|
||||
pub rest: f32,
|
||||
}
|
||||
|
||||
impl<N: UiNum> From<N> for Len {
|
||||
fn from(value: N) -> Self {
|
||||
Len::abs(value.to_f32())
|
||||
}
|
||||
}
|
||||
|
||||
impl<Nx: UiNum, Ny: UiNum> From<(Nx, Ny)> for Size {
|
||||
fn from((x, y): (Nx, Ny)) -> Self {
|
||||
Self {
|
||||
x: x.into(),
|
||||
y: y.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Len> for Size {
|
||||
fn from(value: Len) -> Self {
|
||||
Self { x: value, y: value }
|
||||
}
|
||||
}
|
||||
|
||||
impl Size {
|
||||
pub const ZERO: Self = Self {
|
||||
x: Len::ZERO,
|
||||
y: Len::ZERO,
|
||||
};
|
||||
|
||||
pub const REST: Self = Self {
|
||||
x: Len::REST,
|
||||
y: Len::REST,
|
||||
};
|
||||
|
||||
pub fn abs(v: Vec2) -> Self {
|
||||
Self {
|
||||
x: Len::abs(v.x),
|
||||
y: Len::abs(v.y),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rel(v: Vec2) -> Self {
|
||||
Self {
|
||||
x: Len::rel(v.x),
|
||||
y: Len::rel(v.y),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rest(v: Vec2) -> Self {
|
||||
Self {
|
||||
x: Len::rest(v.x),
|
||||
y: Len::rest(v.y),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_uivec2(self) -> UiVec2 {
|
||||
UiVec2 {
|
||||
x: self.x.apply_rest(),
|
||||
y: self.y.apply_rest(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_axis(axis: Axis, aligned: Len, ortho: Len) -> Self {
|
||||
match axis {
|
||||
Axis::X => Self {
|
||||
x: aligned,
|
||||
y: ortho,
|
||||
},
|
||||
Axis::Y => Self {
|
||||
x: ortho,
|
||||
y: aligned,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn axis(&self, axis: Axis) -> Len {
|
||||
match axis {
|
||||
Axis::X => self.x,
|
||||
Axis::Y => self.y,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Len {
|
||||
pub const ZERO: Self = Self {
|
||||
abs: 0.0,
|
||||
rel: 0.0,
|
||||
rest: 0.0,
|
||||
};
|
||||
|
||||
pub const REST: Self = Self {
|
||||
abs: 0.0,
|
||||
rel: 0.0,
|
||||
rest: 1.0,
|
||||
};
|
||||
|
||||
pub fn apply_rest(&self) -> UiScalar {
|
||||
UiScalar {
|
||||
rel: self.rel + if self.rest > 0.0 { 1.0 } else { 0.0 },
|
||||
abs: self.abs,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn abs(abs: impl UiNum) -> Self {
|
||||
Self {
|
||||
abs: abs.to_f32(),
|
||||
rel: 0.0,
|
||||
rest: 0.0,
|
||||
}
|
||||
}
|
||||
pub fn rel(rel: impl UiNum) -> Self {
|
||||
Self {
|
||||
abs: 0.0,
|
||||
rel: rel.to_f32(),
|
||||
rest: 0.0,
|
||||
}
|
||||
}
|
||||
pub fn rest(ratio: impl UiNum) -> Self {
|
||||
Self {
|
||||
abs: 0.0,
|
||||
rel: 0.0,
|
||||
rest: ratio.to_f32(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod len_fns {
|
||||
use super::*;
|
||||
|
||||
pub fn abs(abs: impl UiNum) -> Len {
|
||||
Len {
|
||||
abs: abs.to_f32(),
|
||||
rel: 0.0,
|
||||
rest: 0.0,
|
||||
}
|
||||
}
|
||||
pub fn rel(rel: impl UiNum) -> Len {
|
||||
Len {
|
||||
abs: 0.0,
|
||||
rel: rel.to_f32(),
|
||||
rest: 0.0,
|
||||
}
|
||||
}
|
||||
pub fn rest(ratio: impl UiNum) -> Len {
|
||||
Len {
|
||||
abs: 0.0,
|
||||
rel: 0.0,
|
||||
rest: ratio.to_f32(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_op!(Len Add add; abs rel rest);
|
||||
impl_op!(Len Sub sub; abs rel rest);
|
||||
|
||||
impl_op!(Size Add add; x y);
|
||||
impl_op!(Size Sub sub; x y);
|
||||
|
||||
impl Default for Len {
|
||||
fn default() -> Self {
|
||||
Self::rest(1.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Size {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "({}, {})", self.x, self.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Len {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
if self.abs != 0.0 {
|
||||
write!(f, "{} abs;", self.abs)?;
|
||||
}
|
||||
if self.rel != 0.0 {
|
||||
write!(f, "{} rel;", self.rel)?;
|
||||
}
|
||||
if self.rest != 0.0 {
|
||||
write!(f, "{} rest;", self.rest)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
mod align;
|
||||
mod axis;
|
||||
mod len;
|
||||
mod pos;
|
||||
|
||||
use super::Vec2;
|
||||
pub use align::*;
|
||||
pub use axis::*;
|
||||
pub use len::*;
|
||||
pub use pos::*;
|
||||
@@ -1,464 +0,0 @@
|
||||
use std::{fmt::Display, hash::Hash, marker::Destruct};
|
||||
|
||||
use super::*;
|
||||
use crate::{
|
||||
layout::UiNum,
|
||||
util::{LerpUtil, impl_op},
|
||||
};
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, bytemuck::Pod, bytemuck::Zeroable, Default)]
|
||||
pub struct UiVec2 {
|
||||
pub x: UiScalar,
|
||||
pub y: UiScalar,
|
||||
}
|
||||
|
||||
impl UiVec2 {
|
||||
pub const ZERO: Self = Self {
|
||||
x: UiScalar::ZERO,
|
||||
y: UiScalar::ZERO,
|
||||
};
|
||||
|
||||
pub const fn new(x: UiScalar, y: UiScalar) -> Self {
|
||||
Self { x, y }
|
||||
}
|
||||
|
||||
pub const fn abs(abs: impl const Into<Vec2>) -> Self {
|
||||
let abs = abs.into();
|
||||
Self {
|
||||
x: UiScalar::abs(abs.x),
|
||||
y: UiScalar::abs(abs.y),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn rel(rel: impl const Into<Vec2>) -> Self {
|
||||
let rel = rel.into();
|
||||
Self {
|
||||
x: UiScalar::rel(rel.x),
|
||||
y: UiScalar::rel(rel.y),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn shift(&mut self, offset: impl const Into<UiVec2>) {
|
||||
let offset = offset.into();
|
||||
*self += offset;
|
||||
}
|
||||
|
||||
pub const fn offset(mut self, offset: impl const Into<UiVec2>) -> Self {
|
||||
self.shift(offset);
|
||||
self
|
||||
}
|
||||
|
||||
pub const fn within(&self, region: &UiRegion) -> UiVec2 {
|
||||
UiVec2 {
|
||||
x: self.x.within(®ion.x),
|
||||
y: self.y.within(®ion.y),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn outside(&self, region: &UiRegion) -> UiVec2 {
|
||||
UiVec2 {
|
||||
x: self.x.outside(®ion.x),
|
||||
y: self.y.outside(®ion.y),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn axis_mut(&mut self, axis: Axis) -> &mut UiScalar {
|
||||
match axis {
|
||||
Axis::X => &mut self.x,
|
||||
Axis::Y => &mut self.y,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn axis(&self, axis: Axis) -> UiScalar {
|
||||
match axis {
|
||||
Axis::X => self.x,
|
||||
Axis::Y => self.y,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_abs(&self, rel: Vec2) -> Vec2 {
|
||||
Vec2 {
|
||||
x: self.x.to_abs(rel.x),
|
||||
y: self.y.to_abs(rel.y),
|
||||
}
|
||||
}
|
||||
|
||||
pub const FULL_SIZE: Self = Self::rel(Vec2::ONE);
|
||||
|
||||
pub const fn from_axis(axis: Axis, aligned: UiScalar, ortho: UiScalar) -> Self {
|
||||
match axis {
|
||||
Axis::X => Self {
|
||||
x: aligned,
|
||||
y: ortho,
|
||||
},
|
||||
Axis::Y => Self {
|
||||
x: ortho,
|
||||
y: aligned,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_abs(&self) -> Vec2 {
|
||||
(self.x.abs, self.y.abs).into()
|
||||
}
|
||||
|
||||
pub fn get_rel(&self) -> Vec2 {
|
||||
(self.x.rel, self.y.rel).into()
|
||||
}
|
||||
|
||||
pub fn abs_mut(&mut self) -> Vec2View<'_> {
|
||||
Vec2View {
|
||||
x: &mut self.x.abs,
|
||||
y: &mut self.y.abs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for UiVec2 {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "rel{};abs{}", self.get_rel(), self.get_abs())
|
||||
}
|
||||
}
|
||||
|
||||
impl_op!(UiVec2 Add add; x y);
|
||||
impl_op!(UiVec2 Sub sub; x y);
|
||||
|
||||
impl const From<Vec2> for UiVec2 {
|
||||
fn from(abs: Vec2) -> Self {
|
||||
Self::abs(abs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: const UiNum, U: const UiNum> const From<(T, U)> for UiVec2
|
||||
where
|
||||
(T, U): const Destruct,
|
||||
{
|
||||
fn from(abs: (T, U)) -> Self {
|
||||
Self::abs(abs)
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, bytemuck::Pod, Default, bytemuck::Zeroable)]
|
||||
pub struct UiScalar {
|
||||
pub rel: f32,
|
||||
pub abs: f32,
|
||||
}
|
||||
|
||||
impl Eq for UiScalar {}
|
||||
impl Hash for UiScalar {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
state.write_u32(self.rel.to_bits());
|
||||
state.write_u32(self.abs.to_bits());
|
||||
}
|
||||
}
|
||||
|
||||
impl_op!(UiScalar Add add; rel abs);
|
||||
impl_op!(UiScalar Sub sub; rel abs);
|
||||
|
||||
impl UiScalar {
|
||||
pub const ZERO: Self = Self { rel: 0.0, abs: 0.0 };
|
||||
pub const FULL: Self = Self { rel: 1.0, abs: 0.0 };
|
||||
|
||||
pub const fn new(rel: f32, abs: f32) -> Self {
|
||||
Self { rel, abs }
|
||||
}
|
||||
|
||||
pub const fn rel(rel: f32) -> Self {
|
||||
Self { rel, abs: 0.0 }
|
||||
}
|
||||
|
||||
pub const fn abs(abs: f32) -> Self {
|
||||
Self { rel: 0.0, abs }
|
||||
}
|
||||
|
||||
pub const fn rel_min() -> Self {
|
||||
Self::new(0.0, 0.0)
|
||||
}
|
||||
|
||||
pub const fn rel_max() -> Self {
|
||||
Self::new(1.0, 0.0)
|
||||
}
|
||||
|
||||
pub const fn max(&self, other: Self) -> Self {
|
||||
Self {
|
||||
rel: self.rel.max(other.rel),
|
||||
abs: self.abs.max(other.abs),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn min(&self, other: Self) -> Self {
|
||||
Self {
|
||||
rel: self.rel.min(other.rel),
|
||||
abs: self.abs.min(other.abs),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn offset(mut self, amt: f32) -> Self {
|
||||
self.abs += amt;
|
||||
self
|
||||
}
|
||||
|
||||
pub const fn within(&self, span: &UiSpan) -> Self {
|
||||
let anchor = self.rel.lerp(span.start.rel, span.end.rel);
|
||||
let offset = self.abs + self.rel.lerp(span.start.abs, span.end.abs);
|
||||
Self {
|
||||
rel: anchor,
|
||||
abs: offset,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn outside(&self, span: &UiSpan) -> Self {
|
||||
let rel = self.rel.lerp_inv(span.start.rel, span.end.rel);
|
||||
let abs = self.abs - rel.lerp(span.start.abs, span.end.abs);
|
||||
Self { rel, abs }
|
||||
}
|
||||
|
||||
pub fn within_len(&self, len: UiScalar) -> Self {
|
||||
self.within(&UiSpan {
|
||||
start: UiScalar::ZERO,
|
||||
end: len,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn select_len(&self, len: UiScalar) -> Self {
|
||||
len.within_len(*self)
|
||||
}
|
||||
|
||||
pub const fn flip(&mut self) {
|
||||
self.rel = 1.0 - self.rel;
|
||||
self.abs = -self.abs;
|
||||
}
|
||||
|
||||
pub const fn to(&self, end: Self) -> UiSpan {
|
||||
UiSpan { start: *self, end }
|
||||
}
|
||||
|
||||
pub const fn to_abs(&self, rel: f32) -> f32 {
|
||||
self.rel * rel + self.abs
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, bytemuck::Pod, bytemuck::Zeroable)]
|
||||
pub struct UiSpan {
|
||||
pub start: UiScalar,
|
||||
pub end: UiScalar,
|
||||
}
|
||||
|
||||
impl UiSpan {
|
||||
pub const FULL: Self = Self {
|
||||
start: UiScalar::ZERO,
|
||||
end: UiScalar::FULL,
|
||||
};
|
||||
|
||||
pub const fn rel(rel: f32) -> Self {
|
||||
Self {
|
||||
start: UiScalar::rel(rel),
|
||||
end: UiScalar::rel(rel),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn new(start: UiScalar, end: UiScalar) -> Self {
|
||||
Self { start, end }
|
||||
}
|
||||
|
||||
pub const fn flip(&mut self) {
|
||||
self.start.flip();
|
||||
self.end.flip();
|
||||
std::mem::swap(&mut self.start.rel, &mut self.end.rel);
|
||||
std::mem::swap(&mut self.start.abs, &mut self.end.abs);
|
||||
}
|
||||
|
||||
pub const fn shift(&mut self, offset: UiScalar) {
|
||||
self.start += offset;
|
||||
self.end += offset;
|
||||
}
|
||||
|
||||
pub const fn within(&self, parent: &Self) -> Self {
|
||||
Self {
|
||||
start: self.start.within(parent),
|
||||
end: self.end.within(parent),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn outside(&self, parent: &Self) -> Self {
|
||||
Self {
|
||||
start: self.start.outside(parent),
|
||||
end: self.end.outside(parent),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn len(&self) -> UiScalar {
|
||||
self.end - self.start
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, bytemuck::Pod, bytemuck::Zeroable)]
|
||||
pub struct UiRegion {
|
||||
pub x: UiSpan,
|
||||
pub y: UiSpan,
|
||||
}
|
||||
|
||||
impl UiRegion {
|
||||
pub const FULL: Self = Self {
|
||||
x: UiSpan::FULL,
|
||||
y: UiSpan::FULL,
|
||||
};
|
||||
|
||||
pub const fn new(x: UiSpan, y: UiSpan) -> Self {
|
||||
Self { x, y }
|
||||
}
|
||||
|
||||
pub const fn rel(rel: Vec2) -> Self {
|
||||
Self {
|
||||
x: UiSpan::rel(rel.x),
|
||||
y: UiSpan::rel(rel.y),
|
||||
}
|
||||
}
|
||||
pub const fn within(&self, parent: &Self) -> Self {
|
||||
Self {
|
||||
x: self.x.within(&parent.x),
|
||||
y: self.y.within(&parent.y),
|
||||
}
|
||||
}
|
||||
pub const fn outside(&self, parent: &Self) -> Self {
|
||||
Self {
|
||||
x: self.x.outside(&parent.x),
|
||||
y: self.y.outside(&parent.y),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn axis(&mut self, axis: Axis) -> &UiSpan {
|
||||
match axis {
|
||||
Axis::X => &self.x,
|
||||
Axis::Y => &self.y,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn axis_mut(&mut self, axis: Axis) -> &mut UiSpan {
|
||||
match axis {
|
||||
Axis::X => &mut self.x,
|
||||
Axis::Y => &mut self.y,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn flip(&mut self, axis: Axis) {
|
||||
match axis {
|
||||
Axis::X => self.x.flip(),
|
||||
Axis::Y => self.y.flip(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn shift(&mut self, offset: impl Into<UiVec2>) {
|
||||
let offset = offset.into();
|
||||
self.x.shift(offset.x);
|
||||
self.y.shift(offset.y);
|
||||
}
|
||||
|
||||
pub fn offset(mut self, offset: impl Into<UiVec2>) -> Self {
|
||||
self.shift(offset);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn to_px(&self, size: Vec2) -> PixelRegion {
|
||||
PixelRegion {
|
||||
top_left: self.top_left().get_rel() * size + self.top_left().get_abs(),
|
||||
bot_right: self.bot_right().get_rel() * size + self.bot_right().get_abs(),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn center(&self) -> UiVec2 {
|
||||
Align::CENTER.pos().within(self)
|
||||
}
|
||||
|
||||
pub const fn size(&self) -> UiVec2 {
|
||||
UiVec2 {
|
||||
x: self.x.len(),
|
||||
y: self.y.len(),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn top_left(&self) -> UiVec2 {
|
||||
UiVec2 {
|
||||
x: self.x.start,
|
||||
y: self.y.start,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn bot_right(&self) -> UiVec2 {
|
||||
UiVec2 {
|
||||
x: self.x.end,
|
||||
y: self.y.end,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn from_axis(axis: Axis, aligned: UiSpan, ortho: UiSpan) -> Self {
|
||||
Self {
|
||||
x: match axis {
|
||||
Axis::X => aligned,
|
||||
Axis::Y => ortho,
|
||||
},
|
||||
y: match axis {
|
||||
Axis::X => ortho,
|
||||
Axis::Y => aligned,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for UiRegion {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{} -> {} (size: {})",
|
||||
self.top_left(),
|
||||
self.bot_right(),
|
||||
self.size()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PixelRegion {
|
||||
pub top_left: Vec2,
|
||||
pub bot_right: Vec2,
|
||||
}
|
||||
|
||||
impl PixelRegion {
|
||||
pub fn contains(&self, pos: Vec2) -> bool {
|
||||
pos.x >= self.top_left.x
|
||||
&& pos.x <= self.bot_right.x
|
||||
&& pos.y >= self.top_left.y
|
||||
&& pos.y <= self.bot_right.y
|
||||
}
|
||||
|
||||
pub fn size(&self) -> Vec2 {
|
||||
self.bot_right - self.top_left
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for PixelRegion {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{} -> {}", self.top_left, self.bot_right)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Vec2View<'a> {
|
||||
pub x: &'a mut f32,
|
||||
pub y: &'a mut f32,
|
||||
}
|
||||
|
||||
impl Vec2View<'_> {
|
||||
pub fn set(&mut self, other: Vec2) {
|
||||
*self.x = other.x;
|
||||
*self.y = other.y;
|
||||
}
|
||||
|
||||
pub fn add(&mut self, other: Vec2) {
|
||||
*self.x += other.x;
|
||||
*self.y += other.y;
|
||||
}
|
||||
}
|
||||
@@ -1,609 +0,0 @@
|
||||
use std::{cell::Ref, marker::Unsize, sync::mpsc::Sender};
|
||||
|
||||
use crate::{
|
||||
layout::{
|
||||
Axis, Len, Modules, PrimitiveLayers, RenderedText, Size, TextAttrs, TextBuffer, TextData,
|
||||
TextureHandle, Textures, UiRegion, UiVec2, Vec2, Widget, WidgetRef, WidgetUpdate, Widgets,
|
||||
},
|
||||
render::{Mask, MaskIdx, Primitive, PrimitiveHandle, PrimitiveInst},
|
||||
util::{HashMap, HashSet, Id, TrackedArena},
|
||||
};
|
||||
|
||||
/// makes your surfaces look pretty
|
||||
pub struct Painter<'a, 'c> {
|
||||
ctx: &'a mut PainterCtx<'c>,
|
||||
widget: WidgetRef,
|
||||
id: Id,
|
||||
region: UiRegion,
|
||||
mask: MaskIdx,
|
||||
textures: Vec<TextureHandle>,
|
||||
primitives: Vec<PrimitiveHandle>,
|
||||
children: Vec<Id>,
|
||||
children_width: HashMap<Id, (UiVec2, Len)>,
|
||||
children_height: HashMap<Id, (UiVec2, Len)>,
|
||||
pub layer: usize,
|
||||
}
|
||||
|
||||
/// context for a painter; lets you draw and redraw widgets
|
||||
struct PainterCtx<'a> {
|
||||
pub widgets: &'a Widgets,
|
||||
pub active: &'a mut HashMap<Id, WidgetInstance>,
|
||||
pub layers: &'a mut PrimitiveLayers,
|
||||
pub textures: &'a mut Textures,
|
||||
pub masks: &'a mut TrackedArena<Mask, u32>,
|
||||
pub text: &'a mut TextData,
|
||||
pub output_size: Vec2,
|
||||
pub modules: &'a mut Modules,
|
||||
pub cache_width: HashMap<Id, (UiVec2, Len)>,
|
||||
pub cache_height: HashMap<Id, (UiVec2, Len)>,
|
||||
pub needs_redraw: HashSet<Id>,
|
||||
draw_started: HashSet<Id>,
|
||||
}
|
||||
|
||||
/// stores information for children about the highest level parent that needed their size
|
||||
/// so that they can redraw the parent if their size changes
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct ResizeRef {
|
||||
x: Option<(Id, (UiVec2, Len))>,
|
||||
y: Option<(Id, (UiVec2, Len))>,
|
||||
}
|
||||
|
||||
/// important non rendering data for retained drawing
|
||||
#[derive(Debug)]
|
||||
pub struct WidgetInstance {
|
||||
pub id: Id,
|
||||
pub region: UiRegion,
|
||||
pub parent: Option<Id>,
|
||||
pub textures: Vec<TextureHandle>,
|
||||
pub primitives: Vec<PrimitiveHandle>,
|
||||
pub children: Vec<Id>,
|
||||
pub resize: ResizeRef,
|
||||
pub mask: MaskIdx,
|
||||
pub layer: usize,
|
||||
}
|
||||
|
||||
/// data to be stored in Ui to create PainterCtxs easily
|
||||
pub struct PainterData {
|
||||
pub widgets: Widgets,
|
||||
pub active: HashMap<Id, WidgetInstance>,
|
||||
pub layers: PrimitiveLayers,
|
||||
pub textures: Textures,
|
||||
pub text: TextData,
|
||||
pub output_size: Vec2,
|
||||
pub modules: Modules,
|
||||
pub px_dependent: HashSet<Id>,
|
||||
pub masks: TrackedArena<Mask, u32>,
|
||||
}
|
||||
|
||||
impl PainterData {
|
||||
pub fn new(send: Sender<WidgetUpdate>) -> Self {
|
||||
Self {
|
||||
widgets: Widgets::new(send),
|
||||
active: Default::default(),
|
||||
layers: Default::default(),
|
||||
textures: Default::default(),
|
||||
text: Default::default(),
|
||||
output_size: Default::default(),
|
||||
modules: Default::default(),
|
||||
px_dependent: Default::default(),
|
||||
masks: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PainterCtx<'a> {
|
||||
/// redraws a widget that's currently active (drawn)
|
||||
/// can be called on something already drawn or removed,
|
||||
/// will just return if so
|
||||
pub fn redraw<W: Widget + ?Sized + Unsize<dyn Widget>>(&mut self, widget: &WidgetRef<W>) {
|
||||
let id = widget.id();
|
||||
self.needs_redraw.remove(&id);
|
||||
if self.draw_started.contains(&id) {
|
||||
return;
|
||||
}
|
||||
let Some(active) = self.active.get(&id) else {
|
||||
return;
|
||||
};
|
||||
let mut resize = active.resize;
|
||||
|
||||
// set resize back after redrawing
|
||||
let finish = |s: &mut Self, resize| {
|
||||
if let Some(active) = s.active.get_mut(&id) {
|
||||
// might need to get_or_insert here instead of just assuming
|
||||
active.resize = resize;
|
||||
}
|
||||
};
|
||||
|
||||
// check if a parent depends on the desired size of this, if so then redraw it first
|
||||
// TODO: this is stupid having 2 of these, don't ask me what the consequences are
|
||||
let mut ret = false;
|
||||
if let Some((rid, (outer, old_desired))) = &mut resize.x {
|
||||
let new_desired = SizeCtx {
|
||||
source: id,
|
||||
cache_width: &mut self.cache_width,
|
||||
cache_height: &mut self.cache_height,
|
||||
text: self.text,
|
||||
textures: self.textures,
|
||||
outer: *outer,
|
||||
output_size: self.output_size,
|
||||
checked_width: &mut Default::default(),
|
||||
checked_height: &mut Default::default(),
|
||||
id,
|
||||
}
|
||||
.width(widget);
|
||||
if new_desired != *old_desired {
|
||||
// unsure if I need to walk down the tree here
|
||||
self.redraw(&self.widgets.get(*rid));
|
||||
*old_desired = new_desired;
|
||||
if self.draw_started.contains(&id) {
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some((rid, (outer, old_desired))) = &mut resize.y {
|
||||
// NOTE: might need hack in Span here (or also do it properly here)
|
||||
let new_desired = SizeCtx {
|
||||
source: id,
|
||||
cache_width: &mut self.cache_width,
|
||||
cache_height: &mut self.cache_height,
|
||||
text: self.text,
|
||||
textures: self.textures,
|
||||
outer: *outer,
|
||||
output_size: self.output_size,
|
||||
checked_width: &mut Default::default(),
|
||||
checked_height: &mut Default::default(),
|
||||
id,
|
||||
}
|
||||
.height(widget);
|
||||
if new_desired != *old_desired {
|
||||
self.redraw(&self.widgets.get(*rid));
|
||||
*old_desired = new_desired;
|
||||
if self.draw_started.contains(&id) {
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ret {
|
||||
return finish(self, resize);
|
||||
}
|
||||
|
||||
let Some(active) = self.remove(id) else {
|
||||
return;
|
||||
};
|
||||
|
||||
self.draw_inner(
|
||||
active.layer,
|
||||
widget,
|
||||
active.region,
|
||||
active.parent,
|
||||
active.mask,
|
||||
Some(active.children),
|
||||
);
|
||||
finish(self, resize);
|
||||
}
|
||||
|
||||
fn draw_inner<W: Widget + ?Sized + Unsize<dyn Widget>>(
|
||||
&mut self,
|
||||
layer: usize,
|
||||
widget: &WidgetRef<W>,
|
||||
region: UiRegion,
|
||||
parent: Option<Id>,
|
||||
mask: MaskIdx,
|
||||
old_children: Option<Vec<Id>>,
|
||||
) {
|
||||
let id = widget.id();
|
||||
// I have no idea if these checks work lol
|
||||
// the idea is u can't redraw stuff u already drew,
|
||||
// and if parent is different then there's another copy with a different parent
|
||||
// but this has a very weird issue where you can't move widgets unless u remove first
|
||||
// so swapping is impossible rn I think?
|
||||
// there's definitely better solutions like a counter (>1 = panic) but don't care rn
|
||||
// if self.draw_started.contains(&id) {
|
||||
// panic!(
|
||||
// "Cannot draw the same widget ({}) twice (1)",
|
||||
// self.widgets.data(&id).unwrap().label
|
||||
// );
|
||||
// }
|
||||
let mut old_children = old_children.unwrap_or_default();
|
||||
let mut resize = ResizeRef::default();
|
||||
if let Some(active) = self.active.get_mut(&id)
|
||||
&& !self.needs_redraw.contains(&id)
|
||||
{
|
||||
// check to see if we can skip drawing first
|
||||
if active.parent != parent {
|
||||
panic!("Cannot draw the same widget twice (2)");
|
||||
}
|
||||
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).unwrap();
|
||||
old_children = active.children;
|
||||
resize = active.resize;
|
||||
}
|
||||
|
||||
// draw widget
|
||||
self.draw_started.insert(id);
|
||||
|
||||
let mut painter = Painter {
|
||||
region,
|
||||
mask,
|
||||
layer,
|
||||
widget: widget.as_any(),
|
||||
id,
|
||||
textures: Vec::new(),
|
||||
primitives: Vec::new(),
|
||||
ctx: self,
|
||||
children: Vec::new(),
|
||||
children_width: Default::default(),
|
||||
children_height: Default::default(),
|
||||
};
|
||||
|
||||
widget.get_mut_quiet().draw(&mut painter);
|
||||
|
||||
let children_width = painter.children_width;
|
||||
let children_height = painter.children_height;
|
||||
|
||||
// add to active
|
||||
let instance = WidgetInstance {
|
||||
id,
|
||||
region,
|
||||
parent,
|
||||
textures: painter.textures,
|
||||
primitives: painter.primitives,
|
||||
children: painter.children,
|
||||
resize,
|
||||
mask: painter.mask,
|
||||
layer,
|
||||
};
|
||||
// set resize for children who's size this widget depends on
|
||||
for (cid, outer) in children_width {
|
||||
if let Some(w) = self.active.get_mut(&cid)
|
||||
&& w.resize.x.is_none()
|
||||
{
|
||||
w.resize.x = Some((id, outer))
|
||||
}
|
||||
}
|
||||
for (cid, outer) in children_height {
|
||||
if let Some(w) = self.active.get_mut(&cid)
|
||||
&& w.resize.y.is_none()
|
||||
{
|
||||
w.resize.y = Some((id, outer))
|
||||
}
|
||||
}
|
||||
|
||||
// remove old children that weren't kept
|
||||
for c in &old_children {
|
||||
if !instance.children.contains(c) {
|
||||
self.remove_rec(*c);
|
||||
}
|
||||
}
|
||||
|
||||
// update modules
|
||||
for m in self.modules.iter_mut() {
|
||||
m.on_draw(&instance);
|
||||
}
|
||||
self.active.insert(id, instance);
|
||||
}
|
||||
|
||||
fn mov(&mut self, id: Id, from: UiRegion, to: UiRegion) {
|
||||
let active = self.active.get_mut(&id).unwrap();
|
||||
for h in &active.primitives {
|
||||
let region = self.layers[h.layer].region_mut(h);
|
||||
*region = region.outside(&from).within(&to);
|
||||
}
|
||||
active.region = active.region.outside(&from).within(&to);
|
||||
for m in self.modules.iter_mut() {
|
||||
m.on_move(active);
|
||||
}
|
||||
// children will not be changed, so this technically should not be needed
|
||||
// probably need unsafe
|
||||
let children = active.children.clone();
|
||||
for child in children {
|
||||
self.mov(child, from, to);
|
||||
}
|
||||
}
|
||||
|
||||
/// NOTE: instance textures are cleared and self.textures freed
|
||||
fn remove(&mut self, id: Id) -> Option<WidgetInstance> {
|
||||
let mut inst = self.active.remove(&id);
|
||||
if let Some(inst) = &mut inst {
|
||||
for h in &inst.primitives {
|
||||
let mask = self.layers.free(h);
|
||||
if mask != MaskIdx::NONE {
|
||||
self.masks.remove(mask);
|
||||
}
|
||||
}
|
||||
inst.textures.clear();
|
||||
self.textures.free();
|
||||
for m in self.modules.iter_mut() {
|
||||
m.on_undraw(inst);
|
||||
}
|
||||
}
|
||||
inst
|
||||
}
|
||||
|
||||
fn remove_rec(&mut self, id: Id) -> Option<WidgetInstance> {
|
||||
let inst = self.remove(id);
|
||||
if let Some(inst) = &inst {
|
||||
for c in &inst.children {
|
||||
self.remove_rec(*c);
|
||||
}
|
||||
}
|
||||
inst
|
||||
}
|
||||
}
|
||||
|
||||
impl PainterData {
|
||||
fn ctx(&mut self, needs_redraw: HashSet<Id>) -> PainterCtx<'_> {
|
||||
PainterCtx {
|
||||
widgets: &self.widgets,
|
||||
active: &mut self.active,
|
||||
layers: &mut self.layers,
|
||||
textures: &mut self.textures,
|
||||
text: &mut self.text,
|
||||
output_size: self.output_size,
|
||||
modules: &mut self.modules,
|
||||
masks: &mut self.masks,
|
||||
cache_width: Default::default(),
|
||||
cache_height: Default::default(),
|
||||
draw_started: Default::default(),
|
||||
needs_redraw,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw<W: Widget + ?Sized + Unsize<dyn Widget>>(&mut self, id: &WidgetRef<W>) {
|
||||
let mut ctx = self.ctx(Default::default());
|
||||
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>) {
|
||||
let mut ctx = self.ctx(ids);
|
||||
while let Some(&id) = ctx.needs_redraw.iter().next() {
|
||||
ctx.redraw(&ctx.widgets.get(id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'c> Painter<'a, 'c> {
|
||||
fn primitive_at<P: Primitive>(&mut self, primitive: P, region: UiRegion) {
|
||||
let h = self.ctx.layers.write(
|
||||
self.layer,
|
||||
PrimitiveInst {
|
||||
id: self.id(),
|
||||
primitive,
|
||||
region,
|
||||
mask_idx: self.mask,
|
||||
},
|
||||
);
|
||||
if self.mask != MaskIdx::NONE {
|
||||
// TODO: I have no clue if this works at all :joy:
|
||||
self.ctx.masks.push_ref(self.mask);
|
||||
}
|
||||
self.primitives.push(h);
|
||||
}
|
||||
|
||||
/// Writes a primitive to be rendered
|
||||
pub fn primitive<P: Primitive>(&mut self, primitive: P) {
|
||||
self.primitive_at(primitive, self.region)
|
||||
}
|
||||
|
||||
pub fn primitive_within<P: Primitive>(&mut self, primitive: P, region: UiRegion) {
|
||||
self.primitive_at(primitive, region.within(&self.region));
|
||||
}
|
||||
|
||||
pub fn set_mask(&mut self, region: UiRegion) {
|
||||
assert!(self.mask == MaskIdx::NONE);
|
||||
self.mask = self.ctx.masks.push(Mask { region });
|
||||
}
|
||||
|
||||
/// Draws a widget within this widget's region.
|
||||
pub fn widget<W: Widget + ?Sized + Unsize<dyn Widget>>(&mut self, id: &WidgetRef<W>) {
|
||||
self.widget_at(id, self.region);
|
||||
}
|
||||
|
||||
/// Draws a widget somewhere within this one.
|
||||
/// Useful for drawing child widgets in select areas.
|
||||
pub fn widget_within<W: Widget + ?Sized + Unsize<dyn Widget>>(
|
||||
&mut self,
|
||||
id: &WidgetRef<W>,
|
||||
region: UiRegion,
|
||||
) {
|
||||
self.widget_at(id, region.within(&self.region));
|
||||
}
|
||||
|
||||
fn widget_at<W: Widget + ?Sized + Unsize<dyn Widget>>(
|
||||
&mut self,
|
||||
id: &WidgetRef<W>,
|
||||
region: UiRegion,
|
||||
) {
|
||||
self.children.push(id.id());
|
||||
self.ctx
|
||||
.draw_inner(self.layer, id, region, Some(self.id()), self.mask, None);
|
||||
}
|
||||
|
||||
pub fn texture_within(&mut self, handle: &TextureHandle, region: UiRegion) {
|
||||
self.textures.push(handle.clone());
|
||||
self.primitive_at(handle.primitive(), region.within(&self.region));
|
||||
}
|
||||
|
||||
pub fn texture(&mut self, handle: &TextureHandle) {
|
||||
self.textures.push(handle.clone());
|
||||
self.primitive(handle.primitive());
|
||||
}
|
||||
|
||||
pub fn texture_at(&mut self, handle: &TextureHandle, region: UiRegion) {
|
||||
self.textures.push(handle.clone());
|
||||
self.primitive_at(handle.primitive(), region);
|
||||
}
|
||||
|
||||
/// returns (handle, offset from top left)
|
||||
pub fn render_text(&mut self, buffer: &mut TextBuffer, attrs: &TextAttrs) -> RenderedText {
|
||||
self.ctx
|
||||
.text
|
||||
.get_mut()
|
||||
.draw(buffer, attrs, self.ctx.textures)
|
||||
}
|
||||
|
||||
pub fn region(&self) -> UiRegion {
|
||||
self.region
|
||||
}
|
||||
|
||||
pub fn size<W: Widget + ?Sized>(&mut self, id: &WidgetRef<W>) -> Size {
|
||||
self.size_ctx().size(id)
|
||||
}
|
||||
|
||||
pub fn len_axis<W: Widget + ?Sized>(&mut self, id: &WidgetRef<W>, axis: Axis) -> Len {
|
||||
match axis {
|
||||
Axis::X => self.size_ctx().width(id),
|
||||
Axis::Y => self.size_ctx().height(id),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn size_ctx(&mut self) -> SizeCtx<'_> {
|
||||
SizeCtx {
|
||||
source: self.id(),
|
||||
id: self.id(),
|
||||
text: self.ctx.text,
|
||||
textures: self.ctx.textures,
|
||||
output_size: self.ctx.output_size,
|
||||
checked_width: &mut self.children_width,
|
||||
checked_height: &mut self.children_height,
|
||||
cache_width: &mut self.ctx.cache_width,
|
||||
cache_height: &mut self.ctx.cache_height,
|
||||
outer: self.region.size(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn output_size(&self) -> Vec2 {
|
||||
self.ctx.output_size
|
||||
}
|
||||
|
||||
pub fn px_size(&mut self) -> Vec2 {
|
||||
self.region.size().to_abs(self.ctx.output_size)
|
||||
}
|
||||
|
||||
pub fn text_data(&mut self) -> &mut TextData {
|
||||
self.ctx.text
|
||||
}
|
||||
|
||||
pub fn child_layer(&mut self) {
|
||||
self.layer = self.ctx.layers.child(self.layer);
|
||||
}
|
||||
|
||||
pub fn next_layer(&mut self) {
|
||||
self.layer = self.ctx.layers.next(self.layer);
|
||||
}
|
||||
|
||||
pub fn label(&self) -> Ref<'_, String> {
|
||||
self.widget.get_label()
|
||||
}
|
||||
|
||||
pub fn id(&self) -> Id {
|
||||
self.id
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SizeCtx<'a> {
|
||||
pub text: &'a mut TextData,
|
||||
pub textures: &'a mut Textures,
|
||||
source: Id,
|
||||
cache_width: &'a mut HashMap<Id, (UiVec2, Len)>,
|
||||
cache_height: &'a mut HashMap<Id, (UiVec2, Len)>,
|
||||
checked_width: &'a mut HashMap<Id, (UiVec2, Len)>,
|
||||
checked_height: &'a mut HashMap<Id, (UiVec2, Len)>,
|
||||
/// TODO: should this be pub? rn used for sized
|
||||
pub outer: UiVec2,
|
||||
output_size: Vec2,
|
||||
id: Id,
|
||||
}
|
||||
|
||||
impl SizeCtx<'_> {
|
||||
pub fn id(&self) -> &Id {
|
||||
&self.id
|
||||
}
|
||||
|
||||
pub fn source(&self) -> &Id {
|
||||
&self.source
|
||||
}
|
||||
|
||||
pub fn width<W: Widget + ?Sized>(&mut self, widget: &WidgetRef<W>) -> Len {
|
||||
// first check cache
|
||||
// TODO: is this needed? broken rn bc does not store children during upper size check,
|
||||
// so if something actually using check_* hits cache it fails to add them
|
||||
// if let Some(&(outer, len)) = self.cache_width.get(&id)
|
||||
// && outer == self.outer
|
||||
// {
|
||||
// self.checked_width.insert(id, (self.outer, len));
|
||||
// return len;
|
||||
// }
|
||||
// store self vars that need to be maintained
|
||||
let id = widget.id();
|
||||
let self_outer = self.outer;
|
||||
let self_id = self.id;
|
||||
// get size of input id
|
||||
self.id = id;
|
||||
let len = widget.get_mut_quiet().desired_width(self);
|
||||
// restore vars & update cache + checked
|
||||
self.outer = self_outer;
|
||||
self.id = self_id;
|
||||
self.cache_width.insert(id, (self.outer, len));
|
||||
self.checked_width.insert(id, (self.outer, len));
|
||||
len
|
||||
}
|
||||
|
||||
// TODO: should be refactored to share code w width_inner
|
||||
pub fn height<W: Widget + ?Sized>(&mut self, widget: &WidgetRef<W>) -> Len {
|
||||
// if let Some(&(outer, len)) = self.cache_height.get(&id)
|
||||
// && outer == self.outer
|
||||
// {
|
||||
// self.checked_height.insert(id, (self.outer, len));
|
||||
// return len;
|
||||
// }
|
||||
let id = widget.id();
|
||||
let self_outer = self.outer;
|
||||
let self_id = self.id;
|
||||
self.id = id;
|
||||
let len = widget.get_mut_quiet().desired_height(self);
|
||||
self.outer = self_outer;
|
||||
self.id = self_id;
|
||||
self.cache_height.insert(id, (self.outer, len));
|
||||
self.checked_height.insert(id, (self.outer, len));
|
||||
len
|
||||
}
|
||||
|
||||
pub fn len_axis<W: Widget + ?Sized>(&mut self, id: &WidgetRef<W>, axis: Axis) -> Len {
|
||||
match axis {
|
||||
Axis::X => self.width(id),
|
||||
Axis::Y => self.height(id),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn size<W: Widget + ?Sized>(&mut self, id: &WidgetRef<W>) -> Size {
|
||||
Size {
|
||||
x: self.width(id),
|
||||
y: self.height(id),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn px_size(&mut self) -> Vec2 {
|
||||
self.outer.to_abs(self.output_size)
|
||||
}
|
||||
|
||||
pub fn output_size(&mut self) -> Vec2 {
|
||||
self.output_size
|
||||
}
|
||||
|
||||
pub fn draw_text(&mut self, buffer: &mut TextBuffer, attrs: &TextAttrs) -> RenderedText {
|
||||
self.text.get_mut().draw(buffer, attrs, self.textures)
|
||||
}
|
||||
}
|
||||
@@ -1,193 +0,0 @@
|
||||
use std::simd::{Simd, num::SimdUint};
|
||||
|
||||
use crate::{
|
||||
layout::{Align, RegionAlign, TextureHandle, Textures, UiColor, Vec2},
|
||||
util::Handle,
|
||||
};
|
||||
use cosmic_text::{
|
||||
Attrs, AttrsList, Buffer, CacheKey, Color, Family, FontSystem, Metrics, Placement, SwashCache,
|
||||
SwashContent,
|
||||
};
|
||||
use image::{GenericImageView, RgbaImage};
|
||||
|
||||
/// TODO: properly wrap this
|
||||
pub mod text_lib {
|
||||
pub use cosmic_text::*;
|
||||
}
|
||||
|
||||
pub type TextData = Handle<TextDataInner>;
|
||||
|
||||
pub struct TextDataInner {
|
||||
pub font_system: FontSystem,
|
||||
pub swash_cache: SwashCache,
|
||||
glyph_cache: Vec<(Placement, CacheKey, Color)>,
|
||||
}
|
||||
|
||||
impl Default for TextDataInner {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
font_system: FontSystem::new(),
|
||||
swash_cache: SwashCache::new(),
|
||||
glyph_cache: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct TextAttrs {
|
||||
pub color: UiColor,
|
||||
pub font_size: f32,
|
||||
pub line_height: f32,
|
||||
pub family: Family<'static>,
|
||||
pub wrap: bool,
|
||||
/// inner alignment of text region (within where it's drawn)
|
||||
pub align: RegionAlign,
|
||||
}
|
||||
|
||||
impl TextAttrs {
|
||||
pub fn apply(&self, font_system: &mut FontSystem, buf: &mut Buffer, width: Option<f32>) {
|
||||
buf.set_metrics_and_size(
|
||||
font_system,
|
||||
Metrics::new(self.font_size, self.line_height),
|
||||
width,
|
||||
None,
|
||||
);
|
||||
let attrs = Attrs::new().family(self.family);
|
||||
let list = AttrsList::new(&attrs);
|
||||
for line in &mut buf.lines {
|
||||
line.set_attrs_list(list.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type TextBuffer = Buffer;
|
||||
|
||||
impl Default for TextAttrs {
|
||||
fn default() -> Self {
|
||||
let size = 14.0;
|
||||
Self {
|
||||
color: UiColor::WHITE,
|
||||
font_size: size,
|
||||
line_height: size * LINE_HEIGHT_MULT,
|
||||
family: Family::SansSerif,
|
||||
wrap: false,
|
||||
align: Align::CENTER_LEFT,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const LINE_HEIGHT_MULT: f32 = 1.1;
|
||||
|
||||
impl TextDataInner {
|
||||
pub fn draw(
|
||||
&mut self,
|
||||
buffer: &mut TextBuffer,
|
||||
attrs: &TextAttrs,
|
||||
textures: &mut Textures,
|
||||
) -> RenderedText {
|
||||
// TODO: either this or the layout stuff (or both) is super slow,
|
||||
// should probably do texture packing and things if possible.
|
||||
// very visible if you add just a couple of wrapping texts and resize window
|
||||
// should also be timed to figure out exactly what points need to be sped up
|
||||
// let mut pixels = HashMap::<_, [u8; 4]>::default();
|
||||
let mut min_x = 0;
|
||||
let mut min_y = 0;
|
||||
let mut max_x = 0;
|
||||
let mut max_y = 0;
|
||||
let text_color = {
|
||||
let c = attrs.color;
|
||||
cosmic_text::Color::rgba(c.r, c.g, c.b, c.a)
|
||||
};
|
||||
let mut max_width = 0.0f32;
|
||||
let mut height = 0.0;
|
||||
|
||||
for run in buffer.layout_runs() {
|
||||
for glyph in run.glyphs.iter() {
|
||||
let physical_glyph = glyph.physical((0., 0.), 1.0);
|
||||
|
||||
let glyph_color = match glyph.color_opt {
|
||||
Some(some) => some,
|
||||
None => text_color,
|
||||
};
|
||||
|
||||
if let Some(img) = self
|
||||
.swash_cache
|
||||
.get_image(&mut self.font_system, physical_glyph.cache_key)
|
||||
{
|
||||
let mut pos = img.placement;
|
||||
pos.left += physical_glyph.x;
|
||||
pos.top = physical_glyph.y + run.line_y as i32 - pos.top;
|
||||
min_x = min_x.min(pos.left);
|
||||
min_y = min_y.min(pos.top);
|
||||
max_x = max_x.max(pos.left + pos.width as i32);
|
||||
max_y = max_y.max(pos.top + pos.height as i32);
|
||||
self.glyph_cache
|
||||
.push((pos, physical_glyph.cache_key, glyph_color));
|
||||
}
|
||||
}
|
||||
max_width = max_width.max(run.line_w);
|
||||
height += run.line_height;
|
||||
}
|
||||
let img_width = (max_x - min_x + 1) as u32;
|
||||
let img_height = (max_y - min_y + 1) as u32;
|
||||
let mut image = RgbaImage::new(img_width, img_height);
|
||||
|
||||
for (pos, key, color) in self.glyph_cache.drain(..) {
|
||||
let img = self
|
||||
.swash_cache
|
||||
.get_image(&mut self.font_system, key)
|
||||
.as_ref()
|
||||
.unwrap();
|
||||
let mut merge = |i, color: [u8; 4]| {
|
||||
let i = i as i32;
|
||||
let x = (i % pos.width as i32 + pos.left - min_x) as u32;
|
||||
let y = (i / pos.width as i32 + pos.top - min_y) as u32;
|
||||
let pixel = &mut image[(x, y)].0;
|
||||
// TODO: no clue if proper alpha blending should be done
|
||||
*pixel = Simd::from(color).saturating_add(Simd::from(*pixel)).into();
|
||||
};
|
||||
|
||||
match img.content {
|
||||
SwashContent::Mask => {
|
||||
for (i, a) in img.data.iter().enumerate() {
|
||||
let mut color = color.as_rgba();
|
||||
color[3] = ((color[3] as u32 * *a as u32) / u8::MAX as u32) as u8;
|
||||
merge(i, color);
|
||||
}
|
||||
}
|
||||
SwashContent::SubpixelMask => todo!("subpixel mask text rendering"),
|
||||
SwashContent::Color => {
|
||||
let (colors, _) = img.data.as_chunks::<4>();
|
||||
for (i, color) in colors.iter().enumerate() {
|
||||
merge(i, *color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let max_dim = 8192;
|
||||
if image.width() > max_dim || image.height() > max_dim {
|
||||
let width = image.width().min(max_dim);
|
||||
let height = image.height().min(max_dim);
|
||||
eprintln!(
|
||||
"WARNING: image of size {:?} cropped to {:?} (texture too big)",
|
||||
image.dimensions(),
|
||||
(width, height)
|
||||
);
|
||||
image = image.view(0, 0, width, height).to_image();
|
||||
}
|
||||
|
||||
RenderedText {
|
||||
handle: textures.add(image),
|
||||
top_left_offset: Vec2::new(min_x as f32, min_y as f32),
|
||||
size: Vec2::new(max_width, height),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RenderedText {
|
||||
pub handle: TextureHandle,
|
||||
pub top_left_offset: Vec2,
|
||||
pub size: Vec2,
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
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,
|
||||
size: Vec2,
|
||||
counter: RefCounter,
|
||||
send: Sender<u32>,
|
||||
}
|
||||
|
||||
/// a texture manager for a ui
|
||||
/// note that this is heavily oriented towards wgpu's renderer so the primitives don't need mapped
|
||||
pub struct Textures {
|
||||
free: Vec<u32>,
|
||||
images: Vec<Option<DynamicImage>>,
|
||||
updates: Vec<Update>,
|
||||
send: Sender<u32>,
|
||||
recv: Receiver<u32>,
|
||||
}
|
||||
|
||||
pub enum TextureUpdate<'a> {
|
||||
Push(&'a DynamicImage),
|
||||
Set(u32, &'a DynamicImage),
|
||||
Free(u32),
|
||||
PushFree,
|
||||
SetFree,
|
||||
}
|
||||
|
||||
enum Update {
|
||||
Push(u32),
|
||||
Set(u32),
|
||||
Free(u32),
|
||||
}
|
||||
|
||||
impl Textures {
|
||||
pub fn new() -> Self {
|
||||
let (send, recv) = channel();
|
||||
Self {
|
||||
free: Vec::new(),
|
||||
images: Vec::new(),
|
||||
updates: Vec::new(),
|
||||
send,
|
||||
recv,
|
||||
}
|
||||
}
|
||||
pub fn add(&mut self, image: impl Into<DynamicImage>) -> TextureHandle {
|
||||
let image = image.into();
|
||||
let size = image.dimensions().into();
|
||||
let view_idx = self.push(image);
|
||||
// 0 == default in renderer; TODO: actually create samplers here
|
||||
let sampler_idx = 0;
|
||||
TextureHandle {
|
||||
inner: TexturePrimitive {
|
||||
view_idx,
|
||||
sampler_idx,
|
||||
},
|
||||
size,
|
||||
counter: RefCounter::new(),
|
||||
send: self.send.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
fn push(&mut self, image: DynamicImage) -> u32 {
|
||||
if let Some(i) = self.free.pop() {
|
||||
self.images[i as usize] = Some(image);
|
||||
self.updates.push(Update::Set(i));
|
||||
i
|
||||
} else {
|
||||
let i = self.images.len() as u32;
|
||||
self.images.push(Some(image));
|
||||
self.updates.push(Update::Push(i));
|
||||
i
|
||||
}
|
||||
}
|
||||
|
||||
pub fn free(&mut self) {
|
||||
for idx in self.recv.try_iter() {
|
||||
self.images[idx as usize] = None;
|
||||
self.updates.push(Update::Free(idx));
|
||||
self.free.push(idx);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn updates(&mut self) -> impl Iterator<Item = TextureUpdate<'_>> {
|
||||
self.updates.drain(..).map(|u| match u {
|
||||
Update::Push(i) => self.images[i as usize]
|
||||
.as_ref()
|
||||
.map(TextureUpdate::Push)
|
||||
.unwrap_or(TextureUpdate::PushFree),
|
||||
Update::Set(i) => self.images[i as usize]
|
||||
.as_ref()
|
||||
.map(|img| TextureUpdate::Set(i, img))
|
||||
.unwrap_or(TextureUpdate::SetFree),
|
||||
Update::Free(i) => TextureUpdate::Free(i),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TextureHandle {
|
||||
pub fn primitive(&self) -> TexturePrimitive {
|
||||
self.inner
|
||||
}
|
||||
pub fn size(&self) -> Vec2 {
|
||||
self.size
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TextureHandle {
|
||||
fn drop(&mut self) {
|
||||
if self.counter.drop() {
|
||||
let _ = self.send.send(self.inner.view_idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<&TextureHandle> for Textures {
|
||||
type Output = DynamicImage;
|
||||
|
||||
fn index(&self, index: &TextureHandle) -> &Self::Output {
|
||||
self.images[index.inner.view_idx as usize].as_ref().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Textures {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
@@ -1,160 +0,0 @@
|
||||
use image::DynamicImage;
|
||||
|
||||
use crate::{
|
||||
layout::{
|
||||
Event, EventFn, EventModule, IdLike, PainterData, PixelRegion, TextureHandle, Vec2, Widget,
|
||||
WidgetEventFn, WidgetInstance, WidgetLike, WidgetRef, WidgetUpdate,
|
||||
},
|
||||
util::{HashSet, Id},
|
||||
};
|
||||
use std::sync::mpsc::{Receiver, channel};
|
||||
|
||||
pub struct Ui {
|
||||
// TODO: make this at least pub(super)
|
||||
pub(crate) data: PainterData,
|
||||
root: Option<WidgetRef>,
|
||||
updates: HashSet<Id>,
|
||||
free: Vec<Id>,
|
||||
recv: Receiver<WidgetUpdate>,
|
||||
full_redraw: bool,
|
||||
resized: bool,
|
||||
}
|
||||
|
||||
impl Ui {
|
||||
pub fn add<W: Widget, Tag>(&mut self, w: impl WidgetLike<Tag, Widget = W>) -> WidgetRef<W> {
|
||||
w.add(self)
|
||||
}
|
||||
|
||||
pub fn add_widget<W: Widget>(&mut self, w: W) -> WidgetRef<W> {
|
||||
self.data.widgets.insert(w)
|
||||
}
|
||||
|
||||
pub fn set_root<Tag>(&mut self, w: impl WidgetLike<Tag>) {
|
||||
self.root = Some(w.add(self).any());
|
||||
self.full_redraw = true;
|
||||
}
|
||||
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn add_texture(&mut self, image: DynamicImage) -> TextureHandle {
|
||||
self.data.textures.add(image)
|
||||
}
|
||||
|
||||
pub fn resize(&mut self, size: impl Into<Vec2>) {
|
||||
self.data.output_size = size.into();
|
||||
self.resized = true;
|
||||
}
|
||||
|
||||
fn redraw_all(&mut self) {
|
||||
for (_, inst) in self.data.active.drain() {
|
||||
for m in self.data.modules.iter_mut() {
|
||||
m.on_undraw(&inst);
|
||||
}
|
||||
}
|
||||
// free before bc nothing should exist
|
||||
self.free();
|
||||
if let Some(root) = &self.root {
|
||||
self.data.draw(root);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(&mut self) -> bool {
|
||||
for update in self.recv.try_iter() {
|
||||
match update {
|
||||
WidgetUpdate::Drop(id) => {
|
||||
self.free.push(id);
|
||||
}
|
||||
WidgetUpdate::Mutate(id) => {
|
||||
self.updates.insert(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
if self.full_redraw {
|
||||
self.redraw_all();
|
||||
self.full_redraw = false;
|
||||
true
|
||||
} else if self.resized {
|
||||
self.resized = false;
|
||||
self.redraw_all();
|
||||
true
|
||||
} else if !self.updates.is_empty() {
|
||||
self.redraw_updates();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn redraw_updates(&mut self) {
|
||||
self.data.redraw(std::mem::take(&mut self.updates));
|
||||
self.free();
|
||||
}
|
||||
|
||||
/// free any resources that don't have references anymore
|
||||
fn free(&mut self) {
|
||||
for id in self.free.drain(..) {
|
||||
for m in self.data.modules.iter_mut() {
|
||||
m.on_remove(&id);
|
||||
}
|
||||
self.data.widgets.delete(id);
|
||||
}
|
||||
self.data.textures.free();
|
||||
}
|
||||
|
||||
pub fn needs_redraw(&self) -> bool {
|
||||
self.full_redraw || !self.updates.is_empty()
|
||||
}
|
||||
|
||||
pub fn num_widgets(&self) -> usize {
|
||||
self.data.widgets.len()
|
||||
}
|
||||
|
||||
pub fn active_widgets(&self) -> usize {
|
||||
self.data.active.len()
|
||||
}
|
||||
|
||||
pub fn debug_layers(&self) {
|
||||
for ((idx, depth), primitives) in self.data.layers.iter_depth() {
|
||||
let indent = " ".repeat(depth * 2);
|
||||
let len = primitives.instances().len();
|
||||
print!("{indent}{idx}: {len} primitives");
|
||||
if len >= 1 {
|
||||
print!(" ({})", primitives.instances()[0].binding);
|
||||
}
|
||||
println!();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn window_region<W>(&self, id: &impl IdLike<W>) -> Option<PixelRegion> {
|
||||
let region = self.data.active.get(&id.id())?.region;
|
||||
Some(region.to_px(self.data.output_size))
|
||||
}
|
||||
|
||||
pub fn debug(&self, label: &str) -> impl Iterator<Item = &WidgetInstance> {
|
||||
self.data.active.iter().filter_map(move |(id, inst)| {
|
||||
let widget = &self.data.widgets.get(*id);
|
||||
if widget.get_label().as_str() == label {
|
||||
Some(inst)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Ui {
|
||||
fn default() -> Self {
|
||||
let (send, recv) = channel();
|
||||
Self {
|
||||
data: PainterData::new(send),
|
||||
root: Default::default(),
|
||||
updates: Default::default(),
|
||||
free: Default::default(),
|
||||
full_redraw: false,
|
||||
recv,
|
||||
resized: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
use crate::layout::{Widget, WidgetLike, WidgetRef};
|
||||
use std::marker::Unsize;
|
||||
|
||||
pub trait WidgetView {
|
||||
type Widget: Widget + ?Sized + Unsize<dyn Widget> = dyn Widget;
|
||||
fn view(&self) -> &WidgetRef<Self::Widget>;
|
||||
}
|
||||
|
||||
pub struct ViewTag;
|
||||
|
||||
impl<WV: WidgetView> WidgetLike<ViewTag> for WV {
|
||||
type Widget = WV::Widget;
|
||||
|
||||
fn add(self, _ui: &mut super::Ui) -> WidgetRef<Self::Widget> {
|
||||
self.view().clone()
|
||||
}
|
||||
}
|
||||
@@ -1,147 +0,0 @@
|
||||
use crate::{
|
||||
core::WidgetPtr,
|
||||
layout::{Len, Painter, SizeCtx, Ui, WidgetIdFn, WidgetRef},
|
||||
};
|
||||
|
||||
use std::{any::Any, marker::Unsize};
|
||||
|
||||
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;
|
||||
|
||||
pub trait WidgetLike<Tag> {
|
||||
type Widget: Widget + ?Sized + Unsize<dyn Widget> + 'static;
|
||||
|
||||
fn add(self, ui: &mut Ui) -> WidgetRef<Self::Widget>;
|
||||
|
||||
fn with_id<W2>(
|
||||
self,
|
||||
f: impl FnOnce(&mut Ui, WidgetRef<Self::Widget>) -> WidgetRef<W2>,
|
||||
) -> impl WidgetIdFn<W2>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
move |ui| {
|
||||
let id = self.add(ui);
|
||||
f(ui, id)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_root(self, ui: &mut Ui)
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
ui.set_root(self);
|
||||
}
|
||||
|
||||
fn set_ptr(self, ptr: &WidgetRef<WidgetPtr>, ui: &mut Ui)
|
||||
where
|
||||
Self: Sized,
|
||||
Self::Widget: Widget,
|
||||
{
|
||||
ptr.get_mut().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 + ?Sized>: 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) -> WidgetRef<W> {
|
||||
self(ui).add(ui)
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Widget> WidgetLike<WidgetTag> for W {
|
||||
type Widget = W;
|
||||
fn add(self, ui: &mut Ui) -> WidgetRef<W> {
|
||||
ui.add_widget(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WidgetArr<const LEN: usize> {
|
||||
pub arr: [WidgetRef; LEN],
|
||||
}
|
||||
|
||||
impl<const LEN: usize> WidgetArr<LEN> {
|
||||
pub fn new(arr: [WidgetRef; LEN]) -> Self {
|
||||
Self { arr }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ArrTag;
|
||||
pub trait WidgetArrLike<const LEN: usize, Tag> {
|
||||
fn ui(self, ui: &mut Ui) -> WidgetArr<LEN>;
|
||||
}
|
||||
|
||||
impl<const LEN: usize> WidgetArrLike<LEN, ArrTag> for WidgetArr<LEN> {
|
||||
fn ui(self, _: &mut Ui) -> WidgetArr<LEN> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// I hate this language it's so bad why do I even use it
|
||||
macro_rules! impl_widget_arr {
|
||||
($n:expr;$($W:ident)*) => {
|
||||
impl_widget_arr!($n;$($W)*;$(${concat($W,Tag)})*);
|
||||
};
|
||||
($n:expr;$($W:ident)*;$($Tag:ident)*) => {
|
||||
impl<$($W: WidgetLike<$Tag>,$Tag,)*> WidgetArrLike<$n, ($($Tag,)*)> for ($($W,)*) {
|
||||
fn ui(self, ui: &mut Ui) -> WidgetArr<$n> {
|
||||
#[allow(non_snake_case)]
|
||||
let ($($W,)*) = self;
|
||||
WidgetArr::new(
|
||||
[$($W.add(ui).any(),)*],
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_widget_arr!(1;A);
|
||||
impl_widget_arr!(2;A B);
|
||||
impl_widget_arr!(3;A B C);
|
||||
impl_widget_arr!(4;A B C D);
|
||||
impl_widget_arr!(5;A B C D E);
|
||||
impl_widget_arr!(6;A B C D E F);
|
||||
impl_widget_arr!(7;A B C D E F G);
|
||||
impl_widget_arr!(8;A B C D E F G H);
|
||||
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<WidgetRef>;
|
||||
}
|
||||
|
||||
impl WidgetOption for () {
|
||||
fn get(self, _: &mut Ui) -> Option<WidgetRef> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: FnOnce(&mut Ui) -> Option<WidgetRef>> WidgetOption for F {
|
||||
fn get(self, ui: &mut Ui) -> Option<WidgetRef> {
|
||||
self(ui)
|
||||
}
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
use std::{
|
||||
cell::{Ref, RefMut},
|
||||
marker::Unsize,
|
||||
rc::Rc,
|
||||
sync::mpsc::Sender,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
layout::{Ui, Widget, WidgetLike},
|
||||
util::{Handle, Id, WeakHandle},
|
||||
};
|
||||
|
||||
/// An handle for a widget in a UI.
|
||||
///
|
||||
/// TODO: ergonomic clones when they get put in rust-analyzer & don't cause ICEs?
|
||||
pub struct WidgetRef<W: ?Sized = dyn Widget>(Handle<Inner<W>>);
|
||||
pub struct WeakWidgetRef<W: ?Sized = dyn Widget>(WeakHandle<Inner<W>>);
|
||||
|
||||
struct Inner<W: ?Sized> {
|
||||
id: Id,
|
||||
send: Sender<WidgetUpdate>,
|
||||
label: String,
|
||||
widget: W,
|
||||
}
|
||||
|
||||
pub enum WidgetUpdate {
|
||||
Drop(Id),
|
||||
Mutate(Id),
|
||||
}
|
||||
|
||||
impl<W> PartialEq for WidgetRef<W> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.id() == other.id()
|
||||
}
|
||||
}
|
||||
|
||||
impl<W> std::fmt::Debug for WidgetRef<W> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.id().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: ?Sized> Clone for WidgetRef<W> {
|
||||
fn clone(&self) -> Self {
|
||||
Self(self.0.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Widget> WidgetRef<W> {
|
||||
pub(super) fn new(id: Id, widget: W, send: Sender<WidgetUpdate>) -> Self {
|
||||
let mut label = std::any::type_name::<W>().to_string();
|
||||
if let (Some(first), Some(last)) = (label.find(":"), label.rfind(":")) {
|
||||
label = label.split_at(first).0.to_string() + "::" + label.split_at(last + 1).1;
|
||||
}
|
||||
Self(
|
||||
Inner {
|
||||
widget,
|
||||
id,
|
||||
send,
|
||||
label,
|
||||
}
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Widget + ?Sized + Unsize<dyn Widget>> WidgetRef<W> {
|
||||
pub fn any(self) -> WidgetRef<dyn Widget> {
|
||||
WidgetRef(self.0)
|
||||
}
|
||||
pub fn as_any(&self) -> WidgetRef<dyn Widget> {
|
||||
WidgetRef(self.0.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: ?Sized> WidgetRef<W> {
|
||||
pub fn id(&self) -> Id {
|
||||
self.0.get().id
|
||||
}
|
||||
|
||||
pub fn get(&self) -> Ref<'_, W> {
|
||||
Ref::map(self.0.get(), |i| &i.widget)
|
||||
}
|
||||
|
||||
pub fn get_mut(&self) -> RefMut<'_, W> {
|
||||
let inner = self.0.get_mut();
|
||||
let _ = inner.send.send(WidgetUpdate::Mutate(inner.id));
|
||||
RefMut::map(inner, |i| &mut i.widget)
|
||||
}
|
||||
|
||||
pub fn get_mut_quiet(&self) -> RefMut<'_, W> {
|
||||
RefMut::map(self.0.get_mut(), |i| &mut i.widget)
|
||||
}
|
||||
|
||||
pub fn get_label(&self) -> Ref<'_, String> {
|
||||
Ref::map(self.0.get(), |i| &i.label)
|
||||
}
|
||||
|
||||
pub fn set_label(&self, label: impl Into<String>) {
|
||||
self.0.get_mut().label = label.into();
|
||||
}
|
||||
|
||||
pub fn refs(&self) -> usize {
|
||||
self.0.refs()
|
||||
}
|
||||
|
||||
pub fn weak(&self) -> WeakWidgetRef<W> {
|
||||
WeakWidgetRef(self.0.weak())
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: ?Sized> WeakWidgetRef<W> {
|
||||
/// should guarantee that widget is still valid to prevent indexing failures
|
||||
pub(crate) fn expect_strong(&self) -> WidgetRef<W> {
|
||||
WidgetRef(self.0.strong().expect("widget should not be dropped"))
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Widget + ?Sized + Unsize<dyn Widget>> WeakWidgetRef<W> {
|
||||
pub fn any(self) -> WeakWidgetRef<dyn Widget> {
|
||||
WeakWidgetRef(self.0.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: ?Sized> Drop for Inner<W> {
|
||||
fn drop(&mut self) {
|
||||
let _ = self.send.send(WidgetUpdate::Drop(self.id));
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IdTag;
|
||||
pub struct IdFnTag;
|
||||
|
||||
pub trait WidgetIdFn<W: ?Sized>: FnOnce(&mut Ui) -> WidgetRef<W> {}
|
||||
impl<W: ?Sized, F: FnOnce(&mut Ui) -> WidgetRef<W>> WidgetIdFn<W> for F {}
|
||||
|
||||
pub trait WidgetRet: FnOnce(&mut Ui) -> WidgetRef {}
|
||||
impl<F: FnOnce(&mut Ui) -> WidgetRef> WidgetRet for F {}
|
||||
|
||||
impl<W: Widget + ?Sized + Unsize<dyn Widget> + 'static> WidgetLike<IdTag> for WidgetRef<W> {
|
||||
type Widget = W;
|
||||
fn add(self, _: &mut Ui) -> WidgetRef<W> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Widget + ?Sized + Unsize<dyn Widget> + 'static, F: FnOnce(&mut Ui) -> WidgetRef<W>>
|
||||
WidgetLike<IdFnTag> for F
|
||||
{
|
||||
type Widget = W;
|
||||
fn add(self, ui: &mut Ui) -> WidgetRef<W> {
|
||||
self(ui)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait IdLike<W> {
|
||||
fn id(&self) -> Id;
|
||||
}
|
||||
|
||||
impl<W> IdLike<W> for WidgetRef<W> {
|
||||
fn id(&self) -> Id {
|
||||
self.id()
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
use std::sync::mpsc::Sender;
|
||||
|
||||
use crate::{
|
||||
layout::{WeakWidgetRef, Widget, WidgetRef, WidgetUpdate},
|
||||
util::{HashMap, Id, IdTracker},
|
||||
};
|
||||
|
||||
pub struct Widgets {
|
||||
ids: IdTracker,
|
||||
map: HashMap<Id, WeakWidgetRef>,
|
||||
send: Sender<WidgetUpdate>,
|
||||
}
|
||||
|
||||
impl Widgets {
|
||||
pub fn new(send: Sender<WidgetUpdate>) -> Self {
|
||||
Self {
|
||||
ids: IdTracker::default(),
|
||||
map: HashMap::default(),
|
||||
send,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self, id: Id) -> WidgetRef {
|
||||
self.map.get(&id).unwrap().expect_strong()
|
||||
}
|
||||
|
||||
pub fn insert<W: Widget>(&mut self, widget: W) -> WidgetRef<W> {
|
||||
let id = self.ids.next();
|
||||
let rf = WidgetRef::new(id, widget, self.send.clone());
|
||||
self.map.insert(id, rf.weak().any());
|
||||
rf
|
||||
}
|
||||
|
||||
pub fn delete(&mut self, id: Id) {
|
||||
self.map.remove(&id);
|
||||
self.ids.free(id);
|
||||
}
|
||||
|
||||
pub fn reserve(&mut self) -> Id {
|
||||
self.ids.next()
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.map.len()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.map.is_empty()
|
||||
}
|
||||
}
|
||||
+15
-20
@@ -1,28 +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)]
|
||||
#![feature(unsize)]
|
||||
#![feature(coerce_unsized)]
|
||||
|
||||
pub mod core;
|
||||
pub mod layout;
|
||||
pub mod render;
|
||||
pub mod util;
|
||||
pub mod winit;
|
||||
mod default;
|
||||
mod traits;
|
||||
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::*;
|
||||
pub use crate::util::Handle;
|
||||
pub use crate::winit::*;
|
||||
pub use super::default::*;
|
||||
pub use super::traits::*;
|
||||
pub use super::widget::*;
|
||||
|
||||
pub use iris_core::layout::*;
|
||||
pub use iris_core::render::*;
|
||||
pub use iris_core::util::Handle;
|
||||
|
||||
pub use iris_macro::*;
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
use crate::{layout::UiRegion, util::Id};
|
||||
use wgpu::*;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable, Default)]
|
||||
pub struct WindowUniform {
|
||||
pub width: f32,
|
||||
pub height: f32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
||||
pub struct PrimitiveInstance {
|
||||
pub region: UiRegion,
|
||||
pub binding: u32,
|
||||
pub idx: u32,
|
||||
pub mask_idx: MaskIdx,
|
||||
}
|
||||
|
||||
impl PrimitiveInstance {
|
||||
const ATTRIBS: [VertexAttribute; 7] = vertex_attr_array![
|
||||
0 => Float32x2,
|
||||
1 => Float32x2,
|
||||
2 => Float32x2,
|
||||
3 => Float32x2,
|
||||
4 => Uint32,
|
||||
5 => Uint32,
|
||||
6 => Uint32,
|
||||
];
|
||||
|
||||
pub fn desc() -> VertexBufferLayout<'static> {
|
||||
VertexBufferLayout {
|
||||
array_stride: std::mem::size_of::<Self>() as BufferAddress,
|
||||
step_mode: VertexStepMode::Instance,
|
||||
attributes: &Self::ATTRIBS,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type MaskIdx = Id<u32>;
|
||||
|
||||
impl MaskIdx {
|
||||
pub const NONE: Self = Self::preset(u32::MAX);
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
||||
pub struct Mask {
|
||||
pub region: UiRegion,
|
||||
}
|
||||
@@ -1,356 +0,0 @@
|
||||
use std::num::NonZero;
|
||||
|
||||
use crate::{
|
||||
layout::Ui,
|
||||
render::{data::PrimitiveInstance, texture::GpuTextures, util::ArrBuf},
|
||||
util::HashMap,
|
||||
};
|
||||
use data::WindowUniform;
|
||||
use wgpu::{
|
||||
util::{BufferInitDescriptor, DeviceExt},
|
||||
*,
|
||||
};
|
||||
use winit::dpi::PhysicalSize;
|
||||
|
||||
mod data;
|
||||
mod primitive;
|
||||
mod texture;
|
||||
mod util;
|
||||
|
||||
pub use data::{Mask, MaskIdx};
|
||||
pub use primitive::*;
|
||||
|
||||
const SHAPE_SHADER: &str = include_str!("./shader.wgsl");
|
||||
|
||||
pub struct UiRenderNode {
|
||||
uniform_group: BindGroup,
|
||||
primitive_layout: BindGroupLayout,
|
||||
rsc_layout: BindGroupLayout,
|
||||
rsc_group: BindGroup,
|
||||
|
||||
pipeline: RenderPipeline,
|
||||
|
||||
layers: HashMap<usize, RenderLayer>,
|
||||
active: Vec<usize>,
|
||||
window_buffer: Buffer,
|
||||
textures: GpuTextures,
|
||||
masks: ArrBuf<Mask>,
|
||||
}
|
||||
|
||||
struct RenderLayer {
|
||||
instance: ArrBuf<PrimitiveInstance>,
|
||||
primitives: PrimitiveBuffers,
|
||||
primitive_group: BindGroup,
|
||||
}
|
||||
|
||||
impl UiRenderNode {
|
||||
pub fn draw<'a>(&'a self, pass: &mut RenderPass<'a>) {
|
||||
pass.set_pipeline(&self.pipeline);
|
||||
pass.set_bind_group(0, &self.uniform_group, &[]);
|
||||
pass.set_bind_group(2, &self.rsc_group, &[]);
|
||||
for i in &self.active {
|
||||
let layer = &self.layers[i];
|
||||
if layer.instance.len() == 0 {
|
||||
continue;
|
||||
}
|
||||
pass.set_bind_group(1, &layer.primitive_group, &[]);
|
||||
pass.set_vertex_buffer(0, layer.instance.buffer.slice(..));
|
||||
pass.draw(0..4, 0..layer.instance.len() as u32);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(&mut self, device: &Device, queue: &Queue, ui: &mut Ui) {
|
||||
self.active.clear();
|
||||
for (i, primitives) in ui.data.layers.iter_mut() {
|
||||
self.active.push(i);
|
||||
for change in primitives.apply_free() {
|
||||
if let Some(inst) = ui.data.active.get_mut(&change.id) {
|
||||
for h in &mut inst.primitives {
|
||||
if h.layer == i && h.inst_idx == change.old {
|
||||
h.inst_idx = change.new;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let rlayer = self.layers.entry(i).or_insert_with(|| {
|
||||
let primitives = PrimitiveBuffers::new(device);
|
||||
let primitive_group =
|
||||
Self::primitive_group(device, &self.primitive_layout, primitives.buffers());
|
||||
RenderLayer {
|
||||
instance: ArrBuf::new(
|
||||
device,
|
||||
BufferUsages::VERTEX | BufferUsages::COPY_DST,
|
||||
"instance",
|
||||
),
|
||||
primitives,
|
||||
primitive_group,
|
||||
}
|
||||
});
|
||||
if primitives.updated {
|
||||
rlayer
|
||||
.instance
|
||||
.update(device, queue, primitives.instances());
|
||||
rlayer.primitives.update(device, queue, primitives.data());
|
||||
rlayer.primitive_group = Self::primitive_group(
|
||||
device,
|
||||
&self.primitive_layout,
|
||||
rlayer.primitives.buffers(),
|
||||
);
|
||||
primitives.updated = false;
|
||||
}
|
||||
}
|
||||
let mut changed = false;
|
||||
changed |= self.textures.update(&mut ui.data.textures);
|
||||
if ui.data.masks.changed {
|
||||
ui.data.masks.changed = false;
|
||||
self.masks.update(device, queue, &ui.data.masks[..]);
|
||||
changed = true;
|
||||
}
|
||||
if changed {
|
||||
self.rsc_group = Self::rsc_group(device, &self.rsc_layout, &self.textures, &self.masks);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resize(&mut self, size: &PhysicalSize<u32>, queue: &Queue) {
|
||||
let slice = &[WindowUniform {
|
||||
width: size.width as f32,
|
||||
height: size.height as f32,
|
||||
}];
|
||||
queue.write_buffer(&self.window_buffer, 0, bytemuck::cast_slice(slice));
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
device: &Device,
|
||||
queue: &Queue,
|
||||
config: &SurfaceConfiguration,
|
||||
limits: UiLimits,
|
||||
) -> Self {
|
||||
let shader = device.create_shader_module(ShaderModuleDescriptor {
|
||||
label: Some("UI Shape Shader"),
|
||||
source: ShaderSource::Wgsl(SHAPE_SHADER.into()),
|
||||
});
|
||||
|
||||
let window_uniform = WindowUniform::default();
|
||||
let window_buffer = device.create_buffer_init(&BufferInitDescriptor {
|
||||
label: Some("window"),
|
||||
contents: bytemuck::cast_slice(&[window_uniform]),
|
||||
usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST,
|
||||
});
|
||||
|
||||
let uniform_layout = device.create_bind_group_layout(&BindGroupLayoutDescriptor {
|
||||
entries: &[BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT,
|
||||
ty: BindingType::Buffer {
|
||||
ty: BufferBindingType::Uniform,
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: None,
|
||||
},
|
||||
count: None,
|
||||
}],
|
||||
label: Some("window"),
|
||||
});
|
||||
|
||||
let uniform_group = Self::bind_group_0(device, &uniform_layout, &window_buffer);
|
||||
|
||||
let primitive_layout = device.create_bind_group_layout(&BindGroupLayoutDescriptor {
|
||||
entries: &core::array::from_fn::<_, { PrimitiveBuffers::LEN }, _>(|i| {
|
||||
BindGroupLayoutEntry {
|
||||
binding: i as u32,
|
||||
visibility: ShaderStages::FRAGMENT,
|
||||
ty: BindingType::Buffer {
|
||||
ty: BufferBindingType::Storage { read_only: true },
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: None,
|
||||
},
|
||||
count: None,
|
||||
}
|
||||
}),
|
||||
label: Some("primitive"),
|
||||
});
|
||||
|
||||
let tex_manager = GpuTextures::new(device, queue);
|
||||
let masks = ArrBuf::new(
|
||||
device,
|
||||
BufferUsages::STORAGE | BufferUsages::COPY_DST,
|
||||
"ui masks",
|
||||
);
|
||||
|
||||
let rsc_layout = Self::rsc_layout(device, &limits);
|
||||
let rsc_group = Self::rsc_group(device, &rsc_layout, &tex_manager, &masks);
|
||||
|
||||
let pipeline_layout = device.create_pipeline_layout(&PipelineLayoutDescriptor {
|
||||
label: Some("UI Shape Pipeline Layout"),
|
||||
bind_group_layouts: &[&uniform_layout, &primitive_layout, &rsc_layout],
|
||||
push_constant_ranges: &[],
|
||||
});
|
||||
let pipeline = device.create_render_pipeline(&RenderPipelineDescriptor {
|
||||
label: Some("UI Shape Pipeline"),
|
||||
layout: Some(&pipeline_layout),
|
||||
vertex: VertexState {
|
||||
module: &shader,
|
||||
entry_point: Some("vs_main"),
|
||||
buffers: &[PrimitiveInstance::desc()],
|
||||
compilation_options: Default::default(),
|
||||
},
|
||||
fragment: Some(FragmentState {
|
||||
module: &shader,
|
||||
entry_point: Some("fs_main"),
|
||||
targets: &[Some(ColorTargetState {
|
||||
format: config.format,
|
||||
blend: Some(BlendState::ALPHA_BLENDING),
|
||||
write_mask: ColorWrites::ALL,
|
||||
})],
|
||||
compilation_options: Default::default(),
|
||||
}),
|
||||
primitive: PrimitiveState {
|
||||
topology: PrimitiveTopology::TriangleStrip,
|
||||
strip_index_format: None,
|
||||
front_face: FrontFace::Cw,
|
||||
cull_mode: Some(Face::Back),
|
||||
polygon_mode: PolygonMode::Fill,
|
||||
unclipped_depth: false,
|
||||
conservative: false,
|
||||
},
|
||||
depth_stencil: None,
|
||||
multisample: MultisampleState {
|
||||
count: 1,
|
||||
mask: !0,
|
||||
alpha_to_coverage_enabled: false,
|
||||
},
|
||||
multiview: None,
|
||||
cache: None,
|
||||
});
|
||||
|
||||
Self {
|
||||
uniform_group,
|
||||
primitive_layout,
|
||||
rsc_layout,
|
||||
rsc_group,
|
||||
pipeline,
|
||||
window_buffer,
|
||||
layers: HashMap::default(),
|
||||
active: Vec::new(),
|
||||
textures: tex_manager,
|
||||
masks,
|
||||
}
|
||||
}
|
||||
|
||||
fn bind_group_0(
|
||||
device: &Device,
|
||||
layout: &BindGroupLayout,
|
||||
window_buffer: &Buffer,
|
||||
) -> BindGroup {
|
||||
device.create_bind_group(&BindGroupDescriptor {
|
||||
layout,
|
||||
entries: &[BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: window_buffer.as_entire_binding(),
|
||||
}],
|
||||
label: Some("ui window"),
|
||||
})
|
||||
}
|
||||
|
||||
fn primitive_group(
|
||||
device: &Device,
|
||||
layout: &BindGroupLayout,
|
||||
buffers: [(u32, &Buffer); PrimitiveBuffers::LEN],
|
||||
) -> BindGroup {
|
||||
device.create_bind_group(&BindGroupDescriptor {
|
||||
layout,
|
||||
entries: &buffers.map(|(binding, buf)| BindGroupEntry {
|
||||
binding,
|
||||
resource: buf.as_entire_binding(),
|
||||
}),
|
||||
label: Some("ui primitives"),
|
||||
})
|
||||
}
|
||||
|
||||
fn rsc_layout(device: &Device, limits: &UiLimits) -> BindGroupLayout {
|
||||
device.create_bind_group_layout(&BindGroupLayoutDescriptor {
|
||||
entries: &[
|
||||
BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: ShaderStages::FRAGMENT,
|
||||
ty: BindingType::Texture {
|
||||
sample_type: TextureSampleType::Float { filterable: false },
|
||||
view_dimension: TextureViewDimension::D2,
|
||||
multisampled: false,
|
||||
},
|
||||
count: Some(NonZero::new(limits.max_textures).unwrap()),
|
||||
},
|
||||
BindGroupLayoutEntry {
|
||||
binding: 1,
|
||||
visibility: ShaderStages::FRAGMENT,
|
||||
ty: BindingType::Sampler(SamplerBindingType::NonFiltering),
|
||||
count: Some(NonZero::new(limits.max_samplers).unwrap()),
|
||||
},
|
||||
BindGroupLayoutEntry {
|
||||
binding: 2,
|
||||
visibility: ShaderStages::FRAGMENT,
|
||||
ty: BindingType::Buffer {
|
||||
ty: BufferBindingType::Storage { read_only: true },
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: None,
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
],
|
||||
label: Some("ui rsc"),
|
||||
})
|
||||
}
|
||||
|
||||
fn rsc_group(
|
||||
device: &Device,
|
||||
layout: &BindGroupLayout,
|
||||
tex_manager: &GpuTextures,
|
||||
masks: &ArrBuf<Mask>,
|
||||
) -> BindGroup {
|
||||
device.create_bind_group(&BindGroupDescriptor {
|
||||
layout,
|
||||
entries: &[
|
||||
BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: BindingResource::TextureViewArray(&tex_manager.views()),
|
||||
},
|
||||
BindGroupEntry {
|
||||
binding: 1,
|
||||
resource: BindingResource::SamplerArray(&tex_manager.samplers()),
|
||||
},
|
||||
BindGroupEntry {
|
||||
binding: 2,
|
||||
resource: masks.buffer.as_entire_binding(),
|
||||
},
|
||||
],
|
||||
label: Some("ui rsc"),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn view_count(&self) -> usize {
|
||||
self.textures.view_count()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UiLimits {
|
||||
max_textures: u32,
|
||||
max_samplers: u32,
|
||||
}
|
||||
|
||||
impl Default for UiLimits {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
max_textures: 100000,
|
||||
max_samplers: 1000,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UiLimits {
|
||||
pub fn max_binding_array_elements_per_shader_stage(&self) -> u32 {
|
||||
self.max_textures + self.max_samplers
|
||||
}
|
||||
pub fn max_binding_array_sampler_elements_per_shader_stage(&self) -> u32 {
|
||||
self.max_samplers
|
||||
}
|
||||
}
|
||||
@@ -1,283 +0,0 @@
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use crate::{
|
||||
layout::{Color, UiRegion},
|
||||
render::{
|
||||
ArrBuf,
|
||||
data::{MaskIdx, PrimitiveInstance},
|
||||
},
|
||||
util::Id,
|
||||
};
|
||||
use bytemuck::Pod;
|
||||
use wgpu::*;
|
||||
|
||||
pub struct Primitives {
|
||||
instances: Vec<PrimitiveInstance>,
|
||||
assoc: Vec<Id>,
|
||||
data: PrimitiveData,
|
||||
free: Vec<usize>,
|
||||
pub updated: bool,
|
||||
}
|
||||
|
||||
impl Default for Primitives {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
instances: Default::default(),
|
||||
assoc: Default::default(),
|
||||
data: Default::default(),
|
||||
free: Vec::new(),
|
||||
updated: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Primitive: Pod {
|
||||
const BINDING: u32;
|
||||
fn vec(data: &mut PrimitiveData) -> &mut PrimitiveVec<Self>;
|
||||
}
|
||||
|
||||
macro_rules! primitives {
|
||||
($($name:ident: $ty:ty => $binding:expr,)*) => {
|
||||
#[derive(Default)]
|
||||
pub struct PrimitiveData {
|
||||
$(pub(crate) $name: PrimitiveVec<$ty>,)*
|
||||
}
|
||||
|
||||
pub struct PrimitiveBuffers {
|
||||
$($name: ArrBuf<$ty>,)*
|
||||
}
|
||||
|
||||
impl PrimitiveBuffers {
|
||||
pub fn update(&mut self, device: &Device, queue: &Queue, data: &PrimitiveData) {
|
||||
$(self.$name.update(device, queue, &data.$name);)*
|
||||
}
|
||||
}
|
||||
|
||||
impl PrimitiveBuffers {
|
||||
pub const LEN: usize = primitives!(@count $($name)*);
|
||||
pub fn buffers(&self) -> [(u32, &Buffer); Self::LEN] {
|
||||
[
|
||||
$((<$ty>::BINDING, &self.$name.buffer),)*
|
||||
]
|
||||
}
|
||||
pub fn new(device: &Device) -> Self {
|
||||
Self {
|
||||
$($name: ArrBuf::new(
|
||||
device,
|
||||
BufferUsages::STORAGE | BufferUsages::COPY_DST,
|
||||
stringify!($name),
|
||||
),)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PrimitiveData {
|
||||
pub fn clear(&mut self) {
|
||||
$(self.$name.clear();)*
|
||||
}
|
||||
pub fn free(&mut self, binding: u32, idx: usize) {
|
||||
match binding {
|
||||
$(<$ty>::BINDING => self.$name.free(idx),)*
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$(
|
||||
unsafe impl bytemuck::Pod for $ty {}
|
||||
unsafe impl bytemuck::Zeroable for $ty {}
|
||||
impl Primitive for $ty {
|
||||
const BINDING: u32 = $binding;
|
||||
fn vec(data: &mut PrimitiveData) -> &mut PrimitiveVec<Self> {
|
||||
&mut data.$name
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
(@count $t1:tt $($t:tt)+) => { 1 + primitives!(@count $($t),+) };
|
||||
(@count $t:tt) => { 1 };
|
||||
}
|
||||
|
||||
pub struct PrimitiveInst<P> {
|
||||
pub id: Id,
|
||||
pub primitive: P,
|
||||
pub region: UiRegion,
|
||||
pub mask_idx: MaskIdx,
|
||||
}
|
||||
|
||||
impl Primitives {
|
||||
pub fn write<P: Primitive>(
|
||||
&mut self,
|
||||
layer: usize,
|
||||
PrimitiveInst {
|
||||
id,
|
||||
primitive,
|
||||
region,
|
||||
mask_idx,
|
||||
}: PrimitiveInst<P>,
|
||||
) -> PrimitiveHandle {
|
||||
self.updated = true;
|
||||
let vec = P::vec(&mut self.data);
|
||||
let i = vec.add(primitive);
|
||||
let inst = PrimitiveInstance {
|
||||
region,
|
||||
idx: i as u32,
|
||||
mask_idx,
|
||||
binding: P::BINDING,
|
||||
};
|
||||
let inst_i = if let Some(i) = self.free.pop() {
|
||||
self.instances[i] = inst;
|
||||
self.assoc[i] = id;
|
||||
i
|
||||
} else {
|
||||
let i = self.instances.len();
|
||||
self.instances.push(inst);
|
||||
self.assoc.push(id);
|
||||
i
|
||||
};
|
||||
PrimitiveHandle::new::<P>(layer, inst_i, i)
|
||||
}
|
||||
|
||||
/// returns (old index, new index)
|
||||
pub fn apply_free(&mut self) -> impl Iterator<Item = PrimitiveChange> {
|
||||
self.free.sort_by(|a, b| b.cmp(a));
|
||||
self.free.drain(..).filter_map(|i| {
|
||||
self.instances.swap_remove(i);
|
||||
self.assoc.swap_remove(i);
|
||||
if i == self.instances.len() {
|
||||
return None;
|
||||
}
|
||||
let id = self.assoc[i];
|
||||
let old = self.instances.len();
|
||||
Some(PrimitiveChange { id, old, new: i })
|
||||
})
|
||||
}
|
||||
|
||||
pub fn free(&mut self, h: &PrimitiveHandle) -> MaskIdx {
|
||||
self.updated = true;
|
||||
self.data.free(h.binding, h.data_idx);
|
||||
self.free.push(h.inst_idx);
|
||||
self.instances[h.inst_idx].mask_idx
|
||||
}
|
||||
|
||||
pub fn data(&self) -> &PrimitiveData {
|
||||
&self.data
|
||||
}
|
||||
|
||||
pub fn instances(&self) -> &Vec<PrimitiveInstance> {
|
||||
&self.instances
|
||||
}
|
||||
|
||||
pub fn region_mut(&mut self, h: &PrimitiveHandle) -> &mut UiRegion {
|
||||
self.updated = true;
|
||||
&mut self.instances[h.inst_idx].region
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PrimitiveChange {
|
||||
pub id: Id,
|
||||
pub old: usize,
|
||||
pub new: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PrimitiveHandle {
|
||||
pub layer: usize,
|
||||
pub inst_idx: usize,
|
||||
pub data_idx: usize,
|
||||
pub binding: u32,
|
||||
}
|
||||
|
||||
impl PrimitiveHandle {
|
||||
fn new<P: Primitive>(layer: usize, inst_idx: usize, data_idx: usize) -> Self {
|
||||
Self {
|
||||
layer,
|
||||
inst_idx,
|
||||
data_idx,
|
||||
binding: P::BINDING,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
primitives!(
|
||||
rects: RectPrimitive => 0,
|
||||
textures: TexturePrimitive => 1,
|
||||
);
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct RectPrimitive {
|
||||
pub color: Color<u8>,
|
||||
pub radius: f32,
|
||||
pub thickness: f32,
|
||||
pub inner_radius: f32,
|
||||
}
|
||||
|
||||
impl RectPrimitive {
|
||||
pub fn color(color: Color<u8>) -> Self {
|
||||
Self {
|
||||
color,
|
||||
radius: 0.0,
|
||||
thickness: 0.0,
|
||||
inner_radius: 0.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct TexturePrimitive {
|
||||
pub view_idx: u32,
|
||||
pub sampler_idx: u32,
|
||||
}
|
||||
|
||||
pub struct PrimitiveVec<T> {
|
||||
vec: Vec<T>,
|
||||
free: Vec<usize>,
|
||||
}
|
||||
|
||||
impl<T> PrimitiveVec<T> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
vec: Vec::new(),
|
||||
free: Vec::new(),
|
||||
}
|
||||
}
|
||||
pub fn add(&mut self, t: T) -> usize {
|
||||
if let Some(i) = self.free.pop() {
|
||||
self.vec[i] = t;
|
||||
i
|
||||
} else {
|
||||
let i = self.vec.len();
|
||||
self.vec.push(t);
|
||||
i
|
||||
}
|
||||
}
|
||||
pub fn free(&mut self, i: usize) {
|
||||
self.free.push(i);
|
||||
}
|
||||
pub fn clear(&mut self) {
|
||||
self.free.clear();
|
||||
self.vec.clear();
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for PrimitiveVec<T> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for PrimitiveVec<T> {
|
||||
type Target = Vec<T>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.vec
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for PrimitiveVec<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.vec
|
||||
}
|
||||
}
|
||||
@@ -1,178 +0,0 @@
|
||||
const RECT: u32 = 0u;
|
||||
const TEXTURE: u32 = 1u;
|
||||
|
||||
@group(0) @binding(0)
|
||||
var<uniform> window: WindowUniform;
|
||||
@group(1) @binding(RECT)
|
||||
var<storage> rects: array<Rect>;
|
||||
@group(1) @binding(TEXTURE)
|
||||
var<storage> textures: array<TextureInfo>;
|
||||
|
||||
struct Rect {
|
||||
color: u32,
|
||||
radius: f32,
|
||||
thickness: f32,
|
||||
inner_radius: f32,
|
||||
}
|
||||
|
||||
struct TextureInfo {
|
||||
view_idx: u32,
|
||||
sampler_idx: u32,
|
||||
}
|
||||
|
||||
struct Mask {
|
||||
x: UiSpan,
|
||||
y: UiSpan,
|
||||
}
|
||||
|
||||
struct UiSpan {
|
||||
start: UiScalar,
|
||||
end: UiScalar,
|
||||
}
|
||||
|
||||
struct UiScalar {
|
||||
rel: f32,
|
||||
abs: f32,
|
||||
}
|
||||
|
||||
struct UiVec2 {
|
||||
rel: vec2<f32>,
|
||||
abs: vec2<f32>,
|
||||
}
|
||||
|
||||
@group(2) @binding(0)
|
||||
var views: binding_array<texture_2d<f32>>;
|
||||
@group(2) @binding(1)
|
||||
var samplers: binding_array<sampler>;
|
||||
@group(2) @binding(2)
|
||||
var<storage> masks: array<Mask>;
|
||||
|
||||
struct WindowUniform {
|
||||
dim: vec2<f32>,
|
||||
};
|
||||
|
||||
struct InstanceInput {
|
||||
@location(0) x_start: vec2<f32>,
|
||||
@location(1) x_end: vec2<f32>,
|
||||
@location(2) y_start: vec2<f32>,
|
||||
@location(3) y_end: vec2<f32>,
|
||||
@location(4) binding: u32,
|
||||
@location(5) idx: u32,
|
||||
@location(6) mask_idx: u32,
|
||||
}
|
||||
|
||||
struct VertexOutput {
|
||||
@location(0) top_left: vec2<f32>,
|
||||
@location(1) bot_right: vec2<f32>,
|
||||
@location(2) uv: vec2<f32>,
|
||||
@location(3) binding: u32,
|
||||
@location(4) idx: u32,
|
||||
@location(5) mask_idx: u32,
|
||||
@builtin(position) clip_position: vec4<f32>,
|
||||
};
|
||||
|
||||
struct Region {
|
||||
pos: vec2<f32>,
|
||||
uv: vec2<f32>,
|
||||
top_left: vec2<f32>,
|
||||
bot_right: vec2<f32>,
|
||||
}
|
||||
|
||||
@vertex
|
||||
fn vs_main(
|
||||
@builtin(vertex_index) vi: u32,
|
||||
in: InstanceInput,
|
||||
) -> VertexOutput {
|
||||
var out: VertexOutput;
|
||||
|
||||
let top_left_rel = vec2(in.x_start.x, in.y_start.x);
|
||||
let top_left_abs = vec2(in.x_start.y, in.y_start.y);
|
||||
let bot_right_rel = vec2(in.x_end.x, in.y_end.x);
|
||||
let bot_right_abs = vec2(in.x_end.y, in.y_end.y);
|
||||
|
||||
let top_left = floor(top_left_rel * window.dim) + floor(top_left_abs);
|
||||
let bot_right = floor(bot_right_rel * window.dim) + floor(bot_right_abs);
|
||||
let size = bot_right - top_left;
|
||||
|
||||
let uv = vec2<f32>(
|
||||
f32(vi % 2u),
|
||||
f32(vi / 2u)
|
||||
);
|
||||
let pos = (top_left + uv * size) / window.dim * 2.0 - 1.0;
|
||||
out.clip_position = vec4<f32>(pos.x, -pos.y, 0.0, 1.0);
|
||||
out.uv = uv;
|
||||
out.binding = in.binding;
|
||||
out.idx = in.idx;
|
||||
out.top_left = top_left;
|
||||
out.bot_right = bot_right;
|
||||
out.mask_idx = in.mask_idx;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fs_main(
|
||||
in: VertexOutput
|
||||
) -> @location(0) vec4<f32> {
|
||||
let pos = in.clip_position.xy;
|
||||
let region = Region(pos, in.uv, in.top_left, in.bot_right);
|
||||
let i = in.idx;
|
||||
var color: vec4<f32>;
|
||||
switch in.binding {
|
||||
case RECT: {
|
||||
color = draw_rounded_rect(region, rects[i]);
|
||||
}
|
||||
case TEXTURE: {
|
||||
color = draw_texture(region, textures[i]);
|
||||
}
|
||||
default: {
|
||||
color = vec4(1.0, 0.0, 1.0, 1.0);
|
||||
}
|
||||
}
|
||||
if in.mask_idx != 4294967295u {
|
||||
let mask = masks[in.mask_idx];
|
||||
let tl = UiVec2(vec2(mask.x.start.rel, mask.y.start.rel), vec2(mask.x.start.abs, mask.y.start.abs));
|
||||
let br = UiVec2(vec2(mask.x.end.rel, mask.y.end.rel), vec2(mask.x.end.abs, mask.y.end.abs));
|
||||
|
||||
let top_left = floor(tl.rel * window.dim) + floor(tl.abs);
|
||||
let bot_right = floor(br.rel * window.dim) + floor(br.abs);
|
||||
if pos.x < top_left.x || pos.x > bot_right.x || pos.y < top_left.y || pos.y > bot_right.y {
|
||||
color *= 0.0;
|
||||
}
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
// TODO: this seems really inefficient (per frag indexing)?
|
||||
fn draw_texture(region: Region, info: TextureInfo) -> vec4<f32> {
|
||||
return textureSample(views[info.view_idx], samplers[info.sampler_idx], region.uv);
|
||||
}
|
||||
|
||||
fn draw_rounded_rect(region: Region, rect: Rect) -> vec4<f32> {
|
||||
var color = unpack4x8unorm(rect.color);
|
||||
|
||||
let edge = 0.5;
|
||||
|
||||
let size = region.bot_right - region.top_left;
|
||||
let corner = size / 2.0;
|
||||
let center = region.top_left + corner;
|
||||
|
||||
let dist = distance_from_rect(region.pos, center, corner, rect.radius);
|
||||
color.a *= 1.0 - smoothstep(-min(edge, rect.radius), edge, dist);
|
||||
|
||||
if rect.thickness > 0.0 {
|
||||
let dist2 = distance_from_rect(region.pos, center, corner - rect.thickness, rect.inner_radius);
|
||||
color.a *= smoothstep(-min(edge, rect.inner_radius), edge, dist2);
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
fn distance_from_rect(pixel_pos: vec2<f32>, rect_center: vec2<f32>, rect_corner: vec2<f32>, radius: f32) -> f32 {
|
||||
// vec from center to pixel
|
||||
let p = pixel_pos - rect_center;
|
||||
// vec from inner rect corner to pixel
|
||||
let q = abs(p) - (rect_corner - radius);
|
||||
return length(max(q, vec2(0.0))) - radius;
|
||||
}
|
||||
|
||||
@@ -1,129 +0,0 @@
|
||||
use image::{DynamicImage, EncodableLayout};
|
||||
use wgpu::{util::DeviceExt, *};
|
||||
|
||||
use crate::layout::{TextureUpdate, Textures};
|
||||
|
||||
pub struct GpuTextures {
|
||||
device: Device,
|
||||
queue: Queue,
|
||||
views: Vec<TextureView>,
|
||||
view_count: usize,
|
||||
samplers: Vec<Sampler>,
|
||||
null_view: TextureView,
|
||||
no_views: Vec<TextureView>,
|
||||
}
|
||||
|
||||
impl GpuTextures {
|
||||
pub fn update(&mut self, textures: &mut Textures) -> bool {
|
||||
let mut changed = false;
|
||||
for update in textures.updates() {
|
||||
changed = true;
|
||||
match update {
|
||||
TextureUpdate::Push(image) => self.push(image),
|
||||
TextureUpdate::Set(i, image) => self.set(i, image),
|
||||
TextureUpdate::SetFree => self.view_count += 1,
|
||||
TextureUpdate::Free(i) => self.free(i),
|
||||
TextureUpdate::PushFree => self.push_free(),
|
||||
}
|
||||
}
|
||||
changed
|
||||
}
|
||||
fn set(&mut self, i: u32, image: &DynamicImage) {
|
||||
self.view_count += 1;
|
||||
let view = self.create_view(image);
|
||||
self.views[i as usize] = view;
|
||||
}
|
||||
fn free(&mut self, i: u32) {
|
||||
self.view_count -= 1;
|
||||
self.views[i as usize] = self.null_view.clone();
|
||||
}
|
||||
fn push(&mut self, image: &DynamicImage) {
|
||||
self.view_count += 1;
|
||||
let view = self.create_view(image);
|
||||
self.views.push(view);
|
||||
}
|
||||
fn push_free(&mut self) {
|
||||
self.view_count += 1;
|
||||
self.views.push(self.null_view.clone());
|
||||
}
|
||||
|
||||
fn create_view(&self, image: &DynamicImage) -> TextureView {
|
||||
let image = image.to_rgba8();
|
||||
let (width, height) = image.dimensions();
|
||||
let texture = self.device.create_texture_with_data(
|
||||
&self.queue,
|
||||
&TextureDescriptor {
|
||||
label: None,
|
||||
size: Extent3d {
|
||||
width,
|
||||
height,
|
||||
depth_or_array_layers: 1,
|
||||
},
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: TextureDimension::D2,
|
||||
format: TextureFormat::Rgba8Unorm,
|
||||
usage: TextureUsages::TEXTURE_BINDING,
|
||||
view_formats: &[],
|
||||
},
|
||||
wgt::TextureDataOrder::MipMajor,
|
||||
image.as_bytes(),
|
||||
);
|
||||
texture.create_view(&TextureViewDescriptor::default())
|
||||
}
|
||||
|
||||
pub fn new(device: &Device, queue: &Queue) -> Self {
|
||||
let null_view = null_texture_view(device);
|
||||
Self {
|
||||
device: device.clone(),
|
||||
queue: queue.clone(),
|
||||
views: Vec::new(),
|
||||
samplers: vec![default_sampler(device)],
|
||||
no_views: vec![null_view.clone()],
|
||||
null_view,
|
||||
view_count: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn views(&self) -> Vec<&TextureView> {
|
||||
if self.views.is_empty() {
|
||||
&self.no_views
|
||||
} else {
|
||||
&self.views
|
||||
}
|
||||
.iter()
|
||||
.by_ref()
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn samplers(&self) -> Vec<&Sampler> {
|
||||
self.samplers.iter().by_ref().collect()
|
||||
}
|
||||
|
||||
pub fn view_count(&self) -> usize {
|
||||
self.view_count
|
||||
}
|
||||
}
|
||||
|
||||
pub fn null_texture_view(device: &Device) -> TextureView {
|
||||
device
|
||||
.create_texture(&TextureDescriptor {
|
||||
label: Some("null"),
|
||||
size: Extent3d {
|
||||
width: 1,
|
||||
height: 1,
|
||||
depth_or_array_layers: 1,
|
||||
},
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: TextureDimension::D2,
|
||||
format: TextureFormat::Rgba8Unorm,
|
||||
usage: TextureUsages::TEXTURE_BINDING,
|
||||
view_formats: &[],
|
||||
})
|
||||
.create_view(&TextureViewDescriptor::default())
|
||||
}
|
||||
|
||||
pub fn default_sampler(device: &Device) -> Sampler {
|
||||
device.create_sampler(&SamplerDescriptor::default())
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use bytemuck::Pod;
|
||||
use wgpu::*;
|
||||
|
||||
pub struct ArrBuf<T: Pod> {
|
||||
label: &'static str,
|
||||
usage: BufferUsages,
|
||||
pub buffer: Buffer,
|
||||
len: usize,
|
||||
_pd: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: Pod> ArrBuf<T> {
|
||||
pub fn new(device: &Device, usage: BufferUsages, label: &'static str) -> Self {
|
||||
Self {
|
||||
label,
|
||||
usage,
|
||||
buffer: Self::init_buf(device, 0, usage, label),
|
||||
len: 0,
|
||||
_pd: PhantomData,
|
||||
}
|
||||
}
|
||||
pub fn update(&mut self, device: &Device, queue: &Queue, data: &[T]) {
|
||||
if self.len != data.len() {
|
||||
self.len = data.len();
|
||||
self.buffer =
|
||||
Self::init_buf(device, std::mem::size_of_val(data), self.usage, self.label);
|
||||
}
|
||||
queue.write_buffer(&self.buffer, 0, bytemuck::cast_slice(data));
|
||||
}
|
||||
fn init_buf(device: &Device, size: usize, usage: BufferUsages, label: &'static str) -> Buffer {
|
||||
let mut size = size as u64;
|
||||
if usage.contains(BufferUsages::STORAGE) {
|
||||
size = size.max(std::mem::size_of::<T>() as u64);
|
||||
}
|
||||
device.create_buffer(&BufferDescriptor {
|
||||
label: Some(label),
|
||||
size,
|
||||
mapped_at_creation: false,
|
||||
usage,
|
||||
})
|
||||
}
|
||||
#[allow(clippy::len_without_is_empty)]
|
||||
pub fn len(&self) -> usize {
|
||||
self.len
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
use crate::prelude::*;
|
||||
use iris_macro::widget_trait;
|
||||
|
||||
// TODO: naming in here is a bit weird like eventable
|
||||
#[macro_export]
|
||||
macro_rules! event_ctx {
|
||||
($ty: ty) => {
|
||||
mod local_event_trait {
|
||||
use super::*;
|
||||
use std::marker::Sized;
|
||||
#[allow(unused_imports)]
|
||||
use $crate::prelude::*;
|
||||
|
||||
#[allow(unused)]
|
||||
pub trait EventableCtx<W: ?Sized, 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)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub trait EventableCtxUi<W: ?Sized, Tag, Ctx: 'static>
|
||||
where
|
||||
WidgetRef<W>: EventableCtx<W, Tag, Ctx>,
|
||||
{
|
||||
fn on<E: Event>(
|
||||
&mut self,
|
||||
widget: &WidgetRef<W>,
|
||||
event: E,
|
||||
f: impl WidgetEventFn<Ctx, E::Data, W>,
|
||||
);
|
||||
}
|
||||
|
||||
impl<W: ?Sized + 'static, Tag> EventableCtxUi<W, Tag, $ty> for Ui
|
||||
where
|
||||
WidgetRef<W>: EventableCtx<W, Tag, $ty>,
|
||||
{
|
||||
fn on<E: Event>(
|
||||
&mut self,
|
||||
widget: &WidgetRef<W>,
|
||||
event: E,
|
||||
f: impl WidgetEventFn<$ty, E::Data, W>,
|
||||
) {
|
||||
self.register_widget_event(&widget, event, f);
|
||||
}
|
||||
}
|
||||
}
|
||||
use local_event_trait::*;
|
||||
};
|
||||
}
|
||||
pub use event_ctx;
|
||||
|
||||
pub mod eventable {
|
||||
use super::*;
|
||||
|
||||
widget_trait!(
|
||||
pub Eventable;
|
||||
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);
|
||||
ui.register_widget_event(&id, event, f);
|
||||
id
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
use std::ops::Deref;
|
||||
|
||||
use crate::util::{Id, IdNum, IdTracker};
|
||||
|
||||
pub struct Arena<T, I> {
|
||||
data: Vec<T>,
|
||||
tracker: IdTracker<I>,
|
||||
}
|
||||
|
||||
impl<T, I: IdNum> Arena<T, I> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
data: Vec::new(),
|
||||
tracker: IdTracker::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push(&mut self, value: T) -> Id<I> {
|
||||
let id = self.tracker.next();
|
||||
let i = id.idx();
|
||||
if i == self.data.len() {
|
||||
self.data.push(value);
|
||||
} else {
|
||||
self.data[i] = value;
|
||||
}
|
||||
id
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, id: Id<I>) -> T
|
||||
where
|
||||
T: Copy,
|
||||
{
|
||||
let i = id.idx();
|
||||
self.tracker.free(id);
|
||||
self.data[i]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, I: IdNum> Default for Arena<T, I> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TrackedArena<T, I> {
|
||||
inner: Arena<T, I>,
|
||||
refs: Vec<u32>,
|
||||
pub changed: bool,
|
||||
}
|
||||
|
||||
impl<T, I: IdNum> TrackedArena<T, I> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
inner: Arena::default(),
|
||||
refs: Vec::new(),
|
||||
changed: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push(&mut self, value: T) -> Id<I> {
|
||||
self.changed = true;
|
||||
let id = self.inner.push(value);
|
||||
let i = id.idx();
|
||||
if i == self.refs.len() {
|
||||
self.refs.push(0);
|
||||
}
|
||||
id
|
||||
}
|
||||
|
||||
pub fn push_ref(&mut self, i: Id<I>) {
|
||||
self.refs[i.idx()] += 1;
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, id: Id<I>) -> T
|
||||
where
|
||||
T: Copy,
|
||||
{
|
||||
let i = id.idx();
|
||||
self.refs[i] -= 1;
|
||||
if self.refs[i] == 0 {
|
||||
self.changed = true;
|
||||
self.inner.remove(id)
|
||||
} else {
|
||||
self[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, I: IdNum> Default for TrackedArena<T, I> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, I> Deref for TrackedArena<T, I> {
|
||||
type Target = Vec<T>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, I> Deref for Arena<T, I> {
|
||||
type Target = Vec<T>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
pub struct MutDetect<T> {
|
||||
inner: T,
|
||||
pub changed: bool,
|
||||
}
|
||||
|
||||
impl<T> Deref for MutDetect<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for MutDetect<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.changed = true;
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<T> for MutDetect<T> {
|
||||
fn from(inner: T) -> Self {
|
||||
MutDetect {
|
||||
inner,
|
||||
changed: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
use std::{
|
||||
cell::{Ref, RefCell, RefMut},
|
||||
marker::Unsize,
|
||||
ops::CoerceUnsized,
|
||||
rc::{Rc, Weak},
|
||||
};
|
||||
|
||||
pub struct Handle<T: ?Sized>(Rc<RefCell<T>>);
|
||||
pub struct WeakHandle<T: ?Sized>(Weak<RefCell<T>>);
|
||||
|
||||
impl<T: ?Sized> Handle<T> {
|
||||
pub fn get(&self) -> Ref<T> {
|
||||
self.0.borrow()
|
||||
}
|
||||
|
||||
pub fn get_mut(&self) -> RefMut<'_, T> {
|
||||
self.0.borrow_mut()
|
||||
}
|
||||
|
||||
pub fn refs(&self) -> usize {
|
||||
Rc::strong_count(&self.0)
|
||||
}
|
||||
|
||||
pub fn weak(&self) -> WeakHandle<T> {
|
||||
WeakHandle(Rc::downgrade(&self.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> WeakHandle<T> {
|
||||
pub fn strong(&self) -> Option<Handle<T>> {
|
||||
Some(Handle(self.0.upgrade()?))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Clone for Handle<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self(self.0.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Clone for WeakHandle<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self(self.0.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + Default> Default for Handle<T> {
|
||||
fn default() -> Self {
|
||||
Self(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<T> for Handle<T> {
|
||||
fn from(value: T) -> Self {
|
||||
Self(Rc::new(RefCell::new(value)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<Handle<U>> for Handle<T> {}
|
||||
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<WeakHandle<U>> for WeakHandle<T> {}
|
||||
@@ -1,87 +0,0 @@
|
||||
#[repr(C)]
|
||||
#[derive(Eq, Hash, PartialEq, Debug, Clone, Copy, bytemuck::Zeroable)]
|
||||
pub struct Id<I = u64>(I);
|
||||
|
||||
unsafe impl<I: Copy + bytemuck::Zeroable + 'static> bytemuck::Pod for Id<I> {}
|
||||
|
||||
pub struct IdTracker<I = u64> {
|
||||
free: Vec<Id<I>>,
|
||||
cur: Id<I>,
|
||||
}
|
||||
|
||||
impl<I: IdNum> IdTracker<I> {
|
||||
#[allow(clippy::should_implement_trait)]
|
||||
pub fn next(&mut self) -> Id<I> {
|
||||
if let Some(id) = self.free.pop() {
|
||||
return id;
|
||||
}
|
||||
let next = self.cur.next();
|
||||
std::mem::replace(&mut self.cur, next)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn free(&mut self, id: Id<I>) {
|
||||
self.free.push(id);
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: IdNum> Id<I> {
|
||||
#[allow(dead_code)]
|
||||
/// for debug purposes; should this be exposed?
|
||||
/// generally you want to use labels with widgets
|
||||
pub(crate) fn raw(id: I) -> Self {
|
||||
Self(id)
|
||||
}
|
||||
pub fn idx(&self) -> usize {
|
||||
self.0.idx()
|
||||
}
|
||||
pub fn next(&self) -> Id<I> {
|
||||
Self(self.0.next())
|
||||
}
|
||||
pub const fn preset(value: I) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: IdNum> Default for IdTracker<I> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
free: Vec::new(),
|
||||
cur: Id(I::first()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait IdNum {
|
||||
fn first() -> Self;
|
||||
fn next(&self) -> Self;
|
||||
fn idx(&self) -> usize;
|
||||
}
|
||||
|
||||
impl IdNum for u64 {
|
||||
fn first() -> Self {
|
||||
0
|
||||
}
|
||||
|
||||
fn next(&self) -> Self {
|
||||
self + 1
|
||||
}
|
||||
|
||||
fn idx(&self) -> usize {
|
||||
*self as usize
|
||||
}
|
||||
}
|
||||
|
||||
impl IdNum for u32 {
|
||||
fn first() -> Self {
|
||||
0
|
||||
}
|
||||
|
||||
fn next(&self) -> Self {
|
||||
self + 1
|
||||
}
|
||||
|
||||
fn idx(&self) -> usize {
|
||||
*self as usize
|
||||
}
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
use std::ops::*;
|
||||
|
||||
pub const trait LerpUtil {
|
||||
fn lerp(self, from: Self, to: Self) -> Self;
|
||||
fn lerp_inv(self, from: Self, to: Self) -> Self;
|
||||
}
|
||||
|
||||
pub const trait DivOr {
|
||||
fn div_or(self, rhs: Self, other: Self) -> Self;
|
||||
}
|
||||
|
||||
impl const DivOr for f32 {
|
||||
fn div_or(self, rhs: Self, other: Self) -> Self {
|
||||
let res = self / rhs;
|
||||
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
|
||||
LerpUtil for T
|
||||
{
|
||||
/// linear interpolation
|
||||
/// from * (1.0 - self) + to * self
|
||||
fn lerp(self, from: Self, to: Self) -> Self {
|
||||
from + (to - from) * self
|
||||
}
|
||||
/// inverse of lerp
|
||||
fn lerp_inv(self, from: Self, to: Self) -> Self {
|
||||
(self - from).div_or(to - from, from)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_op {
|
||||
($T:ident $op:ident $fn:ident $opa:ident $fna:ident; $($field:ident)*) => {
|
||||
#[allow(non_snake_case)]
|
||||
mod ${concat($T, _op_, $fn, _impl)} {
|
||||
use super::*;
|
||||
#[allow(unused_imports)]
|
||||
use std::ops::*;
|
||||
impl const $op for $T {
|
||||
type Output = Self;
|
||||
|
||||
fn $fn(self, rhs: Self) -> Self::Output {
|
||||
Self {
|
||||
$($field: self.$field.$fn(rhs.$field),)*
|
||||
}
|
||||
}
|
||||
}
|
||||
impl const $opa for $T {
|
||||
fn $fna(&mut self, rhs: Self) {
|
||||
*self = self.$fn(rhs);
|
||||
}
|
||||
}
|
||||
impl const $op<f32> for $T {
|
||||
type Output = Self;
|
||||
|
||||
fn $fn(self, rhs: f32) -> Self::Output {
|
||||
Self {
|
||||
$($field: self.$field.$fn(rhs),)*
|
||||
}
|
||||
}
|
||||
}
|
||||
impl const $op<$T> for f32 {
|
||||
type Output = $T;
|
||||
|
||||
fn $fn(self, rhs: $T) -> Self::Output {
|
||||
$T {
|
||||
$($field: self.$fn(rhs.$field),)*
|
||||
}
|
||||
}
|
||||
}
|
||||
impl const $opa<f32> for $T {
|
||||
fn $fna(&mut self, rhs: f32) {
|
||||
*self = self.$fn(rhs);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
($T:ident $op:ident $fn:ident; $($field:ident)*) => {
|
||||
impl_op!($T $op $fn ${concat($op,Assign)} ${concat($fn,_assign)}; $($field)*);
|
||||
};
|
||||
(impl $op:ident for $T:ident: $fn:ident $($field:ident)*) => {
|
||||
impl_op!($T $op $fn ${concat($op,Assign)} ${concat($fn,_assign)}; $($field)*);
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) use impl_op;
|
||||
@@ -1,18 +0,0 @@
|
||||
mod arena;
|
||||
mod change;
|
||||
mod id;
|
||||
mod math;
|
||||
mod refcount;
|
||||
mod vec2;
|
||||
mod handle;
|
||||
|
||||
pub(crate) use arena::*;
|
||||
pub use change::*;
|
||||
pub(crate) use id::*;
|
||||
pub(crate) use math::*;
|
||||
pub(crate) use refcount::*;
|
||||
pub use vec2::*;
|
||||
pub use handle::*;
|
||||
|
||||
pub type HashMap<K, V> = fxhash::FxHashMap<K, V>;
|
||||
pub type HashSet<K> = fxhash::FxHashSet<K>;
|
||||
@@ -1,32 +0,0 @@
|
||||
use std::sync::{
|
||||
Arc,
|
||||
atomic::{AtomicU32, Ordering},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RefCounter(Arc<AtomicU32>);
|
||||
|
||||
impl RefCounter {
|
||||
pub fn new() -> Self {
|
||||
Self(Arc::new(0.into()))
|
||||
}
|
||||
#[allow(unused)]
|
||||
pub fn refs(&self) -> u32 {
|
||||
self.0.load(Ordering::Acquire)
|
||||
}
|
||||
pub fn drop(&mut self) -> bool {
|
||||
let refs = self.0.fetch_sub(1, Ordering::Release);
|
||||
refs == 0
|
||||
}
|
||||
#[allow(unused)]
|
||||
pub fn quiet_clone(&self) -> Self {
|
||||
Self(self.0.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for RefCounter {
|
||||
fn clone(&self) -> Self {
|
||||
self.0.fetch_add(1, Ordering::Release);
|
||||
Self(self.0.clone())
|
||||
}
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
use crate::util::{DivOr, impl_op};
|
||||
use std::{hash::Hash, ops::*};
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, PartialEq, Default, bytemuck::Pod, bytemuck::Zeroable)]
|
||||
pub struct Vec2 {
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
}
|
||||
|
||||
impl Eq for Vec2 {}
|
||||
|
||||
impl Hash for Vec2 {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
state.write_u32(self.x.to_bits());
|
||||
state.write_u32(self.y.to_bits());
|
||||
}
|
||||
}
|
||||
|
||||
impl Vec2 {
|
||||
pub const ZERO: Self = Self::new(0.0, 0.0);
|
||||
pub const ONE: Self = Self::new(1.0, 1.0);
|
||||
|
||||
pub const fn new(x: f32, y: f32) -> Self {
|
||||
Self { x, y }
|
||||
}
|
||||
|
||||
pub const fn round(self) -> Self {
|
||||
Self {
|
||||
x: self.x.round(),
|
||||
y: self.y.round(),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn floor(self) -> Self {
|
||||
Self {
|
||||
x: self.x.floor(),
|
||||
y: self.y.floor(),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn ceil(self) -> Self {
|
||||
Self {
|
||||
x: self.x.ceil(),
|
||||
y: self.y.ceil(),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn tuple(&self) -> (f32, f32) {
|
||||
(self.x, self.y)
|
||||
}
|
||||
|
||||
pub const fn with_x(mut self, x: f32) -> Self {
|
||||
self.x = x;
|
||||
self
|
||||
}
|
||||
|
||||
pub const fn with_y(mut self, y: f32) -> Self {
|
||||
self.y = y;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
impl_op!(Vec2 Mul mul; x y);
|
||||
impl_op!(Vec2 Div div; x y);
|
||||
|
||||
impl const DivOr for Vec2 {
|
||||
fn div_or(self, rhs: Self, other: Self) -> Self {
|
||||
Self {
|
||||
x: self.x.div_or(rhs.x, other.x),
|
||||
y: self.y.div_or(rhs.y, other.y),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for Vec2 {
|
||||
type Output = Self;
|
||||
|
||||
fn neg(mut self) -> Self::Output {
|
||||
self.x = -self.x;
|
||||
self.y = -self.y;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Vec2 {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "({}, {})", self.x, self.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Vec2 {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "({}, {})", self.x, self.y)
|
||||
}
|
||||
}
|
||||
File renamed without changes.
@@ -1,5 +1,5 @@
|
||||
use crate::prelude::*;
|
||||
use image::DynamicImage;
|
||||
use crate::prelude::*;
|
||||
|
||||
pub struct Image {
|
||||
handle: TextureHandle,
|
||||
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.
@@ -1,4 +1,5 @@
|
||||
use crate::prelude::*;
|
||||
use crate::util::Id;
|
||||
|
||||
use std::{
|
||||
ops::{BitOr, Deref, DerefMut},
|
||||
@@ -7,7 +8,7 @@ use std::{
|
||||
|
||||
use crate::{
|
||||
layout::{UiModule, UiRegion, Vec2},
|
||||
util::{HashMap, Id},
|
||||
util::HashMap,
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
@@ -166,8 +167,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,
|
||||
@@ -179,8 +184,8 @@ impl Ui {
|
||||
|
||||
impl<Ctx: 'static> CursorModule<Ctx> {
|
||||
pub fn run(ui: &mut Ui, ctx: &mut Ctx, cursor: &CursorState, window_size: Vec2) {
|
||||
let layers = std::mem::take(&mut ui.data.layers);
|
||||
let mut module = std::mem::take(ui.data.modules.get_mut::<Self>());
|
||||
let layers = std::mem::take(&mut ui.data_mut().layers);
|
||||
let mut module = std::mem::take(ui.data_mut().modules.get_mut::<Self>());
|
||||
|
||||
for i in layers.indices().rev() {
|
||||
let Some(list) = module.active.get_mut(&i) else {
|
||||
@@ -218,10 +223,10 @@ impl<Ctx: 'static> CursorModule<Ctx> {
|
||||
}
|
||||
}
|
||||
|
||||
let ui_mod = ui.data.modules.get_mut::<Self>();
|
||||
let ui_mod = ui.data_mut().modules.get_mut::<Self>();
|
||||
std::mem::swap(ui_mod, &mut module);
|
||||
ui_mod.merge(module);
|
||||
ui.data.layers = layers;
|
||||
ui.data_mut().layers = layers;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ impl TextBuilderOutput for TextOutput {
|
||||
builder.attrs.line_height,
|
||||
));
|
||||
let hint = builder.hint.get(ui);
|
||||
let font_system = &mut ui.data.text.get_mut().font_system;
|
||||
let font_system = &mut ui.data().text.get_mut().font_system;
|
||||
buf.set_text(font_system, &builder.content, &Attrs::new(), SHAPING, None);
|
||||
let mut text = Text {
|
||||
content: builder.content.into(),
|
||||
@@ -101,9 +101,9 @@ impl TextBuilderOutput for TextEditOutput {
|
||||
let mut text = TextEdit::new(
|
||||
TextView::new(buf, builder.attrs, builder.hint.get(ui)),
|
||||
builder.output.single_line,
|
||||
ui.data.text.clone(),
|
||||
ui.data().text.clone(),
|
||||
);
|
||||
let font_system = &mut ui.data.text.get_mut().font_system;
|
||||
let font_system = &mut ui.data().text.get_mut().font_system;
|
||||
text.buf
|
||||
.set_text(font_system, &builder.content, &Attrs::new(), SHAPING, None);
|
||||
builder.attrs.apply(font_system, &mut text.buf, None);
|
||||
File renamed without changes.
File renamed without changes.
@@ -1,39 +1,22 @@
|
||||
use super::*;
|
||||
use crate::prelude::*;
|
||||
use iris_macro::widget_trait;
|
||||
|
||||
// these methods should "not require any context" (require unit) because they're in core
|
||||
event_ctx!(());
|
||||
|
||||
pub trait CoreWidget<W: ?std::marker::Sized, Tag> {
|
||||
fn pad(self, padding: impl Into<Padding>) -> impl WidgetFn<Pad>;
|
||||
fn align(self, align: impl Into<Align>) -> impl WidgetFn<Aligned>;
|
||||
fn center(self) -> impl WidgetFn<Aligned>;
|
||||
fn label(self, label: impl Into<String>) -> impl WidgetIdFn<W>;
|
||||
fn sized(self, size: impl Into<Size>) -> impl WidgetFn<Sized>;
|
||||
fn width(self, len: impl Into<Len>) -> impl WidgetFn<Sized>;
|
||||
fn height(self, len: impl Into<Len>) -> impl WidgetFn<Sized>;
|
||||
fn max_width(self, width: impl Into<Len>) -> impl WidgetFn<MaxSize>;
|
||||
fn max_height(self, height: impl Into<Len>) -> impl WidgetFn<MaxSize>;
|
||||
fn offset(self, amt: impl Into<UiVec2>) -> impl WidgetFn<Offset>;
|
||||
fn scroll(self) -> impl WidgetIdFn<Scroll>;
|
||||
fn masked(self) -> impl WidgetFn<Masked>;
|
||||
fn background<T>(self, w: impl WidgetLike<T>) -> impl WidgetFn<Stack>;
|
||||
fn foreground<T>(self, w: impl WidgetLike<T>) -> impl WidgetFn<Stack>;
|
||||
fn layer_offset(self, offset: usize) -> impl WidgetFn<LayerOffset>;
|
||||
fn to_any(self) -> impl WidgetRet;
|
||||
}
|
||||
widget_trait!(
|
||||
pub CoreWidget;
|
||||
|
||||
impl<W: WidgetLike<Tag>, Tag> CoreWidget<W::Widget, Tag> for W {
|
||||
fn pad(self, padding: impl Into<Padding>) -> impl WidgetFn<Pad> {
|
||||
|ui| Pad {
|
||||
padding: padding.into(),
|
||||
inner: self.add(ui).any(),
|
||||
inner: self.add(ui),
|
||||
}
|
||||
}
|
||||
|
||||
fn align(self, align: impl Into<Align>) -> impl WidgetFn<Aligned> {
|
||||
move |ui| Aligned {
|
||||
inner: self.add(ui).any(),
|
||||
inner: self.add(ui),
|
||||
align: align.into(),
|
||||
}
|
||||
}
|
||||
@@ -42,7 +25,7 @@ impl<W: WidgetLike<Tag>, Tag> CoreWidget<W::Widget, Tag> for W {
|
||||
self.align(Align::CENTER)
|
||||
}
|
||||
|
||||
fn label(self, label: impl Into<String>) -> impl WidgetIdFn<W::Widget> {
|
||||
fn label(self, label: impl Into<String>) -> impl WidgetIdFn<WL::Widget> {
|
||||
|ui| {
|
||||
let id = self.add(ui);
|
||||
id.set_label(label);
|
||||
@@ -53,7 +36,7 @@ impl<W: WidgetLike<Tag>, Tag> CoreWidget<W::Widget, Tag> for W {
|
||||
fn sized(self, size: impl Into<Size>) -> impl WidgetFn<Sized> {
|
||||
let size = size.into();
|
||||
move |ui| Sized {
|
||||
inner: self.add(ui).any(),
|
||||
inner: self.add(ui),
|
||||
x: Some(size.x),
|
||||
y: Some(size.y),
|
||||
}
|
||||
@@ -62,7 +45,7 @@ impl<W: WidgetLike<Tag>, Tag> CoreWidget<W::Widget, Tag> for W {
|
||||
fn max_width(self, len: impl Into<Len>) -> impl WidgetFn<MaxSize> {
|
||||
let len = len.into();
|
||||
move |ui| MaxSize {
|
||||
inner: self.add(ui).any(),
|
||||
inner: self.add(ui),
|
||||
x: Some(len),
|
||||
y: None,
|
||||
}
|
||||
@@ -71,7 +54,7 @@ impl<W: WidgetLike<Tag>, Tag> CoreWidget<W::Widget, Tag> for W {
|
||||
fn max_height(self, len: impl Into<Len>) -> impl WidgetFn<MaxSize> {
|
||||
let len = len.into();
|
||||
move |ui| MaxSize {
|
||||
inner: self.add(ui).any(),
|
||||
inner: self.add(ui),
|
||||
x: None,
|
||||
y: Some(len),
|
||||
}
|
||||
@@ -80,7 +63,7 @@ impl<W: WidgetLike<Tag>, Tag> CoreWidget<W::Widget, Tag> for W {
|
||||
fn width(self, len: impl Into<Len>) -> impl WidgetFn<Sized> {
|
||||
let len = len.into();
|
||||
move |ui| Sized {
|
||||
inner: self.add(ui).any(),
|
||||
inner: self.add(ui),
|
||||
x: Some(len),
|
||||
y: None,
|
||||
}
|
||||
@@ -89,7 +72,7 @@ impl<W: WidgetLike<Tag>, Tag> CoreWidget<W::Widget, Tag> for W {
|
||||
fn height(self, len: impl Into<Len>) -> impl WidgetFn<Sized> {
|
||||
let len = len.into();
|
||||
move |ui| Sized {
|
||||
inner: self.add(ui).any(),
|
||||
inner: self.add(ui),
|
||||
x: None,
|
||||
y: Some(len),
|
||||
}
|
||||
@@ -97,14 +80,14 @@ impl<W: WidgetLike<Tag>, Tag> CoreWidget<W::Widget, Tag> for W {
|
||||
|
||||
fn offset(self, amt: impl Into<UiVec2>) -> impl WidgetFn<Offset> {
|
||||
move |ui| Offset {
|
||||
inner: self.add(ui).any(),
|
||||
inner: self.add(ui),
|
||||
amt: amt.into(),
|
||||
}
|
||||
}
|
||||
|
||||
fn scroll(self) -> impl WidgetIdFn<Scroll> {
|
||||
move |ui| {
|
||||
Scroll::new(self.add(ui).any(), Axis::Y)
|
||||
Scroll::new(self.add(ui), Axis::Y)
|
||||
.on(CursorSense::Scroll, |ctx| {
|
||||
let s = &mut *ctx.widget.get_mut();
|
||||
s.scroll(ctx.data.scroll_delta.y * 50.0);
|
||||
@@ -115,35 +98,39 @@ impl<W: WidgetLike<Tag>, Tag> CoreWidget<W::Widget, Tag> for W {
|
||||
|
||||
fn masked(self) -> impl WidgetFn<Masked> {
|
||||
move |ui| Masked {
|
||||
inner: self.add(ui).any(),
|
||||
inner: self.add(ui),
|
||||
}
|
||||
}
|
||||
|
||||
fn background<T>(self, w: impl WidgetLike<T>) -> impl WidgetFn<Stack> {
|
||||
fn background<T,>(self, w: impl WidgetLike<T>) -> impl WidgetFn<Stack> {
|
||||
move |ui| Stack {
|
||||
children: vec![w.add(ui).any(), self.add(ui).any()],
|
||||
children: vec![w.add(ui), self.add(ui)],
|
||||
size: StackSize::Child(1),
|
||||
}
|
||||
}
|
||||
|
||||
fn foreground<T>(self, w: impl WidgetLike<T>) -> impl WidgetFn<Stack> {
|
||||
fn foreground<T,>(self, w: impl WidgetLike<T>) -> impl WidgetFn<Stack> {
|
||||
move |ui| Stack {
|
||||
children: vec![self.add(ui).any(), w.add(ui).any()],
|
||||
children: vec![self.add(ui), w.add(ui)],
|
||||
size: StackSize::Child(0),
|
||||
}
|
||||
}
|
||||
|
||||
fn layer_offset(self, offset: usize) -> impl WidgetFn<LayerOffset> {
|
||||
move |ui| LayerOffset {
|
||||
inner: self.add(ui).any(),
|
||||
inner: self.add(ui),
|
||||
offset,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_any(self) -> impl WidgetRet {
|
||||
|ui| self.add(ui).any()
|
||||
|ui| self.add(ui)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_ptr(self, ptr: &WidgetRef<WidgetPtr>, ui: &mut Ui) {
|
||||
ptr.get_mut().inner = Some(self.add(ui));
|
||||
}
|
||||
);
|
||||
|
||||
pub trait CoreWidgetArr<const LEN: usize, Wa: WidgetArrLike<LEN, Tag>, Tag> {
|
||||
fn span(self, dir: Dir) -> SpanBuilder<LEN, Wa, Tag>;
|
||||
Reference in new issue
Block a user