text update for more code reuse + much better caching
This commit is contained in:
@@ -8,7 +8,6 @@ mod sized;
|
||||
mod span;
|
||||
mod stack;
|
||||
mod text;
|
||||
mod text_edit;
|
||||
mod trait_fns;
|
||||
|
||||
pub use align::*;
|
||||
@@ -21,5 +20,4 @@ pub use sized::*;
|
||||
pub use span::*;
|
||||
pub use stack::*;
|
||||
pub use text::*;
|
||||
pub use text_edit::*;
|
||||
pub use trait_fns::*;
|
||||
|
||||
140
src/core/text.rs
140
src/core/text.rs
@@ -1,140 +0,0 @@
|
||||
use cosmic_text::{Attrs, Family, FontSystem, Metrics, Shaping};
|
||||
|
||||
use crate::{prelude::*, util::MutDetect};
|
||||
|
||||
pub struct Text {
|
||||
pub content: MutDetect<String>,
|
||||
pub attrs: TextAttrs,
|
||||
/// inner alignment of text region (within where its drawn)
|
||||
pub align: Align,
|
||||
pub(super) buf: TextBuffer,
|
||||
size: Vec2,
|
||||
}
|
||||
|
||||
impl Text {
|
||||
pub fn size(mut self, size: impl UiNum) -> Self {
|
||||
self.attrs.font_size = size.to_f32();
|
||||
self.attrs.line_height = self.attrs.font_size * 1.1;
|
||||
self
|
||||
}
|
||||
pub fn color(mut self, color: UiColor) -> Self {
|
||||
self.attrs.color = color;
|
||||
self
|
||||
}
|
||||
pub fn family(mut self, family: Family<'static>) -> Self {
|
||||
self.attrs.family = family;
|
||||
self
|
||||
}
|
||||
pub fn line_height(mut self, height: f32) -> Self {
|
||||
self.attrs.line_height = height;
|
||||
self
|
||||
}
|
||||
pub fn new(content: impl Into<String>) -> Self {
|
||||
let attrs = TextAttrs::default();
|
||||
Self {
|
||||
content: content.into().into(),
|
||||
buf: TextBuffer::new_empty(Metrics::new(attrs.font_size, attrs.line_height)),
|
||||
attrs,
|
||||
align: Align::Center,
|
||||
size: Vec2::ZERO,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn region(&self) -> UiRegion {
|
||||
UiRegion::from_size_align(self.size, self.align)
|
||||
}
|
||||
|
||||
fn update_buf(&mut self, font_system: &mut FontSystem, width: Option<f32>) {
|
||||
self.attrs.apply(font_system, &mut self.buf, width);
|
||||
if self.content.changed {
|
||||
self.content.changed = false;
|
||||
self.buf.set_text(
|
||||
font_system,
|
||||
&self.content,
|
||||
&Attrs::new().family(self.attrs.family),
|
||||
Shaping::Advanced,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for Text {
|
||||
fn draw(&mut self, painter: &mut Painter) {
|
||||
let width = if self.attrs.wrap {
|
||||
Some(painter.px_size().x)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let font_system = &mut painter.text_data().font_system;
|
||||
self.update_buf(font_system, width);
|
||||
let (handle, offset) = painter.render_text(&mut self.buf, &self.attrs);
|
||||
let dims = handle.size();
|
||||
self.size = offset.size(&handle);
|
||||
let mut region = self.region();
|
||||
region.top_left.abs += offset.top_left;
|
||||
region.bot_right.abs = region.top_left.abs + dims;
|
||||
painter.texture_within(&handle, region);
|
||||
}
|
||||
|
||||
fn desired_size(&mut self, ctx: &mut SizeCtx) -> UiVec2 {
|
||||
self.update_buf(&mut ctx.text.font_system, None);
|
||||
let (handle, offset) = ctx.draw_text(&mut self.buf, &self.attrs);
|
||||
UiVec2::abs(offset.size(&handle))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn text(content: impl Into<String>) -> TextBuilder {
|
||||
TextBuilder {
|
||||
content: content.into(),
|
||||
align: Align::Center,
|
||||
attrs: TextAttrs::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TextBuilder {
|
||||
pub content: String,
|
||||
pub attrs: TextAttrs,
|
||||
/// inner alignment of text region (within where its drawn)
|
||||
pub align: Align,
|
||||
}
|
||||
|
||||
impl TextBuilder {
|
||||
pub fn size(mut self, size: impl UiNum) -> Self {
|
||||
self.attrs.font_size = size.to_f32();
|
||||
self.attrs.line_height = self.attrs.font_size * 1.1;
|
||||
self
|
||||
}
|
||||
pub fn color(mut self, color: UiColor) -> Self {
|
||||
self.attrs.color = color;
|
||||
self
|
||||
}
|
||||
pub fn family(mut self, family: Family<'static>) -> Self {
|
||||
self.attrs.family = family;
|
||||
self
|
||||
}
|
||||
pub fn line_height(mut self, height: f32) -> Self {
|
||||
self.attrs.line_height = height;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl FnOnce<(&mut Ui,)> for TextBuilder {
|
||||
type Output = Text;
|
||||
|
||||
extern "rust-call" fn call_once(self, args: (&mut Ui,)) -> Self::Output {
|
||||
let mut buf =
|
||||
TextBuffer::new_empty(Metrics::new(self.attrs.font_size, self.attrs.line_height));
|
||||
let font_system = &mut args.0.data.text.font_system;
|
||||
buf.set_text(font_system, &self.content, &Attrs::new(), Shaping::Advanced);
|
||||
let mut text = Text {
|
||||
content: self.content.into(),
|
||||
buf,
|
||||
attrs: self.attrs,
|
||||
align: self.align,
|
||||
size: Vec2::ZERO,
|
||||
};
|
||||
text.content.changed = false;
|
||||
self.attrs.apply(font_system, &mut text.buf, None);
|
||||
text
|
||||
}
|
||||
}
|
||||
117
src/core/text/build.rs
Normal file
117
src/core/text/build.rs
Normal file
@@ -0,0 +1,117 @@
|
||||
use std::marker::{PhantomData, Sized};
|
||||
|
||||
use crate::prelude::*;
|
||||
use cosmic_text::{Attrs, Family, Metrics, Shaping};
|
||||
|
||||
pub trait TextBuilderOutput: Sized {
|
||||
type Output;
|
||||
fn run(ui: &mut Ui, builder: TextBuilder<Self>) -> Self::Output;
|
||||
}
|
||||
|
||||
pub struct TextBuilder<O = TextOutput> {
|
||||
pub content: String,
|
||||
pub attrs: TextAttrs,
|
||||
_pd: PhantomData<O>,
|
||||
}
|
||||
|
||||
impl<O> TextBuilder<O> {
|
||||
pub fn size(mut self, size: impl UiNum) -> Self {
|
||||
self.attrs.font_size = size.to_f32();
|
||||
self.attrs.line_height = self.attrs.font_size * 1.1;
|
||||
self
|
||||
}
|
||||
pub fn color(mut self, color: UiColor) -> Self {
|
||||
self.attrs.color = color;
|
||||
self
|
||||
}
|
||||
pub fn family(mut self, family: Family<'static>) -> Self {
|
||||
self.attrs.family = family;
|
||||
self
|
||||
}
|
||||
pub fn line_height(mut self, height: f32) -> Self {
|
||||
self.attrs.line_height = height;
|
||||
self
|
||||
}
|
||||
pub fn text_align(mut self, align: Align) -> Self {
|
||||
self.attrs.align = align;
|
||||
self
|
||||
}
|
||||
pub fn wrap(mut self, wrap: bool) -> Self {
|
||||
self.attrs.wrap = wrap;
|
||||
self
|
||||
}
|
||||
pub fn editable(self) -> TextBuilder<TextEditOutput> {
|
||||
TextBuilder {
|
||||
content: self.content,
|
||||
attrs: self.attrs,
|
||||
_pd: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TextOutput;
|
||||
impl TextBuilderOutput for TextOutput {
|
||||
type Output = Text;
|
||||
|
||||
fn run(ui: &mut Ui, builder: TextBuilder<Self>) -> Self::Output {
|
||||
let mut buf = TextBuffer::new_empty(Metrics::new(
|
||||
builder.attrs.font_size,
|
||||
builder.attrs.line_height,
|
||||
));
|
||||
let font_system = &mut ui.data.text.font_system;
|
||||
buf.set_text(
|
||||
font_system,
|
||||
&builder.content,
|
||||
&Attrs::new(),
|
||||
Shaping::Advanced,
|
||||
);
|
||||
let mut text = Text {
|
||||
content: builder.content.into(),
|
||||
view: TextView::new(buf, builder.attrs),
|
||||
};
|
||||
text.content.changed = false;
|
||||
builder.attrs.apply(font_system, &mut text.view.buf, None);
|
||||
text
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TextEditOutput;
|
||||
impl TextBuilderOutput for TextEditOutput {
|
||||
type Output = TextEdit;
|
||||
|
||||
fn run(ui: &mut Ui, builder: TextBuilder<Self>) -> Self::Output {
|
||||
let buf = TextBuffer::new_empty(Metrics::new(
|
||||
builder.attrs.font_size,
|
||||
builder.attrs.line_height,
|
||||
));
|
||||
let mut text = TextEdit {
|
||||
view: TextView::new(buf, builder.attrs),
|
||||
cursor: None,
|
||||
};
|
||||
let font_system = &mut ui.data.text.font_system;
|
||||
text.buf.set_text(
|
||||
font_system,
|
||||
&builder.content,
|
||||
&Attrs::new(),
|
||||
Shaping::Advanced,
|
||||
);
|
||||
builder.attrs.apply(font_system, &mut text.buf, None);
|
||||
text
|
||||
}
|
||||
}
|
||||
|
||||
impl<O: TextBuilderOutput> FnOnce<(&mut Ui,)> for TextBuilder<O> {
|
||||
type Output = O::Output;
|
||||
|
||||
extern "rust-call" fn call_once(self, args: (&mut Ui,)) -> Self::Output {
|
||||
O::run(args.0, self)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn text(content: impl Into<String>) -> TextBuilder {
|
||||
TextBuilder {
|
||||
content: content.into(),
|
||||
attrs: TextAttrs::default(),
|
||||
_pd: PhantomData,
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use crate::prelude::*;
|
||||
use cosmic_text::{Affinity, Attrs, Cursor, Family, FontSystem, Metrics, Motion, Shaping};
|
||||
use cosmic_text::{Affinity, Attrs, Cursor, FontSystem, Motion, Shaping};
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
use winit::{
|
||||
event::KeyEvent,
|
||||
@@ -7,38 +9,24 @@ use winit::{
|
||||
};
|
||||
|
||||
pub struct TextEdit {
|
||||
pub attrs: TextAttrs,
|
||||
/// inner alignment of text region (within where its drawn)
|
||||
pub align: Align,
|
||||
buf: TextBuffer,
|
||||
cursor: Option<Cursor>,
|
||||
size: Vec2,
|
||||
pub(super) view: TextView,
|
||||
pub(super) cursor: Option<Cursor>,
|
||||
}
|
||||
|
||||
impl TextEdit {
|
||||
pub fn region(&self) -> UiRegion {
|
||||
UiRegion::from_size_align(self.size, self.align)
|
||||
UiRegion::from_size_align(
|
||||
self.tex().map(|t| t.size()).unwrap_or(Vec2::ZERO),
|
||||
self.align,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for TextEdit {
|
||||
fn draw(&mut self, painter: &mut Painter) {
|
||||
let width = if self.attrs.wrap {
|
||||
Some(painter.px_size().x)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let font_system = &mut painter.text_data().font_system;
|
||||
self.attrs.apply(font_system, &mut self.buf, width);
|
||||
self.buf.shape_until_scroll(font_system, false);
|
||||
let (handle, tex_offset) = painter.render_text(&mut self.buf, &self.attrs);
|
||||
let dims = handle.size();
|
||||
self.size = tex_offset.size(&handle);
|
||||
let region = self.region();
|
||||
let mut tex_region = region;
|
||||
tex_region.top_left.abs += tex_offset.top_left;
|
||||
tex_region.bot_right.abs = tex_region.top_left.abs + dims;
|
||||
painter.texture_within(&handle, tex_region);
|
||||
let tex = self.view.draw(&mut painter.size_ctx());
|
||||
let region = text_region(&tex, self.align);
|
||||
painter.texture_within(&tex.handle, region);
|
||||
|
||||
if let Some(cursor) = &self.cursor
|
||||
&& let Some(offset) = cursor_pos(cursor, &self.buf)
|
||||
@@ -50,24 +38,11 @@ impl Widget for TextEdit {
|
||||
.offset(offset)
|
||||
.within(®ion),
|
||||
);
|
||||
} else {
|
||||
// keep number of primitives constant so shifting isn't needed
|
||||
painter.primitive(RectPrimitive::color(Color::NONE));
|
||||
}
|
||||
}
|
||||
|
||||
fn desired_size(&mut self, ctx: &mut SizeCtx) -> UiVec2 {
|
||||
let width = if self.attrs.wrap {
|
||||
Some(ctx.px_size().x)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
self.attrs
|
||||
.apply(&mut ctx.text.font_system, &mut self.buf, width);
|
||||
self.buf
|
||||
.shape_until_scroll(&mut ctx.text.font_system, false);
|
||||
let (handle, offset) = ctx.draw_text(&mut self.buf, &self.attrs);
|
||||
UiVec2::abs(offset.size(&handle))
|
||||
UiVec2::abs(self.view.draw(ctx).size())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,41 +79,6 @@ fn cursor_pos(cursor: &Cursor, buf: &TextBuffer) -> Option<(f32, f32)> {
|
||||
prev
|
||||
}
|
||||
|
||||
pub struct TextEditBuilder {
|
||||
pub content: String,
|
||||
pub attrs: TextAttrs,
|
||||
/// inner alignment of text region (within where its drawn)
|
||||
pub align: Align,
|
||||
}
|
||||
|
||||
impl TextEditBuilder {
|
||||
pub fn size(mut self, size: impl UiNum) -> Self {
|
||||
self.attrs.font_size = size.to_f32();
|
||||
self.attrs.line_height = self.attrs.font_size * 1.1;
|
||||
self
|
||||
}
|
||||
pub fn color(mut self, color: UiColor) -> Self {
|
||||
self.attrs.color = color;
|
||||
self
|
||||
}
|
||||
pub fn family(mut self, family: Family<'static>) -> Self {
|
||||
self.attrs.family = family;
|
||||
self
|
||||
}
|
||||
pub fn line_height(mut self, height: f32) -> Self {
|
||||
self.attrs.line_height = height;
|
||||
self
|
||||
}
|
||||
pub fn text_align(mut self, align: Align) -> Self {
|
||||
self.align = align;
|
||||
self
|
||||
}
|
||||
pub fn wrap(mut self, wrap: bool) -> Self {
|
||||
self.attrs.wrap = wrap;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TextEditCtx<'a> {
|
||||
pub text: &'a mut TextEdit,
|
||||
pub font_system: &'a mut FontSystem,
|
||||
@@ -190,7 +130,7 @@ impl<'a> TextEditCtx<'a> {
|
||||
|
||||
fn insert_inner(&mut self, text: &str) {
|
||||
if let Some(cursor) = &mut self.text.cursor {
|
||||
let line = &mut self.text.buf.lines[cursor.line];
|
||||
let line = &mut self.text.view.buf.lines[cursor.line];
|
||||
let mut line_text = line.text().to_string();
|
||||
line_text.insert_str(cursor.index, text);
|
||||
line.set_text(line_text, line.ending(), line.attrs_list().clone());
|
||||
@@ -202,10 +142,11 @@ impl<'a> TextEditCtx<'a> {
|
||||
|
||||
pub fn newline(&mut self) {
|
||||
if let Some(cursor) = &mut self.text.cursor {
|
||||
let line = &mut self.text.buf.lines[cursor.line];
|
||||
let lines = &mut self.text.view.buf.lines;
|
||||
let line = &mut lines[cursor.line];
|
||||
let new = line.split_off(cursor.index);
|
||||
cursor.line += 1;
|
||||
self.text.buf.lines.insert(cursor.line, new);
|
||||
lines.insert(cursor.line, new);
|
||||
cursor.index = 0;
|
||||
}
|
||||
}
|
||||
@@ -221,13 +162,14 @@ impl<'a> TextEditCtx<'a> {
|
||||
|
||||
pub fn delete(&mut self) {
|
||||
if let Some(cursor) = &mut self.text.cursor {
|
||||
let line = &mut self.text.buf.lines[cursor.line];
|
||||
let lines = &mut self.text.view.buf.lines;
|
||||
let line = &mut lines[cursor.line];
|
||||
if cursor.index == line.text().len() {
|
||||
if cursor.line == self.text.buf.lines.len() - 1 {
|
||||
if cursor.line == lines.len() - 1 {
|
||||
return;
|
||||
}
|
||||
let add = self.text.buf.lines.remove(cursor.line + 1).into_text();
|
||||
let line = &mut self.text.buf.lines[cursor.line];
|
||||
let add = lines.remove(cursor.line + 1).into_text();
|
||||
let line = &mut lines[cursor.line];
|
||||
let mut cur = line.text().to_string();
|
||||
cur.push_str(&add);
|
||||
line.set_text(cur, line.ending(), line.attrs_list().clone());
|
||||
@@ -311,29 +253,16 @@ impl TextInputResult {
|
||||
}
|
||||
}
|
||||
|
||||
impl FnOnce<(&mut Ui,)> for TextEditBuilder {
|
||||
type Output = TextEdit;
|
||||
impl Deref for TextEdit {
|
||||
type Target = TextView;
|
||||
|
||||
extern "rust-call" fn call_once(self, args: (&mut Ui,)) -> Self::Output {
|
||||
let mut text = TextEdit {
|
||||
buf: TextBuffer::new_empty(Metrics::new(self.attrs.font_size, self.attrs.line_height)),
|
||||
attrs: self.attrs,
|
||||
align: self.align,
|
||||
cursor: None,
|
||||
size: Vec2::ZERO,
|
||||
};
|
||||
let font_system = &mut args.0.data.text.font_system;
|
||||
text.buf
|
||||
.set_text(font_system, &self.content, &Attrs::new(), Shaping::Advanced);
|
||||
self.attrs.apply(font_system, &mut text.buf, None);
|
||||
text
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.view
|
||||
}
|
||||
}
|
||||
|
||||
pub fn text_edit(content: impl Into<String>) -> TextEditBuilder {
|
||||
TextEditBuilder {
|
||||
content: content.into(),
|
||||
attrs: TextAttrs::default(),
|
||||
align: Align::Center,
|
||||
impl DerefMut for TextEdit {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.view
|
||||
}
|
||||
}
|
||||
130
src/core/text/mod.rs
Normal file
130
src/core/text/mod.rs
Normal file
@@ -0,0 +1,130 @@
|
||||
mod build;
|
||||
mod edit;
|
||||
|
||||
pub use build::*;
|
||||
pub use edit::*;
|
||||
|
||||
use crate::{prelude::*, util::MutDetect};
|
||||
use cosmic_text::{Attrs, Metrics, Shaping};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
pub struct Text {
|
||||
pub content: MutDetect<String>,
|
||||
view: TextView,
|
||||
}
|
||||
|
||||
pub struct TextView {
|
||||
pub attrs: MutDetect<TextAttrs>,
|
||||
pub buf: MutDetect<TextBuffer>,
|
||||
// cache
|
||||
tex: Option<TextTexture>,
|
||||
width: Option<f32>,
|
||||
}
|
||||
|
||||
impl TextView {
|
||||
pub fn new(buf: TextBuffer, attrs: TextAttrs) -> Self {
|
||||
Self {
|
||||
attrs: attrs.into(),
|
||||
buf: buf.into(),
|
||||
tex: None,
|
||||
width: None,
|
||||
}
|
||||
}
|
||||
pub fn draw(&mut self, ctx: &mut SizeCtx) -> TextTexture {
|
||||
let width = if self.attrs.wrap {
|
||||
Some(ctx.px_size().x)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if width == self.width
|
||||
&& let Some(tex) = &self.tex
|
||||
&& !self.attrs.changed
|
||||
&& !self.buf.changed
|
||||
{
|
||||
return tex.clone();
|
||||
}
|
||||
self.width = width;
|
||||
let font_system = &mut ctx.text.font_system;
|
||||
self.attrs.apply(font_system, &mut self.buf, width);
|
||||
self.buf.shape_until_scroll(font_system, false);
|
||||
let tex = ctx.draw_text(&mut self.buf, &self.attrs);
|
||||
self.tex = Some(tex.clone());
|
||||
self.attrs.changed = false;
|
||||
self.buf.changed = false;
|
||||
tex
|
||||
}
|
||||
pub fn tex(&self) -> Option<&TextTexture> {
|
||||
self.tex.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl Text {
|
||||
pub fn new(content: impl Into<String>) -> Self {
|
||||
let attrs = TextAttrs::default();
|
||||
let buf = TextBuffer::new_empty(Metrics::new(attrs.font_size, attrs.line_height));
|
||||
Self {
|
||||
content: content.into().into(),
|
||||
view: TextView::new(buf, attrs),
|
||||
}
|
||||
}
|
||||
fn update_buf(&mut self, ctx: &mut SizeCtx) -> TextTexture {
|
||||
if self.content.changed {
|
||||
self.content.changed = false;
|
||||
self.view.buf.set_text(
|
||||
&mut ctx.text.font_system,
|
||||
&self.content,
|
||||
&Attrs::new().family(self.view.attrs.family),
|
||||
Shaping::Advanced,
|
||||
);
|
||||
}
|
||||
self.view.draw(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for Text {
|
||||
fn draw(&mut self, painter: &mut Painter) {
|
||||
let tex = self.update_buf(&mut painter.size_ctx());
|
||||
let region = text_region(&tex, self.align);
|
||||
painter.texture_within(&tex.handle, region);
|
||||
}
|
||||
|
||||
fn desired_size(&mut self, ctx: &mut SizeCtx) -> UiVec2 {
|
||||
UiVec2::abs(self.update_buf(ctx).size())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn text_region(tex: &TextTexture, align: Align) -> UiRegion {
|
||||
let tex_dims = tex.handle.size();
|
||||
let mut region = UiRegion::from_size_align(tex.size(), align);
|
||||
region.top_left.abs += tex.top_left;
|
||||
region.bot_right.abs = region.top_left.abs + tex_dims;
|
||||
region
|
||||
}
|
||||
|
||||
impl Deref for Text {
|
||||
type Target = TextAttrs;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.view
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for Text {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.view
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for TextView {
|
||||
type Target = TextAttrs;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.attrs
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for TextView {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.attrs
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
layout::{
|
||||
Layers, Modules, TextAttrs, TextBuffer, TextData, TextOffset, TextureHandle, Textures,
|
||||
Layers, Modules, TextAttrs, TextBuffer, TextData, TextTexture, TextureHandle, Textures,
|
||||
UiRegion, UiVec2, Vec2, WidgetId, Widgets,
|
||||
},
|
||||
render::{Primitive, PrimitiveHandle},
|
||||
@@ -29,7 +29,7 @@ pub struct PainterCtx<'a> {
|
||||
pub screen_size: Vec2,
|
||||
pub modules: &'a mut Modules,
|
||||
pub px_dependent: &'a mut HashSet<Id>,
|
||||
drawing: HashSet<Id>,
|
||||
draw_started: HashSet<Id>,
|
||||
}
|
||||
|
||||
pub struct WidgetInstance {
|
||||
@@ -80,12 +80,14 @@ impl<'a> PainterCtx<'a> {
|
||||
screen_size: data.output_size,
|
||||
modules: &mut data.modules,
|
||||
px_dependent: &mut data.px_dependent,
|
||||
drawing: HashSet::default(),
|
||||
draw_started: HashSet::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn redraw(&mut self, id: Id) {
|
||||
self.drawing.clear();
|
||||
if self.draw_started.contains(&id) {
|
||||
return;
|
||||
}
|
||||
let Some(active) = self.active.get(&id) else {
|
||||
return;
|
||||
};
|
||||
@@ -99,11 +101,13 @@ impl<'a> PainterCtx<'a> {
|
||||
widgets: self.widgets,
|
||||
region: UiRegion::full(),
|
||||
screen_size: self.screen_size,
|
||||
px_dependent: self.px_dependent,
|
||||
id,
|
||||
};
|
||||
let desired = ctx.size_inner(id, active.region);
|
||||
if size != desired {
|
||||
self.redraw(rid);
|
||||
if self.drawing.contains(&id) {
|
||||
if self.draw_started.contains(&id) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -124,7 +128,7 @@ impl<'a> PainterCtx<'a> {
|
||||
}
|
||||
|
||||
pub fn draw(&mut self, id: Id) {
|
||||
self.drawing.clear();
|
||||
self.draw_started.clear();
|
||||
self.layers.clear();
|
||||
self.draw_inner(0, id, UiRegion::full(), None, None);
|
||||
}
|
||||
@@ -143,7 +147,7 @@ impl<'a> PainterCtx<'a> {
|
||||
// but this has a very weird issue where you can't move widgets unless u remove first
|
||||
// so swapping is impossible rn I think?
|
||||
// there's definitely better solutions like a counter (>1 = panic) but don't care rn
|
||||
if self.drawing.contains(&id) {
|
||||
if self.draw_started.contains(&id) {
|
||||
panic!("Cannot draw the same widget twice (1)");
|
||||
}
|
||||
let mut old_children = old_children.unwrap_or_default();
|
||||
@@ -165,7 +169,7 @@ impl<'a> PainterCtx<'a> {
|
||||
resize = active.resize;
|
||||
}
|
||||
|
||||
self.drawing.insert(id);
|
||||
self.draw_started.insert(id);
|
||||
|
||||
let mut painter = Painter {
|
||||
region,
|
||||
@@ -308,11 +312,7 @@ impl<'a, 'c> Painter<'a, 'c> {
|
||||
}
|
||||
|
||||
/// returns (handle, offset from top left)
|
||||
pub fn render_text(
|
||||
&mut self,
|
||||
buffer: &mut TextBuffer,
|
||||
attrs: &TextAttrs,
|
||||
) -> (TextureHandle, TextOffset) {
|
||||
pub fn render_text(&mut self, buffer: &mut TextBuffer, attrs: &TextAttrs) -> TextTexture {
|
||||
self.ctx.text.draw(buffer, attrs, self.ctx.textures)
|
||||
}
|
||||
|
||||
@@ -331,6 +331,8 @@ impl<'a, 'c> Painter<'a, 'c> {
|
||||
widgets: self.ctx.widgets,
|
||||
checked: &mut self.sized_children,
|
||||
screen_size: self.ctx.screen_size,
|
||||
px_dependent: self.ctx.px_dependent,
|
||||
id: self.id,
|
||||
region: self.region,
|
||||
}
|
||||
}
|
||||
@@ -357,9 +359,11 @@ pub struct SizeCtx<'a> {
|
||||
pub text: &'a mut TextData,
|
||||
pub textures: &'a mut Textures,
|
||||
widgets: &'a Widgets,
|
||||
px_dependent: &'a mut HashSet<Id>,
|
||||
checked: &'a mut HashMap<Id, UiVec2>,
|
||||
region: UiRegion,
|
||||
screen_size: Vec2,
|
||||
id: Id,
|
||||
}
|
||||
|
||||
impl SizeCtx<'_> {
|
||||
@@ -381,14 +385,11 @@ impl SizeCtx<'_> {
|
||||
pub fn size_within<W>(&mut self, id: &WidgetId<W>, region: UiRegion) -> UiVec2 {
|
||||
self.size_inner(id.id, region.within(&self.region))
|
||||
}
|
||||
pub fn px_size(&self) -> Vec2 {
|
||||
pub fn px_size(&mut self) -> Vec2 {
|
||||
self.px_dependent.insert(self.id);
|
||||
self.region.in_size(self.screen_size)
|
||||
}
|
||||
pub fn draw_text(
|
||||
&mut self,
|
||||
buffer: &mut TextBuffer,
|
||||
attrs: &TextAttrs,
|
||||
) -> (TextureHandle, TextOffset) {
|
||||
pub fn draw_text(&mut self, buffer: &mut TextBuffer, attrs: &TextAttrs) -> TextTexture {
|
||||
self.text.draw(buffer, attrs, self.textures)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use cosmic_text::{Attrs, AttrsList, Buffer, Family, FontSystem, Metrics, SwashCa
|
||||
use image::{Rgba, RgbaImage};
|
||||
|
||||
use crate::{
|
||||
layout::{TextureHandle, Textures, UiColor, Vec2},
|
||||
layout::{Align, TextureHandle, Textures, UiColor, Vec2},
|
||||
util::HashMap,
|
||||
};
|
||||
|
||||
@@ -27,6 +27,8 @@ pub struct TextAttrs {
|
||||
pub line_height: f32,
|
||||
pub family: Family<'static>,
|
||||
pub wrap: bool,
|
||||
/// inner alignment of text region (within where its drawn)
|
||||
pub align: Align,
|
||||
}
|
||||
|
||||
impl TextAttrs {
|
||||
@@ -56,6 +58,7 @@ impl Default for TextAttrs {
|
||||
line_height: size * 1.2,
|
||||
family: Family::SansSerif,
|
||||
wrap: false,
|
||||
align: Align::Center,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -66,7 +69,11 @@ impl TextData {
|
||||
buffer: &mut TextBuffer,
|
||||
attrs: &TextAttrs,
|
||||
textures: &mut Textures,
|
||||
) -> (TextureHandle, TextOffset) {
|
||||
) -> TextTexture {
|
||||
// TODO: either this or the layout stuff (or both) is super slow,
|
||||
// should probably do texture packing and things if possible.
|
||||
// very visible if you add just a couple of wrapping texts and resize window
|
||||
// should also be timed to figure out exactly what points need to be sped up
|
||||
let mut pixels = HashMap::default();
|
||||
let mut min_x = 0;
|
||||
let mut min_y = 0;
|
||||
@@ -113,22 +120,23 @@ impl TextData {
|
||||
let y = (y - min_y) as u32;
|
||||
image.put_pixel(x, y, color);
|
||||
}
|
||||
let offset = TextOffset {
|
||||
TextTexture {
|
||||
handle: textures.add(image),
|
||||
top_left: Vec2::new(min_x as f32, min_y as f32),
|
||||
bot_right: Vec2::new(max_width - max_x as f32, height - max_y as f32),
|
||||
};
|
||||
(textures.add(image), offset)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct TextOffset {
|
||||
#[derive(Clone)]
|
||||
pub struct TextTexture {
|
||||
pub handle: TextureHandle,
|
||||
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
|
||||
impl TextTexture {
|
||||
pub fn size(&self) -> Vec2 {
|
||||
self.handle.size() - self.top_left + self.bot_right
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ use std::{
|
||||
};
|
||||
|
||||
pub struct Ui {
|
||||
// TODO: make this at least pub(super)
|
||||
pub(crate) data: PainterData,
|
||||
root: Option<WidgetId>,
|
||||
updates: Vec<Id>,
|
||||
|
||||
@@ -130,7 +130,8 @@ impl Client {
|
||||
|
||||
let texts = Span::empty(Dir::DOWN).add_static(&mut ui);
|
||||
let msg_area = (Rect::new(Color::SKY), texts.scroll()).stack();
|
||||
let add_text = text_edit("add")
|
||||
let add_text = text("add")
|
||||
.editable()
|
||||
.text_align(Align::Left)
|
||||
.size(30)
|
||||
.id_on(CursorSense::click(), |id, client: &mut Client, ctx| {
|
||||
@@ -139,7 +140,8 @@ impl Client {
|
||||
})
|
||||
.id_on(Submit, move |id, client: &mut Client, _| {
|
||||
let content = client.ui.text(id).take();
|
||||
let text = text_edit(content)
|
||||
let text = text(content)
|
||||
.editable()
|
||||
.size(30)
|
||||
.text_align(Align::Left)
|
||||
.wrap(true)
|
||||
|
||||
Reference in New Issue
Block a user