refactor text layout with a new struct Galley
This commit is contained in:
parent
152e644fb2
commit
cdfd42eb3e
|
|
@ -61,18 +61,18 @@ impl CollapsingHeader {
|
||||||
|
|
||||||
let available = ui.available_finite();
|
let available = ui.available_finite();
|
||||||
let text_pos = available.min + vec2(ui.style().indent, 0.0);
|
let text_pos = available.min + vec2(ui.style().indent, 0.0);
|
||||||
let (text, text_size) = label.layout(available.width() - ui.style().indent, ui);
|
let galley = label.layout(available.width() - ui.style().indent, ui);
|
||||||
let text_max_x = text_pos.x + text_size.x;
|
let text_max_x = text_pos.x + galley.size.x;
|
||||||
let desired_width = available.width().max(text_max_x - available.left());
|
let desired_width = available.width().max(text_max_x - available.left());
|
||||||
|
|
||||||
let interact = ui.reserve_space(
|
let interact = ui.reserve_space(
|
||||||
vec2(
|
vec2(
|
||||||
desired_width,
|
desired_width,
|
||||||
text_size.y + 2.0 * ui.style().button_padding.y,
|
galley.size.y + 2.0 * ui.style().button_padding.y,
|
||||||
),
|
),
|
||||||
Some(id),
|
Some(id),
|
||||||
);
|
);
|
||||||
let text_pos = pos2(text_pos.x, interact.rect.center().y - text_size.y / 2.0);
|
let text_pos = pos2(text_pos.x, interact.rect.center().y - galley.size.y / 2.0);
|
||||||
|
|
||||||
let mut state = {
|
let mut state = {
|
||||||
let mut memory = ui.memory();
|
let mut memory = ui.memory();
|
||||||
|
|
@ -94,7 +94,7 @@ impl CollapsingHeader {
|
||||||
ui.add_text(
|
ui.add_text(
|
||||||
text_pos,
|
text_pos,
|
||||||
label.text_style,
|
label.text_style,
|
||||||
text,
|
galley.fragments,
|
||||||
Some(ui.style().interact(&interact).stroke_color),
|
Some(ui.style().interact(&interact).stroke_color),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -359,8 +359,8 @@ impl Context {
|
||||||
let layer = Layer::debug();
|
let layer = Layer::debug();
|
||||||
let text_style = TextStyle::Monospace;
|
let text_style = TextStyle::Monospace;
|
||||||
let font = &self.fonts[text_style];
|
let font = &self.fonts[text_style];
|
||||||
let (text, size) = font.layout_multiline(text, f32::INFINITY);
|
let galley = font.layout_multiline(text, f32::INFINITY);
|
||||||
let rect = align_rect(Rect::from_min_size(pos, size), align);
|
let rect = align_rect(Rect::from_min_size(pos, galley.size), align);
|
||||||
self.add_paint_cmd(
|
self.add_paint_cmd(
|
||||||
layer,
|
layer,
|
||||||
PaintCmd::Rect {
|
PaintCmd::Rect {
|
||||||
|
|
@ -370,7 +370,13 @@ impl Context {
|
||||||
rect: rect.expand(2.0),
|
rect: rect.expand(2.0),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
self.add_text(layer, rect.min, text_style, text, Some(color::RED));
|
self.add_text(
|
||||||
|
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) {
|
||||||
|
|
@ -414,10 +420,10 @@ impl Context {
|
||||||
text_color: Option<Color>,
|
text_color: Option<Color>,
|
||||||
) -> Vec2 {
|
) -> Vec2 {
|
||||||
let font = &self.fonts[text_style];
|
let font = &self.fonts[text_style];
|
||||||
let (text, size) = font.layout_multiline(text, f32::INFINITY);
|
let galley = font.layout_multiline(text, f32::INFINITY);
|
||||||
let rect = align_rect(Rect::from_min_size(pos, size), align);
|
let rect = align_rect(Rect::from_min_size(pos, galley.size), align);
|
||||||
self.add_text(layer, rect.min, text_style, text, text_color);
|
self.add_text(layer, rect.min, text_style, galley.fragments, text_color);
|
||||||
size
|
galley.size
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Already layed out text.
|
/// Already layed out text.
|
||||||
|
|
@ -426,7 +432,7 @@ impl Context {
|
||||||
layer: Layer,
|
layer: Layer,
|
||||||
pos: Pos2,
|
pos: Pos2,
|
||||||
text_style: TextStyle,
|
text_style: TextStyle,
|
||||||
text: Vec<font::TextFragment>,
|
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());
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,8 @@ use crate::{
|
||||||
texture_atlas::TextureAtlas,
|
texture_atlas::TextureAtlas,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct TextFragment {
|
/// A typeset piece of text on a single line. Could be a whole line, or just a word.
|
||||||
|
pub struct Fragment {
|
||||||
/// The start of each character, starting at zero.
|
/// The start of each character, starting at zero.
|
||||||
/// Unit: points.
|
/// Unit: points.
|
||||||
pub x_offsets: Vec<f32>,
|
pub x_offsets: Vec<f32>,
|
||||||
|
|
@ -16,10 +17,12 @@ pub struct TextFragment {
|
||||||
/// 0 for the first line, n * line_spacing for the rest
|
/// 0 for the first line, n * line_spacing for the rest
|
||||||
/// Unit: points.
|
/// Unit: points.
|
||||||
pub y_offset: f32,
|
pub y_offset: f32,
|
||||||
|
|
||||||
|
/// The actual characters
|
||||||
pub text: String,
|
pub text: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TextFragment {
|
impl Fragment {
|
||||||
pub fn min_x(&self) -> f32 {
|
pub fn min_x(&self) -> f32 {
|
||||||
*self.x_offsets.first().unwrap()
|
*self.x_offsets.first().unwrap()
|
||||||
}
|
}
|
||||||
|
|
@ -29,7 +32,7 @@ impl TextFragment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub fn fn_text_width(fragmens: &[TextFragment]) -> f32 {
|
// pub fn fn_text_width(fragmens: &[Fragment]) -> f32 {
|
||||||
// if fragmens.is_empty() {
|
// if fragmens.is_empty() {
|
||||||
// 0.0
|
// 0.0
|
||||||
// } else {
|
// } else {
|
||||||
|
|
@ -37,6 +40,12 @@ impl TextFragment {
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
/// A collection of text locked into place.
|
||||||
|
pub struct Galley {
|
||||||
|
pub fragments: Vec<Fragment>,
|
||||||
|
pub size: Vec2,
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
|
@ -184,17 +193,16 @@ impl Font {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the a single line of characters separated into words
|
/// Returns the a single line of characters separated into words
|
||||||
/// Always returns at least one frament. TODO: Vec1
|
/// Always returns at least one frament.
|
||||||
/// Returns total size.
|
fn layout_words(&self, text: &str) -> Galley {
|
||||||
pub fn layout_single_line(&self, text: &str) -> (Vec<TextFragment>, Vec2) {
|
|
||||||
let scale_in_pixels = Scale::uniform(self.scale_in_pixels);
|
let scale_in_pixels = Scale::uniform(self.scale_in_pixels);
|
||||||
|
|
||||||
let mut current_fragment = TextFragment {
|
let mut current_fragment = Fragment {
|
||||||
x_offsets: vec![0.0],
|
x_offsets: vec![0.0],
|
||||||
y_offset: 0.0,
|
y_offset: 0.0,
|
||||||
text: String::new(),
|
text: String::new(),
|
||||||
};
|
};
|
||||||
let mut all_fragments = vec![];
|
let mut fragments = vec![];
|
||||||
let mut cursor_x_in_points = 0.0f32;
|
let mut cursor_x_in_points = 0.0f32;
|
||||||
let mut last_glyph_id = None;
|
let mut last_glyph_id = None;
|
||||||
|
|
||||||
|
|
@ -214,13 +222,14 @@ impl Font {
|
||||||
if is_space {
|
if is_space {
|
||||||
// TODO: also break after hyphens etc
|
// TODO: also break after hyphens etc
|
||||||
if !current_fragment.text.is_empty() {
|
if !current_fragment.text.is_empty() {
|
||||||
all_fragments.push(current_fragment);
|
fragments.push(current_fragment);
|
||||||
current_fragment = TextFragment {
|
current_fragment = Fragment {
|
||||||
x_offsets: vec![cursor_x_in_points],
|
x_offsets: vec![cursor_x_in_points],
|
||||||
y_offset: 0.0,
|
y_offset: 0.0,
|
||||||
text: String::new(),
|
text: String::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// TODO: add a fragment for the space aswell
|
||||||
} else {
|
} else {
|
||||||
current_fragment.text.push(c);
|
current_fragment.text.push(c);
|
||||||
current_fragment.x_offsets.push(cursor_x_in_points);
|
current_fragment.x_offsets.push(cursor_x_in_points);
|
||||||
|
|
@ -231,29 +240,39 @@ impl Font {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !current_fragment.text.is_empty() {
|
if !current_fragment.text.is_empty() {
|
||||||
all_fragments.push(current_fragment)
|
fragments.push(current_fragment)
|
||||||
}
|
}
|
||||||
|
|
||||||
let width = if all_fragments.is_empty() {
|
let width = if fragments.is_empty() {
|
||||||
0.0
|
0.0
|
||||||
} else {
|
} else {
|
||||||
all_fragments.last().unwrap().max_x()
|
fragments.last().unwrap().max_x()
|
||||||
};
|
};
|
||||||
|
|
||||||
let size = vec2(width, self.height());
|
let size = vec2(width, self.height());
|
||||||
|
|
||||||
(all_fragments, size)
|
Galley { fragments, size }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Typeset the given text onto one line.
|
||||||
|
/// Always returns at least one frament.
|
||||||
|
pub fn layout_single_line(&self, text: &str) -> Galley {
|
||||||
|
// TODO: return a single Fragment instead of calling layout_words
|
||||||
|
// saves a lot of allocations
|
||||||
|
self.layout_words(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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.
|
||||||
|
/// TODO: return Galley ?
|
||||||
pub fn layout_paragraph_max_width(
|
pub fn layout_paragraph_max_width(
|
||||||
&self,
|
&self,
|
||||||
text: &str,
|
text: &str,
|
||||||
max_width_in_points: f32,
|
max_width_in_points: f32,
|
||||||
) -> Vec<TextFragment> {
|
) -> Vec<Fragment> {
|
||||||
let (mut words, size) = self.layout_single_line(text);
|
let mut galley = self.layout_words(text);
|
||||||
if words.is_empty() || size.x <= max_width_in_points {
|
if galley.fragments.is_empty() || galley.size.x <= max_width_in_points {
|
||||||
return words; // Early-out
|
return galley.fragments; // Early-out
|
||||||
}
|
}
|
||||||
|
|
||||||
let line_spacing = self.line_spacing();
|
let line_spacing = self.line_spacing();
|
||||||
|
|
@ -262,7 +281,7 @@ impl Font {
|
||||||
let mut line_start_x = 0.0;
|
let mut line_start_x = 0.0;
|
||||||
let mut cursor_y = 0.0;
|
let mut cursor_y = 0.0;
|
||||||
|
|
||||||
for word in words.iter_mut().skip(1) {
|
for word in galley.fragments.iter_mut().skip(1) {
|
||||||
if word.max_x() - line_start_x >= max_width_in_points {
|
if word.max_x() - line_start_x >= max_width_in_points {
|
||||||
// Time for a new line:
|
// Time for a new line:
|
||||||
cursor_y += line_spacing;
|
cursor_y += line_spacing;
|
||||||
|
|
@ -275,18 +294,13 @@ impl Font {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
words
|
galley.fragments
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns each line + total bounding box size.
|
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,
|
|
||||||
) -> (Vec<TextFragment>, Vec2) {
|
|
||||||
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 text_fragments = Vec::new();
|
let mut fragments = Vec::new();
|
||||||
for line in text.split('\n') {
|
for line in text.split('\n') {
|
||||||
let mut line_fragments = self.layout_paragraph_max_width(line, max_width_in_points);
|
let mut line_fragments = self.layout_paragraph_max_width(line, max_width_in_points);
|
||||||
if let Some(last_word) = line_fragments.last() {
|
if let Some(last_word) = line_fragments.last() {
|
||||||
|
|
@ -294,7 +308,7 @@ impl Font {
|
||||||
for fragment in &mut line_fragments {
|
for fragment in &mut line_fragments {
|
||||||
fragment.y_offset += cursor_y;
|
fragment.y_offset += cursor_y;
|
||||||
}
|
}
|
||||||
text_fragments.append(&mut line_fragments);
|
fragments.append(&mut line_fragments);
|
||||||
cursor_y += line_height; // TODO: add extra spacing between paragraphs
|
cursor_y += line_height; // TODO: add extra spacing between paragraphs
|
||||||
} else {
|
} else {
|
||||||
cursor_y += line_spacing;
|
cursor_y += line_spacing;
|
||||||
|
|
@ -303,11 +317,13 @@ impl Font {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut widest_line = 0.0;
|
let mut widest_line = 0.0;
|
||||||
for fragment in &text_fragments {
|
for fragment in &fragments {
|
||||||
widest_line = fragment.max_x().max(widest_line);
|
widest_line = fragment.max_x().max(widest_line);
|
||||||
}
|
}
|
||||||
|
|
||||||
let bounding_size = vec2(widest_line, cursor_y);
|
Galley {
|
||||||
(text_fragments, bounding_size)
|
fragments,
|
||||||
|
size: vec2(widest_line, cursor_y),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use std::{hash::Hash, sync::Arc};
|
use std::{hash::Hash, sync::Arc};
|
||||||
|
|
||||||
use crate::{color::*, containers::*, font::TextFragment, layout::*, widgets::*, *};
|
use crate::{color::*, containers::*, font::Fragment, 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).
|
||||||
|
|
@ -459,10 +459,10 @@ impl Ui {
|
||||||
text_color: Option<Color>,
|
text_color: Option<Color>,
|
||||||
) -> Vec2 {
|
) -> Vec2 {
|
||||||
let font = &self.fonts()[text_style];
|
let font = &self.fonts()[text_style];
|
||||||
let (text, size) = font.layout_multiline(text, f32::INFINITY);
|
let galley = font.layout_multiline(text, f32::INFINITY);
|
||||||
let rect = align_rect(Rect::from_min_size(pos, size), align);
|
let rect = align_rect(Rect::from_min_size(pos, galley.size), align);
|
||||||
self.add_text(rect.min, text_style, text, text_color);
|
self.add_text(rect.min, text_style, galley.fragments, text_color);
|
||||||
size
|
galley.size
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Already layed out text.
|
/// Already layed out text.
|
||||||
|
|
@ -470,11 +470,11 @@ impl Ui {
|
||||||
&mut self,
|
&mut self,
|
||||||
pos: Pos2,
|
pos: Pos2,
|
||||||
text_style: TextStyle,
|
text_style: TextStyle,
|
||||||
text: Vec<TextFragment>,
|
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 text {
|
for fragment in fragments {
|
||||||
self.add_paint_cmd(PaintCmd::Text {
|
self.add_paint_cmd(PaintCmd::Text {
|
||||||
color,
|
color,
|
||||||
pos: pos + vec2(0.0, fragment.y_offset),
|
pos: pos + vec2(0.0, fragment.y_offset),
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ impl Label {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn layout(&self, max_width: f32, ui: &Ui) -> (Vec<font::TextFragment>, Vec2) {
|
pub fn layout(&self, max_width: f32, ui: &Ui) -> font::Galley {
|
||||||
let font = &ui.fonts()[self.text_style];
|
let font = &ui.fonts()[self.text_style];
|
||||||
if self.multiline {
|
if self.multiline {
|
||||||
font.layout_multiline(&self.text, max_width)
|
font.layout_multiline(&self.text, max_width)
|
||||||
|
|
@ -95,9 +95,14 @@ impl Widget for Label {
|
||||||
} else {
|
} else {
|
||||||
ui.available().width()
|
ui.available().width()
|
||||||
};
|
};
|
||||||
let (text, text_size) = self.layout(max_width, ui);
|
let galley = self.layout(max_width, ui);
|
||||||
let interact = ui.reserve_space(text_size, None);
|
let interact = ui.reserve_space(galley.size, None);
|
||||||
ui.add_text(interact.rect.min, self.text_style, text, self.text_color);
|
ui.add_text(
|
||||||
|
interact.rect.min,
|
||||||
|
self.text_style,
|
||||||
|
galley.fragments,
|
||||||
|
self.text_color,
|
||||||
|
);
|
||||||
ui.response(interact)
|
ui.response(interact)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -145,8 +150,8 @@ impl Widget for Hyperlink {
|
||||||
let font = &ui.fonts()[text_style];
|
let font = &ui.fonts()[text_style];
|
||||||
let line_spacing = font.line_spacing();
|
let line_spacing = font.line_spacing();
|
||||||
// TODO: underline
|
// TODO: underline
|
||||||
let (text, text_size) = font.layout_multiline(&self.text, ui.available().width());
|
let galley = font.layout_multiline(&self.text, ui.available().width());
|
||||||
let interact = ui.reserve_space(text_size, Some(id));
|
let interact = ui.reserve_space(galley.size, Some(id));
|
||||||
if interact.hovered {
|
if interact.hovered {
|
||||||
ui.ctx().output().cursor_icon = CursorIcon::PointingHand;
|
ui.ctx().output().cursor_icon = CursorIcon::PointingHand;
|
||||||
}
|
}
|
||||||
|
|
@ -157,7 +162,7 @@ impl Widget for Hyperlink {
|
||||||
if interact.hovered {
|
if interact.hovered {
|
||||||
// Underline:
|
// Underline:
|
||||||
// TODO: underline spaces between words too.
|
// TODO: underline spaces between words too.
|
||||||
for fragment in &text {
|
for fragment in &galley.fragments {
|
||||||
let pos = interact.rect.min;
|
let pos = interact.rect.min;
|
||||||
let y = pos.y + fragment.y_offset + line_spacing;
|
let y = pos.y + fragment.y_offset + line_spacing;
|
||||||
let y = ui.round_to_pixel(y);
|
let y = ui.round_to_pixel(y);
|
||||||
|
|
@ -171,7 +176,7 @@ impl Widget for Hyperlink {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.add_text(interact.rect.min, text_style, text, Some(color));
|
ui.add_text(interact.rect.min, text_style, galley.fragments, Some(color));
|
||||||
|
|
||||||
ui.response(interact)
|
ui.response(interact)
|
||||||
}
|
}
|
||||||
|
|
@ -224,12 +229,12 @@ impl Widget for Button {
|
||||||
|
|
||||||
let id = ui.make_position_id();
|
let id = ui.make_position_id();
|
||||||
let font = &ui.fonts()[text_style];
|
let font = &ui.fonts()[text_style];
|
||||||
let (text, text_size) = font.layout_multiline(&text, ui.available().width());
|
let galley = font.layout_multiline(&text, ui.available().width());
|
||||||
let padding = ui.style().button_padding;
|
let padding = ui.style().button_padding;
|
||||||
let mut size = text_size + 2.0 * padding;
|
let mut size = galley.size + 2.0 * padding;
|
||||||
size.y = size.y.max(ui.style().clickable_diameter);
|
size.y = size.y.max(ui.style().clickable_diameter);
|
||||||
let interact = ui.reserve_space(size, Some(id));
|
let interact = ui.reserve_space(size, Some(id));
|
||||||
let mut text_cursor = interact.rect.left_center() + vec2(padding.x, -0.5 * text_size.y);
|
let mut text_cursor = interact.rect.left_center() + vec2(padding.x, -0.5 * galley.size.y);
|
||||||
text_cursor.y += 2.0; // TODO: why is this needed?
|
text_cursor.y += 2.0; // TODO: why is this needed?
|
||||||
let fill_color = fill_color.or(ui.style().interact(&interact).fill_color);
|
let fill_color = fill_color.or(ui.style().interact(&interact).fill_color);
|
||||||
ui.add_paint_cmd(PaintCmd::Rect {
|
ui.add_paint_cmd(PaintCmd::Rect {
|
||||||
|
|
@ -240,7 +245,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, text, Some(text_color));
|
ui.add_text(text_cursor, text_style, galley.fragments, Some(text_color));
|
||||||
ui.response(interact)
|
ui.response(interact)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -274,11 +279,11 @@ impl<'a> Widget for Checkbox<'a> {
|
||||||
let id = ui.make_position_id();
|
let id = ui.make_position_id();
|
||||||
let text_style = TextStyle::Button;
|
let text_style = TextStyle::Button;
|
||||||
let font = &ui.fonts()[text_style];
|
let font = &ui.fonts()[text_style];
|
||||||
let (text, text_size) = font.layout_single_line(&self.text);
|
let galley = font.layout_single_line(&self.text);
|
||||||
let interact = ui.reserve_space(
|
let interact = ui.reserve_space(
|
||||||
ui.style().button_padding
|
ui.style().button_padding
|
||||||
+ vec2(ui.style().start_icon_width, 0.0)
|
+ vec2(ui.style().start_icon_width, 0.0)
|
||||||
+ text_size
|
+ galley.size
|
||||||
+ ui.style().button_padding,
|
+ ui.style().button_padding,
|
||||||
Some(id),
|
Some(id),
|
||||||
);
|
);
|
||||||
|
|
@ -310,7 +315,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, text, Some(text_color));
|
ui.add_text(text_cursor, text_style, galley.fragments, Some(text_color));
|
||||||
ui.response(interact)
|
ui.response(interact)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -348,11 +353,11 @@ impl Widget for RadioButton {
|
||||||
let id = ui.make_position_id();
|
let id = ui.make_position_id();
|
||||||
let text_style = TextStyle::Button;
|
let text_style = TextStyle::Button;
|
||||||
let font = &ui.fonts()[text_style];
|
let font = &ui.fonts()[text_style];
|
||||||
let (text, text_size) = font.layout_multiline(&self.text, ui.available().width());
|
let galley = font.layout_multiline(&self.text, ui.available().width());
|
||||||
let interact = ui.reserve_space(
|
let interact = ui.reserve_space(
|
||||||
ui.style().button_padding
|
ui.style().button_padding
|
||||||
+ vec2(ui.style().start_icon_width, 0.0)
|
+ vec2(ui.style().start_icon_width, 0.0)
|
||||||
+ text_size
|
+ galley.size
|
||||||
+ ui.style().button_padding,
|
+ ui.style().button_padding,
|
||||||
Some(id),
|
Some(id),
|
||||||
);
|
);
|
||||||
|
|
@ -381,7 +386,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, text, Some(text_color));
|
ui.add_text(text_cursor, text_style, galley.fragments, Some(text_color));
|
||||||
ui.response(interact)
|
ui.response(interact)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -116,10 +116,10 @@ impl<'a> Widget for Slider<'a> {
|
||||||
let slider_sans_text = Slider { text: None, ..self };
|
let slider_sans_text = Slider { text: None, ..self };
|
||||||
|
|
||||||
if text_on_top {
|
if text_on_top {
|
||||||
// let (text, text_size) = font.layout_multiline(&full_text, ui.available().width());
|
// let galley = font.layout_multiline(&full_text, ui.available().width());
|
||||||
let (text, text_size) = font.layout_single_line(&full_text);
|
let galley = font.layout_single_line(&full_text);
|
||||||
let pos = ui.reserve_space(text_size, None).rect.min;
|
let pos = ui.reserve_space(galley.size, None).rect.min;
|
||||||
ui.add_text(pos, text_style, text, text_color);
|
ui.add_text(pos, text_style, galley.fragments, text_color);
|
||||||
slider_sans_text.ui(ui)
|
slider_sans_text.ui(ui)
|
||||||
} else {
|
} else {
|
||||||
ui.columns(2, |columns| {
|
ui.columns(2, |columns| {
|
||||||
|
|
|
||||||
|
|
@ -40,8 +40,8 @@ impl<'t> Widget for TextEdit<'t> {
|
||||||
|
|
||||||
let font = &ui.fonts()[self.text_style];
|
let font = &ui.fonts()[self.text_style];
|
||||||
let line_spacing = font.line_spacing();
|
let line_spacing = font.line_spacing();
|
||||||
let (text, text_size) = font.layout_multiline(self.text.as_str(), ui.available().width());
|
let galley = font.layout_multiline(self.text.as_str(), ui.available().width());
|
||||||
let desired_size = text_size.max(vec2(ui.available().width(), line_spacing));
|
let desired_size = galley.size.max(vec2(ui.available().width(), line_spacing));
|
||||||
let interact = ui.reserve_space(desired_size, Some(id));
|
let interact = ui.reserve_space(desired_size, Some(id));
|
||||||
|
|
||||||
if interact.clicked {
|
if interact.clicked {
|
||||||
|
|
@ -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) = text.last() {
|
let cursor_pos = if let Some(last) = galley.fragments.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,7 +103,12 @@ impl<'t> Widget for TextEdit<'t> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.add_text(interact.rect.min, self.text_style, text, self.text_color);
|
ui.add_text(
|
||||||
|
interact.rect.min,
|
||||||
|
self.text_style,
|
||||||
|
galley.fragments,
|
||||||
|
self.text_color,
|
||||||
|
);
|
||||||
|
|
||||||
ui.response(interact)
|
ui.response(interact)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue