TAG TECHNOLOGY
This commit is contained in:
45
src/core/frame.rs
Normal file
45
src/core/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<Ctx: 'static> Widget<Ctx> for Regioned {
|
||||
fn draw(&self, painter: &mut Painter<Ctx>) {
|
||||
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())
|
||||
}
|
||||
}
|
||||
13
src/core/mod.rs
Normal file
13
src/core/mod.rs
Normal file
@@ -0,0 +1,13 @@
|
||||
mod frame;
|
||||
mod num;
|
||||
mod rect;
|
||||
mod sense;
|
||||
mod span;
|
||||
mod trait_fns;
|
||||
|
||||
pub use frame::*;
|
||||
pub use num::*;
|
||||
pub use rect::*;
|
||||
pub use sense::*;
|
||||
pub use span::*;
|
||||
pub use trait_fns::*;
|
||||
50
src/core/num.rs
Normal file
50
src/core/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,
|
||||
}
|
||||
35
src/core/rect.rs
Normal file
35
src/core/rect.rs
Normal file
@@ -0,0 +1,35 @@
|
||||
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 new(color: UiColor) -> Self {
|
||||
Self {
|
||||
color,
|
||||
radius: 0.0,
|
||||
inner_radius: 0.0,
|
||||
thickness: 0.0,
|
||||
}
|
||||
}
|
||||
pub fn color(mut self, color: UiColor) -> Self {
|
||||
self.color = color;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<Ctx> Widget<Ctx> for Rect {
|
||||
fn draw(&self, painter: &mut Painter<Ctx>) {
|
||||
painter.write(RoundedRectData {
|
||||
color: self.color,
|
||||
radius: self.radius,
|
||||
thickness: self.thickness,
|
||||
inner_radius: self.inner_radius,
|
||||
});
|
||||
}
|
||||
}
|
||||
35
src/core/sense.rs
Normal file
35
src/core/sense.rs
Normal file
@@ -0,0 +1,35 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::{Painter, Widget, WidgetFn, WidgetId, WidgetLike};
|
||||
|
||||
pub struct Sensor<F: SenseFn<Ctx>, Ctx> {
|
||||
inner: WidgetId,
|
||||
f: F,
|
||||
_pd: PhantomData<Ctx>,
|
||||
}
|
||||
|
||||
impl<F: SenseFn<Ctx>, Ctx: 'static> Widget<Ctx> for Sensor<F, Ctx> {
|
||||
fn draw(&self, painter: &mut Painter<Ctx>) {
|
||||
(self.f)(painter.ctx_mut());
|
||||
painter.draw(&self.inner);
|
||||
}
|
||||
}
|
||||
|
||||
pub trait SenseFn<Ctx> = Fn(&mut Ctx) + 'static;
|
||||
|
||||
pub trait Sensable<Ctx: 'static, Tag> {
|
||||
fn sense<F: SenseFn<Ctx>>(self, f: F) -> impl WidgetFn<Sensor<F, Ctx>, Ctx>;
|
||||
}
|
||||
|
||||
impl<W: WidgetLike<Ctx, Tag>, Ctx: 'static, Tag> Sensable<Ctx, Tag> for W {
|
||||
fn sense<F: SenseFn<Ctx>>(self, f: F) -> impl WidgetFn<Sensor<F, Ctx>, Ctx> {
|
||||
|ui| {
|
||||
let inner = self.add(ui).erase_type();
|
||||
Sensor {
|
||||
inner,
|
||||
f,
|
||||
_pd: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
99
src/core/span.rs
Normal file
99
src/core/span.rs
Normal file
@@ -0,0 +1,99 @@
|
||||
use crate::{Dir, Painter, Sign, UINum, UiRegion, UIScalar, Widget, WidgetId};
|
||||
|
||||
pub struct Span {
|
||||
pub children: Vec<(WidgetId, SpanLen)>,
|
||||
pub dir: Dir,
|
||||
}
|
||||
|
||||
impl<Ctx: 'static> Widget<Ctx> for Span {
|
||||
fn draw(&self, painter: &mut Painter<Ctx>) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
if self.dir.sign == Sign::Neg {
|
||||
child_region.flip();
|
||||
}
|
||||
painter.draw_within(child, child_region);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct SpanLenSums {
|
||||
pub fixed: f32,
|
||||
pub ratio: f32,
|
||||
pub relative: f32,
|
||||
}
|
||||
|
||||
impl Span {
|
||||
pub fn empty(dir: Dir) -> Self {
|
||||
Self {
|
||||
children: Vec::new(),
|
||||
dir,
|
||||
}
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
}
|
||||
39
src/core/trait_fns.rs
Normal file
39
src/core/trait_fns.rs
Normal file
@@ -0,0 +1,39 @@
|
||||
use super::*;
|
||||
use crate::{UiRegion, Vec2, WidgetArrLike, WidgetFn, WidgetLike};
|
||||
|
||||
pub trait CoreWidget<Ctx: 'static, Tag> {
|
||||
fn pad(self, padding: impl Into<Padding>) -> impl WidgetFn<Regioned, Ctx>;
|
||||
fn center(self, size: impl Into<Vec2>) -> impl WidgetFn<Regioned, Ctx>;
|
||||
}
|
||||
|
||||
impl<W: WidgetLike<Ctx, Tag>, Ctx: 'static, Tag> CoreWidget<Ctx, Tag> for W {
|
||||
fn pad(self, padding: impl Into<Padding>) -> impl WidgetFn<Regioned, Ctx> {
|
||||
|ui| Regioned {
|
||||
region: padding.into().region(),
|
||||
inner: self.add(ui).erase_type(),
|
||||
}
|
||||
}
|
||||
|
||||
fn center(self, size: impl Into<Vec2>) -> impl WidgetFn<Regioned, Ctx> {
|
||||
|ui| Regioned {
|
||||
region: UiRegion::center(size.into()),
|
||||
inner: self.add(ui).erase_type(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CoreWidgetArr<const LEN: usize, Ctx: 'static, Tag> {
|
||||
fn span(self, dir: Dir, lengths: [impl Into<SpanLen>; LEN]) -> impl WidgetFn<Span, Ctx>;
|
||||
}
|
||||
|
||||
impl<const LEN: usize, Wa: WidgetArrLike<LEN, Ctx, Tag>, Ctx: 'static, Tag> CoreWidgetArr<LEN, Ctx, Tag>
|
||||
for Wa
|
||||
{
|
||||
fn span(self, dir: Dir, lengths: [impl Into<SpanLen>; LEN]) -> impl WidgetFn<Span, Ctx> {
|
||||
let lengths = lengths.map(Into::into);
|
||||
move |ui| Span {
|
||||
dir,
|
||||
children: self.ui(ui).arr.into_iter().zip(lengths).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user