Restructure mod paint
This commit is contained in:
parent
fb2317c993
commit
c0041d032a
18 changed files with 438 additions and 408 deletions
|
@ -8,7 +8,7 @@ use std::sync::{
|
||||||
use crate::{
|
use crate::{
|
||||||
animation_manager::AnimationManager,
|
animation_manager::AnimationManager,
|
||||||
mutex::{Mutex, MutexGuard},
|
mutex::{Mutex, MutexGuard},
|
||||||
paint::{stats::*, *},
|
paint::{stats::*, text::Fonts, *},
|
||||||
*,
|
*,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -54,7 +54,7 @@ impl Texture {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl paint::FontDefinitions {
|
impl paint::text::FontDefinitions {
|
||||||
pub fn ui(&mut self, ui: &mut Ui) {
|
pub fn ui(&mut self, ui: &mut Ui) {
|
||||||
for (text_style, (_family, size)) in self.family_and_size.iter_mut() {
|
for (text_style, (_family, size)) in self.family_and_size.iter_mut() {
|
||||||
// TODO: radio button for family
|
// TODO: radio button for family
|
||||||
|
|
|
@ -108,8 +108,9 @@ pub use {
|
||||||
math::{clamp, lerp, pos2, remap, remap_clamp, vec2, Align, Align2, NumExt, Pos2, Rect, Vec2},
|
math::{clamp, lerp, pos2, remap, remap_clamp, vec2, Align, Align2, NumExt, Pos2, Rect, Vec2},
|
||||||
memory::Memory,
|
memory::Memory,
|
||||||
paint::{
|
paint::{
|
||||||
color, Color32, FontDefinitions, FontFamily, PaintJobs, Rgba, Shape, Stroke, TextStyle,
|
color,
|
||||||
Texture, TextureId,
|
text::{FontDefinitions, FontFamily, TextStyle},
|
||||||
|
Color32, PaintJobs, Rgba, Shape, Stroke, Texture, TextureId,
|
||||||
},
|
},
|
||||||
painter::Painter,
|
painter::Painter,
|
||||||
style::Style,
|
style::Style,
|
||||||
|
|
|
@ -68,7 +68,7 @@ pub(crate) struct Options {
|
||||||
/// Controls the tessellator.
|
/// Controls the tessellator.
|
||||||
pub(crate) tessellation_options: crate::paint::TessellationOptions,
|
pub(crate) tessellation_options: crate::paint::TessellationOptions,
|
||||||
/// Font sizes etc.
|
/// Font sizes etc.
|
||||||
pub(crate) font_definitions: crate::paint::FontDefinitions,
|
pub(crate) font_definitions: crate::paint::text::FontDefinitions,
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
|
@ -1,28 +1,51 @@
|
||||||
//! 2D graphics/rendering. Fonts, textures, color, geometry, tessellation etc.
|
//! 2D graphics/rendering. Fonts, textures, color, geometry, tessellation etc.
|
||||||
|
|
||||||
pub mod color;
|
pub mod color;
|
||||||
pub mod font;
|
|
||||||
pub mod fonts;
|
|
||||||
mod galley;
|
|
||||||
mod shadow;
|
mod shadow;
|
||||||
pub mod shape;
|
pub mod shape;
|
||||||
pub mod stats;
|
pub mod stats;
|
||||||
|
mod stroke;
|
||||||
pub mod tessellator;
|
pub mod tessellator;
|
||||||
|
pub mod text;
|
||||||
mod texture_atlas;
|
mod texture_atlas;
|
||||||
|
mod triangles;
|
||||||
|
|
||||||
pub use {
|
pub use {
|
||||||
color::{Color32, Rgba},
|
color::{Color32, Rgba},
|
||||||
fonts::{FontDefinitions, FontFamily, Fonts, TextStyle},
|
|
||||||
galley::*,
|
|
||||||
shadow::Shadow,
|
shadow::Shadow,
|
||||||
shape::{Shape, Stroke},
|
shape::Shape,
|
||||||
stats::PaintStats,
|
stats::PaintStats,
|
||||||
tessellator::{
|
stroke::Stroke,
|
||||||
PaintJob, PaintJobs, TessellationOptions, TextureId, Triangles, Vertex, WHITE_UV,
|
tessellator::{PaintJob, PaintJobs, TessellationOptions},
|
||||||
},
|
text::{Galley, TextStyle},
|
||||||
texture_atlas::{Texture, TextureAtlas},
|
texture_atlas::{Texture, TextureAtlas},
|
||||||
|
triangles::{Triangles, Vertex},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// The UV coordinate of a white region of the texture mesh.
|
||||||
|
/// The default Egui texture has the top-left corner pixel fully white.
|
||||||
|
/// You need need use a clamping texture sampler for this to work
|
||||||
|
/// (so it doesn't do bilinear blending with bottom right corner).
|
||||||
|
pub const WHITE_UV: crate::Pos2 = crate::pos2(0.0, 0.0);
|
||||||
|
|
||||||
|
/// What texture to use in a [`Triangles`] mesh.
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub enum TextureId {
|
||||||
|
/// The Egui font texture.
|
||||||
|
/// If you don't want to use a texture, pick this and the [`WHITE_UV`] for uv-coord.
|
||||||
|
Egui,
|
||||||
|
|
||||||
|
/// Your own texture, defined in any which way you want.
|
||||||
|
/// Egui won't care. The backend renderer will presumably use this to look up what texture to use.
|
||||||
|
User(u64),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for TextureId {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Egui
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) struct PaintRect {
|
pub(crate) struct PaintRect {
|
||||||
pub rect: crate::Rect,
|
pub rect: crate::Rect,
|
||||||
/// How rounded the corners are. Use `0.0` for no rounding.
|
/// How rounded the corners are. Use `0.0` for no rounding.
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
use {
|
use {
|
||||||
super::{fonts::TextStyle, Color32, Fonts, Galley, Triangles},
|
super::{
|
||||||
|
text::{Fonts, Galley, TextStyle},
|
||||||
|
Color32, Triangles,
|
||||||
|
},
|
||||||
crate::*,
|
crate::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -189,33 +192,3 @@ impl Shape {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Describes the width and color of a line.
|
|
||||||
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
|
||||||
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
|
||||||
pub struct Stroke {
|
|
||||||
pub width: f32,
|
|
||||||
pub color: Color32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Stroke {
|
|
||||||
pub fn none() -> Self {
|
|
||||||
Self::new(0.0, Color32::TRANSPARENT)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new(width: impl Into<f32>, color: impl Into<Color32>) -> Self {
|
|
||||||
Self {
|
|
||||||
width: width.into(),
|
|
||||||
color: color.into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Color> From<(f32, Color)> for Stroke
|
|
||||||
where
|
|
||||||
Color: Into<Color32>,
|
|
||||||
{
|
|
||||||
fn from((width, color): (f32, Color)) -> Stroke {
|
|
||||||
Stroke::new(width, color)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
31
egui/src/paint/stroke.rs
Normal file
31
egui/src/paint/stroke.rs
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// Describes the width and color of a line.
|
||||||
|
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||||
|
pub struct Stroke {
|
||||||
|
pub width: f32,
|
||||||
|
pub color: Color32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Stroke {
|
||||||
|
pub fn none() -> Self {
|
||||||
|
Self::new(0.0, Color32::TRANSPARENT)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(width: impl Into<f32>, color: impl Into<Color32>) -> Self {
|
||||||
|
Self {
|
||||||
|
width: width.into(),
|
||||||
|
color: color.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Color> From<(f32, Color)> for Stroke
|
||||||
|
where
|
||||||
|
Color: Into<Color32>,
|
||||||
|
{
|
||||||
|
fn from((width, color): (f32, Color)) -> Stroke {
|
||||||
|
Stroke::new(width, color)
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,70 +6,11 @@
|
||||||
#![allow(clippy::identity_op)]
|
#![allow(clippy::identity_op)]
|
||||||
|
|
||||||
use {
|
use {
|
||||||
super::{
|
super::{text::Fonts, *},
|
||||||
color::{Color32, Rgba},
|
|
||||||
*,
|
|
||||||
},
|
|
||||||
crate::math::*,
|
crate::math::*,
|
||||||
std::f32::consts::TAU,
|
std::f32::consts::TAU,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// What texture to use in a [`Triangles`] mesh.
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
|
||||||
pub enum TextureId {
|
|
||||||
/// The Egui font texture.
|
|
||||||
/// If you don't want to use a texture, pick this and the [`WHITE_UV`] for uv-coord.
|
|
||||||
Egui,
|
|
||||||
|
|
||||||
/// Your own texture, defined in any which way you want.
|
|
||||||
/// Egui won't care. The backend renderer will presumably use this to look up what texture to use.
|
|
||||||
User(u64),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for TextureId {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::Egui
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The UV coordinate of a white region of the texture mesh.
|
|
||||||
/// The default Egui texture has the top-left corner pixel fully white.
|
|
||||||
/// You need need use a clamping texture sampler for this to work
|
|
||||||
/// (so it doesn't do bilinear blending with bottom right corner).
|
|
||||||
pub const WHITE_UV: Pos2 = pos2(0.0, 0.0);
|
|
||||||
|
|
||||||
/// The vertex type.
|
|
||||||
///
|
|
||||||
/// Should be friendly to send to GPU as is.
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
|
||||||
pub struct Vertex {
|
|
||||||
/// Logical pixel coordinates (points).
|
|
||||||
/// (0,0) is the top left corner of the screen.
|
|
||||||
pub pos: Pos2, // 64 bit
|
|
||||||
|
|
||||||
/// Normalized texture coordinates.
|
|
||||||
/// (0, 0) is the top left corner of the texture.
|
|
||||||
/// (1, 1) is the bottom right corner of the texture.
|
|
||||||
pub uv: Pos2, // 64 bit
|
|
||||||
|
|
||||||
/// sRGBA with premultiplied alpha
|
|
||||||
pub color: Color32, // 32 bit
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Textured triangles.
|
|
||||||
#[derive(Clone, Debug, Default)]
|
|
||||||
pub struct Triangles {
|
|
||||||
/// Draw as triangles (i.e. the length is always multiple of three).
|
|
||||||
pub indices: Vec<u32>,
|
|
||||||
|
|
||||||
/// The vertex data indexed by `indices`.
|
|
||||||
pub vertices: Vec<Vertex>,
|
|
||||||
|
|
||||||
/// The texture to use when drawing these triangles
|
|
||||||
pub texture_id: TextureId,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A clip triangle and some textured triangles.
|
/// A clip triangle and some textured triangles.
|
||||||
pub type PaintJob = (Rect, Triangles);
|
pub type PaintJob = (Rect, Triangles);
|
||||||
|
|
||||||
|
@ -78,182 +19,6 @@ pub type PaintJobs = Vec<PaintJob>;
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
/// ## Helpers for adding
|
|
||||||
impl Triangles {
|
|
||||||
pub fn with_texture(texture_id: TextureId) -> Self {
|
|
||||||
Self {
|
|
||||||
texture_id,
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn bytes_used(&self) -> usize {
|
|
||||||
std::mem::size_of::<Self>()
|
|
||||||
+ self.vertices.len() * std::mem::size_of::<Vertex>()
|
|
||||||
+ self.indices.len() * std::mem::size_of::<u32>()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Are all indices within the bounds of the contained vertices?
|
|
||||||
pub fn is_valid(&self) -> bool {
|
|
||||||
let n = self.vertices.len() as u32;
|
|
||||||
self.indices.iter().all(|&i| i < n)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
self.indices.is_empty() && self.vertices.is_empty()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Append all the indices and vertices of `other` to `self`.
|
|
||||||
pub fn append(&mut self, other: Triangles) {
|
|
||||||
debug_assert!(other.is_valid());
|
|
||||||
|
|
||||||
if self.is_empty() {
|
|
||||||
*self = other;
|
|
||||||
} else {
|
|
||||||
assert_eq!(
|
|
||||||
self.texture_id, other.texture_id,
|
|
||||||
"Can't merge Triangles using different textures"
|
|
||||||
);
|
|
||||||
|
|
||||||
let index_offset = self.vertices.len() as u32;
|
|
||||||
for index in &other.indices {
|
|
||||||
self.indices.push(index_offset + index);
|
|
||||||
}
|
|
||||||
self.vertices.extend(other.vertices.iter());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn colored_vertex(&mut self, pos: Pos2, color: Color32) {
|
|
||||||
debug_assert!(self.texture_id == TextureId::Egui);
|
|
||||||
self.vertices.push(Vertex {
|
|
||||||
pos,
|
|
||||||
uv: WHITE_UV,
|
|
||||||
color,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add a triangle.
|
|
||||||
pub fn add_triangle(&mut self, a: u32, b: u32, c: u32) {
|
|
||||||
self.indices.push(a);
|
|
||||||
self.indices.push(b);
|
|
||||||
self.indices.push(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Make room for this many additional triangles (will reserve 3x as many indices).
|
|
||||||
/// See also `reserve_vertices`.
|
|
||||||
pub fn reserve_triangles(&mut self, additional_triangles: usize) {
|
|
||||||
self.indices.reserve(3 * additional_triangles);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Make room for this many additional vertices.
|
|
||||||
/// See also `reserve_triangles`.
|
|
||||||
pub fn reserve_vertices(&mut self, additional: usize) {
|
|
||||||
self.vertices.reserve(additional);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Rectangle with a texture and color.
|
|
||||||
pub fn add_rect_with_uv(&mut self, pos: Rect, uv: Rect, color: Color32) {
|
|
||||||
let idx = self.vertices.len() as u32;
|
|
||||||
self.add_triangle(idx + 0, idx + 1, idx + 2);
|
|
||||||
self.add_triangle(idx + 2, idx + 1, idx + 3);
|
|
||||||
|
|
||||||
let right_top = Vertex {
|
|
||||||
pos: pos.right_top(),
|
|
||||||
uv: uv.right_top(),
|
|
||||||
color,
|
|
||||||
};
|
|
||||||
let left_top = Vertex {
|
|
||||||
pos: pos.left_top(),
|
|
||||||
uv: uv.left_top(),
|
|
||||||
color,
|
|
||||||
};
|
|
||||||
let left_bottom = Vertex {
|
|
||||||
pos: pos.left_bottom(),
|
|
||||||
uv: uv.left_bottom(),
|
|
||||||
color,
|
|
||||||
};
|
|
||||||
let right_bottom = Vertex {
|
|
||||||
pos: pos.right_bottom(),
|
|
||||||
uv: uv.right_bottom(),
|
|
||||||
color,
|
|
||||||
};
|
|
||||||
self.vertices.push(left_top);
|
|
||||||
self.vertices.push(right_top);
|
|
||||||
self.vertices.push(left_bottom);
|
|
||||||
self.vertices.push(right_bottom);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Uniformly colored rectangle.
|
|
||||||
pub fn add_colored_rect(&mut self, rect: Rect, color: Color32) {
|
|
||||||
debug_assert!(self.texture_id == TextureId::Egui);
|
|
||||||
self.add_rect_with_uv(rect, [WHITE_UV, WHITE_UV].into(), color)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This is for platforms that only support 16-bit index buffers.
|
|
||||||
///
|
|
||||||
/// Splits this mesh into many smaller meshes (if needed).
|
|
||||||
/// All the returned meshes will have indices that fit into a `u16`.
|
|
||||||
pub fn split_to_u16(self) -> Vec<Triangles> {
|
|
||||||
const MAX_SIZE: u32 = 1 << 16;
|
|
||||||
|
|
||||||
if self.vertices.len() < MAX_SIZE as usize {
|
|
||||||
return vec![self]; // Common-case optimization
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut output = vec![];
|
|
||||||
let mut index_cursor = 0;
|
|
||||||
|
|
||||||
while index_cursor < self.indices.len() {
|
|
||||||
let span_start = index_cursor;
|
|
||||||
let mut min_vindex = self.indices[index_cursor];
|
|
||||||
let mut max_vindex = self.indices[index_cursor];
|
|
||||||
|
|
||||||
while index_cursor < self.indices.len() {
|
|
||||||
let (mut new_min, mut new_max) = (min_vindex, max_vindex);
|
|
||||||
for i in 0..3 {
|
|
||||||
let idx = self.indices[index_cursor + i];
|
|
||||||
new_min = new_min.min(idx);
|
|
||||||
new_max = new_max.max(idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
if new_max - new_min < MAX_SIZE {
|
|
||||||
// Triangle fits
|
|
||||||
min_vindex = new_min;
|
|
||||||
max_vindex = new_max;
|
|
||||||
index_cursor += 3;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assert!(
|
|
||||||
index_cursor > span_start,
|
|
||||||
"One triangle spanned more than {} vertices",
|
|
||||||
MAX_SIZE
|
|
||||||
);
|
|
||||||
|
|
||||||
output.push(Triangles {
|
|
||||||
indices: self.indices[span_start..index_cursor]
|
|
||||||
.iter()
|
|
||||||
.map(|vi| vi - min_vindex)
|
|
||||||
.collect(),
|
|
||||||
vertices: self.vertices[(min_vindex as usize)..=(max_vindex as usize)].to_vec(),
|
|
||||||
texture_id: self.texture_id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
output
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Translate location by this much, in-place
|
|
||||||
pub fn translate(&mut self, delta: Vec2) {
|
|
||||||
for v in &mut self.vertices {
|
|
||||||
v.pos += delta;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct PathPoint {
|
pub struct PathPoint {
|
||||||
pos: Pos2,
|
pos: Pos2,
|
||||||
|
@ -669,10 +434,11 @@ fn mul_color(color: Color32, factor: f32) -> Color32 {
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// Converts [`Shape`]s into [`Triangles`].
|
||||||
pub struct Tessellator {
|
pub struct Tessellator {
|
||||||
pub options: TessellationOptions,
|
options: TessellationOptions,
|
||||||
/// Only used for culling
|
/// Only used for culling
|
||||||
pub clip_rect: Rect,
|
clip_rect: Rect,
|
||||||
scratchpad_points: Vec<Pos2>,
|
scratchpad_points: Vec<Pos2>,
|
||||||
scratchpad_path: Path,
|
scratchpad_path: Path,
|
||||||
}
|
}
|
||||||
|
|
108
egui/src/paint/text/cursor.rs
Normal file
108
egui/src/paint/text/cursor.rs
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
//! Different types of text cursors, i.e. ways to point into a [`super::Galley`].
|
||||||
|
|
||||||
|
/// Character cursor
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||||
|
pub struct CCursor {
|
||||||
|
/// Character offset (NOT byte offset!).
|
||||||
|
pub index: usize,
|
||||||
|
|
||||||
|
/// If this cursors sits right at the border of a wrapped row break (NOT paragraph break)
|
||||||
|
/// do we prefer the next row?
|
||||||
|
/// This is *almost* always what you want, *except* for when
|
||||||
|
/// explicitly clicking the end of a row or pressing the end key.
|
||||||
|
pub prefer_next_row: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CCursor {
|
||||||
|
pub fn new(index: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
index,
|
||||||
|
prefer_next_row: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Two `CCursor`s are considered equal if they refer to the same character boundary,
|
||||||
|
/// even if one prefers the start of the next row.
|
||||||
|
impl PartialEq for CCursor {
|
||||||
|
fn eq(&self, other: &CCursor) -> bool {
|
||||||
|
self.index == other.index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Add<usize> for CCursor {
|
||||||
|
type Output = CCursor;
|
||||||
|
fn add(self, rhs: usize) -> Self::Output {
|
||||||
|
CCursor {
|
||||||
|
index: self.index.saturating_add(rhs),
|
||||||
|
prefer_next_row: self.prefer_next_row,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Sub<usize> for CCursor {
|
||||||
|
type Output = CCursor;
|
||||||
|
fn sub(self, rhs: usize) -> Self::Output {
|
||||||
|
CCursor {
|
||||||
|
index: self.index.saturating_sub(rhs),
|
||||||
|
prefer_next_row: self.prefer_next_row,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Row Cursor
|
||||||
|
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||||
|
pub struct RCursor {
|
||||||
|
/// 0 is first row, and so on.
|
||||||
|
/// Note that a single paragraph can span multiple rows.
|
||||||
|
/// (a paragraph is text separated by `\n`).
|
||||||
|
pub row: usize,
|
||||||
|
|
||||||
|
/// Character based (NOT bytes).
|
||||||
|
/// It is fine if this points to something beyond the end of the current row.
|
||||||
|
/// When moving up/down it may again be within the next row.
|
||||||
|
pub column: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Paragraph Cursor
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||||
|
pub struct PCursor {
|
||||||
|
/// 0 is first paragraph, and so on.
|
||||||
|
/// Note that a single paragraph can span multiple rows.
|
||||||
|
/// (a paragraph is text separated by `\n`).
|
||||||
|
pub paragraph: usize,
|
||||||
|
|
||||||
|
/// Character based (NOT bytes).
|
||||||
|
/// It is fine if this points to something beyond the end of the current paragraph.
|
||||||
|
/// When moving up/down it may again be within the next paragraph.
|
||||||
|
pub offset: usize,
|
||||||
|
|
||||||
|
/// If this cursors sits right at the border of a wrapped row break (NOT paragraph break)
|
||||||
|
/// do we prefer the next row?
|
||||||
|
/// This is *almost* always what you want, *except* for when
|
||||||
|
/// explicitly clicking the end of a row or pressing the end key.
|
||||||
|
pub prefer_next_row: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Two `PCursor`s are considered equal if they refer to the same character boundary,
|
||||||
|
/// even if one prefers the start of the next row.
|
||||||
|
impl PartialEq for PCursor {
|
||||||
|
fn eq(&self, other: &PCursor) -> bool {
|
||||||
|
self.paragraph == other.paragraph && self.offset == other.offset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// All different types of cursors together.
|
||||||
|
/// They all point to the same place, but in their own different ways.
|
||||||
|
/// pcursor/rcursor can also point to after the end of the paragraph/row.
|
||||||
|
/// Does not implement `PartialEq` because you must think which cursor should be equivalent.
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||||
|
pub struct Cursor {
|
||||||
|
pub ccursor: CCursor,
|
||||||
|
pub rcursor: RCursor,
|
||||||
|
pub pcursor: PCursor,
|
||||||
|
}
|
|
@ -8,11 +8,12 @@ use {
|
||||||
use crate::{
|
use crate::{
|
||||||
math::{vec2, Vec2},
|
math::{vec2, Vec2},
|
||||||
mutex::{Mutex, RwLock},
|
mutex::{Mutex, RwLock},
|
||||||
paint::{Galley, Row},
|
paint::{
|
||||||
|
text::galley::{Galley, Row},
|
||||||
|
TextureAtlas,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::texture_atlas::TextureAtlas;
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
|
@ -4,12 +4,9 @@ use std::{
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::font::{Font, FontImpl};
|
||||||
use crate::mutex::Mutex;
|
use crate::mutex::Mutex;
|
||||||
|
use crate::paint::{Texture, TextureAtlas};
|
||||||
use super::{
|
|
||||||
font::{Font, FontImpl},
|
|
||||||
texture_atlas::{Texture, TextureAtlas},
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: rename
|
// TODO: rename
|
||||||
/// One of a few categories of styles of text, e.g. body, button or heading.
|
/// One of a few categories of styles of text, e.g. body, button or heading.
|
||||||
|
@ -118,22 +115,22 @@ impl Default for FontDefinitions {
|
||||||
// Use size 13 for this. NOTHING ELSE:
|
// Use size 13 for this. NOTHING ELSE:
|
||||||
font_data.insert(
|
font_data.insert(
|
||||||
"ProggyClean".to_owned(),
|
"ProggyClean".to_owned(),
|
||||||
std::borrow::Cow::Borrowed(include_bytes!("../../fonts/ProggyClean.ttf")),
|
std::borrow::Cow::Borrowed(include_bytes!("../../../fonts/ProggyClean.ttf")),
|
||||||
);
|
);
|
||||||
font_data.insert(
|
font_data.insert(
|
||||||
"Ubuntu-Light".to_owned(),
|
"Ubuntu-Light".to_owned(),
|
||||||
std::borrow::Cow::Borrowed(include_bytes!("../../fonts/Ubuntu-Light.ttf")),
|
std::borrow::Cow::Borrowed(include_bytes!("../../../fonts/Ubuntu-Light.ttf")),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Some good looking emojis. Use as first priority:
|
// Some good looking emojis. Use as first priority:
|
||||||
font_data.insert(
|
font_data.insert(
|
||||||
"NotoEmoji-Regular".to_owned(),
|
"NotoEmoji-Regular".to_owned(),
|
||||||
std::borrow::Cow::Borrowed(include_bytes!("../../fonts/NotoEmoji-Regular.ttf")),
|
std::borrow::Cow::Borrowed(include_bytes!("../../../fonts/NotoEmoji-Regular.ttf")),
|
||||||
);
|
);
|
||||||
// Bigger emojis, and more. <http://jslegers.github.io/emoji-icon-font/>:
|
// Bigger emojis, and more. <http://jslegers.github.io/emoji-icon-font/>:
|
||||||
font_data.insert(
|
font_data.insert(
|
||||||
"emoji-icon-font".to_owned(),
|
"emoji-icon-font".to_owned(),
|
||||||
std::borrow::Cow::Borrowed(include_bytes!("../../fonts/emoji-icon-font.ttf")),
|
std::borrow::Cow::Borrowed(include_bytes!("../../../fonts/emoji-icon-font.ttf")),
|
||||||
);
|
);
|
||||||
|
|
||||||
fonts_for_family.insert(
|
fonts_for_family.insert(
|
|
@ -1,3 +1,6 @@
|
||||||
|
//! A [`Galley`] is a piece of text after layout, i.e. where each character has been assigned a position.
|
||||||
|
//!
|
||||||
|
//! ## How it works
|
||||||
//! This is going to get complicated.
|
//! This is going to get complicated.
|
||||||
//!
|
//!
|
||||||
//! To avoid confusion, we never use the word "line".
|
//! To avoid confusion, we never use the word "line".
|
||||||
|
@ -14,117 +17,11 @@
|
||||||
//!
|
//!
|
||||||
//! The offset `6` is both the end of the first row
|
//! The offset `6` is both the end of the first row
|
||||||
//! and the start of the second row.
|
//! and the start of the second row.
|
||||||
//! The `prefer_next_row` selects which.
|
//! [`CCursor::prefer_next_row`] etc selects which.
|
||||||
|
|
||||||
|
use super::cursor::*;
|
||||||
use crate::math::{pos2, NumExt, Rect, Vec2};
|
use crate::math::{pos2, NumExt, Rect, Vec2};
|
||||||
|
|
||||||
/// Character cursor
|
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
|
||||||
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
|
||||||
pub struct CCursor {
|
|
||||||
/// Character offset (NOT byte offset!).
|
|
||||||
pub index: usize,
|
|
||||||
|
|
||||||
/// If this cursors sits right at the border of a wrapped row break (NOT paragraph break)
|
|
||||||
/// do we prefer the next row?
|
|
||||||
/// This is *almost* always what you want, *except* for when
|
|
||||||
/// explicitly clicking the end of a row or pressing the end key.
|
|
||||||
pub prefer_next_row: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CCursor {
|
|
||||||
pub fn new(index: usize) -> Self {
|
|
||||||
Self {
|
|
||||||
index,
|
|
||||||
prefer_next_row: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Two `CCursor`s are considered equal if they refer to the same character boundary,
|
|
||||||
/// even if one prefers the start of the next row.
|
|
||||||
impl PartialEq for CCursor {
|
|
||||||
fn eq(&self, other: &CCursor) -> bool {
|
|
||||||
self.index == other.index
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::ops::Add<usize> for CCursor {
|
|
||||||
type Output = CCursor;
|
|
||||||
fn add(self, rhs: usize) -> Self::Output {
|
|
||||||
CCursor {
|
|
||||||
index: self.index.saturating_add(rhs),
|
|
||||||
prefer_next_row: self.prefer_next_row,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::ops::Sub<usize> for CCursor {
|
|
||||||
type Output = CCursor;
|
|
||||||
fn sub(self, rhs: usize) -> Self::Output {
|
|
||||||
CCursor {
|
|
||||||
index: self.index.saturating_sub(rhs),
|
|
||||||
prefer_next_row: self.prefer_next_row,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Row Cursor
|
|
||||||
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
|
||||||
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
|
||||||
pub struct RCursor {
|
|
||||||
/// 0 is first row, and so on.
|
|
||||||
/// Note that a single paragraph can span multiple rows.
|
|
||||||
/// (a paragraph is text separated by `\n`).
|
|
||||||
pub row: usize,
|
|
||||||
|
|
||||||
/// Character based (NOT bytes).
|
|
||||||
/// It is fine if this points to something beyond the end of the current row.
|
|
||||||
/// When moving up/down it may again be within the next row.
|
|
||||||
pub column: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Paragraph Cursor
|
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
|
||||||
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
|
||||||
pub struct PCursor {
|
|
||||||
/// 0 is first paragraph, and so on.
|
|
||||||
/// Note that a single paragraph can span multiple rows.
|
|
||||||
/// (a paragraph is text separated by `\n`).
|
|
||||||
pub paragraph: usize,
|
|
||||||
|
|
||||||
/// Character based (NOT bytes).
|
|
||||||
/// It is fine if this points to something beyond the end of the current paragraph.
|
|
||||||
/// When moving up/down it may again be within the next paragraph.
|
|
||||||
pub offset: usize,
|
|
||||||
|
|
||||||
/// If this cursors sits right at the border of a wrapped row break (NOT paragraph break)
|
|
||||||
/// do we prefer the next row?
|
|
||||||
/// This is *almost* always what you want, *except* for when
|
|
||||||
/// explicitly clicking the end of a row or pressing the end key.
|
|
||||||
pub prefer_next_row: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Two `PCursor`s are considered equal if they refer to the same character boundary,
|
|
||||||
/// even if one prefers the start of the next row.
|
|
||||||
impl PartialEq for PCursor {
|
|
||||||
fn eq(&self, other: &PCursor) -> bool {
|
|
||||||
self.paragraph == other.paragraph && self.offset == other.offset
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// All different types of cursors together.
|
|
||||||
/// They all point to the same place, but in their own different ways.
|
|
||||||
/// pcursor/rcursor can also point to after the end of the paragraph/row.
|
|
||||||
/// Does not implement `PartialEq` because you must think which cursor should be equivalent.
|
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
|
||||||
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
|
||||||
pub struct Cursor {
|
|
||||||
pub ccursor: CCursor,
|
|
||||||
pub rcursor: RCursor,
|
|
||||||
pub pcursor: PCursor,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A collection of text locked into place.
|
/// A collection of text locked into place.
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct Galley {
|
pub struct Galley {
|
||||||
|
@ -657,7 +554,7 @@ fn test_text_layout() {
|
||||||
use crate::paint::*;
|
use crate::paint::*;
|
||||||
|
|
||||||
let pixels_per_point = 1.0;
|
let pixels_per_point = 1.0;
|
||||||
let fonts = Fonts::from_definitions(pixels_per_point, FontDefinitions::default());
|
let fonts = text::Fonts::from_definitions(pixels_per_point, text::FontDefinitions::default());
|
||||||
let font = &fonts[TextStyle::Monospace];
|
let font = &fonts[TextStyle::Monospace];
|
||||||
|
|
||||||
let galley = font.layout_multiline("".to_owned(), 1024.0);
|
let galley = font.layout_multiline("".to_owned(), 1024.0);
|
9
egui/src/paint/text/mod.rs
Normal file
9
egui/src/paint/text/mod.rs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
pub mod cursor;
|
||||||
|
mod font;
|
||||||
|
mod fonts;
|
||||||
|
mod galley;
|
||||||
|
|
||||||
|
pub use {
|
||||||
|
fonts::{FontDefinitions, FontFamily, Fonts, TextStyle},
|
||||||
|
galley::{Galley, Row},
|
||||||
|
};
|
209
egui/src/paint/triangles.rs
Normal file
209
egui/src/paint/triangles.rs
Normal file
|
@ -0,0 +1,209 @@
|
||||||
|
use super::*;
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
|
/// The vertex type.
|
||||||
|
///
|
||||||
|
/// Should be friendly to send to GPU as is.
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
pub struct Vertex {
|
||||||
|
/// Logical pixel coordinates (points).
|
||||||
|
/// (0,0) is the top left corner of the screen.
|
||||||
|
pub pos: Pos2, // 64 bit
|
||||||
|
|
||||||
|
/// Normalized texture coordinates.
|
||||||
|
/// (0, 0) is the top left corner of the texture.
|
||||||
|
/// (1, 1) is the bottom right corner of the texture.
|
||||||
|
pub uv: Pos2, // 64 bit
|
||||||
|
|
||||||
|
/// sRGBA with premultiplied alpha
|
||||||
|
pub color: Color32, // 32 bit
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Textured triangles.
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub struct Triangles {
|
||||||
|
/// Draw as triangles (i.e. the length is always multiple of three).
|
||||||
|
pub indices: Vec<u32>,
|
||||||
|
|
||||||
|
/// The vertex data indexed by `indices`.
|
||||||
|
pub vertices: Vec<Vertex>,
|
||||||
|
|
||||||
|
/// The texture to use when drawing these triangles
|
||||||
|
pub texture_id: TextureId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Triangles {
|
||||||
|
pub fn with_texture(texture_id: TextureId) -> Self {
|
||||||
|
Self {
|
||||||
|
texture_id,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bytes_used(&self) -> usize {
|
||||||
|
std::mem::size_of::<Self>()
|
||||||
|
+ self.vertices.len() * std::mem::size_of::<Vertex>()
|
||||||
|
+ self.indices.len() * std::mem::size_of::<u32>()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Are all indices within the bounds of the contained vertices?
|
||||||
|
pub fn is_valid(&self) -> bool {
|
||||||
|
let n = self.vertices.len() as u32;
|
||||||
|
self.indices.iter().all(|&i| i < n)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.indices.is_empty() && self.vertices.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Append all the indices and vertices of `other` to `self`.
|
||||||
|
pub fn append(&mut self, other: Triangles) {
|
||||||
|
debug_assert!(other.is_valid());
|
||||||
|
|
||||||
|
if self.is_empty() {
|
||||||
|
*self = other;
|
||||||
|
} else {
|
||||||
|
assert_eq!(
|
||||||
|
self.texture_id, other.texture_id,
|
||||||
|
"Can't merge Triangles using different textures"
|
||||||
|
);
|
||||||
|
|
||||||
|
let index_offset = self.vertices.len() as u32;
|
||||||
|
for index in &other.indices {
|
||||||
|
self.indices.push(index_offset + index);
|
||||||
|
}
|
||||||
|
self.vertices.extend(other.vertices.iter());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn colored_vertex(&mut self, pos: Pos2, color: Color32) {
|
||||||
|
debug_assert!(self.texture_id == TextureId::Egui);
|
||||||
|
self.vertices.push(Vertex {
|
||||||
|
pos,
|
||||||
|
uv: WHITE_UV,
|
||||||
|
color,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a triangle.
|
||||||
|
pub fn add_triangle(&mut self, a: u32, b: u32, c: u32) {
|
||||||
|
self.indices.push(a);
|
||||||
|
self.indices.push(b);
|
||||||
|
self.indices.push(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Make room for this many additional triangles (will reserve 3x as many indices).
|
||||||
|
/// See also `reserve_vertices`.
|
||||||
|
pub fn reserve_triangles(&mut self, additional_triangles: usize) {
|
||||||
|
self.indices.reserve(3 * additional_triangles);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Make room for this many additional vertices.
|
||||||
|
/// See also `reserve_triangles`.
|
||||||
|
pub fn reserve_vertices(&mut self, additional: usize) {
|
||||||
|
self.vertices.reserve(additional);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Rectangle with a texture and color.
|
||||||
|
pub fn add_rect_with_uv(&mut self, pos: Rect, uv: Rect, color: Color32) {
|
||||||
|
#![allow(clippy::identity_op)]
|
||||||
|
|
||||||
|
let idx = self.vertices.len() as u32;
|
||||||
|
self.add_triangle(idx + 0, idx + 1, idx + 2);
|
||||||
|
self.add_triangle(idx + 2, idx + 1, idx + 3);
|
||||||
|
|
||||||
|
let right_top = Vertex {
|
||||||
|
pos: pos.right_top(),
|
||||||
|
uv: uv.right_top(),
|
||||||
|
color,
|
||||||
|
};
|
||||||
|
let left_top = Vertex {
|
||||||
|
pos: pos.left_top(),
|
||||||
|
uv: uv.left_top(),
|
||||||
|
color,
|
||||||
|
};
|
||||||
|
let left_bottom = Vertex {
|
||||||
|
pos: pos.left_bottom(),
|
||||||
|
uv: uv.left_bottom(),
|
||||||
|
color,
|
||||||
|
};
|
||||||
|
let right_bottom = Vertex {
|
||||||
|
pos: pos.right_bottom(),
|
||||||
|
uv: uv.right_bottom(),
|
||||||
|
color,
|
||||||
|
};
|
||||||
|
self.vertices.push(left_top);
|
||||||
|
self.vertices.push(right_top);
|
||||||
|
self.vertices.push(left_bottom);
|
||||||
|
self.vertices.push(right_bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Uniformly colored rectangle.
|
||||||
|
pub fn add_colored_rect(&mut self, rect: Rect, color: Color32) {
|
||||||
|
debug_assert!(self.texture_id == TextureId::Egui);
|
||||||
|
self.add_rect_with_uv(rect, [WHITE_UV, WHITE_UV].into(), color)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This is for platforms that only support 16-bit index buffers.
|
||||||
|
///
|
||||||
|
/// Splits this mesh into many smaller meshes (if needed).
|
||||||
|
/// All the returned meshes will have indices that fit into a `u16`.
|
||||||
|
pub fn split_to_u16(self) -> Vec<Triangles> {
|
||||||
|
const MAX_SIZE: u32 = 1 << 16;
|
||||||
|
|
||||||
|
if self.vertices.len() < MAX_SIZE as usize {
|
||||||
|
return vec![self]; // Common-case optimization
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut output = vec![];
|
||||||
|
let mut index_cursor = 0;
|
||||||
|
|
||||||
|
while index_cursor < self.indices.len() {
|
||||||
|
let span_start = index_cursor;
|
||||||
|
let mut min_vindex = self.indices[index_cursor];
|
||||||
|
let mut max_vindex = self.indices[index_cursor];
|
||||||
|
|
||||||
|
while index_cursor < self.indices.len() {
|
||||||
|
let (mut new_min, mut new_max) = (min_vindex, max_vindex);
|
||||||
|
for i in 0..3 {
|
||||||
|
let idx = self.indices[index_cursor + i];
|
||||||
|
new_min = new_min.min(idx);
|
||||||
|
new_max = new_max.max(idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if new_max - new_min < MAX_SIZE {
|
||||||
|
// Triangle fits
|
||||||
|
min_vindex = new_min;
|
||||||
|
max_vindex = new_max;
|
||||||
|
index_cursor += 3;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
index_cursor > span_start,
|
||||||
|
"One triangle spanned more than {} vertices",
|
||||||
|
MAX_SIZE
|
||||||
|
);
|
||||||
|
|
||||||
|
output.push(Triangles {
|
||||||
|
indices: self.indices[span_start..index_cursor]
|
||||||
|
.iter()
|
||||||
|
.map(|vi| vi - min_vindex)
|
||||||
|
.collect(),
|
||||||
|
vertices: self.vertices[(min_vindex as usize)..=(max_vindex as usize)].to_vec(),
|
||||||
|
texture_id: self.texture_id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
output
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Translate location by this much, in-place
|
||||||
|
pub fn translate(&mut self, delta: Vec2) {
|
||||||
|
for v in &mut self.vertices {
|
||||||
|
v.pos += delta;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,10 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
layers::ShapeIdx,
|
layers::ShapeIdx,
|
||||||
math::{Align2, Pos2, Rect, Vec2},
|
math::{Align2, Pos2, Rect, Vec2},
|
||||||
paint::{Fonts, Galley, Shape, Stroke, TextStyle},
|
paint::{
|
||||||
|
text::{Fonts, Galley, TextStyle},
|
||||||
|
Shape, Stroke,
|
||||||
|
},
|
||||||
Color32, CtxRef, LayerId,
|
Color32, CtxRef, LayerId,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,15 @@
|
||||||
|
|
||||||
use std::{hash::Hash, sync::Arc};
|
use std::{hash::Hash, sync::Arc};
|
||||||
|
|
||||||
use crate::{color::*, containers::*, layout::*, mutex::MutexGuard, paint::*, widgets::*, *};
|
use crate::{
|
||||||
|
color::*,
|
||||||
|
containers::*,
|
||||||
|
layout::*,
|
||||||
|
mutex::MutexGuard,
|
||||||
|
paint::{text::Fonts, *},
|
||||||
|
widgets::*,
|
||||||
|
*,
|
||||||
|
};
|
||||||
|
|
||||||
/// This is what you use to place widgets.
|
/// This is what you use to place widgets.
|
||||||
///
|
///
|
||||||
|
|
|
@ -73,7 +73,7 @@ impl Label {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn font_height(&self, fonts: &paint::Fonts, style: &Style) -> f32 {
|
pub fn font_height(&self, fonts: &paint::text::Fonts, style: &Style) -> f32 {
|
||||||
let text_style = self.text_style_or_default(style);
|
let text_style = self.text_style_or_default(style);
|
||||||
fonts[text_style].row_height()
|
fonts[text_style].row_height()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
use crate::{paint::*, util::undoer::Undoer, *};
|
use crate::{
|
||||||
|
paint::{text::cursor::*, *},
|
||||||
|
util::undoer::Undoer,
|
||||||
|
*,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||||
|
|
Loading…
Reference in a new issue