snap text on shader
This commit is contained in:
@@ -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<Ctx> Widget<Ctx> for Text {
|
||||
fn draw(&self, painter: &mut Painter<Ctx>) {
|
||||
painter.draw_text(&self.content);
|
||||
painter.draw_text(&self.content, &self.attrs);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn text(text: impl Into<String>) -> Text {
|
||||
Text {
|
||||
content: text.into(),
|
||||
attrs: TextAttrs::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<W = AnyWidget> {
|
||||
pub(super) ty: TypeId,
|
||||
pub(super) id: Id,
|
||||
counter: RefCounter,
|
||||
send: UiMsgSender,
|
||||
send: Sender<Id>,
|
||||
_pd: PhantomData<W>,
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ impl<W> Clone for WidgetId<W> {
|
||||
}
|
||||
|
||||
impl<W> WidgetId<W> {
|
||||
pub(super) fn new(id: Id, ty: TypeId, send: UiMsgSender) -> Self {
|
||||
pub(super) fn new(id: Id, ty: TypeId, send: Sender<Id>) -> Self {
|
||||
Self {
|
||||
ty,
|
||||
id,
|
||||
@@ -73,7 +73,7 @@ impl<W> WidgetId<W> {
|
||||
impl<W> Drop for WidgetId<W> {
|
||||
fn drop(&mut self) {
|
||||
if self.counter.drop() {
|
||||
let _ = self.send.send(UiMsg::FreeWidget(self.id.duplicate()));
|
||||
let _ = self.send.send(self.id.duplicate());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Ctx>,
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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<Vec2>) {
|
||||
self.offset += offset.into();
|
||||
}
|
||||
|
||||
pub const fn shifted(mut self, offset: Vec2) -> Self {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<u32>,
|
||||
}
|
||||
|
||||
/// a texture manager for a ui
|
||||
@@ -21,7 +20,8 @@ pub struct Textures {
|
||||
free: Vec<u32>,
|
||||
images: Vec<Option<DynamicImage>>,
|
||||
updates: Vec<Update>,
|
||||
send: UiMsgSender,
|
||||
send: Sender<u32>,
|
||||
recv: Receiver<u32>,
|
||||
}
|
||||
|
||||
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<DynamicImage>) -> TextureHandle {
|
||||
@@ -72,11 +74,13 @@ impl Textures {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn free(&mut self, idx: u32) {
|
||||
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<Item = TextureUpdate<'_>> {
|
||||
self.updates.drain(..).map(|u| match u {
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,8 +19,8 @@ pub struct Ui<Ctx> {
|
||||
base: Option<WidgetId>,
|
||||
widgets: Widgets<Ctx>,
|
||||
updates: Vec<WidgetId>,
|
||||
recv: Receiver<UiMsg>,
|
||||
send: UiMsgSender,
|
||||
recv: Receiver<Id>,
|
||||
send: Sender<Id>,
|
||||
size: Vec2,
|
||||
// TODO: make these non pub(crate)
|
||||
pub(crate) primitives: Primitives,
|
||||
@@ -32,12 +32,6 @@ pub struct Ui<Ctx> {
|
||||
pub(super) sensor_map: SensorMap<Ctx>,
|
||||
}
|
||||
|
||||
pub enum UiMsg {
|
||||
FreeWidget(Id),
|
||||
FreeTexture(u32),
|
||||
}
|
||||
pub type UiMsgSender = Sender<UiMsg>;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Widgets<Ctx> {
|
||||
ids: IdTracker,
|
||||
@@ -96,7 +90,6 @@ impl<Ctx> Ui<Ctx> {
|
||||
|
||||
pub fn resize(&mut self, size: impl Into<Vec2>) {
|
||||
self.size = size.into();
|
||||
self.full_redraw = true;
|
||||
}
|
||||
|
||||
pub fn redraw_all(&mut self, ctx: &mut Ctx)
|
||||
@@ -105,6 +98,7 @@ impl<Ctx> Ui<Ctx> {
|
||||
{
|
||||
self.active_sensors.clear();
|
||||
self.primitives.clear();
|
||||
self.free();
|
||||
let mut painter = Painter::new(
|
||||
&self.widgets,
|
||||
&mut self.primitives,
|
||||
@@ -124,8 +118,6 @@ impl<Ctx> Ui<Ctx> {
|
||||
where
|
||||
Ctx: 'static,
|
||||
{
|
||||
self.recv_msgs();
|
||||
|
||||
if self.full_redraw {
|
||||
self.redraw_all(ctx);
|
||||
self.full_redraw = false;
|
||||
@@ -136,13 +128,12 @@ impl<Ctx> Ui<Ctx> {
|
||||
}
|
||||
}
|
||||
|
||||
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<Ctx: 'static> Default for Ui<Ctx> {
|
||||
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(),
|
||||
|
||||
@@ -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<f32>(
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user