sized widgets!

This commit is contained in:
2025-08-28 01:35:43 -04:00
parent d4d0b3b580
commit 834182ffe8
14 changed files with 313 additions and 106 deletions

View File

@@ -6,7 +6,7 @@ pub struct Regioned {
} }
impl Widget for Regioned { impl Widget for Regioned {
fn draw(&self, painter: &mut Painter) { fn draw(&mut self, painter: &mut Painter) {
painter.draw_within(&self.inner, self.region); painter.draw_within(&self.inner, self.region);
} }
} }

View File

@@ -2,34 +2,23 @@ use crate::prelude::*;
use image::DynamicImage; use image::DynamicImage;
pub struct Image { pub struct Image {
handle: Option<TextureHandle>, handle: TextureHandle,
} }
impl Widget for Image { impl Widget for Image {
fn draw(&self, painter: &mut Painter) { fn draw(&mut self, painter: &mut Painter) {
if let Some(handle) = &self.handle { painter.draw_texture(&self.handle);
painter.draw_texture(handle); }
} else {
painter.write(RectPrimitive { fn size(&mut self, _: SizeCtx) -> Vec2 {
color: Color::MAGENTA, self.handle.size()
inner_radius: 0.0,
radius: 0.0,
thickness: 0.0,
});
}
} }
} }
pub fn image(image: impl LoadableImage) -> WidgetFnRet!(Image) { pub fn image(image: impl LoadableImage) -> WidgetFnRet!(Image) {
let image = match image.get_image() { let image = image.get_image().expect("Failed to load image");
Ok(image) => Some(image),
Err(e) => {
println!("Failed to load image: {e}");
None
}
};
move |ui| Image { move |ui| Image {
handle: image.map(|image| ui.add_texture(image)), handle: ui.add_texture(image),
} }
} }

View File

@@ -28,7 +28,7 @@ impl Rect {
} }
impl Widget for Rect { impl Widget for Rect {
fn draw(&self, painter: &mut Painter) { fn draw(&mut self, painter: &mut Painter) {
painter.write(RectPrimitive { painter.write(RectPrimitive {
color: self.color, color: self.color,
radius: self.radius, radius: self.radius,

View File

@@ -6,8 +6,8 @@ pub struct Span {
} }
impl Widget for Span { impl Widget for Span {
fn draw(&self, painter: &mut Painter) { fn draw(&mut self, painter: &mut Painter) {
let total = self.sums(); let total = self.setup(painter);
let mut start = UIScalar::min(); let mut start = UIScalar::min();
for (child, length) in &self.children { for (child, length) in &self.children {
let mut child_region = UiRegion::full(); let mut child_region = UiRegion::full();
@@ -29,6 +29,14 @@ impl Widget for Span {
start.anchor += rel; start.anchor += rel;
axis.bot_right.set(start); axis.bot_right.set(start);
} }
SpanLen::Sized(size) => {
let offset = size.axis(self.dir.axis);
start.offset += offset;
*axis.bot_right.offset = start.offset;
child_region.bot_right.anchor = child_region.top_left.anchor;
let opposite = !self.dir.axis;
*child_region.bot_right.offset.axis_mut(opposite) += size.axis(opposite);
}
} }
if self.dir.sign == Sign::Neg { if self.dir.sign == Sign::Neg {
child_region.flip(); child_region.flip();
@@ -53,18 +61,23 @@ impl Span {
} }
} }
pub fn sums(&self) -> SpanLenSums { fn setup(&mut self, painter: &mut Painter) -> SpanLenSums {
self.lengths().fold(SpanLenSums::default(), |mut s, l| { self.children
match l { .iter_mut()
SpanLen::Fixed(v) => s.fixed += v, .fold(SpanLenSums::default(), |mut s, (id, l)| {
SpanLen::Ratio(v) => s.ratio += v, match l {
SpanLen::Relative(v) => s.relative += v, SpanLen::Fixed(v) => s.fixed += *v,
} SpanLen::Ratio(v) => s.ratio += *v,
s SpanLen::Relative(v) => s.relative += *v,
}) SpanLen::Sized(v) => {
} let size = painter.size(id);
pub fn lengths(&self) -> impl ExactSizeIterator<Item = &SpanLen> { let len = size.axis(self.dir.axis);
self.children.iter().map(|(_, s)| s) *v = size;
s.fixed += len;
}
}
s
})
} }
} }
@@ -78,6 +91,10 @@ pub enum SpanLen {
/// relative to the total space (of the entire span) /// relative to the total space (of the entire span)
/// eg. 0.5 means 1/2 of the total space /// eg. 0.5 means 1/2 of the total space
Relative(f32), Relative(f32),
/// size determined by the child widget itself
/// the value is not used externally, I just don't wanna make a duplicate enum
/// there are util functions instead so
Sized(Vec2),
} }
pub fn fixed<N: UiNum>(x: N) -> SpanLen { pub fn fixed<N: UiNum>(x: N) -> SpanLen {
@@ -92,6 +109,10 @@ pub fn relative<N: UiNum>(x: N) -> SpanLen {
SpanLen::Relative(x.to_f32()) SpanLen::Relative(x.to_f32())
} }
pub fn sized() -> SpanLen {
SpanLen::Sized(Vec2::ZERO)
}
impl<N: UiNum> From<N> for SpanLen { impl<N: UiNum> From<N> for SpanLen {
fn from(value: N) -> Self { fn from(value: N) -> Self {
Self::Ratio(value.to_f32()) Self::Ratio(value.to_f32())

View File

@@ -5,7 +5,7 @@ pub struct Stack {
} }
impl Widget for Stack { impl Widget for Stack {
fn draw(&self, painter: &mut Painter) { fn draw(&mut self, painter: &mut Painter) {
for child in &self.children { for child in &self.children {
painter.draw(child); painter.draw(child);
} }

View File

@@ -1,13 +1,17 @@
use cosmic_text::Metrics;
use crate::prelude::*; use crate::prelude::*;
pub struct Text { pub struct Text {
pub content: String, pub content: String,
pub attrs: TextAttrs, pub attrs: TextAttrs,
buf: TextBuffer,
} }
impl Text { impl Text {
pub fn size(mut self, size: impl UiNum) -> Self { pub fn size(mut self, size: impl UiNum) -> Self {
self.attrs.size = size.to_f32(); self.attrs.size = size.to_f32();
self.attrs.line_height = self.attrs.size * 1.1;
self self
} }
pub fn color(mut self, color: UiColor) -> Self { pub fn color(mut self, color: UiColor) -> Self {
@@ -21,16 +25,31 @@ impl Text {
} }
impl Widget for Text { impl Widget for Text {
fn draw(&self, painter: &mut Painter) { fn draw(&mut self, painter: &mut Painter) {
let (handle, offset) = painter.render_text(&mut self.buf, &self.content, &self.attrs);
let dims = handle.size();
let size = offset.size(&handle);
let mut region = painter.region().center().expand(size);
region.top_left.offset += offset.top_left;
region.bot_right.offset = region.top_left.offset + dims;
painter.draw_texture_at(&handle, region);
// TODO: when on_update is added to painter, // TODO: when on_update is added to painter,
// return & store TextureHandle to reuse // reuse TextureHandle
painter.draw_text(&self.content, &self.attrs); }
fn size(&mut self, ctx: SizeCtx) -> Vec2 {
let (handle, offset) =
ctx.text
.draw(&mut self.buf, &self.content, &self.attrs, ctx.textures);
offset.size(&handle)
} }
} }
pub fn text(text: impl Into<String>) -> Text { pub fn text(text: impl Into<String>) -> Text {
let attrs = TextAttrs::default();
Text { Text {
content: text.into(), content: text.into(),
attrs: TextAttrs::default(), buf: TextBuffer::new_empty(Metrics::new(attrs.size, attrs.line_height)),
attrs,
} }
} }

View File

@@ -1,3 +1,5 @@
use std::ops::Not;
use crate::layout::{Vec2, vec2}; use crate::layout::{Vec2, vec2};
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
@@ -25,6 +27,17 @@ pub enum Axis {
Y, Y,
} }
impl 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)] #[derive(Clone, Copy, Eq, PartialEq)]
pub struct Dir { pub struct Dir {
pub axis: Axis, pub axis: Axis,
@@ -47,3 +60,19 @@ pub enum Sign {
Neg, Neg,
Pos, 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,
}
}
}

View File

@@ -1,9 +1,7 @@
use image::GenericImageView;
use crate::{ use crate::{
layout::{ layout::{
Active, SensorMap, TextAttrs, TextData, TextureHandle, Textures, UiRegion, Vec2, WidgetId, Active, SensorMap, SizeCtx, TextAttrs, TextBuffer, TextData, TextOffset, TextureHandle,
WidgetInstance, Widgets, Textures, UiRegion, Vec2, WidgetId, WidgetInstance, Widgets,
}, },
render::{Primitive, PrimitiveHandle, Primitives}, render::{Primitive, PrimitiveHandle, Primitives},
util::Id, util::Id,
@@ -33,7 +31,7 @@ pub struct Painter<'a> {
impl<'a> Painter<'a> { impl<'a> Painter<'a> {
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub(super) fn new( pub(super) fn new(
nodes: &'a Widgets, widgets: &'a Widgets,
primitives: &'a mut Primitives, primitives: &'a mut Primitives,
sensors_map: &'a SensorMap, sensors_map: &'a SensorMap,
active: &'a mut Active, active: &'a mut Active,
@@ -42,7 +40,7 @@ impl<'a> Painter<'a> {
screen_size: Vec2, screen_size: Vec2,
) -> Self { ) -> Self {
Self { Self {
widgets: nodes, widgets,
active, active,
sensors_map, sensors_map,
primitives, primitives,
@@ -59,9 +57,14 @@ impl<'a> Painter<'a> {
} }
} }
/// Writes a primitive to be rendered
pub fn write_at<P: Primitive>(&mut self, data: P, region: UiRegion) -> PrimitiveHandle<P> {
self.primitives.write(data, region)
}
/// Writes a primitive to be rendered /// Writes a primitive to be rendered
pub fn write<P: Primitive>(&mut self, data: P) -> PrimitiveHandle<P> { pub fn write<P: Primitive>(&mut self, data: P) -> PrimitiveHandle<P> {
self.primitives.write(data, self.state.region) self.write_at(data, self.state.region)
} }
/// Draws a widget within this widget's region. /// Draws a widget within this widget's region.
@@ -81,9 +84,6 @@ impl<'a> Painter<'a> {
} }
fn draw_raw_at(&mut self, id: &Id, region: UiRegion) { fn draw_raw_at(&mut self, id: &Id, region: UiRegion) {
if self.active.widgets.contains_key(id) {
panic!("widget drawn twice!");
}
self.state.children.push(id.duplicate()); self.state.children.push(id.duplicate());
// &mut self is passed to avoid copying all of the "static" pointers in self // &mut self is passed to avoid copying all of the "static" pointers in self
@@ -100,7 +100,7 @@ impl<'a> Painter<'a> {
// draw widgets // draw widgets
let start_i = self.primitives.cur_pos(); let start_i = self.primitives.cur_pos();
self.widgets.get_dyn(id).draw(self); self.widgets.get_dyn_dynamic(id).draw(self);
let end_i = self.primitives.cur_pos(); let end_i = self.primitives.cur_pos();
// restore state // restore state
@@ -122,7 +122,7 @@ impl<'a> Painter<'a> {
pub fn draw_texture(&mut self, handle: &TextureHandle) { pub fn draw_texture(&mut self, handle: &TextureHandle) {
self.state.textures.push(handle.clone()); self.state.textures.push(handle.clone());
self.write(handle.inner); self.write(handle.primitive());
} }
pub fn draw_texture_at(&mut self, handle: &TextureHandle, region: UiRegion) { pub fn draw_texture_at(&mut self, handle: &TextureHandle, region: UiRegion) {
@@ -132,11 +132,14 @@ impl<'a> Painter<'a> {
self.state.region = old; self.state.region = old;
} }
pub fn draw_text(&mut self, content: &str, attrs: &TextAttrs) { /// returns (handle, offset from top left)
let handle = self.text.draw(content, attrs, self.textures); pub fn render_text(
let dims: Vec2 = self.textures[&handle].dimensions().into(); &mut self,
let region = self.state.region.center().expand(dims); buffer: &mut TextBuffer,
self.draw_texture_at(&handle, region); content: &str,
attrs: &TextAttrs,
) -> (TextureHandle, TextOffset) {
self.text.draw(buffer, content, attrs, self.textures)
} }
pub fn region(&self) -> UiRegion { pub fn region(&self) -> UiRegion {
@@ -147,6 +150,14 @@ impl<'a> Painter<'a> {
self.state.region.in_size(self.screen_size) self.state.region.in_size(self.screen_size)
} }
pub fn size<W>(&mut self, id: &WidgetId<W>) -> Vec2 {
self.widgets.get_dyn_dynamic(&id.id).size(SizeCtx {
size: self.region().in_size(self.screen_size),
text: self.text,
textures: self.textures,
})
}
pub(crate) fn redraw(&mut self, id: &Id) { pub(crate) fn redraw(&mut self, id: &Id) {
if !self.active.widgets.contains_key(id) { if !self.active.widgets.contains_key(id) {
return; return;

View File

@@ -2,7 +2,7 @@ use cosmic_text::{Attrs, Buffer, FontSystem, Metrics, Shaping, SwashCache};
use image::{Rgba, RgbaImage}; use image::{Rgba, RgbaImage};
use crate::{ use crate::{
layout::{TextureHandle, Textures, UiColor}, layout::{TextureHandle, Textures, UiColor, Vec2},
util::HashMap, util::HashMap,
}; };
@@ -20,12 +20,15 @@ impl Default for TextData {
} }
} }
#[derive(Clone, Copy)]
pub struct TextAttrs { pub struct TextAttrs {
pub color: UiColor, pub color: UiColor,
pub size: f32, pub size: f32,
pub line_height: f32, pub line_height: f32,
} }
pub type TextBuffer = Buffer;
impl Default for TextAttrs { impl Default for TextAttrs {
fn default() -> Self { fn default() -> Self {
let size = 14.0; let size = 14.0;
@@ -38,35 +41,57 @@ impl Default for TextAttrs {
} }
impl TextData { impl TextData {
/// returns (handle, offset from top left)
pub fn draw( pub fn draw(
&mut self, &mut self,
buffer: &mut TextBuffer,
content: &str, content: &str,
attrs: &TextAttrs, attrs: &TextAttrs,
textures: &mut Textures, textures: &mut Textures,
) -> TextureHandle { ) -> (TextureHandle, TextOffset) {
let metrics = Metrics::new(attrs.size, attrs.line_height); buffer.set_metrics(
let mut buffer = Buffer::new(&mut self.font_system, metrics); &mut self.font_system,
let mut buffer = buffer.borrow_with(&mut self.font_system); Metrics::new(attrs.size, attrs.line_height),
buffer.set_text(content, &Attrs::new(), Shaping::Advanced); );
buffer.set_text(
// dawg what is this api ??? &mut self.font_system,
content,
&Attrs::new(),
Shaping::Advanced,
);
let mut pixels = HashMap::new(); let mut pixels = HashMap::new();
let mut min_x = 0; let mut min_x = 0;
let mut min_y = 0; let mut min_y = 0;
let mut max_x = 0; let mut max_x = 0;
let mut max_y = 0; let mut max_y = 0;
let c = attrs.color; let c = attrs.color;
buffer.draw( let mut max_width = 0.0f32;
&mut self.swash_cache, for run in buffer.layout_runs() {
cosmic_text::Color::rgba(c.r, c.g, c.b, c.a), for glyph in run.glyphs.iter() {
|x, y, _, _, color| { let physical_glyph = glyph.physical((0., 0.), 1.0);
min_x = min_x.min(x);
min_y = min_y.min(y); let glyph_color = match glyph.color_opt {
max_x = max_x.max(x); Some(some) => some,
max_y = max_y.max(y); None => cosmic_text::Color::rgba(c.r, c.g, c.b, c.a),
pixels.insert((x, y), Rgba(color.as_rgba())); };
},
); self.swash_cache.with_pixels(
&mut self.font_system,
physical_glyph.cache_key,
glyph_color,
|x, y, color| {
let x = physical_glyph.x + x;
let y = run.line_y as i32 + physical_glyph.y + y;
min_x = min_x.min(x);
min_y = min_y.min(y);
max_x = max_x.max(x);
max_y = max_y.max(y);
pixels.insert((x, y), Rgba(color.as_rgba()));
},
);
}
max_width = max_width.max(run.line_w);
}
let width = (max_x - min_x + 1) as u32; let width = (max_x - min_x + 1) as u32;
let height = (max_y - min_y + 1) as u32; let height = (max_y - min_y + 1) as u32;
let mut image = RgbaImage::new(width, height); let mut image = RgbaImage::new(width, height);
@@ -75,6 +100,25 @@ impl TextData {
let y = (y - min_y) as u32; let y = (y - min_y) as u32;
image.put_pixel(x, y, color); image.put_pixel(x, y, color);
} }
textures.add(image) let offset = TextOffset {
top_left: Vec2::new(min_x as f32, min_y as f32),
bot_right: Vec2::new(
max_width - max_x as f32,
attrs.line_height * buffer.lines.len() as f32 - max_y as f32,
),
};
(textures.add(image), offset)
}
}
#[derive(Clone, Copy)]
pub struct TextOffset {
pub top_left: Vec2,
pub bot_right: Vec2,
}
impl TextOffset {
pub fn size(&self, handle: &TextureHandle) -> Vec2 {
handle.size() - self.top_left + self.bot_right
} }
} }

View File

@@ -3,13 +3,14 @@ use std::{
sync::mpsc::{Receiver, Sender, channel}, sync::mpsc::{Receiver, Sender, channel},
}; };
use image::DynamicImage; use image::{DynamicImage, GenericImageView};
use crate::{render::TexturePrimitive, util::RefCounter}; use crate::{layout::Vec2, render::TexturePrimitive, util::RefCounter};
#[derive(Clone)] #[derive(Clone)]
pub struct TextureHandle { pub struct TextureHandle {
pub inner: TexturePrimitive, inner: TexturePrimitive,
size: Vec2,
counter: RefCounter, counter: RefCounter,
send: Sender<u32>, send: Sender<u32>,
} }
@@ -25,10 +26,11 @@ pub struct Textures {
} }
pub enum TextureUpdate<'a> { pub enum TextureUpdate<'a> {
PushFree,
Push(&'a DynamicImage), Push(&'a DynamicImage),
Set(u32, &'a DynamicImage), Set(u32, &'a DynamicImage),
Free(u32), Free(u32),
PushFree,
SetFree,
} }
enum Update { enum Update {
@@ -49,7 +51,9 @@ impl Textures {
} }
} }
pub fn add(&mut self, image: impl Into<DynamicImage>) -> TextureHandle { pub fn add(&mut self, image: impl Into<DynamicImage>) -> TextureHandle {
let view_idx = self.push(image.into()); let image = image.into();
let size = image.dimensions().into();
let view_idx = self.push(image);
// 0 == default in renderer; TODO: actually create samplers here // 0 == default in renderer; TODO: actually create samplers here
let sampler_idx = 0; let sampler_idx = 0;
TextureHandle { TextureHandle {
@@ -57,6 +61,7 @@ impl Textures {
view_idx, view_idx,
sampler_idx, sampler_idx,
}, },
size,
counter: RefCounter::new(), counter: RefCounter::new(),
send: self.send.clone(), send: self.send.clone(),
} }
@@ -89,12 +94,24 @@ impl Textures {
.as_ref() .as_ref()
.map(TextureUpdate::Push) .map(TextureUpdate::Push)
.unwrap_or(TextureUpdate::PushFree), .unwrap_or(TextureUpdate::PushFree),
Update::Set(i) => TextureUpdate::Set(i, self.images[i as usize].as_ref().unwrap()), 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), 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 { impl Drop for TextureHandle {
fn drop(&mut self) { fn drop(&mut self) {
if self.counter.drop() { if self.counter.drop() {

View File

@@ -10,7 +10,7 @@ use crate::{
}; };
use std::{ use std::{
any::{Any, TypeId}, any::{Any, TypeId},
ops::{Index, IndexMut, Range}, ops::{Deref, DerefMut, Index, IndexMut, Range},
sync::mpsc::{Receiver, Sender, channel}, sync::mpsc::{Receiver, Sender, channel},
}; };
@@ -35,7 +35,12 @@ pub struct Ui {
#[derive(Default)] #[derive(Default)]
pub struct Widgets { pub struct Widgets {
ids: IdTracker, ids: IdTracker,
map: HashMap<Id, Box<dyn Widget>>, map: HashMap<Id, WidgetData>,
}
pub struct WidgetData {
widget: Box<dyn Widget>,
borrowed: bool,
} }
impl Ui { impl Ui {
@@ -212,40 +217,57 @@ impl Widgets {
} }
} }
pub fn get_dyn(&self, id: &Id) -> &dyn Widget { pub fn get_dyn(&self, id: &Id) -> Option<&dyn Widget> {
self.map.get(id).unwrap().as_ref() Some(self.map.get(id)?.widget.as_ref())
}
pub fn get_dyn_mut(&mut self, id: &Id) -> Option<&mut dyn Widget> {
Some(self.map.get_mut(id)?.widget.as_mut())
}
/// get_dyn but dynamic borrow checking of widgets
/// lets you do recursive (tree) operations, like the painter does
pub fn get_dyn_dynamic(&self, id: &Id) -> WidgetWrapper<'_> {
// must guarantee no other mutable references to this widget exist
// done through the borrow variable
let data: &mut WidgetData = unsafe { std::mem::transmute(self.map.get(id)) };
if data.borrowed {
panic!("tried to mutably borrow the same widget twice");
}
WidgetWrapper {
widget: data.widget.as_mut(),
borrowed: &mut data.borrowed,
}
} }
pub fn get_static<W: Widget>(&self, id: &StaticWidgetId<W>) -> Option<&W> { pub fn get_static<W: Widget>(&self, id: &StaticWidgetId<W>) -> Option<&W> {
self.map.get(&id.id.id()).unwrap().as_any().downcast_ref() self.get_dyn(&id.id.id())?.as_any().downcast_ref()
} }
pub fn get_static_mut<W: Widget>(&mut self, id: &StaticWidgetId<W>) -> Option<&mut W> { pub fn get_static_mut<W: Widget>(&mut self, id: &StaticWidgetId<W>) -> Option<&mut W> {
self.map self.get_dyn_mut(&id.id.id())?.as_any_mut().downcast_mut()
.get_mut(&id.id.id())
.unwrap()
.as_any_mut()
.downcast_mut()
} }
pub fn get<W: Widget>(&self, id: &WidgetId<W>) -> Option<&W> { pub fn get<W: Widget>(&self, id: &WidgetId<W>) -> Option<&W> {
self.map.get(&id.id).unwrap().as_any().downcast_ref() self.get_dyn(&id.id)?.as_any().downcast_ref()
} }
pub fn get_mut<W: Widget>(&mut self, id: &WidgetId<W>) -> Option<&mut W> { pub fn get_mut<W: Widget>(&mut self, id: &WidgetId<W>) -> Option<&mut W> {
self.map self.get_dyn_mut(&id.id)?.as_any_mut().downcast_mut()
.get_mut(&id.id)
.unwrap()
.as_any_mut()
.downcast_mut()
} }
pub fn insert(&mut self, id: Id, widget: impl Widget) { pub fn insert(&mut self, id: Id, widget: impl Widget) {
self.map.insert(id, Box::new(widget)); self.insert_any(id, Box::new(widget));
} }
pub fn insert_any(&mut self, id: Id, widget: Box<dyn Widget>) { pub fn insert_any(&mut self, id: Id, widget: Box<dyn Widget>) {
self.map.insert(id, widget); self.map.insert(
id,
WidgetData {
widget,
borrowed: false,
},
);
} }
pub fn delete(&mut self, id: Id) { pub fn delete(&mut self, id: Id) {
@@ -266,6 +288,37 @@ impl Widgets {
} }
} }
pub struct WidgetWrapper<'a> {
widget: &'a mut dyn Widget,
borrowed: &'a mut bool,
}
impl<'a> WidgetWrapper<'a> {
pub fn new(widget: &'a mut dyn Widget, borrowed: &'a mut bool) -> Self {
Self { widget, borrowed }
}
}
impl Drop for WidgetWrapper<'_> {
fn drop(&mut self) {
*self.borrowed = false;
}
}
impl Deref for WidgetWrapper<'_> {
type Target = dyn Widget;
fn deref(&self) -> &Self::Target {
self.widget
}
}
impl DerefMut for WidgetWrapper<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.widget
}
}
impl dyn Widget { impl dyn Widget {
pub fn as_any(&self) -> &dyn Any { pub fn as_any(&self) -> &dyn Any {
self self

View File

@@ -1,9 +1,18 @@
use crate::layout::{Painter, Ui, WidgetId, WidgetIdFnRet}; use crate::layout::{Painter, TextData, Textures, Ui, Vec2, WidgetId, WidgetIdFnRet};
use std::{any::Any, marker::PhantomData}; use std::{any::Any, marker::PhantomData};
pub trait Widget: Any { pub trait Widget: Any {
fn draw(&self, painter: &mut Painter); fn draw(&mut self, painter: &mut Painter);
fn size(&mut self, ctx: SizeCtx) -> Vec2 {
ctx.size
}
}
pub struct SizeCtx<'a> {
pub size: Vec2,
pub text: &'a mut TextData,
pub textures: &'a mut Textures,
} }
pub struct WidgetTag; pub struct WidgetTag;

View File

@@ -21,6 +21,7 @@ impl GpuTextures {
match update { match update {
TextureUpdate::Push(image) => self.push(image), TextureUpdate::Push(image) => self.push(image),
TextureUpdate::Set(i, image) => self.set(i, image), TextureUpdate::Set(i, image) => self.set(i, image),
TextureUpdate::SetFree => self.view_count += 1,
TextureUpdate::Free(i) => self.free(i), TextureUpdate::Free(i) => self.free(i),
TextureUpdate::PushFree => self.push_free(), TextureUpdate::PushFree => self.push_free(),
} }

View File

@@ -88,11 +88,25 @@ impl Client {
(rect, text(label).size(30)).stack() (rect, text(label).size(30)).stack()
}; };
let text_test = ui.add_static(
(
text("this is a").size(30),
text("teeeeeeeest").size(30),
text("okkk\nokkkkkk!").size(30),
text("hmm").size(30),
text("a").size(30),
text("'").size(30),
text("pretty cool right?").size(30),
)
.span(Dir::RIGHT, [sized(); _]),
);
let tabs = ui.add( let tabs = ui.add(
( (
switch_button(Color::RED, pad_test, "pad test"), switch_button(Color::RED, pad_test, "pad test"),
switch_button(Color::GREEN, span_test, "span test"), switch_button(Color::GREEN, span_test, "span test"),
switch_button(Color::BLUE, span_add, "span add test"), switch_button(Color::BLUE, span_add, "span add test"),
switch_button(Color::MAGENTA, text_test, "text test"),
) )
.span(Dir::RIGHT, [1; _]), .span(Dir::RIGHT, [1; _]),
); );
@@ -102,7 +116,7 @@ impl Client {
let child = ui let child = ui
.add(image(include_bytes!("assets/sungals.png"))) .add(image(include_bytes!("assets/sungals.png")))
.erase_type(); .erase_type();
ui[span_add].children.push((child, ratio(1))); ui[span_add].children.push((child, sized()));
}) })
.region( .region(
UiPos::corner(Corner::BotRight) UiPos::corner(Corner::BotRight)
@@ -134,7 +148,7 @@ impl Client {
main, main,
add_button.label("add button"), add_button.label("add button"),
del_button.label("del button"), del_button.label("del button"),
info_sect.label("info sect"), // info_sect.label("info sect"),
) )
.stack() .stack()
.label("main stack"), .label("main stack"),