203 lines
5.2 KiB
Rust
203 lines
5.2 KiB
Rust
mod build;
|
|
mod edit;
|
|
|
|
pub use build::*;
|
|
pub use edit::*;
|
|
|
|
use crate::{prelude::*, util::MutDetect};
|
|
use cosmic_text::{Attrs, BufferLine, Cursor, Metrics, Shaping};
|
|
use std::ops::{Deref, DerefMut};
|
|
|
|
pub const SHAPING: Shaping = Shaping::Advanced;
|
|
|
|
pub struct Text {
|
|
pub content: MutDetect<String>,
|
|
view: TextView,
|
|
}
|
|
|
|
pub struct TextView {
|
|
pub attrs: MutDetect<TextAttrs>,
|
|
pub buf: MutDetect<TextBuffer>,
|
|
// cache
|
|
tex: Option<RenderedText>,
|
|
width: Option<f32>,
|
|
pub hint: Option<WidgetId>,
|
|
}
|
|
|
|
impl TextView {
|
|
pub fn new(buf: TextBuffer, attrs: TextAttrs, hint: Option<WidgetId>) -> Self {
|
|
Self {
|
|
attrs: attrs.into(),
|
|
buf: buf.into(),
|
|
tex: None,
|
|
width: None,
|
|
hint,
|
|
}
|
|
}
|
|
|
|
/// region where the text should be draw
|
|
/// does not include extra height or width from weird unicode
|
|
pub fn region(&self) -> UiRegion {
|
|
self.tex()
|
|
.map(|t| t.size)
|
|
.unwrap_or(Vec2::ZERO)
|
|
.align(self.align)
|
|
}
|
|
|
|
fn tex_region(&self, tex: &RenderedText) -> UiRegion {
|
|
let region = tex.size.align(self.align);
|
|
let dims = tex.handle.size();
|
|
let mut region = region.offset(tex.top_left_offset);
|
|
region.x.end = region.x.start + UiScalar::abs(dims.x);
|
|
region.y.end = region.y.start + UiScalar::abs(dims.y);
|
|
region
|
|
}
|
|
|
|
fn render(&mut self, ctx: &mut SizeCtx) -> RenderedText {
|
|
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<&RenderedText> {
|
|
self.tex.as_ref()
|
|
}
|
|
pub fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len {
|
|
if let Some(hint) = &self.hint
|
|
&& let [line] = &self.buf.lines[..]
|
|
&& line.text().is_empty()
|
|
{
|
|
ctx.width(hint)
|
|
} else {
|
|
Len::abs(self.render(ctx).size.x)
|
|
}
|
|
}
|
|
pub fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len {
|
|
if let Some(hint) = &self.hint
|
|
&& let [line] = &self.buf.lines[..]
|
|
&& line.text().is_empty()
|
|
{
|
|
ctx.height(hint)
|
|
} else {
|
|
Len::abs(self.render(ctx).size.y)
|
|
}
|
|
}
|
|
pub fn draw(&mut self, painter: &mut Painter) -> UiRegion {
|
|
let tex = self.render(&mut painter.size_ctx());
|
|
let region = self.tex_region(&tex);
|
|
if let Some(hint) = &self.hint
|
|
&& let [line] = &self.buf.lines[..]
|
|
&& line.text().is_empty()
|
|
{
|
|
painter.widget(hint);
|
|
} else {
|
|
painter.texture_within(&tex.handle, region);
|
|
}
|
|
region
|
|
}
|
|
|
|
pub fn content(&self) -> String {
|
|
self.buf
|
|
.lines
|
|
.iter()
|
|
.map(|l| l.text())
|
|
.collect::<Vec<_>>()
|
|
.join("\n")
|
|
}
|
|
}
|
|
|
|
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, None),
|
|
}
|
|
}
|
|
fn update_buf(&mut self, ctx: &mut SizeCtx) {
|
|
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,
|
|
None,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Widget for Text {
|
|
fn draw(&mut self, painter: &mut Painter) {
|
|
self.update_buf(&mut painter.size_ctx());
|
|
self.view.draw(painter);
|
|
}
|
|
|
|
fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len {
|
|
self.update_buf(ctx);
|
|
self.view.desired_width(ctx)
|
|
}
|
|
|
|
fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len {
|
|
self.update_buf(ctx);
|
|
self.view.desired_height(ctx)
|
|
}
|
|
}
|
|
|
|
pub fn sort_cursors(a: Cursor, b: Cursor) -> (Cursor, Cursor) {
|
|
let start = a.min(b);
|
|
let end = a.max(b);
|
|
(start, end)
|
|
}
|
|
|
|
pub fn edit_line(line: &mut BufferLine, text: String) {
|
|
line.set_text(text, line.ending(), line.attrs_list().clone());
|
|
}
|
|
|
|
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
|
|
}
|
|
}
|