Compare commits

3 Commits

Author SHA1 Message Date
acd67179b7 fix mask coords... might wanna change cpu output? 2025-11-20 23:01:01 -05:00
dff72d2c43 I love control flow 2025-11-20 22:48:08 -05:00
f6f9ebbe51 tuple gaming 2025-11-20 15:56:00 -05:00
23 changed files with 156 additions and 86 deletions

View File

@@ -2,6 +2,7 @@
name = "iris" name = "iris"
version = "0.1.0" version = "0.1.0"
edition = "2024" edition = "2024"
default-run = "test"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

View File

@@ -1,3 +1,4 @@
use crate::Client;
use iris::{ use iris::{
core::{CursorState, Modifiers}, core::{CursorState, Modifiers},
layout::Vec2, layout::Vec2,
@@ -7,8 +8,6 @@ use winit::{
keyboard::{Key, NamedKey}, keyboard::{Key, NamedKey},
}; };
use crate::testing::Client;
#[derive(Default)] #[derive(Default)]
pub struct Input { pub struct Input {
cursor: CursorState, cursor: CursorState,

View File

@@ -1,20 +1,18 @@
use std::sync::Arc;
use app::App; use app::App;
use arboard::Clipboard; use arboard::Clipboard;
use cosmic_text::Family; use cosmic_text::Family;
use input::Input;
use iris::prelude::*; use iris::prelude::*;
use render::Renderer;
use winit::{event::WindowEvent, event_loop::ActiveEventLoop, window::Window};
use crate::testing::input::Input;
use len_fns::*; use len_fns::*;
use render::Renderer;
use std::sync::Arc;
use winit::{event::WindowEvent, event_loop::ActiveEventLoop, window::Window};
mod app; mod app;
mod input; mod input;
mod render; mod render;
pub fn main() { fn main() {
App::run(); App::run();
} }
@@ -100,8 +98,6 @@ impl Client {
.stack() .stack()
.add_static(&mut ui); .add_static(&mut ui);
let main = pad_test.pad(10).add_static(&mut ui);
let btext = |content| text(content).size(30); let btext = |content| text(content).size(30);
let text_test = ( let text_test = (
@@ -176,6 +172,8 @@ impl Client {
.span(Dir::DOWN) .span(Dir::DOWN)
.add_static(&mut ui); .add_static(&mut ui);
let main = pad_test.pad(10).add_static(&mut ui);
let switch_button = |color, to, label| { let switch_button = |color, to, label| {
let rect = rect(color) let rect = rect(color)
.id_on(CursorSense::click(), move |id, ui: &mut Ui, _| { .id_on(CursorSense::click(), move |id, ui: &mut Ui, _| {

View File

@@ -6,6 +6,7 @@ pub struct Masked {
impl Widget for Masked { impl Widget for Masked {
fn draw(&mut self, painter: &mut Painter) { fn draw(&mut self, painter: &mut Painter) {
println!("yahoo! {}", painter.region());
painter.set_mask(painter.region()); painter.set_mask(painter.region());
painter.widget(&self.inner); painter.widget(&self.inner);
} }

View File

@@ -7,31 +7,20 @@ pub struct Aligned {
impl Widget for Aligned { impl Widget for Aligned {
fn draw(&mut self, painter: &mut Painter) { fn draw(&mut self, painter: &mut Painter) {
let region = match self.align { let region = match self.align.tuple() {
Align { (Some(x), Some(y)) => painter
x: Some(x), .size(&self.inner)
y: Some(y), .to_uivec2()
} => { .align(RegionAlign { x, y }),
painter (Some(x), None) => {
.size(&self.inner)
.to_uivec2()
.align(RegionAlign { x, y })
}
Align {
x: Some(x),
y: None,
} => {
let x = painter.size_ctx().width(&self.inner).apply_rest().align(x); let x = painter.size_ctx().width(&self.inner).apply_rest().align(x);
UiRegion::new(x, UiSpan::FULL) UiRegion::new(x, UiSpan::FULL)
} }
Align { (None, Some(y)) => {
x: None,
y: Some(y),
} => {
let y = painter.size_ctx().height(&self.inner).apply_rest().align(y); let y = painter.size_ctx().height(&self.inner).apply_rest().align(y);
UiRegion::new(UiSpan::FULL, y) UiRegion::new(UiSpan::FULL, y)
} }
Align { x: None, y: None } => UiRegion::FULL, (None, None) => UiRegion::FULL,
}; };
painter.widget_within(&self.inner, region); painter.widget_within(&self.inner, region);
} }

View File

@@ -1,6 +1,7 @@
mod align; mod align;
mod offset; mod offset;
mod pad; mod pad;
mod scroll;
mod sized; mod sized;
mod span; mod span;
mod stack; mod stack;

View File

@@ -0,0 +1,39 @@
use crate::prelude::*;
pub struct Scroll {
inner: WidgetId,
axis: Axis,
snap_end: bool,
amt: f32,
draw_len: f32,
}
impl Widget for Scroll {
fn draw(&mut self, painter: &mut Painter) {
let output_len = painter.output_size().axis(self.axis);
let container_len = painter.region().axis(self.axis).len();
let content_len = painter
.len_axis(&self.inner, self.axis)
.apply_rest()
.within_len(container_len)
.to_abs(output_len);
let container_len = container_len.to_abs(output_len);
self.draw_len = content_len;
// let region = UiRegion::FULL.offset(self.amt);
// painter.widget_within(&self.inner, region);
}
fn desired_width(&mut self, _: &mut SizeCtx) -> Len {
Len::default()
}
fn desired_height(&mut self, _: &mut SizeCtx) -> Len {
Len::default()
}
}
impl Scroll {
pub fn scroll(&mut self, amt: f32) {
self.amt += amt;
}
}

View File

@@ -11,6 +11,9 @@ impl Widget for Span {
fn draw(&mut self, painter: &mut Painter) { fn draw(&mut self, painter: &mut Painter) {
let total = self.len_sum(&mut painter.size_ctx()); let total = self.len_sum(&mut painter.size_ctx());
let mut start = UiScalar::rel_min(); let mut start = UiScalar::rel_min();
if painter.label() == "fricker" {
println!("redraw");
}
for child in &self.children { for child in &self.children {
let mut span = UiSpan::FULL; let mut span = UiSpan::FULL;
span.start = start; span.start = start;
@@ -88,7 +91,10 @@ impl Span {
} }
fn desired_ortho(&mut self, ctx: &mut SizeCtx) -> Len { fn desired_ortho(&mut self, ctx: &mut SizeCtx) -> Len {
// this is an awful hack to get text wrapping to work properly when in a downward span // this is a weird hack to get text wrapping to work properly when in a downward span
// the correct solution here is to add a function to widget that lets them
// request that ctx.outer has an axis "resolved" before checking the other,
// and panicking or warning if two request opposite axis (unsolvable in that case)
let outer = ctx.outer.axis(self.dir.axis); let outer = ctx.outer.axis(self.dir.axis);
if self.dir.axis == Axis::X { if self.dir.axis == Axis::X {
// so....... this literally copies draw so that the lengths are correctly set in the // so....... this literally copies draw so that the lengths are correctly set in the

View File

@@ -26,7 +26,6 @@ impl TextEdit {
.lines .lines
.iter() .iter()
.map(|l| l.text()) .map(|l| l.text())
// why is this needed?? what??
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join("\n") .join("\n")
} }
@@ -44,9 +43,7 @@ impl Widget for TextEdit {
let size = vec2(1, self.attrs.line_height); let size = vec2(1, self.attrs.line_height);
painter.primitive_within( painter.primitive_within(
RectPrimitive::color(Color::WHITE), RectPrimitive::color(Color::WHITE),
size.align(Align::TOP_LEFT) size.align(Align::TOP_LEFT).offset(offset).within(&region),
.offset(offset)
.within(&region),
); );
} }
} }

View File

@@ -24,6 +24,10 @@ impl Align {
pub const TOP: CardinalAlign = CardinalAlign::TOP; pub const TOP: CardinalAlign = CardinalAlign::TOP;
pub const V_CENTER: CardinalAlign = CardinalAlign::V_CENTER; pub const V_CENTER: CardinalAlign = CardinalAlign::V_CENTER;
pub const BOT: CardinalAlign = CardinalAlign::BOT; pub const BOT: CardinalAlign = CardinalAlign::BOT;
pub fn tuple(&self) -> (Option<AxisAlign>, Option<AxisAlign>) {
(self.x, self.y)
}
} }
#[derive(Clone, Copy, PartialEq, Eq)] #[derive(Clone, Copy, PartialEq, Eq)]

View File

@@ -29,6 +29,12 @@ impl<Nx: UiNum, Ny: UiNum> From<(Nx, Ny)> for Size {
} }
} }
impl From<Len> for Size {
fn from(value: Len) -> Self {
Self { x: value, y: value }
}
}
impl Size { impl Size {
pub const ZERO: Self = Self { pub const ZERO: Self = Self {
x: Len::ZERO, x: Len::ZERO,
@@ -171,10 +177,10 @@ impl std::fmt::Display for Len {
write!(f, "{} abs;", self.abs)?; write!(f, "{} abs;", self.abs)?;
} }
if self.rel != 0.0 { if self.rel != 0.0 {
write!(f, "{} rel;", self.abs)?; write!(f, "{} rel;", self.rel)?;
} }
if self.rest != 0.0 { if self.rest != 0.0 {
write!(f, "{} leftover;", self.abs)?; write!(f, "{} rest;", self.rest)?;
} }
Ok(()) Ok(())
} }

View File

@@ -78,7 +78,10 @@ impl UiVec2 {
} }
pub fn to_abs(&self, rel: Vec2) -> Vec2 { pub fn to_abs(&self, rel: Vec2) -> Vec2 {
self.get_rel() * rel + self.get_abs() 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 FULL_SIZE: Self = Self::rel(Vec2::ONE);
@@ -231,6 +234,10 @@ impl UiScalar {
pub const fn to(&self, end: Self) -> UiSpan { pub const fn to(&self, end: Self) -> UiSpan {
UiSpan { start: *self, end } UiSpan { start: *self, end }
} }
pub const fn to_abs(&self, rel: f32) -> f32 {
self.rel * rel + self.abs
}
} }
#[repr(C)] #[repr(C)]
@@ -323,6 +330,14 @@ impl UiRegion {
y: self.y.outside(&parent.y), 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 { pub const fn axis_mut(&mut self, axis: Axis) -> &mut UiSpan {
match axis { match axis {
Axis::X => &mut self.x, Axis::X => &mut self.x,

View File

@@ -29,19 +29,20 @@ pub struct PainterCtx<'a> {
pub textures: &'a mut Textures, pub textures: &'a mut Textures,
pub masks: &'a mut TrackedArena<Mask, u32>, pub masks: &'a mut TrackedArena<Mask, u32>,
pub text: &'a mut TextData, pub text: &'a mut TextData,
pub screen_size: Vec2, pub output_size: Vec2,
pub modules: &'a mut Modules, pub modules: &'a mut Modules,
pub cache_width: HashMap<Id, (UiVec2, Len)>, pub cache_width: HashMap<Id, (UiVec2, Len)>,
pub cache_height: HashMap<Id, (UiVec2, Len)>, pub cache_height: HashMap<Id, (UiVec2, Len)>,
draw_started: HashSet<Id>, draw_started: HashSet<Id>,
} }
#[derive(Default)] #[derive(Clone, Copy, Debug, Default)]
pub struct ResizeRef { pub struct ResizeRef {
x: Option<(Id, (UiVec2, Len))>, x: Option<(Id, (UiVec2, Len))>,
y: Option<(Id, (UiVec2, Len))>, y: Option<(Id, (UiVec2, Len))>,
} }
#[derive(Debug)]
pub struct WidgetInstance { pub struct WidgetInstance {
pub id: Id, pub id: Id,
pub region: UiRegion, pub region: UiRegion,
@@ -75,7 +76,7 @@ impl<'a> PainterCtx<'a> {
layers: &mut data.layers, layers: &mut data.layers,
textures: &mut data.textures, textures: &mut data.textures,
text: &mut data.text, text: &mut data.text,
screen_size: data.output_size, output_size: data.output_size,
modules: &mut data.modules, modules: &mut data.modules,
masks: &mut data.masks, masks: &mut data.masks,
cache_width: Default::default(), cache_width: Default::default(),
@@ -91,9 +92,16 @@ impl<'a> PainterCtx<'a> {
let Some(active) = self.active.get(&id) else { let Some(active) = self.active.get(&id) else {
return; return;
}; };
let mut resize = active.resize;
// TODO: this is stupid having 2 of these let finish = |s: &mut Self, resize| {
if let Some((rid, (outer, old_desired))) = active.resize.x { if let Some(active) = s.active.get_mut(&id) {
active.resize = resize;
}
};
// TODO: this is stupid having 2 of these, don't ask me what the consequences are
if let Some((rid, (outer, old_desired))) = &mut resize.x {
let new_desired = SizeCtx { let new_desired = SizeCtx {
source: id, source: id,
cache_width: &mut self.cache_width, cache_width: &mut self.cache_width,
@@ -101,25 +109,23 @@ impl<'a> PainterCtx<'a> {
text: self.text, text: self.text,
textures: self.textures, textures: self.textures,
widgets: self.widgets, widgets: self.widgets,
outer, outer: *outer,
output_size: self.screen_size, output_size: self.output_size,
checked_width: &mut Default::default(), checked_width: &mut Default::default(),
checked_height: &mut Default::default(), checked_height: &mut Default::default(),
id, id,
} }
.width_inner(id); .width_inner(id);
if new_desired != old_desired { if new_desired != *old_desired {
self.redraw(rid); self.redraw(*rid);
*old_desired = new_desired;
if self.draw_started.contains(&id) { if self.draw_started.contains(&id) {
return; return finish(self, resize);
} }
} }
} }
let Some(active) = self.active.get(&id) else { if let Some((rid, (outer, old_desired))) = &mut resize.y {
return;
};
if let Some((rid, (outer, old_desired))) = active.resize.y {
let new_desired = SizeCtx { let new_desired = SizeCtx {
source: id, source: id,
cache_width: &mut self.cache_width, cache_width: &mut self.cache_width,
@@ -127,17 +133,18 @@ impl<'a> PainterCtx<'a> {
text: self.text, text: self.text,
textures: self.textures, textures: self.textures,
widgets: self.widgets, widgets: self.widgets,
outer, outer: *outer,
output_size: self.screen_size, output_size: self.output_size,
checked_width: &mut Default::default(), checked_width: &mut Default::default(),
checked_height: &mut Default::default(), checked_height: &mut Default::default(),
id, id,
} }
.height_inner(id); .height_inner(id);
if new_desired != old_desired { if new_desired != *old_desired {
self.redraw(rid); self.redraw(*rid);
*old_desired = new_desired;
if self.draw_started.contains(&id) { if self.draw_started.contains(&id) {
return; return finish(self, resize);
} }
} }
} }
@@ -154,7 +161,7 @@ impl<'a> PainterCtx<'a> {
active.mask, active.mask,
Some(active.children), Some(active.children),
); );
self.active.get_mut(&id).unwrap().resize = active.resize; finish(self, resize);
} }
pub fn draw(&mut self, id: Id) { pub fn draw(&mut self, id: Id) {
@@ -399,7 +406,7 @@ impl<'a, 'c> Painter<'a, 'c> {
text: self.ctx.text, text: self.ctx.text,
textures: self.ctx.textures, textures: self.ctx.textures,
widgets: self.ctx.widgets, widgets: self.ctx.widgets,
output_size: self.ctx.screen_size, output_size: self.ctx.output_size,
checked_width: &mut self.children_width, checked_width: &mut self.children_width,
checked_height: &mut self.children_height, checked_height: &mut self.children_height,
cache_width: &mut self.ctx.cache_width, cache_width: &mut self.ctx.cache_width,
@@ -410,8 +417,12 @@ impl<'a, 'c> Painter<'a, 'c> {
} }
} }
pub fn output_size(&self) -> Vec2 {
self.ctx.output_size
}
pub fn px_size(&mut self) -> Vec2 { pub fn px_size(&mut self) -> Vec2 {
self.region.size().to_abs(self.ctx.screen_size) self.region.size().to_abs(self.ctx.output_size)
} }
pub fn text_data(&mut self) -> &mut TextData { pub fn text_data(&mut self) -> &mut TextData {

View File

@@ -7,7 +7,7 @@ use image::{DynamicImage, GenericImageView};
use crate::{layout::Vec2, render::TexturePrimitive, util::RefCounter}; use crate::{layout::Vec2, render::TexturePrimitive, util::RefCounter};
#[derive(Clone)] #[derive(Debug, Clone)]
pub struct TextureHandle { pub struct TextureHandle {
inner: TexturePrimitive, inner: TexturePrimitive,
size: Vec2, size: Vec2,

View File

@@ -4,7 +4,7 @@ use crate::{
core::{TextEdit, TextEditCtx}, core::{TextEdit, TextEditCtx},
layout::{ layout::{
IdLike, PainterCtx, PainterData, PixelRegion, StaticWidgetId, TextureHandle, Vec2, Widget, IdLike, PainterCtx, PainterData, PixelRegion, StaticWidgetId, TextureHandle, Vec2, Widget,
WidgetId, WidgetLike, WidgetId, WidgetInstance, WidgetLike,
}, },
util::{HashSet, Id}, util::{HashSet, Id},
}; };
@@ -184,20 +184,11 @@ impl Ui {
Some(region.to_px(self.data.output_size)) Some(region.to_px(self.data.output_size))
} }
pub fn debug(&self, label: &str) { pub fn debug(&self, label: &str) -> impl Iterator<Item = &WidgetInstance> {
for (id, inst) in &self.data.active { self.data.active.iter().filter_map(move |(id, inst)| {
let l = &self.data.widgets.data(id).unwrap().label; let l = &self.data.widgets.label(id);
if l != label { if *l == label { Some(inst) } else { None }
continue; })
}
println!("\"{label}\" {{");
println!(" region: {}", inst.region);
println!(
" pixel region: {}",
inst.region.to_px(self.data.output_size)
);
println!("}}");
}
} }
} }

View File

@@ -1,5 +0,0 @@
mod testing;
fn main() {
testing::main();
}

View File

@@ -223,7 +223,7 @@ impl RectPrimitive {
} }
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct TexturePrimitive { pub struct TexturePrimitive {
pub view_idx: u32, pub view_idx: u32,
pub sampler_idx: u32, pub sampler_idx: u32,

View File

@@ -21,8 +21,18 @@ struct TextureInfo {
} }
struct Mask { struct Mask {
top_left: UiVec2, x: UiSpan,
bot_right: UiVec2, y: UiSpan,
}
struct UiSpan {
start: UiScalar,
end: UiScalar,
}
struct UiScalar {
rel: f32,
abs: f32,
} }
struct UiVec2 { struct UiVec2 {
@@ -121,8 +131,11 @@ fn fs_main(
} }
if in.mask_idx != 4294967295u { if in.mask_idx != 4294967295u {
let mask = masks[in.mask_idx]; let mask = masks[in.mask_idx];
let top_left = floor(mask.top_left.rel * window.dim) + floor(mask.top_left.abs); let tl = UiVec2(vec2(mask.x.start.rel, mask.y.start.rel), vec2(mask.x.start.abs, mask.y.start.abs));
let bot_right = floor(mask.bot_right.rel * window.dim) + floor(mask.bot_right.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 { if pos.x < top_left.x || pos.x > bot_right.x || pos.y < top_left.y || pos.y > bot_right.y {
color *= 0.0; color *= 0.0;
} }

View File

@@ -26,6 +26,9 @@ impl<I: IdNum> IdTracker<I> {
} }
impl<I: IdNum> Id<I> { impl<I: IdNum> Id<I> {
pub(crate) fn raw(id: I) -> Self {
Self(id)
}
pub fn idx(&self) -> usize { pub fn idx(&self) -> usize {
self.0.idx() self.0.idx()
} }

View File

@@ -3,6 +3,7 @@ use std::sync::{
atomic::{AtomicU32, Ordering}, atomic::{AtomicU32, Ordering},
}; };
#[derive(Debug)]
pub struct RefCounter(Arc<AtomicU32>); pub struct RefCounter(Arc<AtomicU32>);
impl RefCounter { impl RefCounter {