diff --git a/src/core/text.rs b/src/core/text.rs index 93b8bb2..4f089ff 100644 --- a/src/core/text.rs +++ b/src/core/text.rs @@ -2,16 +2,33 @@ use crate::prelude::*; pub struct Text { content: String, + attrs: TextAttrs, +} + +impl Text { + pub fn size(mut self, size: impl UiNum) -> Self { + self.attrs.size = size.to_f32(); + self + } + pub fn color(mut self, color: UiColor) -> Self { + self.attrs.color = color; + self + } + pub fn line_height(mut self, height: f32) -> Self { + self.attrs.line_height = height; + self + } } impl Widget for Text { fn draw(&self, painter: &mut Painter) { - painter.draw_text(&self.content); + painter.draw_text(&self.content, &self.attrs); } } pub fn text(text: impl Into) -> Text { Text { content: text.into(), + attrs: TextAttrs::default(), } } diff --git a/src/layout/id.rs b/src/layout/id.rs index 5d3341d..0a167dd 100644 --- a/src/layout/id.rs +++ b/src/layout/id.rs @@ -1,7 +1,7 @@ -use std::{any::TypeId, marker::PhantomData}; +use std::{any::TypeId, marker::PhantomData, sync::mpsc::Sender}; use crate::{ - layout::{FnTag, Ui, UiMsg, UiMsgSender, Widget, WidgetLike, WidgetTag}, + layout::{FnTag, Ui, Widget, WidgetLike, WidgetTag}, util::{Id, RefCounter}, }; @@ -18,7 +18,7 @@ pub struct WidgetId { pub(super) ty: TypeId, pub(super) id: Id, counter: RefCounter, - send: UiMsgSender, + send: Sender, _pd: PhantomData, } @@ -41,7 +41,7 @@ impl Clone for WidgetId { } impl WidgetId { - pub(super) fn new(id: Id, ty: TypeId, send: UiMsgSender) -> Self { + pub(super) fn new(id: Id, ty: TypeId, send: Sender) -> Self { Self { ty, id, @@ -73,7 +73,7 @@ impl WidgetId { impl Drop for WidgetId { fn drop(&mut self) { if self.counter.drop() { - let _ = self.send.send(UiMsg::FreeWidget(self.id.duplicate())); + let _ = self.send.send(self.id.duplicate()); } } } diff --git a/src/layout/painter.rs b/src/layout/painter.rs index 8518dd6..f920e79 100644 --- a/src/layout/painter.rs +++ b/src/layout/painter.rs @@ -2,8 +2,8 @@ use image::GenericImageView; use crate::{ layout::{ - ActiveSensors, SensorMap, TextData, TextureHandle, Textures, UiPos, UiRegion, Vec2, - WidgetId, Widgets, + ActiveSensors, SensorMap, TextAttrs, TextData, TextureHandle, Textures, UiPos, UiRegion, + Vec2, WidgetId, Widgets, }, render::{Primitive, Primitives}, }; @@ -21,6 +21,7 @@ pub struct Painter<'a, Ctx: 'static> { } impl<'a, Ctx> Painter<'a, Ctx> { + #[allow(clippy::too_many_arguments)] pub(crate) fn new( nodes: &'a Widgets, primitives: &'a mut Primitives, @@ -94,16 +95,14 @@ impl<'a, Ctx> Painter<'a, Ctx> { self.region = old; } - pub fn draw_text(&mut self, content: &str) { - let handle = self.text.draw(content, self.textures); + pub fn draw_text(&mut self, content: &str, attrs: &TextAttrs) { + let handle = self.text.draw(content, attrs, self.textures); let dims: Vec2 = self.textures[&handle].dimensions().into(); - let center = self.region.center().snap(self.screen_size); - let top_left = center - (dims / 2.0).floor(); - let bot_right = center + (dims / 2.0).ceil(); - let region = UiRegion { - top_left: UiPos::offset(top_left), - bot_right: UiPos::offset(bot_right), - }; + let mut region = self.region.center().expand(dims); + // TODO: I feel like this shouldn't be needed + // what this does is makes sure the text doesn't get squeezed into one pixel less + // I'm unsure exactly why it happens or if this will ever expand it too much + region.bot_right.shift(0.01); self.draw_texture_at(&handle, region); } diff --git a/src/layout/pos.rs b/src/layout/pos.rs index 8cf30be..cc39304 100644 --- a/src/layout/pos.rs +++ b/src/layout/pos.rs @@ -42,8 +42,8 @@ impl UiPos { Self::anchor(corner.anchor()) } - pub const fn shift(&mut self, offset: Vec2) { - self.offset += offset; + pub const fn shift(&mut self, offset: impl const Into) { + self.offset += offset.into(); } pub const fn shifted(mut self, offset: Vec2) -> Self { diff --git a/src/layout/text.rs b/src/layout/text.rs index 8cf3ee9..a63abb6 100644 --- a/src/layout/text.rs +++ b/src/layout/text.rs @@ -3,26 +3,48 @@ use image::{Rgba, RgbaImage}; use crate::{ HashMap, - layout::{TextureHandle, Textures}, + layout::{TextureHandle, Textures, UiColor}, }; pub(crate) struct TextData { font_system: FontSystem, - cache: SwashCache, + swash_cache: SwashCache, } impl Default for TextData { fn default() -> Self { Self { font_system: FontSystem::new(), - cache: SwashCache::new(), + swash_cache: SwashCache::new(), + } + } +} + +pub struct TextAttrs { + pub color: UiColor, + pub size: f32, + pub line_height: f32, +} + +impl Default for TextAttrs { + fn default() -> Self { + let size = 14.0; + Self { + color: UiColor::WHITE, + size, + line_height: size * 1.2, } } } impl TextData { - pub fn draw(&mut self, content: &str, textures: &mut Textures) -> TextureHandle { - let metrics = Metrics::new(30.0, 30.0); + pub fn draw( + &mut self, + content: &str, + attrs: &TextAttrs, + textures: &mut Textures, + ) -> TextureHandle { + let metrics = Metrics::new(attrs.size, attrs.line_height); let mut buffer = Buffer::new(&mut self.font_system, metrics); let mut buffer = buffer.borrow_with(&mut self.font_system); buffer.set_text(content, &Attrs::new(), Shaping::Advanced); @@ -33,9 +55,10 @@ impl TextData { let mut min_y = 0; let mut max_x = 0; let mut max_y = 0; + let c = attrs.color; buffer.draw( - &mut self.cache, - cosmic_text::Color::rgb(0xff, 0xff, 0xff), + &mut self.swash_cache, + cosmic_text::Color::rgba(c.r, c.g, c.b, c.a), |x, y, _, _, color| { min_x = min_x.min(x); min_y = min_y.min(y); diff --git a/src/layout/texture.rs b/src/layout/texture.rs index 3fbef99..9d8e384 100644 --- a/src/layout/texture.rs +++ b/src/layout/texture.rs @@ -1,18 +1,17 @@ -use std::ops::Index; +use std::{ + ops::Index, + sync::mpsc::{Receiver, Sender, channel}, +}; use image::DynamicImage; -use crate::{ - layout::{UiMsg, UiMsgSender}, - render::TexturePrimitive, - util::RefCounter, -}; +use crate::{render::TexturePrimitive, util::RefCounter}; #[derive(Clone)] pub struct TextureHandle { pub inner: TexturePrimitive, counter: RefCounter, - send: UiMsgSender, + send: Sender, } /// a texture manager for a ui @@ -21,7 +20,8 @@ pub struct Textures { free: Vec, images: Vec>, updates: Vec, - send: UiMsgSender, + send: Sender, + recv: Receiver, } pub enum TextureUpdate<'a> { @@ -37,12 +37,14 @@ enum Update { } impl Textures { - pub fn new(send: UiMsgSender) -> Self { + pub fn new() -> Self { + let (send, recv) = channel(); Self { free: Vec::new(), images: Vec::new(), updates: Vec::new(), send, + recv, } } pub fn add(&mut self, image: impl Into) -> TextureHandle { @@ -72,10 +74,12 @@ impl Textures { } } - pub fn free(&mut self, idx: u32) { - self.images[idx as usize] = None; - self.updates.push(Update::Free(idx)); - self.free.push(idx); + pub fn free(&mut self) { + for idx in self.recv.try_iter() { + self.images[idx as usize] = None; + self.updates.push(Update::Free(idx)); + self.free.push(idx); + } } pub fn updates(&mut self) -> impl Iterator> { @@ -90,7 +94,7 @@ impl Textures { impl Drop for TextureHandle { fn drop(&mut self) { if self.counter.drop() { - let _ = self.send.send(UiMsg::FreeTexture(self.inner.view_idx)); + let _ = self.send.send(self.inner.view_idx); } } } @@ -102,3 +106,9 @@ impl Index<&TextureHandle> for Textures { self.images[index.inner.view_idx as usize].as_ref().unwrap() } } + +impl Default for Textures { + fn default() -> Self { + Self::new() + } +} diff --git a/src/layout/ui.rs b/src/layout/ui.rs index 0d666a5..6784347 100644 --- a/src/layout/ui.rs +++ b/src/layout/ui.rs @@ -19,8 +19,8 @@ pub struct Ui { base: Option, widgets: Widgets, updates: Vec, - recv: Receiver, - send: UiMsgSender, + recv: Receiver, + send: Sender, size: Vec2, // TODO: make these non pub(crate) pub(crate) primitives: Primitives, @@ -32,12 +32,6 @@ pub struct Ui { pub(super) sensor_map: SensorMap, } -pub enum UiMsg { - FreeWidget(Id), - FreeTexture(u32), -} -pub type UiMsgSender = Sender; - #[derive(Default)] pub struct Widgets { ids: IdTracker, @@ -96,7 +90,6 @@ impl Ui { pub fn resize(&mut self, size: impl Into) { self.size = size.into(); - self.full_redraw = true; } pub fn redraw_all(&mut self, ctx: &mut Ctx) @@ -105,6 +98,7 @@ impl Ui { { self.active_sensors.clear(); self.primitives.clear(); + self.free(); let mut painter = Painter::new( &self.widgets, &mut self.primitives, @@ -124,8 +118,6 @@ impl Ui { where Ctx: 'static, { - self.recv_msgs(); - if self.full_redraw { self.redraw_all(ctx); self.full_redraw = false; @@ -136,13 +128,12 @@ impl Ui { } } - fn recv_msgs(&mut self) { - while let Ok(msg) = self.recv.try_recv() { - match msg { - UiMsg::FreeWidget(id) => self.widgets.delete(id), - UiMsg::FreeTexture(id) => self.textures.free(id), - } + /// free any resources that don't have references anymore + fn free(&mut self) { + for id in self.recv.try_iter() { + self.widgets.delete(id); } + self.textures.free(); } pub fn needs_redraw(&self) -> bool { @@ -237,7 +228,7 @@ impl Default for Ui { widgets: Widgets::new(), updates: Default::default(), primitives: Default::default(), - textures: Textures::new(send.clone()), + textures: Textures::new(), text: TextData::default(), full_redraw: false, active_sensors: Default::default(), diff --git a/src/render/shader.wgsl b/src/render/shader.wgsl index dcd08a6..a726477 100644 --- a/src/render/shader.wgsl +++ b/src/render/shader.wgsl @@ -61,8 +61,8 @@ fn vs_main( ) -> VertexOutput { var out: VertexOutput; - let top_left = in.top_left_anchor * window.dim + in.top_left_offset; - let bot_right = in.bottom_right_anchor * window.dim + in.bottom_right_offset; + let top_left = round(in.top_left_anchor * window.dim) + in.top_left_offset; + let bot_right = round(in.bottom_right_anchor * window.dim) + in.bottom_right_offset; let size = bot_right - top_left; let uv = vec2( diff --git a/src/render/texture.rs b/src/render/texture.rs index 2bc3468..b78c19c 100644 --- a/src/render/texture.rs +++ b/src/render/texture.rs @@ -23,9 +23,6 @@ impl GpuTextures { TextureUpdate::Free(i) => self.free(i), } } - // if changed { - // println!("{}", self.views.len()); - // } changed } fn set(&mut self, i: u32, image: &DynamicImage) { diff --git a/src/testing/mod.rs b/src/testing/mod.rs index f8704fb..97d79cb 100644 --- a/src/testing/mod.rs +++ b/src/testing/mod.rs @@ -98,7 +98,7 @@ impl Client { .edit_on(Sense::HoverEnd, move |r, _| { r.color = color; }); - (rect, text(label)).stack() + (rect, text(label).size(30)).stack() } let tabs = ui.add(