Simplify text layout further with even less allocations
This commit is contained in:
parent
d3a3e4fa73
commit
89aa285255
9 changed files with 152 additions and 158 deletions
|
@ -91,10 +91,10 @@ impl CollapsingHeader {
|
||||||
|
|
||||||
paint_icon(ui, &state, &interact);
|
paint_icon(ui, &state, &interact);
|
||||||
|
|
||||||
ui.add_text(
|
ui.add_galley(
|
||||||
text_pos,
|
text_pos,
|
||||||
|
galley,
|
||||||
label.text_style,
|
label.text_style,
|
||||||
galley.fragments,
|
|
||||||
Some(ui.style().interact(&interact).stroke_color),
|
Some(ui.style().interact(&interact).stroke_color),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -370,13 +370,7 @@ impl Context {
|
||||||
rect: rect.expand(2.0),
|
rect: rect.expand(2.0),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
self.add_text(
|
self.add_galley(layer, rect.min, galley, text_style, Some(color::RED));
|
||||||
layer,
|
|
||||||
rect.min,
|
|
||||||
text_style,
|
|
||||||
galley.fragments,
|
|
||||||
Some(color::RED),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn debug_text(&self, pos: Pos2, text: &str) {
|
pub fn debug_text(&self, pos: Pos2, text: &str) {
|
||||||
|
@ -418,36 +412,33 @@ impl Context {
|
||||||
text_style: TextStyle,
|
text_style: TextStyle,
|
||||||
align: (Align, Align),
|
align: (Align, Align),
|
||||||
text_color: Option<Color>,
|
text_color: Option<Color>,
|
||||||
) -> Vec2 {
|
) -> Rect {
|
||||||
let font = &self.fonts[text_style];
|
let font = &self.fonts[text_style];
|
||||||
let galley = font.layout_multiline(text, f32::INFINITY);
|
let galley = font.layout_multiline(text, f32::INFINITY);
|
||||||
let rect = align_rect(Rect::from_min_size(pos, galley.size), align);
|
let rect = align_rect(Rect::from_min_size(pos, galley.size), align);
|
||||||
self.add_text(layer, rect.min, text_style, galley.fragments, text_color);
|
self.add_galley(layer, rect.min, galley, text_style, text_color);
|
||||||
galley.size
|
rect
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Already layed out text.
|
/// Already layed out text.
|
||||||
pub fn add_text(
|
pub fn add_galley(
|
||||||
&self,
|
&self,
|
||||||
layer: Layer,
|
layer: Layer,
|
||||||
pos: Pos2,
|
pos: Pos2,
|
||||||
|
galley: font::Galley,
|
||||||
text_style: TextStyle,
|
text_style: TextStyle,
|
||||||
text: Vec<font::Fragment>,
|
|
||||||
color: Option<Color>,
|
color: Option<Color>,
|
||||||
) {
|
) {
|
||||||
let color = color.unwrap_or_else(|| self.style().text_color());
|
let color = color.unwrap_or_else(|| self.style().text_color());
|
||||||
for fragment in text {
|
self.add_paint_cmd(
|
||||||
self.add_paint_cmd(
|
layer,
|
||||||
layer,
|
PaintCmd::Text {
|
||||||
PaintCmd::Text {
|
pos,
|
||||||
color,
|
galley,
|
||||||
pos: pos + vec2(0.0, fragment.y_offset),
|
text_style,
|
||||||
text: fragment.text,
|
color,
|
||||||
text_style,
|
},
|
||||||
x_offsets: fragment.x_offsets,
|
);
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_paint_cmd(&self, layer: Layer, paint_cmd: PaintCmd) {
|
pub fn add_paint_cmd(&self, layer: Layer, paint_cmd: PaintCmd) {
|
||||||
|
|
|
@ -9,7 +9,8 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A typeset piece of text on a single line.
|
/// A typeset piece of text on a single line.
|
||||||
pub struct Fragment {
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Line {
|
||||||
/// The start of each character, probably starting at zero.
|
/// The start of each character, probably starting at zero.
|
||||||
/// The last element is the end of the last character.
|
/// The last element is the end of the last character.
|
||||||
/// x_offsets.len() == text.chars().count() + 1
|
/// x_offsets.len() == text.chars().count() + 1
|
||||||
|
@ -17,16 +18,21 @@ pub struct Fragment {
|
||||||
/// Unit: points.
|
/// Unit: points.
|
||||||
pub x_offsets: Vec<f32>,
|
pub x_offsets: Vec<f32>,
|
||||||
|
|
||||||
/// 0 for the first line, n * line_spacing for the rest
|
/// Top y offset of this line. 0.0 for the first line, n * line_spacing for the rest.
|
||||||
/// Unit: points.
|
/// Unit: points.
|
||||||
pub y_offset: f32,
|
pub y_offset: f32,
|
||||||
|
|
||||||
// TODO: make this a str reference into a String owned by Galley
|
|
||||||
/// The actual characters.
|
|
||||||
pub text: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Fragment {
|
impl Line {
|
||||||
|
pub fn sanity_check(&self) {
|
||||||
|
assert!(!self.x_offsets.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn char_count(&self) -> usize {
|
||||||
|
assert!(!self.x_offsets.is_empty());
|
||||||
|
self.x_offsets.len() - 1
|
||||||
|
}
|
||||||
|
|
||||||
pub fn min_x(&self) -> f32 {
|
pub fn min_x(&self) -> f32 {
|
||||||
*self.x_offsets.first().unwrap()
|
*self.x_offsets.first().unwrap()
|
||||||
}
|
}
|
||||||
|
@ -36,22 +42,31 @@ impl Fragment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub fn fn_text_width(fragmens: &[Fragment]) -> f32 {
|
|
||||||
// if fragmens.is_empty() {
|
|
||||||
// 0.0
|
|
||||||
// } else {
|
|
||||||
// fragmens.last().unwrap().max_x() - fragmens.first().unwrap().min_x()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
/// A collection of text locked into place.
|
/// A collection of text locked into place.
|
||||||
#[derive(Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct Galley {
|
pub struct Galley {
|
||||||
// TODO: maybe rename/refactor this as `lines`?
|
/// The full text
|
||||||
pub fragments: Vec<Fragment>,
|
pub text: String,
|
||||||
|
|
||||||
|
/// Lines of text, from top to bottom.
|
||||||
|
/// The number of chars in all lines sum up to text.chars().count()
|
||||||
|
pub lines: Vec<Line>,
|
||||||
|
|
||||||
|
// TODO: remove? Can just calculate on the fly
|
||||||
pub size: Vec2,
|
pub size: Vec2,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Galley {
|
||||||
|
pub fn sanity_check(&self) {
|
||||||
|
let mut char_count = 0;
|
||||||
|
for line in &self.lines {
|
||||||
|
line.sanity_check();
|
||||||
|
char_count += line.char_count();
|
||||||
|
}
|
||||||
|
assert_eq!(char_count, self.text.chars().count());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
@ -233,28 +248,24 @@ impl Font {
|
||||||
/// Always returns exactly one frament.
|
/// Always returns exactly one frament.
|
||||||
pub fn layout_single_line(&self, text: &str) -> Galley {
|
pub fn layout_single_line(&self, text: &str) -> Galley {
|
||||||
let x_offsets = self.layout_single_line_fragment(text);
|
let x_offsets = self.layout_single_line_fragment(text);
|
||||||
let fragment = Fragment {
|
let line = Line {
|
||||||
x_offsets,
|
x_offsets,
|
||||||
y_offset: 0.0,
|
y_offset: 0.0,
|
||||||
text: text.to_owned(),
|
|
||||||
};
|
};
|
||||||
assert_eq!(fragment.x_offsets.len(), fragment.text.chars().count() + 1);
|
let width = line.max_x();
|
||||||
let width = fragment.max_x();
|
|
||||||
let size = vec2(width, self.height());
|
let size = vec2(width, self.height());
|
||||||
Galley {
|
let galley = Galley {
|
||||||
fragments: vec![fragment],
|
text: text.to_owned(),
|
||||||
|
lines: vec![line],
|
||||||
size,
|
size,
|
||||||
}
|
};
|
||||||
|
galley.sanity_check();
|
||||||
|
galley
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A paragraph is text with no line break character in it.
|
/// A paragraph is text with no line break character in it.
|
||||||
/// The text will be linebreaked by the given `max_width_in_points`.
|
/// The text will be linebreaked by the given `max_width_in_points`.
|
||||||
/// TODO: return Galley ?
|
pub fn layout_paragraph_max_width(&self, text: &str, max_width_in_points: f32) -> Vec<Line> {
|
||||||
pub fn layout_paragraph_max_width(
|
|
||||||
&self,
|
|
||||||
text: &str,
|
|
||||||
max_width_in_points: f32,
|
|
||||||
) -> Vec<Fragment> {
|
|
||||||
let full_x_offsets = self.layout_single_line_fragment(text);
|
let full_x_offsets = self.layout_single_line_fragment(text);
|
||||||
|
|
||||||
let mut line_start_x = full_x_offsets[0];
|
let mut line_start_x = full_x_offsets[0];
|
||||||
|
@ -265,7 +276,7 @@ impl Font {
|
||||||
// start index of the last space. A candidate for a new line.
|
// start index of the last space. A candidate for a new line.
|
||||||
let mut last_space = None;
|
let mut last_space = None;
|
||||||
|
|
||||||
let mut out_fragments = vec![];
|
let mut out_lines = vec![];
|
||||||
|
|
||||||
for (i, (x, chr)) in full_x_offsets.iter().skip(1).zip(text.chars()).enumerate() {
|
for (i, (x, chr)) in full_x_offsets.iter().skip(1).zip(text.chars()).enumerate() {
|
||||||
let line_width = x - line_start_x;
|
let line_width = x - line_start_x;
|
||||||
|
@ -273,35 +284,25 @@ impl Font {
|
||||||
if line_width > max_width_in_points {
|
if line_width > max_width_in_points {
|
||||||
if let Some(last_space_idx) = last_space {
|
if let Some(last_space_idx) = last_space {
|
||||||
let include_trailing_space = true;
|
let include_trailing_space = true;
|
||||||
let fragment = if include_trailing_space {
|
let line = if include_trailing_space {
|
||||||
Fragment {
|
Line {
|
||||||
x_offsets: full_x_offsets[line_start_idx..=last_space_idx + 1]
|
x_offsets: full_x_offsets[line_start_idx..=last_space_idx + 1]
|
||||||
.iter()
|
.iter()
|
||||||
.map(|x| x - line_start_x)
|
.map(|x| x - line_start_x)
|
||||||
.collect(),
|
.collect(),
|
||||||
y_offset: cursor_y,
|
y_offset: cursor_y,
|
||||||
text: text
|
|
||||||
.chars()
|
|
||||||
.skip(line_start_idx)
|
|
||||||
.take(last_space_idx + 1 - line_start_idx)
|
|
||||||
.collect(),
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Fragment {
|
Line {
|
||||||
x_offsets: full_x_offsets[line_start_idx..=last_space_idx]
|
x_offsets: full_x_offsets[line_start_idx..=last_space_idx]
|
||||||
.iter()
|
.iter()
|
||||||
.map(|x| x - line_start_x)
|
.map(|x| x - line_start_x)
|
||||||
.collect(),
|
.collect(),
|
||||||
y_offset: cursor_y,
|
y_offset: cursor_y,
|
||||||
text: text
|
|
||||||
.chars()
|
|
||||||
.skip(line_start_idx)
|
|
||||||
.take(last_space_idx - line_start_idx)
|
|
||||||
.collect(),
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
assert_eq!(fragment.x_offsets.len(), fragment.text.chars().count() + 1);
|
line.sanity_check();
|
||||||
out_fragments.push(fragment);
|
out_lines.push(line);
|
||||||
|
|
||||||
line_start_idx = last_space_idx + 1;
|
line_start_idx = last_space_idx + 1;
|
||||||
line_start_x = full_x_offsets[line_start_idx];
|
line_start_x = full_x_offsets[line_start_idx];
|
||||||
|
@ -318,49 +319,60 @@ impl Font {
|
||||||
}
|
}
|
||||||
|
|
||||||
if line_start_idx + 1 < full_x_offsets.len() {
|
if line_start_idx + 1 < full_x_offsets.len() {
|
||||||
let fragment = Fragment {
|
let line = Line {
|
||||||
x_offsets: full_x_offsets[line_start_idx..]
|
x_offsets: full_x_offsets[line_start_idx..]
|
||||||
.iter()
|
.iter()
|
||||||
.map(|x| x - line_start_x)
|
.map(|x| x - line_start_x)
|
||||||
.collect(),
|
.collect(),
|
||||||
y_offset: cursor_y,
|
y_offset: cursor_y,
|
||||||
text: text.chars().skip(line_start_idx).collect(),
|
|
||||||
};
|
};
|
||||||
assert_eq!(fragment.x_offsets.len(), fragment.text.chars().count() + 1);
|
line.sanity_check();
|
||||||
out_fragments.push(fragment);
|
out_lines.push(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
out_fragments
|
out_lines
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn layout_multiline(&self, text: &str, max_width_in_points: f32) -> Galley {
|
pub fn layout_multiline(&self, text: &str, max_width_in_points: f32) -> Galley {
|
||||||
let line_spacing = self.line_spacing();
|
let line_spacing = self.line_spacing();
|
||||||
let mut cursor_y = 0.0;
|
let mut cursor_y = 0.0;
|
||||||
let mut fragments = Vec::new();
|
let mut lines = Vec::new();
|
||||||
for line in text.split('\n') {
|
|
||||||
let mut paragraph_fragments =
|
let mut paragraph_start = 0;
|
||||||
self.layout_paragraph_max_width(line, max_width_in_points);
|
|
||||||
if let Some(last_fragment) = paragraph_fragments.last() {
|
while paragraph_start < text.len() {
|
||||||
let line_height = last_fragment.y_offset + line_spacing;
|
let next_newline = text[paragraph_start..].find('\n');
|
||||||
for fragment in &mut paragraph_fragments {
|
let paragraph_end = next_newline
|
||||||
fragment.y_offset += cursor_y;
|
.map(|newline| paragraph_start + newline + 1)
|
||||||
}
|
.unwrap_or_else(|| text.len());
|
||||||
fragments.append(&mut paragraph_fragments);
|
|
||||||
cursor_y += line_height; // TODO: add extra spacing between paragraphs
|
assert!(paragraph_start < paragraph_end);
|
||||||
} else {
|
let paragraph_text = &text[paragraph_start..paragraph_end];
|
||||||
cursor_y += line_spacing;
|
let mut paragraph_lines =
|
||||||
|
self.layout_paragraph_max_width(paragraph_text, max_width_in_points);
|
||||||
|
assert!(!paragraph_lines.is_empty());
|
||||||
|
|
||||||
|
let line_height = paragraph_lines.last().unwrap().y_offset + line_spacing;
|
||||||
|
for line in &mut paragraph_lines {
|
||||||
|
line.y_offset += cursor_y;
|
||||||
}
|
}
|
||||||
cursor_y = self.round_to_pixel(cursor_y);
|
lines.append(&mut paragraph_lines);
|
||||||
|
cursor_y += line_height; // TODO: add extra spacing between paragraphs
|
||||||
|
|
||||||
|
paragraph_start = paragraph_end;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut widest_line = 0.0;
|
let mut widest_line = 0.0;
|
||||||
for fragment in &fragments {
|
for line in &lines {
|
||||||
widest_line = fragment.max_x().max(widest_line);
|
widest_line = line.max_x().max(widest_line);
|
||||||
}
|
}
|
||||||
|
|
||||||
Galley {
|
let galley = Galley {
|
||||||
fragments,
|
text: text.to_owned(),
|
||||||
|
lines,
|
||||||
size: vec2(widest_line, cursor_y),
|
size: vec2(widest_line, cursor_y),
|
||||||
}
|
};
|
||||||
|
galley.sanity_check();
|
||||||
|
galley
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -564,30 +564,35 @@ pub fn mesh_command(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PaintCmd::Text {
|
PaintCmd::Text {
|
||||||
color,
|
|
||||||
pos,
|
pos,
|
||||||
text,
|
galley,
|
||||||
text_style,
|
text_style,
|
||||||
x_offsets,
|
color,
|
||||||
} => {
|
} => {
|
||||||
|
galley.sanity_check();
|
||||||
let font = &fonts[text_style];
|
let font = &fonts[text_style];
|
||||||
for (c, x_offset) in text.chars().zip(x_offsets.iter()) {
|
let mut chars = galley.text.chars();
|
||||||
if let Some(glyph) = font.uv_rect(c) {
|
for line in &galley.lines {
|
||||||
let mut top_left = Vertex {
|
for x_offset in line.x_offsets.iter().take(line.x_offsets.len() - 1) {
|
||||||
pos: pos + glyph.offset + vec2(*x_offset, 0.0),
|
let c = chars.next().unwrap();
|
||||||
uv: glyph.min,
|
if let Some(glyph) = font.uv_rect(c) {
|
||||||
color,
|
let mut top_left = Vertex {
|
||||||
};
|
pos: pos + glyph.offset + vec2(*x_offset, line.y_offset),
|
||||||
top_left.pos.x = font.round_to_pixel(top_left.pos.x); // Pixel-perfection.
|
uv: glyph.min,
|
||||||
top_left.pos.y = font.round_to_pixel(top_left.pos.y); // Pixel-perfection.
|
color,
|
||||||
let bottom_right = Vertex {
|
};
|
||||||
pos: top_left.pos + glyph.size,
|
top_left.pos.x = font.round_to_pixel(top_left.pos.x); // Pixel-perfection.
|
||||||
uv: glyph.max,
|
top_left.pos.y = font.round_to_pixel(top_left.pos.y); // Pixel-perfection.
|
||||||
color,
|
let bottom_right = Vertex {
|
||||||
};
|
pos: top_left.pos + glyph.size,
|
||||||
out_mesh.add_rect(top_left, bottom_right);
|
uv: glyph.max,
|
||||||
|
color,
|
||||||
|
};
|
||||||
|
out_mesh.add_rect(top_left, bottom_right);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
assert_eq!(chars.next(), None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ use serde_derive::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
color::Color,
|
color::Color,
|
||||||
|
font::Galley,
|
||||||
fonts::TextStyle,
|
fonts::TextStyle,
|
||||||
math::{Pos2, Rect},
|
math::{Pos2, Rect},
|
||||||
mesher::{Mesh, Path},
|
mesher::{Mesh, Path},
|
||||||
|
@ -153,14 +154,12 @@ pub enum PaintCmd {
|
||||||
},
|
},
|
||||||
/// Paint a single line of text
|
/// Paint a single line of text
|
||||||
Text {
|
Text {
|
||||||
color: Color,
|
|
||||||
/// Top left corner of the first character.
|
/// Top left corner of the first character.
|
||||||
pos: Pos2,
|
pos: Pos2,
|
||||||
text: String,
|
/// The layed out text
|
||||||
text_style: TextStyle, // TODO: Font
|
galley: Galley,
|
||||||
/// Start each character in the text, as offset from pos.
|
text_style: TextStyle, // TODO: Font?
|
||||||
x_offsets: Vec<f32>,
|
color: Color,
|
||||||
// TODO: font info
|
|
||||||
},
|
},
|
||||||
/// Low-level triangle mesh
|
/// Low-level triangle mesh
|
||||||
Mesh(Mesh),
|
Mesh(Mesh),
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::{hash::Hash, sync::Arc};
|
use std::{hash::Hash, sync::Arc};
|
||||||
|
|
||||||
use crate::{color::*, containers::*, font::Fragment, layout::*, widgets::*, *};
|
use crate::{color::*, containers::*, layout::*, widgets::*, *};
|
||||||
|
|
||||||
/// Represents a region of the screen
|
/// Represents a region of the screen
|
||||||
/// with a type of layout (horizontal or vertical).
|
/// with a type of layout (horizontal or vertical).
|
||||||
|
@ -457,32 +457,29 @@ impl Ui {
|
||||||
text_style: TextStyle,
|
text_style: TextStyle,
|
||||||
align: (Align, Align),
|
align: (Align, Align),
|
||||||
text_color: Option<Color>,
|
text_color: Option<Color>,
|
||||||
) -> Vec2 {
|
) -> Rect {
|
||||||
let font = &self.fonts()[text_style];
|
let font = &self.fonts()[text_style];
|
||||||
let galley = font.layout_multiline(text, f32::INFINITY);
|
let galley = font.layout_multiline(text, f32::INFINITY);
|
||||||
let rect = align_rect(Rect::from_min_size(pos, galley.size), align);
|
let rect = align_rect(Rect::from_min_size(pos, galley.size), align);
|
||||||
self.add_text(rect.min, text_style, galley.fragments, text_color);
|
self.add_galley(rect.min, galley, text_style, text_color);
|
||||||
galley.size
|
rect
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Already layed out text.
|
/// Already layed out text.
|
||||||
pub fn add_text(
|
pub fn add_galley(
|
||||||
&mut self,
|
&mut self,
|
||||||
pos: Pos2,
|
pos: Pos2,
|
||||||
|
galley: font::Galley,
|
||||||
text_style: TextStyle,
|
text_style: TextStyle,
|
||||||
fragments: Vec<Fragment>,
|
|
||||||
color: Option<Color>,
|
color: Option<Color>,
|
||||||
) {
|
) {
|
||||||
let color = color.unwrap_or_else(|| self.style().text_color());
|
let color = color.unwrap_or_else(|| self.style().text_color());
|
||||||
for fragment in fragments {
|
self.add_paint_cmd(PaintCmd::Text {
|
||||||
self.add_paint_cmd(PaintCmd::Text {
|
pos,
|
||||||
color,
|
galley,
|
||||||
pos: pos + vec2(0.0, fragment.y_offset),
|
text_style,
|
||||||
text: fragment.text,
|
color,
|
||||||
text_style,
|
});
|
||||||
x_offsets: fragment.x_offsets,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
|
|
@ -97,12 +97,7 @@ impl Widget for Label {
|
||||||
};
|
};
|
||||||
let galley = self.layout(max_width, ui);
|
let galley = self.layout(max_width, ui);
|
||||||
let interact = ui.reserve_space(galley.size, None);
|
let interact = ui.reserve_space(galley.size, None);
|
||||||
ui.add_text(
|
ui.add_galley(interact.rect.min, galley, self.text_style, self.text_color);
|
||||||
interact.rect.min,
|
|
||||||
self.text_style,
|
|
||||||
galley.fragments,
|
|
||||||
self.text_color,
|
|
||||||
);
|
|
||||||
ui.response(interact)
|
ui.response(interact)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -160,12 +155,12 @@ impl Widget for Hyperlink {
|
||||||
|
|
||||||
if interact.hovered {
|
if interact.hovered {
|
||||||
// Underline:
|
// Underline:
|
||||||
for fragment in &galley.fragments {
|
for line in &galley.lines {
|
||||||
let pos = interact.rect.min;
|
let pos = interact.rect.min;
|
||||||
let y = pos.y + fragment.y_offset + line_spacing;
|
let y = pos.y + line.y_offset + line_spacing;
|
||||||
let y = ui.round_to_pixel(y);
|
let y = ui.round_to_pixel(y);
|
||||||
let min_x = pos.x + fragment.min_x();
|
let min_x = pos.x + line.min_x();
|
||||||
let max_x = pos.x + fragment.max_x();
|
let max_x = pos.x + line.max_x();
|
||||||
ui.add_paint_cmd(PaintCmd::line_segment(
|
ui.add_paint_cmd(PaintCmd::line_segment(
|
||||||
[pos2(min_x, y), pos2(max_x, y)],
|
[pos2(min_x, y), pos2(max_x, y)],
|
||||||
color,
|
color,
|
||||||
|
@ -174,7 +169,7 @@ impl Widget for Hyperlink {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.add_text(interact.rect.min, text_style, galley.fragments, Some(color));
|
ui.add_galley(interact.rect.min, galley, text_style, Some(color));
|
||||||
|
|
||||||
ui.response(interact)
|
ui.response(interact)
|
||||||
}
|
}
|
||||||
|
@ -243,7 +238,7 @@ impl Widget for Button {
|
||||||
});
|
});
|
||||||
let stroke_color = ui.style().interact(&interact).stroke_color;
|
let stroke_color = ui.style().interact(&interact).stroke_color;
|
||||||
let text_color = text_color.unwrap_or(stroke_color);
|
let text_color = text_color.unwrap_or(stroke_color);
|
||||||
ui.add_text(text_cursor, text_style, galley.fragments, Some(text_color));
|
ui.add_galley(text_cursor, galley, text_style, Some(text_color));
|
||||||
ui.response(interact)
|
ui.response(interact)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -313,7 +308,7 @@ impl<'a> Widget for Checkbox<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let text_color = self.text_color.unwrap_or(stroke_color);
|
let text_color = self.text_color.unwrap_or(stroke_color);
|
||||||
ui.add_text(text_cursor, text_style, galley.fragments, Some(text_color));
|
ui.add_galley(text_cursor, galley, text_style, Some(text_color));
|
||||||
ui.response(interact)
|
ui.response(interact)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -384,7 +379,7 @@ impl Widget for RadioButton {
|
||||||
}
|
}
|
||||||
|
|
||||||
let text_color = self.text_color.unwrap_or(stroke_color);
|
let text_color = self.text_color.unwrap_or(stroke_color);
|
||||||
ui.add_text(text_cursor, text_style, galley.fragments, Some(text_color));
|
ui.add_galley(text_cursor, galley, text_style, Some(text_color));
|
||||||
ui.response(interact)
|
ui.response(interact)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -119,7 +119,7 @@ impl<'a> Widget for Slider<'a> {
|
||||||
// let galley = font.layout_multiline(&full_text, ui.available().width());
|
// let galley = font.layout_multiline(&full_text, ui.available().width());
|
||||||
let galley = font.layout_single_line(&full_text);
|
let galley = font.layout_single_line(&full_text);
|
||||||
let pos = ui.reserve_space(galley.size, None).rect.min;
|
let pos = ui.reserve_space(galley.size, None).rect.min;
|
||||||
ui.add_text(pos, text_style, galley.fragments, text_color);
|
ui.add_galley(pos, galley, text_style, text_color);
|
||||||
slider_sans_text.ui(ui)
|
slider_sans_text.ui(ui)
|
||||||
} else {
|
} else {
|
||||||
ui.columns(2, |columns| {
|
ui.columns(2, |columns| {
|
||||||
|
|
|
@ -90,7 +90,7 @@ impl<'t> Widget for TextEdit<'t> {
|
||||||
let show_cursor =
|
let show_cursor =
|
||||||
(ui.input().time * cursor_blink_hz as f64 * 3.0).floor() as i64 % 3 != 0;
|
(ui.input().time * cursor_blink_hz as f64 * 3.0).floor() as i64 % 3 != 0;
|
||||||
if show_cursor {
|
if show_cursor {
|
||||||
let cursor_pos = if let Some(last) = galley.fragments.last() {
|
let cursor_pos = if let Some(last) = galley.lines.last() {
|
||||||
interact.rect.min + vec2(last.max_x(), last.y_offset)
|
interact.rect.min + vec2(last.max_x(), last.y_offset)
|
||||||
} else {
|
} else {
|
||||||
interact.rect.min
|
interact.rect.min
|
||||||
|
@ -103,12 +103,7 @@ impl<'t> Widget for TextEdit<'t> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.add_text(
|
ui.add_galley(interact.rect.min, galley, self.text_style, self.text_color);
|
||||||
interact.rect.min,
|
|
||||||
self.text_style,
|
|
||||||
galley.fragments,
|
|
||||||
self.text_color,
|
|
||||||
);
|
|
||||||
|
|
||||||
ui.response(interact)
|
ui.response(interact)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue