Compare commits
7 Commits
tuple_atte
...
fa930180c1
| Author | SHA1 | Date | |
|---|---|---|---|
| fa930180c1 | |||
| 95a07786bb | |||
| 132113f09e | |||
| f2cbf90d1d | |||
| 848347e6b3 | |||
| 577d62b1da | |||
| b6e43c157b |
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "gui"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
||||
45
src/base/frame.rs
Normal file
45
src/base/frame.rs
Normal file
@@ -0,0 +1,45 @@
|
||||
use crate::{Painter, UINum, UIRegion, Widget, WidgetId};
|
||||
|
||||
pub struct Regioned {
|
||||
pub region: UIRegion,
|
||||
pub inner: WidgetId,
|
||||
}
|
||||
|
||||
impl Widget for Regioned {
|
||||
fn draw(&self, painter: &mut Painter) {
|
||||
painter.region.select(&self.region);
|
||||
painter.draw(&self.inner);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Padding {
|
||||
left: f32,
|
||||
right: f32,
|
||||
top: f32,
|
||||
bottom: f32,
|
||||
}
|
||||
|
||||
impl Padding {
|
||||
pub fn uniform(amt: f32) -> Self {
|
||||
Self {
|
||||
left: amt,
|
||||
right: amt,
|
||||
top: amt,
|
||||
bottom: amt,
|
||||
}
|
||||
}
|
||||
pub fn region(&self) -> UIRegion {
|
||||
let mut region = UIRegion::full();
|
||||
region.top_left.offset.x += self.left;
|
||||
region.top_left.offset.y += self.top;
|
||||
region.bot_right.offset.x -= self.right;
|
||||
region.bot_right.offset.y -= self.bottom;
|
||||
region
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: UINum> From<T> for Padding {
|
||||
fn from(amt: T) -> Self {
|
||||
Self::uniform(amt.to_f32())
|
||||
}
|
||||
}
|
||||
174
src/base/mod.rs
174
src/base/mod.rs
@@ -1,165 +1,11 @@
|
||||
use std::ops::Range;
|
||||
mod frame;
|
||||
mod num;
|
||||
mod rect;
|
||||
mod span;
|
||||
mod trait_fns;
|
||||
|
||||
use crate::{
|
||||
primitive::{Axis, Painter, RoundedRectData, UIRegion},
|
||||
UIColor, Widget, WidgetArrLike, WidgetFn, WidgetId, WidgetLike,
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct RoundedRect {
|
||||
pub color: UIColor,
|
||||
pub radius: f32,
|
||||
pub thickness: f32,
|
||||
pub inner_radius: f32,
|
||||
}
|
||||
|
||||
impl RoundedRect {
|
||||
pub fn color(mut self, color: UIColor) -> Self {
|
||||
self.color = color;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for RoundedRect {
|
||||
fn draw(&self, painter: &mut Painter) {
|
||||
painter.write(RoundedRectData {
|
||||
color: self.color,
|
||||
radius: self.radius,
|
||||
thickness: self.thickness,
|
||||
inner_radius: self.inner_radius,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Span {
|
||||
pub elements: Vec<(Range<f32>, WidgetId)>,
|
||||
pub axis: Axis,
|
||||
}
|
||||
|
||||
impl Widget for Span {
|
||||
fn draw(&self, painter: &mut Painter) {
|
||||
for (span, child) in &self.elements {
|
||||
let mut sub_region = UIRegion::full();
|
||||
let view = sub_region.axis_mut(self.axis);
|
||||
*view.top_left.anchor = span.start;
|
||||
*view.bot_right.anchor = span.end;
|
||||
painter.draw_within(child, sub_region);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Span {
|
||||
pub fn proportioned(
|
||||
axis: Axis,
|
||||
elements: impl IntoIterator<Item = (WidgetId, impl UINum)>,
|
||||
) -> Self {
|
||||
// TODO: update
|
||||
let elements = elements
|
||||
.into_iter()
|
||||
.map(|(w, r)| (w, r.to_f32()))
|
||||
.collect::<Vec<_>>();
|
||||
let total: f32 = elements.iter().map(|(_, r)| r).sum();
|
||||
let mut start = 0.0;
|
||||
Self {
|
||||
elements: elements
|
||||
.into_iter()
|
||||
.map(|(e, r)| {
|
||||
let end = start + r / total;
|
||||
let res = (start..end, e);
|
||||
start = end;
|
||||
res
|
||||
})
|
||||
.collect(),
|
||||
axis,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Regioned {
|
||||
region: UIRegion,
|
||||
inner: WidgetId,
|
||||
}
|
||||
|
||||
impl Widget for Regioned {
|
||||
fn draw(&self, painter: &mut Painter) {
|
||||
painter.region.select(&self.region);
|
||||
painter.draw(&self.inner);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Padding {
|
||||
left: f32,
|
||||
right: f32,
|
||||
top: f32,
|
||||
bottom: f32,
|
||||
}
|
||||
|
||||
impl Padding {
|
||||
pub fn uniform(amt: f32) -> Self {
|
||||
Self {
|
||||
left: amt,
|
||||
right: amt,
|
||||
top: amt,
|
||||
bottom: amt,
|
||||
}
|
||||
}
|
||||
pub fn region(&self) -> UIRegion {
|
||||
let mut region = UIRegion::full();
|
||||
region.top_left.offset.x += self.left;
|
||||
region.top_left.offset.y += self.top;
|
||||
region.bot_right.offset.x -= self.right;
|
||||
region.bot_right.offset.y -= self.bottom;
|
||||
region
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: UINum> From<T> for Padding {
|
||||
fn from(amt: T) -> Self {
|
||||
Self::uniform(amt.to_f32())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait WidgetUtil<W> {
|
||||
fn pad(self, padding: impl Into<Padding>) -> impl WidgetLike<Widget = Regioned>;
|
||||
}
|
||||
|
||||
impl<W: WidgetLike> WidgetUtil<W::Widget> for W {
|
||||
fn pad(self, padding: impl Into<Padding>) -> impl WidgetLike<Widget = Regioned> {
|
||||
WidgetFn(|ui| Regioned {
|
||||
region: padding.into().region(),
|
||||
inner: self.id(ui).erase_type(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub trait WidgetArrUtil<Wa: WidgetArrLike> {
|
||||
fn span(self, axis: Axis, ratios: [impl UINum; Wa::LEN]) -> impl WidgetLike<Widget = Span>;
|
||||
}
|
||||
|
||||
impl<Wa: WidgetArrLike> WidgetArrUtil<Wa> for Wa {
|
||||
fn span(self, axis: Axis, ratios: [impl UINum; Wa::LEN]) -> impl WidgetLike<Widget = Span> {
|
||||
WidgetFn(move |ui| Span::proportioned(axis, self.ui(ui).ids().into_iter().zip(ratios)))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait UINum {
|
||||
fn to_f32(self) -> f32;
|
||||
}
|
||||
|
||||
impl UINum for f32 {
|
||||
fn to_f32(self) -> f32 {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl UINum for u32 {
|
||||
fn to_f32(self) -> f32 {
|
||||
self as f32
|
||||
}
|
||||
}
|
||||
|
||||
impl UINum for i32 {
|
||||
fn to_f32(self) -> f32 {
|
||||
self as f32
|
||||
}
|
||||
}
|
||||
pub use frame::*;
|
||||
pub use num::*;
|
||||
pub use rect::*;
|
||||
pub use span::*;
|
||||
pub use trait_fns::*;
|
||||
|
||||
50
src/base/num.rs
Normal file
50
src/base/num.rs
Normal file
@@ -0,0 +1,50 @@
|
||||
pub trait UINum {
|
||||
fn to_f32(self) -> f32;
|
||||
}
|
||||
|
||||
impl UINum for f32 {
|
||||
fn to_f32(self) -> f32 {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl UINum for u32 {
|
||||
fn to_f32(self) -> f32 {
|
||||
self as f32
|
||||
}
|
||||
}
|
||||
|
||||
impl UINum for i32 {
|
||||
fn to_f32(self) -> f32 {
|
||||
self as f32
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
pub enum Axis {
|
||||
X,
|
||||
Y,
|
||||
}
|
||||
|
||||
#[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,
|
||||
}
|
||||
27
src/base/rect.rs
Normal file
27
src/base/rect.rs
Normal file
@@ -0,0 +1,27 @@
|
||||
use crate::{Painter, UIColor, Widget, primitive::RoundedRectData};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Rect {
|
||||
pub color: UIColor,
|
||||
pub radius: f32,
|
||||
pub thickness: f32,
|
||||
pub inner_radius: f32,
|
||||
}
|
||||
|
||||
impl Rect {
|
||||
pub fn color(mut self, color: UIColor) -> Self {
|
||||
self.color = color;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for Rect {
|
||||
fn draw(&self, painter: &mut Painter) {
|
||||
painter.write(RoundedRectData {
|
||||
color: self.color,
|
||||
radius: self.radius,
|
||||
thickness: self.thickness,
|
||||
inner_radius: self.inner_radius,
|
||||
});
|
||||
}
|
||||
}
|
||||
89
src/base/span.rs
Normal file
89
src/base/span.rs
Normal file
@@ -0,0 +1,89 @@
|
||||
use crate::{Dir, Painter, UINum, UIRegion, UIScalar, Widget, WidgetId};
|
||||
|
||||
pub struct Span {
|
||||
pub children: Vec<(WidgetId, SpanLen)>,
|
||||
pub dir: Dir,
|
||||
}
|
||||
|
||||
impl Widget for Span {
|
||||
fn draw(&self, painter: &mut Painter) {
|
||||
let total = self.sums();
|
||||
let mut start = UIScalar::min();
|
||||
for (child, length) in &self.children {
|
||||
let mut child_region = UIRegion::full();
|
||||
let mut axis = child_region.axis_mut(self.dir.axis);
|
||||
axis.top_left.set(start);
|
||||
match *length {
|
||||
SpanLen::Fixed(offset) => {
|
||||
start.offset += offset;
|
||||
*axis.bot_right.offset = start.offset;
|
||||
*axis.bot_right.anchor = *axis.top_left.anchor;
|
||||
}
|
||||
SpanLen::Ratio(ratio) => {
|
||||
let offset = UIScalar::new(total.relative, total.fixed);
|
||||
let rel_end = UIScalar::from_anchor(ratio / total.ratio);
|
||||
start = rel_end.within(start, (UIScalar::max() + start) - offset);
|
||||
axis.bot_right.set(start);
|
||||
}
|
||||
SpanLen::Relative(rel) => {
|
||||
start.anchor += rel;
|
||||
axis.bot_right.set(start);
|
||||
}
|
||||
}
|
||||
painter.draw_within(child, child_region);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct SpanLenSums {
|
||||
pub fixed: f32,
|
||||
pub ratio: f32,
|
||||
pub relative: f32,
|
||||
}
|
||||
|
||||
impl Span {
|
||||
pub fn sums(&self) -> SpanLenSums {
|
||||
self.lengths().fold(SpanLenSums::default(), |mut s, l| {
|
||||
match l {
|
||||
SpanLen::Fixed(v) => s.fixed += v,
|
||||
SpanLen::Ratio(v) => s.ratio += v,
|
||||
SpanLen::Relative(v) => s.relative += v,
|
||||
}
|
||||
s
|
||||
})
|
||||
}
|
||||
pub fn lengths(&self) -> impl ExactSizeIterator<Item = &SpanLen> {
|
||||
self.children.iter().map(|(_, s)| s)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum SpanLen {
|
||||
/// exact (non dynamic) size
|
||||
Fixed(f32),
|
||||
/// relative to remaining free space and other ratios
|
||||
/// eg. 1 and 2 would take up 1/3 and 2/3 of the remaining space (after others)
|
||||
Ratio(f32),
|
||||
/// relative to the total space
|
||||
/// eg. 0.5 means 1/2 of the total space
|
||||
Relative(f32),
|
||||
}
|
||||
|
||||
pub fn fixed<N: UINum>(x: N) -> SpanLen {
|
||||
SpanLen::Fixed(x.to_f32())
|
||||
}
|
||||
|
||||
pub fn ratio<N: UINum>(x: N) -> SpanLen {
|
||||
SpanLen::Ratio(x.to_f32())
|
||||
}
|
||||
|
||||
pub fn rel<N: UINum>(x: N) -> SpanLen {
|
||||
SpanLen::Relative(x.to_f32())
|
||||
}
|
||||
|
||||
impl<N: UINum> From<N> for SpanLen {
|
||||
fn from(value: N) -> Self {
|
||||
Self::Ratio(value.to_f32())
|
||||
}
|
||||
}
|
||||
37
src/base/trait_fns.rs
Normal file
37
src/base/trait_fns.rs
Normal file
@@ -0,0 +1,37 @@
|
||||
use super::*;
|
||||
use crate::{UIRegion, Vec2, WidgetArrLike, WidgetFn, WidgetLike};
|
||||
|
||||
pub trait WidgetUtil {
|
||||
fn pad(self, padding: impl Into<Padding>) -> impl WidgetLike<Widget = Regioned>;
|
||||
fn center(self, size: impl Into<Vec2>) -> impl WidgetLike<Widget = Regioned>;
|
||||
}
|
||||
|
||||
impl<W: WidgetLike> WidgetUtil for W {
|
||||
fn pad(self, padding: impl Into<Padding>) -> impl WidgetLike<Widget = Regioned> {
|
||||
WidgetFn(|ui| Regioned {
|
||||
region: padding.into().region(),
|
||||
inner: self.add(ui).erase_type(),
|
||||
})
|
||||
}
|
||||
|
||||
fn center(self, size: impl Into<Vec2>) -> impl WidgetLike<Widget = Regioned> {
|
||||
WidgetFn(|ui| Regioned {
|
||||
region: UIRegion::center(size.into()),
|
||||
inner: self.add(ui).erase_type(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub trait WidgetArrUtil<const LEN: usize> {
|
||||
fn span(self, dir: Dir, lengths: [impl Into<SpanLen>; LEN]) -> impl WidgetLike<Widget = Span>;
|
||||
}
|
||||
|
||||
impl<const LEN: usize, Wa: WidgetArrLike<LEN>> WidgetArrUtil<LEN> for Wa {
|
||||
fn span(self, dir: Dir, lengths: [impl Into<SpanLen>; LEN]) -> impl WidgetLike<Widget = Span> {
|
||||
let lengths = lengths.map(Into::into);
|
||||
WidgetFn(move |ui| Span {
|
||||
dir,
|
||||
children: self.ui(ui).arr.into_iter().zip(lengths).collect(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,10 @@ impl<T: ColorNum> Color<T> {
|
||||
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 trait ColorNum {
|
||||
@@ -1,8 +1,15 @@
|
||||
mod color;
|
||||
mod painter;
|
||||
mod region;
|
||||
mod ui;
|
||||
mod vec2;
|
||||
mod widget;
|
||||
|
||||
pub use color::*;
|
||||
pub use region::*;
|
||||
pub use ui::*;
|
||||
pub use vec2::*;
|
||||
pub use widget::*;
|
||||
pub use painter::*;
|
||||
|
||||
use crate::primitive::Color;
|
||||
pub type UIColor = Color<u8>;
|
||||
|
||||
@@ -1,21 +1,7 @@
|
||||
mod color;
|
||||
mod def;
|
||||
mod format;
|
||||
mod point;
|
||||
|
||||
pub use color::*;
|
||||
pub use def::*;
|
||||
pub use format::*;
|
||||
pub use point::*;
|
||||
|
||||
use crate::{render::data::PrimitiveInstance, WidgetId, Widgets};
|
||||
use bytemuck::Pod;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Primitives {
|
||||
pub instances: Vec<PrimitiveInstance>,
|
||||
pub data: Vec<u32>,
|
||||
}
|
||||
use crate::{
|
||||
UIRegion, WidgetId, Widgets,
|
||||
primitive::{PrimitiveData, PrimitiveInstance, Primitives},
|
||||
};
|
||||
|
||||
pub struct Painter<'a> {
|
||||
nodes: &'a Widgets,
|
||||
@@ -23,11 +9,6 @@ pub struct Painter<'a> {
|
||||
pub region: UIRegion,
|
||||
}
|
||||
|
||||
/// NOTE: Self must have at least u32 alignment
|
||||
pub trait PrimitiveData: Pod {
|
||||
const DISCRIM: u32;
|
||||
}
|
||||
|
||||
impl<'a> Painter<'a> {
|
||||
pub fn new(nodes: &'a Widgets) -> Self {
|
||||
Self {
|
||||
159
src/layout/region.rs
Normal file
159
src/layout/region.rs
Normal file
@@ -0,0 +1,159 @@
|
||||
use crate::{
|
||||
Axis, Vec2,
|
||||
layout::vec2,
|
||||
util::{F32Util, impl_op},
|
||||
};
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable, Default)]
|
||||
pub struct UIPos {
|
||||
pub anchor: Vec2,
|
||||
pub offset: Vec2,
|
||||
}
|
||||
|
||||
impl UIPos {
|
||||
pub const fn anchor_offset(anchor_x: f32, anchor_y: f32, offset_x: f32, offset_y: f32) -> Self {
|
||||
Self {
|
||||
anchor: vec2(anchor_x, anchor_y),
|
||||
offset: vec2(offset_x, offset_y),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn center() -> Self {
|
||||
Self::anchor_offset(0.5, 0.5, 0.0, 0.0)
|
||||
}
|
||||
|
||||
pub const fn top_left() -> Self {
|
||||
Self::anchor_offset(0.0, 0.0, 0.0, 0.0)
|
||||
}
|
||||
|
||||
pub const fn bottom_right() -> Self {
|
||||
Self::anchor_offset(1.0, 1.0, 0.0, 0.0)
|
||||
}
|
||||
|
||||
pub const fn offset(mut self, offset: Vec2) -> Self {
|
||||
self.offset = offset;
|
||||
self
|
||||
}
|
||||
|
||||
pub const fn within(&self, region: &UIRegion) -> UIPos {
|
||||
let anchor = self
|
||||
.anchor
|
||||
.lerp(region.top_left.anchor, region.bot_right.anchor);
|
||||
let offset = self.offset;
|
||||
// + self
|
||||
// .anchor
|
||||
// .lerp(region.top_left.offset, region.bot_right.offset);
|
||||
UIPos { anchor, offset }
|
||||
}
|
||||
|
||||
pub fn axis_mut(&mut self, axis: Axis) -> UIScalarView<'_> {
|
||||
match axis {
|
||||
Axis::X => UIScalarView {
|
||||
anchor: &mut self.anchor.x,
|
||||
offset: &mut self.offset.x,
|
||||
},
|
||||
Axis::Y => UIScalarView {
|
||||
anchor: &mut self.anchor.y,
|
||||
offset: &mut self.offset.y,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct UIScalar {
|
||||
pub anchor: f32,
|
||||
pub offset: f32,
|
||||
}
|
||||
|
||||
impl_op!(UIScalar Add add; anchor offset);
|
||||
impl_op!(UIScalar Sub sub; anchor offset);
|
||||
|
||||
impl UIScalar {
|
||||
pub fn new(anchor: f32, offset: f32) -> Self {
|
||||
Self { anchor, offset }
|
||||
}
|
||||
pub fn min() -> Self {
|
||||
Self::new(0.0, 0.0)
|
||||
}
|
||||
|
||||
pub fn max() -> Self {
|
||||
Self::new(1.0, 0.0)
|
||||
}
|
||||
|
||||
pub fn from_anchor(anchor: f32) -> Self {
|
||||
Self::new(anchor, 0.0)
|
||||
}
|
||||
|
||||
pub fn offset(mut self, amt: f32) -> Self {
|
||||
self.offset += amt;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn within(&self, start: UIScalar, end: UIScalar) -> Self {
|
||||
let anchor = self.anchor.lerp(start.anchor, end.anchor);
|
||||
let offset = self.offset + self.anchor.lerp(start.offset, end.offset);
|
||||
Self { anchor, offset }
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
||||
pub struct UIRegion {
|
||||
pub top_left: UIPos,
|
||||
pub bot_right: UIPos,
|
||||
}
|
||||
|
||||
impl UIRegion {
|
||||
pub const fn full() -> Self {
|
||||
Self {
|
||||
top_left: UIPos::top_left(),
|
||||
bot_right: UIPos::bottom_right(),
|
||||
}
|
||||
}
|
||||
pub fn center(size: Vec2) -> Self {
|
||||
Self {
|
||||
top_left: UIPos::center().offset(-size / 2.0),
|
||||
bot_right: UIPos::center().offset(size / 2.0),
|
||||
}
|
||||
}
|
||||
pub fn within(&self, parent: &Self) -> Self {
|
||||
Self {
|
||||
top_left: self.top_left.within(parent),
|
||||
bot_right: self.bot_right.within(parent),
|
||||
}
|
||||
}
|
||||
pub fn select(&mut self, inner: &Self) {
|
||||
*self = inner.within(self);
|
||||
}
|
||||
pub fn axis_mut(&mut self, axis: Axis) -> UIRegionAxisView<'_> {
|
||||
UIRegionAxisView {
|
||||
top_left: self.top_left.axis_mut(axis),
|
||||
bot_right: self.bot_right.axis_mut(axis),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UIRegionAxisView<'a> {
|
||||
pub top_left: UIScalarView<'a>,
|
||||
pub bot_right: UIScalarView<'a>,
|
||||
}
|
||||
|
||||
pub struct UIScalarView<'a> {
|
||||
pub anchor: &'a mut f32,
|
||||
pub offset: &'a mut f32,
|
||||
}
|
||||
|
||||
impl UIScalarView<'_> {
|
||||
pub fn set(&mut self, scalar: UIScalar) {
|
||||
*self.anchor = scalar.anchor;
|
||||
*self.offset = scalar.offset;
|
||||
}
|
||||
pub fn get(self) -> UIScalar {
|
||||
UIScalar {
|
||||
anchor: *self.anchor,
|
||||
offset: *self.offset,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::{
|
||||
primitive::{Painter, Primitives},
|
||||
util::{IDTracker, ID},
|
||||
HashMap, Widget, WidgetId, WidgetLike, WidgetRef,
|
||||
HashMap, Painter, Widget, WidgetId, WidgetLike, WidgetRef,
|
||||
primitive::Primitives,
|
||||
util::{ID, IDTracker},
|
||||
};
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
@@ -31,19 +31,23 @@ impl From<UI> for UIBuilder {
|
||||
}
|
||||
|
||||
impl UIBuilder {
|
||||
pub fn add<W: Widget>(&mut self, w: W) -> WidgetRef<W> {
|
||||
WidgetRef::new(self.clone(), (self.push(w), ()))
|
||||
pub fn add<W: Widget>(&mut self, w: impl WidgetLike<Widget = W>) -> WidgetRef<W> {
|
||||
WidgetRef::new([w.add(self).erase_type()])
|
||||
}
|
||||
|
||||
pub fn push<W: Widget>(&mut self, w: W) -> WidgetId<W> {
|
||||
pub fn add_widget<W: Widget>(&mut self, w: W) -> WidgetRef<W> {
|
||||
WidgetRef::new([self.push(w)])
|
||||
}
|
||||
|
||||
pub fn push<W: Widget>(&mut self, w: W) -> WidgetId {
|
||||
let mut ui = self.ui.borrow_mut();
|
||||
let id = ui.ids.next();
|
||||
ui.widgets.insert(id.duplicate(), w);
|
||||
WidgetId::new(id, TypeId::of::<W>())
|
||||
}
|
||||
|
||||
pub fn finish<WL: WidgetLike>(mut self, base: WL) -> UI {
|
||||
let base = base.id(&mut self).erase_type();
|
||||
pub fn finish<W: WidgetLike>(mut self, base: W) -> UI {
|
||||
let base = base.add(&mut self).erase_type();
|
||||
let mut ui = Rc::into_inner(self.ui).unwrap().into_inner();
|
||||
ui.base = Some(base);
|
||||
ui
|
||||
|
||||
59
src/layout/vec2.rs
Normal file
59
src/layout/vec2.rs
Normal file
@@ -0,0 +1,59 @@
|
||||
use crate::util::{F32Util, impl_op};
|
||||
use std::ops::*;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Default, bytemuck::Pod, bytemuck::Zeroable)]
|
||||
pub struct Vec2 {
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
}
|
||||
|
||||
pub const fn vec2(x: f32, y: f32) -> Vec2 {
|
||||
Vec2::new(x, y)
|
||||
}
|
||||
|
||||
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 lerp(self, from: impl const Into<Self>, to: impl const Into<Self>) -> Self {
|
||||
let from = from.into();
|
||||
let to = to.into();
|
||||
Self {
|
||||
x: self.x.lerp(from.x, to.x),
|
||||
y: self.y.lerp(from.y, to.y),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl const From<f32> for Vec2 {
|
||||
fn from(v: f32) -> Self {
|
||||
Self { x: v, y: v }
|
||||
}
|
||||
}
|
||||
|
||||
// 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 Neg for Vec2 {
|
||||
type Output = Self;
|
||||
|
||||
fn neg(mut self) -> Self::Output {
|
||||
self.x = -self.x;
|
||||
self.y = -self.y;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(f32, f32)> for Vec2 {
|
||||
fn from((x, y): (f32, f32)) -> Self {
|
||||
Self { x, y }
|
||||
}
|
||||
}
|
||||
@@ -3,11 +3,7 @@ use std::{
|
||||
marker::PhantomData,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
primitive::Painter,
|
||||
util::{IntoTupleList, TupleList, ID},
|
||||
UIBuilder,
|
||||
};
|
||||
use crate::{Painter, util::ID, UIBuilder};
|
||||
|
||||
pub trait Widget: 'static + Any {
|
||||
fn draw(&self, painter: &mut Painter);
|
||||
@@ -19,8 +15,10 @@ impl<W: Widget> Widget for (W,) {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AnyWidget;
|
||||
|
||||
#[derive(Eq, Hash, PartialEq, Debug)]
|
||||
pub struct WidgetId<W = ()> {
|
||||
pub struct WidgetId<W = AnyWidget> {
|
||||
pub(super) ty: TypeId,
|
||||
pub(super) id: ID,
|
||||
_pd: PhantomData<W>,
|
||||
@@ -29,11 +27,7 @@ pub struct WidgetId<W = ()> {
|
||||
// TODO: temp
|
||||
impl<W> Clone for WidgetId<W> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
ty: self.ty,
|
||||
id: self.id.duplicate(),
|
||||
_pd: self._pd,
|
||||
}
|
||||
Self::new(self.id.duplicate(), self.ty)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +39,7 @@ impl<W> WidgetId<W> {
|
||||
_pd: PhantomData,
|
||||
}
|
||||
}
|
||||
pub fn erase_type(self) -> WidgetId<()> {
|
||||
pub fn erase_type(self) -> WidgetId<AnyWidget> {
|
||||
self.cast_type()
|
||||
}
|
||||
|
||||
@@ -60,7 +54,7 @@ impl<W> WidgetId<W> {
|
||||
|
||||
pub trait WidgetLike {
|
||||
type Widget;
|
||||
fn id(self, ui: &mut UIBuilder) -> WidgetId<Self::Widget>;
|
||||
fn add(self, ui: &mut UIBuilder) -> WidgetId<Self::Widget>;
|
||||
}
|
||||
|
||||
/// wouldn't be needed if negative trait bounds & disjoint impls existed
|
||||
@@ -68,119 +62,98 @@ pub struct WidgetFn<F: FnOnce(&mut UIBuilder) -> W, W>(pub F);
|
||||
|
||||
impl<W: Widget, F: FnOnce(&mut UIBuilder) -> W> WidgetLike for WidgetFn<F, W> {
|
||||
type Widget = W;
|
||||
fn id(self, ui: &mut UIBuilder) -> WidgetId<W> {
|
||||
fn add(self, ui: &mut UIBuilder) -> WidgetId<W> {
|
||||
let w = (self.0)(ui);
|
||||
ui.add(w).to_id()
|
||||
ui.add_widget(w).to_id()
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Widget> WidgetLike for W {
|
||||
type Widget = W;
|
||||
fn id(self, ui: &mut UIBuilder) -> WidgetId<W> {
|
||||
ui.add(self).to_id()
|
||||
fn add(self, ui: &mut UIBuilder) -> WidgetId<W> {
|
||||
ui.add_widget(self).to_id()
|
||||
}
|
||||
}
|
||||
|
||||
impl<W> WidgetLike for WidgetId<W> {
|
||||
type Widget = W;
|
||||
fn id(self, _: &mut UIBuilder) -> WidgetId<W> {
|
||||
fn add(self, _: &mut UIBuilder) -> WidgetId<W> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WidgetArr<Ws: WidgetList> {
|
||||
pub ui: UIBuilder,
|
||||
pub arr: Ws::Ids,
|
||||
}
|
||||
|
||||
impl<Ws: WidgetList> WidgetArr<Ws> {
|
||||
pub fn new(ui: UIBuilder, arr: Ws::Ids) -> Self {
|
||||
Self { ui, arr }
|
||||
}
|
||||
pub fn ids(self) -> Vec<WidgetId> {
|
||||
self.arr.all()
|
||||
impl<W> WidgetLike for WidgetArr<1, (W,)> {
|
||||
type Widget = W;
|
||||
fn add(self, _: &mut UIBuilder) -> WidgetId<W> {
|
||||
let [id] = self.arr;
|
||||
id.cast_type()
|
||||
}
|
||||
}
|
||||
|
||||
pub type WidgetRef<W> = WidgetArr<(W, ())>;
|
||||
pub struct WidgetArr<const LEN: usize, Ws> {
|
||||
pub arr: [WidgetId; LEN],
|
||||
_pd: PhantomData<Ws>,
|
||||
}
|
||||
|
||||
impl<const LEN: usize, Ws> WidgetArr<LEN, Ws> {
|
||||
pub fn new(arr: [WidgetId; LEN]) -> Self {
|
||||
Self {
|
||||
arr,
|
||||
_pd: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type WidgetRef<W> = WidgetArr<1, (W,)>;
|
||||
|
||||
impl<W> WidgetRef<W> {
|
||||
pub fn handle(&self) -> WidgetId<W> {
|
||||
self.arr.head().clone()
|
||||
let [id] = &self.arr;
|
||||
id.clone().cast_type()
|
||||
}
|
||||
pub fn to_id(self) -> WidgetId<W> {
|
||||
self.arr.into_head()
|
||||
let [id] = self.arr;
|
||||
id.cast_type()
|
||||
}
|
||||
}
|
||||
|
||||
impl<W> WidgetLike for WidgetRef<W> {
|
||||
type Widget = W;
|
||||
fn id(self, _: &mut UIBuilder) -> WidgetId<W> {
|
||||
self.arr.into_head()
|
||||
}
|
||||
pub trait WidgetArrLike<const LEN: usize> {
|
||||
type Ws;
|
||||
fn ui(self, ui: &mut UIBuilder) -> WidgetArr<LEN, Self::Ws>;
|
||||
}
|
||||
|
||||
pub trait WidgetArrLike {
|
||||
const LEN: usize;
|
||||
type Ws: WidgetList;
|
||||
fn ui(self, ui: &mut UIBuilder) -> WidgetArr<Self::Ws>;
|
||||
}
|
||||
|
||||
impl<Ws: WidgetList> WidgetArrLike for WidgetArr<Ws> {
|
||||
const LEN: usize = Ws::LEN;
|
||||
impl<const LEN: usize, Ws> WidgetArrLike<LEN> for WidgetArr<LEN, Ws> {
|
||||
type Ws = Ws;
|
||||
fn ui(self, _: &mut UIBuilder) -> WidgetArr<Ws> {
|
||||
fn ui(self, _: &mut UIBuilder) -> WidgetArr<LEN, Ws> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: WidgetLike> WidgetArrLike for (W, ())
|
||||
where
|
||||
Self: TupleList,
|
||||
{
|
||||
const LEN: usize = <Self as TupleList>::LEN;
|
||||
type Ws = (W::Widget, ());
|
||||
fn ui(self, ui: &mut UIBuilder) -> WidgetArr<(W::Widget, ())> {
|
||||
WidgetArr::new(ui.clone(), (self.0.id(ui), ()))
|
||||
}
|
||||
}
|
||||
impl<W: WidgetLike, T: WidgetArrLike> WidgetArrLike for (W, T)
|
||||
where
|
||||
Self: TupleList,
|
||||
{
|
||||
const LEN: usize = <Self as TupleList>::LEN;
|
||||
type Ws = (W::Widget, T::Ws);
|
||||
fn ui(self, ui: &mut UIBuilder) -> WidgetArr<(W::Widget, T::Ws)> {
|
||||
WidgetArr::new(ui.clone(), (self.0.id(ui), self.1.ui(ui).arr))
|
||||
}
|
||||
// I hate this language it's so bad why do I even use it
|
||||
macro_rules! impl_widget_arr {
|
||||
($n:expr;$($T:tt)*) => {
|
||||
impl<$($T: WidgetLike,)*> WidgetArrLike<$n> for ($($T,)*) {
|
||||
type Ws = ($($T::Widget,)*);
|
||||
fn ui(self, ui: &mut UIBuilder) -> WidgetArr<$n, ($($T::Widget,)*)> {
|
||||
#[allow(non_snake_case)]
|
||||
let ($($T,)*) = self;
|
||||
WidgetArr::new(
|
||||
[$($T.add(ui).cast_type(),)*],
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub trait WidgetList: TupleList {
|
||||
type Ids: IdList;
|
||||
}
|
||||
impl<H> WidgetList for (H, ()) {
|
||||
type Ids = (WidgetId<H>, ());
|
||||
}
|
||||
impl<H, T: WidgetList> WidgetList for (H, T) {
|
||||
type Ids = (WidgetId<H>, T::Ids);
|
||||
}
|
||||
|
||||
pub trait IdList: TupleList {
|
||||
fn iter(self) -> impl Iterator<Item = WidgetId>;
|
||||
fn all(self) -> Vec<WidgetId>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
self.iter().collect::<Vec<_>>()
|
||||
}
|
||||
}
|
||||
impl<H> IdList for (WidgetId<H>, ()) {
|
||||
fn iter(self) -> impl Iterator<Item = WidgetId> {
|
||||
core::iter::once(self.0.erase_type())
|
||||
}
|
||||
}
|
||||
impl<H, T: IdList> IdList for (WidgetId<H>, T) {
|
||||
fn iter(self) -> impl Iterator<Item = WidgetId> {
|
||||
core::iter::once(self.0.erase_type()).chain(self.1.iter())
|
||||
}
|
||||
}
|
||||
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);
|
||||
|
||||
@@ -3,11 +3,10 @@
|
||||
#![feature(const_trait_impl)]
|
||||
#![feature(const_from)]
|
||||
#![feature(trait_alias)]
|
||||
#![feature(generic_const_exprs)]
|
||||
|
||||
mod layout;
|
||||
mod render;
|
||||
pub mod util;
|
||||
mod util;
|
||||
mod base;
|
||||
|
||||
pub use layout::*;
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
use crate::primitive::UIRegion;
|
||||
use wgpu::VertexAttribute;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
||||
pub struct PrimitiveInstance {
|
||||
pub region: UIRegion,
|
||||
pub ptr: u32,
|
||||
}
|
||||
use crate::UIRegion;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable, Default)]
|
||||
@@ -15,6 +9,13 @@ pub struct WindowUniform {
|
||||
pub height: f32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
||||
pub struct PrimitiveInstance {
|
||||
pub region: UIRegion,
|
||||
pub ptr: u32,
|
||||
}
|
||||
|
||||
impl PrimitiveInstance {
|
||||
const ATTRIBS: [VertexAttribute; 5] = wgpu::vertex_attr_array![
|
||||
0 => Float32x2,
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
use crate::{
|
||||
render::{data::PrimitiveInstance, util::ArrBuf},
|
||||
UI,
|
||||
};
|
||||
use crate::{UI, primitive::PrimitiveInstance, render::util::ArrBuf};
|
||||
use data::WindowUniform;
|
||||
use wgpu::{
|
||||
util::{BufferInitDescriptor, DeviceExt},
|
||||
|
||||
27
src/render/primitive.rs
Normal file
27
src/render/primitive.rs
Normal file
@@ -0,0 +1,27 @@
|
||||
use crate::Color;
|
||||
|
||||
pub use super::data::PrimitiveInstance;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Primitives {
|
||||
pub instances: Vec<PrimitiveInstance>,
|
||||
pub data: Vec<u32>,
|
||||
}
|
||||
|
||||
/// NOTE: Self must have at least u32 alignment
|
||||
pub trait PrimitiveData: bytemuck::Pod {
|
||||
const DISCRIM: u32;
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
||||
pub struct RoundedRectData {
|
||||
pub color: Color<u8>,
|
||||
pub radius: f32,
|
||||
pub thickness: f32,
|
||||
pub inner_radius: f32,
|
||||
}
|
||||
|
||||
impl PrimitiveData for RoundedRectData {
|
||||
const DISCRIM: u32 = 0;
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
use crate::primitive::{Color, PrimitiveData};
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
||||
pub struct RoundedRectData {
|
||||
pub color: Color<u8>,
|
||||
pub radius: f32,
|
||||
pub thickness: f32,
|
||||
pub inner_radius: f32,
|
||||
}
|
||||
|
||||
impl PrimitiveData for RoundedRectData {
|
||||
const DISCRIM: u32 = 0;
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
use crate::primitive::{point::point, Point};
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable, Default)]
|
||||
pub struct UIPos {
|
||||
pub anchor: Point,
|
||||
pub offset: Point,
|
||||
}
|
||||
|
||||
impl UIPos {
|
||||
pub const fn anchor_offset(anchor_x: f32, anchor_y: f32, offset_x: f32, offset_y: f32) -> Self {
|
||||
Self {
|
||||
anchor: point(anchor_x, anchor_y),
|
||||
offset: point(offset_x, offset_y),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn top_left() -> Self {
|
||||
Self::anchor_offset(0.0, 0.0, 0.0, 0.0)
|
||||
}
|
||||
|
||||
pub const fn bottom_right() -> Self {
|
||||
Self::anchor_offset(1.0, 1.0, 0.0, 0.0)
|
||||
}
|
||||
|
||||
pub const fn within(&self, region: &UIRegion) -> UIPos {
|
||||
let range = region.bot_right.anchor - region.top_left.anchor;
|
||||
let region_offset = region
|
||||
.top_left
|
||||
.offset
|
||||
.lerp(region.bot_right.offset, self.anchor);
|
||||
UIPos {
|
||||
anchor: region.top_left.anchor + self.anchor * range,
|
||||
offset: self.offset + region_offset,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn axis_mut(&mut self, axis: Axis) -> UIPosAxisView<'_> {
|
||||
match axis {
|
||||
Axis::X => UIPosAxisView {
|
||||
anchor: &mut self.anchor.x,
|
||||
offset: &mut self.offset.x,
|
||||
},
|
||||
Axis::Y => UIPosAxisView {
|
||||
anchor: &mut self.anchor.y,
|
||||
offset: &mut self.offset.y,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UIPosAxisView<'a> {
|
||||
pub anchor: &'a mut f32,
|
||||
pub offset: &'a mut f32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
||||
pub struct UIRegion {
|
||||
pub top_left: UIPos,
|
||||
pub bot_right: UIPos,
|
||||
}
|
||||
|
||||
impl UIRegion {
|
||||
pub const fn full() -> Self {
|
||||
Self {
|
||||
top_left: UIPos::top_left(),
|
||||
bot_right: UIPos::bottom_right(),
|
||||
}
|
||||
}
|
||||
pub fn within(&self, parent: &Self) -> Self {
|
||||
Self {
|
||||
top_left: self.top_left.within(parent),
|
||||
bot_right: self.bot_right.within(parent),
|
||||
}
|
||||
}
|
||||
pub fn select(&mut self, inner: &Self) {
|
||||
*self = inner.within(self);
|
||||
}
|
||||
pub fn axis_mut(&mut self, axis: Axis) -> UIRegionAxisView<'_> {
|
||||
UIRegionAxisView {
|
||||
top_left: self.top_left.axis_mut(axis),
|
||||
bot_right: self.bot_right.axis_mut(axis),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UIRegionAxisView<'a> {
|
||||
pub top_left: UIPosAxisView<'a>,
|
||||
pub bot_right: UIPosAxisView<'a>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum Axis {
|
||||
X,
|
||||
Y,
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
use std::ops::*;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, PartialEq, Default, bytemuck::Pod, bytemuck::Zeroable)]
|
||||
pub struct Point {
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
}
|
||||
|
||||
pub const fn point(x: f32, y: f32) -> Point {
|
||||
Point::new(x, y)
|
||||
}
|
||||
|
||||
impl Point {
|
||||
pub const fn new(x: f32, y: f32) -> Self {
|
||||
Self { x, y }
|
||||
}
|
||||
|
||||
pub const fn lerp(self, to: Self, amt: impl const Into<Self>) -> Self {
|
||||
let amt = amt.into();
|
||||
Self {
|
||||
x: lerp(self.x, to.x, amt.x),
|
||||
y: lerp(self.y, to.y, amt.y),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const fn lerp(x: f32, y: f32, amt: f32) -> f32 {
|
||||
(1.0 - amt) * x + y * amt
|
||||
}
|
||||
|
||||
impl const From<f32> for Point {
|
||||
fn from(v: f32) -> Self {
|
||||
Self { x: v, y: v }
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_op_inner {
|
||||
($op:ident $fn:ident $opa:ident $fna:ident) => {
|
||||
impl const $op for Point {
|
||||
type Output = Self;
|
||||
|
||||
fn $fn(self, rhs: Self) -> Self::Output {
|
||||
Self {
|
||||
x: self.x.$fn(rhs.x),
|
||||
y: self.y.$fn(rhs.y),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl $opa for Point {
|
||||
fn $fna(&mut self, rhs: Self) {
|
||||
self.x.$fna(rhs.x);
|
||||
self.y.$fna(rhs.y);
|
||||
}
|
||||
}
|
||||
impl const $op<f32> for Point {
|
||||
type Output = Self;
|
||||
|
||||
fn $fn(self, rhs: f32) -> Self::Output {
|
||||
Self {
|
||||
x: self.x.$fn(rhs),
|
||||
y: self.y.$fn(rhs),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl $opa<f32> for Point {
|
||||
fn $fna(&mut self, rhs: f32) {
|
||||
self.x.$fna(rhs);
|
||||
self.y.$fna(rhs);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_op {
|
||||
($op:ident $fn:ident) => {
|
||||
impl_op_inner!($op $fn ${concat($op,Assign)} ${concat($fn,_assign)});
|
||||
};
|
||||
}
|
||||
|
||||
impl_op!(Add add);
|
||||
impl_op!(Sub sub);
|
||||
impl_op!(Mul mul);
|
||||
impl_op!(Div div);
|
||||
@@ -1,8 +1,7 @@
|
||||
use gui::util::IntoTupleList;
|
||||
use std::sync::Arc;
|
||||
|
||||
use app::App;
|
||||
use gui::{primitive::Axis, RoundedRect, UIColor, WidgetArrUtil, WidgetUtil, UI};
|
||||
use gui::{Dir, Rect, UI, UIColor, WidgetArrUtil, WidgetUtil, fixed, ratio, rel};
|
||||
use render::Renderer;
|
||||
use winit::{event::WindowEvent, event_loop::ActiveEventLoop, window::Window};
|
||||
|
||||
@@ -20,9 +19,9 @@ pub struct Client {
|
||||
impl Client {
|
||||
pub fn new(window: Arc<Window>) -> Self {
|
||||
let mut renderer = Renderer::new(window);
|
||||
let rect = RoundedRect {
|
||||
let rect = Rect {
|
||||
color: UIColor::WHITE,
|
||||
radius: 10.0,
|
||||
radius: 20.0,
|
||||
thickness: 0.0,
|
||||
inner_radius: 0.0,
|
||||
};
|
||||
@@ -34,25 +33,39 @@ impl Client {
|
||||
(
|
||||
blue,
|
||||
(
|
||||
rect.color(UIColor::RED),
|
||||
rect.color(UIColor::RED).center((100.0, 100.0)),
|
||||
(
|
||||
rect.color(UIColor::ORANGE),
|
||||
rect.color(UIColor::LIME).pad(10.0),
|
||||
)
|
||||
.into_list()
|
||||
.span(Axis::Y, [1, 1].into()),
|
||||
.span(Dir::RIGHT, [1, 1]),
|
||||
rect.color(UIColor::YELLOW),
|
||||
)
|
||||
.into_list()
|
||||
.span(Axis::X, [2, 2, 1])
|
||||
.span(Dir::RIGHT, [2, 2, 1])
|
||||
.pad(10),
|
||||
)
|
||||
.into_list()
|
||||
.span(Axis::X, [1, 3]),
|
||||
rect.color(UIColor::GREEN),
|
||||
.span(Dir::RIGHT, [1, 3]),
|
||||
(
|
||||
rect.color(UIColor::GREEN),
|
||||
rect.color(UIColor::ORANGE),
|
||||
rect.color(UIColor::CYAN),
|
||||
rect.color(UIColor::BLUE),
|
||||
rect.color(UIColor::MAGENTA),
|
||||
rect.color(UIColor::RED),
|
||||
)
|
||||
.span(
|
||||
Dir::RIGHT,
|
||||
[
|
||||
fixed(100),
|
||||
ratio(1),
|
||||
ratio(1),
|
||||
rel(0.5),
|
||||
fixed(100),
|
||||
fixed(100),
|
||||
],
|
||||
),
|
||||
)
|
||||
.into_list()
|
||||
.span(Axis::Y, [3, 1])
|
||||
.span(Dir::DOWN, [3, 1])
|
||||
.pad(10),
|
||||
);
|
||||
ui.widgets.get_mut(&handle).unwrap().color = UIColor::MAGENTA;
|
||||
|
||||
58
src/util/math.rs
Normal file
58
src/util/math.rs
Normal file
@@ -0,0 +1,58 @@
|
||||
use std::ops::*;
|
||||
|
||||
#[const_trait]
|
||||
pub trait F32Util {
|
||||
fn lerp<T: const Mul<f32, Output = T> + const Add<Output = T>>(self, from: T, to: T) -> T;
|
||||
}
|
||||
|
||||
impl const F32Util for f32 {
|
||||
fn lerp<T: const Mul<f32, Output = T> + const Add<Output = T>>(self, from: T, to: T) -> T {
|
||||
from * (1.0 - self) + to * self
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_op {
|
||||
($T:ident $op:ident $fn:ident $opa:ident $fna:ident; $($field:ident)*) => {
|
||||
mod ${concat(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 $opa for $T {
|
||||
fn $fna(&mut self, rhs: Self) {
|
||||
$(self.$field.$fna(rhs.$field);)*
|
||||
}
|
||||
}
|
||||
impl const $op<f32> for $T {
|
||||
type Output = Self;
|
||||
|
||||
fn $fn(self, rhs: f32) -> Self::Output {
|
||||
Self {
|
||||
$($field: self.$field.$fn(rhs),)*
|
||||
}
|
||||
}
|
||||
}
|
||||
impl $opa<f32> for $T {
|
||||
fn $fna(&mut self, rhs: f32) {
|
||||
$(self.$field.$fna(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,5 +1,5 @@
|
||||
mod id;
|
||||
mod tuple;
|
||||
mod math;
|
||||
|
||||
pub use id::*;
|
||||
pub use tuple::*;
|
||||
pub use math::*;
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
pub trait TupleList {
|
||||
const LEN: usize;
|
||||
type Head;
|
||||
type Tail;
|
||||
fn into_head(self) -> Self::Head;
|
||||
fn head(&self) -> &Self::Head;
|
||||
fn split(self) -> (Self::Head, Self::Tail);
|
||||
}
|
||||
impl TupleList for () {
|
||||
const LEN: usize = 0;
|
||||
type Head = ();
|
||||
type Tail = ();
|
||||
fn head(&self) -> &Self::Head {
|
||||
&()
|
||||
}
|
||||
fn into_head(self) -> Self::Head {}
|
||||
fn split(self) -> (Self::Head, Self::Tail) {
|
||||
((), ())
|
||||
}
|
||||
}
|
||||
impl<T, Rest: TupleList> TupleList for (T, Rest) {
|
||||
const LEN: usize = Rest::LEN + 1;
|
||||
type Head = T;
|
||||
type Tail = Rest;
|
||||
fn head(&self) -> &Self::Head {
|
||||
&self.0
|
||||
}
|
||||
fn into_head(self) -> Self::Head {
|
||||
self.0
|
||||
}
|
||||
fn split(self) -> (Self::Head, Self::Tail) {
|
||||
(self.0, self.1)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait IntoTupleList {
|
||||
type List;
|
||||
fn into_list(self) -> Self::List;
|
||||
}
|
||||
|
||||
impl IntoTupleList for () {
|
||||
type List = ();
|
||||
fn into_list(self) -> Self::List {}
|
||||
}
|
||||
|
||||
macro_rules! impl_tuple {
|
||||
($H:tt $($T:tt)*) => {
|
||||
impl<$H, $($T,)*> IntoTupleList for ($H, $($T,)*) {
|
||||
type List = ($H, <($($T,)*) as IntoTupleList>::List);
|
||||
fn into_list(self) -> Self::List {
|
||||
#[allow(non_snake_case)]
|
||||
let ($H, $($T,)*) = self;
|
||||
($H, ($($T,)*).into_list())
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_tuple!(A);
|
||||
impl_tuple!(A B);
|
||||
impl_tuple!(A B C);
|
||||
impl_tuple!(A B C D);
|
||||
impl_tuple!(A B C D E);
|
||||
impl_tuple!(A B C D E F);
|
||||
impl_tuple!(A B C D E F G);
|
||||
impl_tuple!(A B C D E F G H);
|
||||
impl_tuple!(A B C D E F G H I);
|
||||
impl_tuple!(A B C D E F G H I J);
|
||||
impl_tuple!(A B C D E F G H I J K);
|
||||
impl_tuple!(A B C D E F G H I J K L);
|
||||
Reference in New Issue
Block a user