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