TAG TECHNOLOGY

This commit is contained in:
2025-08-14 12:21:26 -04:00
parent 4d68fa476d
commit e41970287d
19 changed files with 267 additions and 165 deletions

45
src/core/frame.rs Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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(),
}
}
}