Restructure mod paint

This commit is contained in:
Emil Ernerfeldt 2021-01-10 14:39:03 +01:00
parent fb2317c993
commit c0041d032a
18 changed files with 438 additions and 408 deletions

View File

@ -8,7 +8,7 @@ use std::sync::{
use crate::{
animation_manager::AnimationManager,
mutex::{Mutex, MutexGuard},
paint::{stats::*, *},
paint::{stats::*, text::Fonts, *},
*,
};

View File

@ -54,7 +54,7 @@ impl Texture {
}
}
impl paint::FontDefinitions {
impl paint::text::FontDefinitions {
pub fn ui(&mut self, ui: &mut Ui) {
for (text_style, (_family, size)) in self.family_and_size.iter_mut() {
// TODO: radio button for family

View File

@ -108,8 +108,9 @@ pub use {
math::{clamp, lerp, pos2, remap, remap_clamp, vec2, Align, Align2, NumExt, Pos2, Rect, Vec2},
memory::Memory,
paint::{
color, Color32, FontDefinitions, FontFamily, PaintJobs, Rgba, Shape, Stroke, TextStyle,
Texture, TextureId,
color,
text::{FontDefinitions, FontFamily, TextStyle},
Color32, PaintJobs, Rgba, Shape, Stroke, Texture, TextureId,
},
painter::Painter,
style::Style,

View File

@ -68,7 +68,7 @@ pub(crate) struct Options {
/// Controls the tessellator.
pub(crate) tessellation_options: crate::paint::TessellationOptions,
/// Font sizes etc.
pub(crate) font_definitions: crate::paint::FontDefinitions,
pub(crate) font_definitions: crate::paint::text::FontDefinitions,
}
// ----------------------------------------------------------------------------

View File

@ -1,28 +1,51 @@
//! 2D graphics/rendering. Fonts, textures, color, geometry, tessellation etc.
pub mod color;
pub mod font;
pub mod fonts;
mod galley;
mod shadow;
pub mod shape;
pub mod stats;
mod stroke;
pub mod tessellator;
pub mod text;
mod texture_atlas;
mod triangles;
pub use {
color::{Color32, Rgba},
fonts::{FontDefinitions, FontFamily, Fonts, TextStyle},
galley::*,
shadow::Shadow,
shape::{Shape, Stroke},
shape::Shape,
stats::PaintStats,
tessellator::{
PaintJob, PaintJobs, TessellationOptions, TextureId, Triangles, Vertex, WHITE_UV,
},
stroke::Stroke,
tessellator::{PaintJob, PaintJobs, TessellationOptions},
text::{Galley, TextStyle},
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 rect: crate::Rect,
/// How rounded the corners are. Use `0.0` for no rounding.

View File

@ -1,5 +1,8 @@
use {
super::{fonts::TextStyle, Color32, Fonts, Galley, Triangles},
super::{
text::{Fonts, Galley, TextStyle},
Color32, Triangles,
},
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
View 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)
}
}

View File

@ -6,70 +6,11 @@
#![allow(clippy::identity_op)]
use {
super::{
color::{Color32, Rgba},
*,
},
super::{text::Fonts, *},
crate::math::*,
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.
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)]
pub struct PathPoint {
pos: Pos2,
@ -669,10 +434,11 @@ fn mul_color(color: Color32, factor: f32) -> Color32 {
// ----------------------------------------------------------------------------
/// Converts [`Shape`]s into [`Triangles`].
pub struct Tessellator {
pub options: TessellationOptions,
options: TessellationOptions,
/// Only used for culling
pub clip_rect: Rect,
clip_rect: Rect,
scratchpad_points: Vec<Pos2>,
scratchpad_path: Path,
}

View 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,
}

View File

@ -8,11 +8,12 @@ use {
use crate::{
math::{vec2, Vec2},
mutex::{Mutex, RwLock},
paint::{Galley, Row},
paint::{
text::galley::{Galley, Row},
TextureAtlas,
},
};
use super::texture_atlas::TextureAtlas;
// ----------------------------------------------------------------------------
#[derive(Clone, Copy, Debug)]

View File

@ -4,12 +4,9 @@ use std::{
sync::Arc,
};
use super::font::{Font, FontImpl};
use crate::mutex::Mutex;
use super::{
font::{Font, FontImpl},
texture_atlas::{Texture, TextureAtlas},
};
use crate::paint::{Texture, TextureAtlas};
// TODO: rename
/// 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:
font_data.insert(
"ProggyClean".to_owned(),
std::borrow::Cow::Borrowed(include_bytes!("../../fonts/ProggyClean.ttf")),
std::borrow::Cow::Borrowed(include_bytes!("../../../fonts/ProggyClean.ttf")),
);
font_data.insert(
"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:
font_data.insert(
"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/>:
font_data.insert(
"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(

View File

@ -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.
//!
//! To avoid confusion, we never use the word "line".
@ -14,117 +17,11 @@
//!
//! The offset `6` is both the end of the first 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};
/// 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.
#[derive(Clone, Debug, Default)]
pub struct Galley {
@ -657,7 +554,7 @@ fn test_text_layout() {
use crate::paint::*;
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 galley = font.layout_multiline("".to_owned(), 1024.0);

View 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
View 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;
}
}
}

View File

@ -1,7 +1,10 @@
use crate::{
layers::ShapeIdx,
math::{Align2, Pos2, Rect, Vec2},
paint::{Fonts, Galley, Shape, Stroke, TextStyle},
paint::{
text::{Fonts, Galley, TextStyle},
Shape, Stroke,
},
Color32, CtxRef, LayerId,
};

View File

@ -2,7 +2,15 @@
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.
///

View File

@ -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);
fonts[text_style].row_height()
}

View File

@ -1,4 +1,8 @@
use crate::{paint::*, util::undoer::Undoer, *};
use crate::{
paint::{text::cursor::*, *},
util::undoer::Undoer,
*,
};
#[derive(Clone, Debug, Default)]
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]