Allow resizing fonts at runtime
This commit is contained in:
parent
fe3542a28d
commit
88fdd127ea
9 changed files with 156 additions and 57 deletions
|
@ -3,10 +3,12 @@ use std::sync::Arc;
|
||||||
use crate::{
|
use crate::{
|
||||||
layout,
|
layout,
|
||||||
layout::{LayoutOptions, Region},
|
layout::{LayoutOptions, Region},
|
||||||
|
math::vec2,
|
||||||
|
mesher::Vertex,
|
||||||
style,
|
style,
|
||||||
types::GuiInput,
|
types::{Color, GuiCmd, GuiInput, PaintCmd},
|
||||||
widgets::*,
|
widgets::*,
|
||||||
Frame, RawInput, Texture,
|
FontSizes, Fonts, Frame, RawInput, Texture,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Default)]
|
#[derive(Clone, Copy, Default)]
|
||||||
|
@ -37,6 +39,12 @@ fn show_style(style: &mut style::Style, gui: &mut Region) {
|
||||||
gui.add(Slider::new(&mut style.line_width, 0.0, 10.0).text("line_width"));
|
gui.add(Slider::new(&mut style.line_width, 0.0, 10.0).text("line_width"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn show_font_sizes(font_sizes: &mut FontSizes, gui: &mut Region) {
|
||||||
|
for (text_style, mut size) in font_sizes {
|
||||||
|
gui.add(Slider::new(&mut size, 4.0, 40.0).text(format!("{:?}", text_style)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Encapsulates input, layout and painting for ease of use.
|
/// Encapsulates input, layout and painting for ease of use.
|
||||||
pub struct Emigui {
|
pub struct Emigui {
|
||||||
pub last_input: RawInput,
|
pub last_input: RawInput,
|
||||||
|
@ -103,6 +111,39 @@ impl Emigui {
|
||||||
show_style(&mut self.style, gui);
|
show_style(&mut self.style, gui);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
region.foldable("Fonts", |gui| {
|
||||||
|
let texture = self.texture();
|
||||||
|
gui.add(label(format!(
|
||||||
|
"Font texture size: {} x {}",
|
||||||
|
texture.width, texture.height
|
||||||
|
)));
|
||||||
|
let size = vec2(texture.width as f32, texture.height as f32);
|
||||||
|
let rect = gui.reserve_space(size, None).rect;
|
||||||
|
let top_left = Vertex {
|
||||||
|
pos: rect.min(),
|
||||||
|
uv: (0, 0),
|
||||||
|
color: Color::WHITE,
|
||||||
|
};
|
||||||
|
let bottom_right = Vertex {
|
||||||
|
pos: rect.max(),
|
||||||
|
uv: (texture.width as u16 - 1, texture.height as u16 - 1),
|
||||||
|
color: Color::WHITE,
|
||||||
|
};
|
||||||
|
let mut frame = Frame::default();
|
||||||
|
frame.add_rect(top_left, bottom_right);
|
||||||
|
gui.add_graphic(GuiCmd::PaintCommands(vec![PaintCmd::Frame(frame)]));
|
||||||
|
|
||||||
|
let old_font_sizes = self.data.fonts.sizes();
|
||||||
|
let mut new_font_sizes = old_font_sizes.clone();
|
||||||
|
show_font_sizes(&mut new_font_sizes, gui);
|
||||||
|
if *old_font_sizes != new_font_sizes {
|
||||||
|
let mut new_data = (*self.data).clone();
|
||||||
|
let fonts = Fonts::from_sizes(new_font_sizes);
|
||||||
|
new_data.fonts = Arc::new(fonts);
|
||||||
|
self.data = Arc::new(new_data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
region.foldable("Stats", |gui| {
|
region.foldable("Stats", |gui| {
|
||||||
gui.add(label(format!("num_vertices: {}", self.stats.num_vertices)));
|
gui.add(label(format!("num_vertices: {}", self.stats.num_vertices)));
|
||||||
gui.add(label(format!(
|
gui.add(label(format!(
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use std::{
|
use std::{
|
||||||
collections::BTreeMap,
|
collections::{hash_map::DefaultHasher, BTreeMap},
|
||||||
|
hash::{Hash, Hasher},
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -9,7 +10,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
/// TODO: rename
|
/// TODO: rename
|
||||||
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Serialize)]
|
#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
|
||||||
pub enum TextStyle {
|
pub enum TextStyle {
|
||||||
Body,
|
Body,
|
||||||
Button,
|
Button,
|
||||||
|
@ -17,14 +18,43 @@ pub enum TextStyle {
|
||||||
// Monospace,
|
// Monospace,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type FontSizes = BTreeMap<TextStyle, f32>;
|
||||||
|
|
||||||
pub struct Fonts {
|
pub struct Fonts {
|
||||||
|
sizes: FontSizes,
|
||||||
fonts: BTreeMap<TextStyle, Font>,
|
fonts: BTreeMap<TextStyle, Font>,
|
||||||
texture: Texture,
|
texture: Texture,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Fonts {
|
impl Fonts {
|
||||||
pub fn new() -> Fonts {
|
pub fn new() -> Fonts {
|
||||||
let mut atlas = TextureAtlas::new(128, 8); // TODO: better default?
|
let mut sizes = FontSizes::new();
|
||||||
|
sizes.insert(TextStyle::Body, 20.0);
|
||||||
|
sizes.insert(TextStyle::Button, 20.0);
|
||||||
|
sizes.insert(TextStyle::Heading, 30.0);
|
||||||
|
Fonts::from_sizes(sizes)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_sizes(sizes: FontSizes) -> Fonts {
|
||||||
|
let mut fonts = Fonts {
|
||||||
|
sizes: Default::default(),
|
||||||
|
fonts: Default::default(),
|
||||||
|
texture: Default::default(),
|
||||||
|
};
|
||||||
|
fonts.set_sizes(sizes);
|
||||||
|
fonts
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sizes(&self) -> &FontSizes {
|
||||||
|
&self.sizes
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_sizes(&mut self, sizes: FontSizes) {
|
||||||
|
if self.sizes == sizes {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut atlas = TextureAtlas::new(512, 8); // TODO: better default?
|
||||||
|
|
||||||
// Make one white pixel for use for various stuff:
|
// Make one white pixel for use for various stuff:
|
||||||
let pos = atlas.allocate((1, 1));
|
let pos = atlas.allocate((1, 1));
|
||||||
|
@ -32,25 +62,20 @@ impl Fonts {
|
||||||
|
|
||||||
let atlas = Arc::new(Mutex::new(atlas));
|
let atlas = Arc::new(Mutex::new(atlas));
|
||||||
|
|
||||||
// TODO: figure out a way to make the wasm smaller despite including a font.
|
// TODO: figure out a way to make the wasm smaller despite including a font. Zip it?
|
||||||
// let typeface_data = include_bytes!("../fonts/ProggyClean.ttf"); // Use 13 for this. NOTHING ELSE.
|
// let typeface_data = include_bytes!("../fonts/ProggyClean.ttf"); // Use 13 for this. NOTHING ELSE.
|
||||||
// let typeface_data = include_bytes!("../fonts/DejaVuSans.ttf");
|
// let typeface_data = include_bytes!("../fonts/DejaVuSans.ttf");
|
||||||
let typeface_data = include_bytes!("../fonts/Roboto-Regular.ttf");
|
let typeface_data = include_bytes!("../fonts/Roboto-Regular.ttf");
|
||||||
|
self.sizes = sizes.clone();
|
||||||
|
self.fonts = sizes
|
||||||
|
.into_iter()
|
||||||
|
.map(|(text_style, size)| (text_style, Font::new(atlas.clone(), typeface_data, size)))
|
||||||
|
.collect();
|
||||||
|
self.texture = atlas.lock().unwrap().texture().clone();
|
||||||
|
|
||||||
let mut fonts = BTreeMap::new();
|
let mut hasher = DefaultHasher::new();
|
||||||
fonts.insert(
|
self.texture.pixels.hash(&mut hasher);
|
||||||
TextStyle::Body,
|
self.texture.id = hasher.finish();
|
||||||
Font::new(atlas.clone(), typeface_data, 20.0),
|
|
||||||
);
|
|
||||||
fonts.insert(TextStyle::Button, fonts[&TextStyle::Body].clone());
|
|
||||||
fonts.insert(
|
|
||||||
TextStyle::Heading,
|
|
||||||
Font::new(atlas.clone(), typeface_data, 30.0),
|
|
||||||
);
|
|
||||||
|
|
||||||
let texture = atlas.lock().unwrap().clone().texture().clone();
|
|
||||||
|
|
||||||
Fonts { fonts, texture }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn texture(&self) -> &Texture {
|
pub fn texture(&self) -> &Texture {
|
||||||
|
|
|
@ -428,10 +428,10 @@ impl Region {
|
||||||
|
|
||||||
/// Temporarily split split a vertical layout into several columns.
|
/// Temporarily split split a vertical layout into several columns.
|
||||||
///
|
///
|
||||||
/// gui.columns(2, |columns| {
|
/// region.columns(2, |columns| {
|
||||||
/// columns[0].add(label("First column"));
|
/// columns[0].add(emigui::widgets::label("First column"));
|
||||||
/// columns[1].add(label("Second column"));
|
/// columns[1].add(emigui::widgets::label("Second column"));
|
||||||
/// });
|
/// });
|
||||||
pub fn columns<F, R>(&mut self, num_columns: usize, add_contents: F) -> R
|
pub fn columns<F, R>(&mut self, num_columns: usize, add_contents: F) -> R
|
||||||
where
|
where
|
||||||
F: FnOnce(&mut [Region]) -> R,
|
F: FnOnce(&mut [Region]) -> R,
|
||||||
|
|
|
@ -19,7 +19,7 @@ pub mod widgets;
|
||||||
|
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
emigui::Emigui,
|
emigui::Emigui,
|
||||||
fonts::TextStyle,
|
fonts::{FontSizes, Fonts, TextStyle},
|
||||||
layout::{Align, LayoutOptions, Region},
|
layout::{Align, LayoutOptions, Region},
|
||||||
mesher::{Frame, Vertex},
|
mesher::{Frame, Vertex},
|
||||||
style::Style,
|
style::Style,
|
||||||
|
|
|
@ -8,7 +8,7 @@ use crate::{
|
||||||
types::{Color, PaintCmd},
|
types::{Color, PaintCmd},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
#[derive(Clone, Copy, Debug, Default, Serialize)]
|
||||||
pub struct Vertex {
|
pub struct Vertex {
|
||||||
/// Pixel coordinates
|
/// Pixel coordinates
|
||||||
pub pos: Vec2,
|
pub pos: Vec2,
|
||||||
|
@ -18,7 +18,7 @@ pub struct Vertex {
|
||||||
pub color: Color,
|
pub color: Color,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default, Serialize)]
|
||||||
pub struct Frame {
|
pub struct Frame {
|
||||||
/// Draw as triangles (i.e. the length is a multiple of three)
|
/// Draw as triangles (i.e. the length is a multiple of three)
|
||||||
pub indices: Vec<u32>,
|
pub indices: Vec<u32>,
|
||||||
|
@ -33,6 +33,14 @@ pub enum PathType {
|
||||||
use self::PathType::*;
|
use self::PathType::*;
|
||||||
|
|
||||||
impl Frame {
|
impl Frame {
|
||||||
|
pub fn append(&mut self, frame: &Frame) {
|
||||||
|
let index_offset = self.vertices.len() as u32;
|
||||||
|
for index in &frame.indices {
|
||||||
|
self.indices.push(index_offset + index);
|
||||||
|
}
|
||||||
|
self.vertices.extend(frame.vertices.iter());
|
||||||
|
}
|
||||||
|
|
||||||
fn triangle(&mut self, a: u32, b: u32, c: u32) {
|
fn triangle(&mut self, a: u32, b: u32, c: u32) {
|
||||||
self.indices.push(a);
|
self.indices.push(a);
|
||||||
self.indices.push(b);
|
self.indices.push(b);
|
||||||
|
@ -224,6 +232,9 @@ impl Frame {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
PaintCmd::Frame(cmd_frame) => {
|
||||||
|
frame.append(cmd_frame);
|
||||||
|
}
|
||||||
PaintCmd::Line {
|
PaintCmd::Line {
|
||||||
points,
|
points,
|
||||||
color,
|
color,
|
||||||
|
|
|
@ -53,9 +53,15 @@ impl TextureAtlas {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn texture_mut(&mut self) -> &mut Texture {
|
pub fn texture_mut(&mut self) -> &mut Texture {
|
||||||
|
self.texture.id += 1;
|
||||||
&mut self.texture
|
&mut self.texture
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.cursor = (0, 0);
|
||||||
|
self.row_height = 0;
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the coordinates of where the rect ended up.
|
/// Returns the coordinates of where the rect ended up.
|
||||||
pub fn allocate(&mut self, (w, h): (usize, usize)) -> (usize, usize) {
|
pub fn allocate(&mut self, (w, h): (usize, usize)) -> (usize, usize) {
|
||||||
assert!(w <= self.texture.width);
|
assert!(w <= self.texture.width);
|
||||||
|
@ -79,7 +85,7 @@ impl TextureAtlas {
|
||||||
|
|
||||||
let pos = self.cursor;
|
let pos = self.cursor;
|
||||||
self.cursor.0 += w;
|
self.cursor.0 += w;
|
||||||
self.texture.id += 1; // TODO: hash this ?
|
self.texture.id += 1;
|
||||||
(pos.0 as usize, pos.1 as usize)
|
(pos.0 as usize, pos.1 as usize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
fonts::TextStyle,
|
fonts::TextStyle,
|
||||||
math::{Rect, Vec2},
|
math::{Rect, Vec2},
|
||||||
|
mesher::Frame,
|
||||||
};
|
};
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
@ -152,6 +153,7 @@ pub enum PaintCmd {
|
||||||
outline: Option<Outline>,
|
outline: Option<Outline>,
|
||||||
radius: f32,
|
radius: f32,
|
||||||
},
|
},
|
||||||
|
Frame(Frame),
|
||||||
Line {
|
Line {
|
||||||
points: Vec<Vec2>,
|
points: Vec<Vec2>,
|
||||||
color: Color,
|
color: Color,
|
||||||
|
|
|
@ -30,12 +30,10 @@ pub struct State {
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
fn new(canvas_id: &str) -> Result<State, JsValue> {
|
fn new(canvas_id: &str) -> Result<State, JsValue> {
|
||||||
let emigui = Emigui::new();
|
|
||||||
let webgl_painter = webgl::Painter::new(canvas_id, emigui.texture())?;
|
|
||||||
Ok(State {
|
Ok(State {
|
||||||
app: Default::default(),
|
app: Default::default(),
|
||||||
emigui,
|
emigui: Emigui::new(),
|
||||||
webgl_painter,
|
webgl_painter: webgl::Painter::new(canvas_id)?,
|
||||||
everything_ms: 0.0,
|
everything_ms: 0.0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -58,7 +56,7 @@ impl State {
|
||||||
region.add(label(format!("Everything: {:.1} ms", self.everything_ms)));
|
region.add(label(format!("Everything: {:.1} ms", self.everything_ms)));
|
||||||
|
|
||||||
let frame = self.emigui.paint();
|
let frame = self.emigui.paint();
|
||||||
let result = self.webgl_painter.paint(&frame);
|
let result = self.webgl_painter.paint(&frame, self.emigui.texture());
|
||||||
|
|
||||||
self.everything_ms = now_ms() - everything_start;
|
self.everything_ms = now_ms() - everything_start;
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ pub struct Painter {
|
||||||
tc_buffer: WebGlBuffer,
|
tc_buffer: WebGlBuffer,
|
||||||
color_buffer: WebGlBuffer,
|
color_buffer: WebGlBuffer,
|
||||||
tex_size: (u16, u16),
|
tex_size: (u16, u16),
|
||||||
|
current_texture_id: Option<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Painter {
|
impl Painter {
|
||||||
|
@ -32,7 +33,7 @@ impl Painter {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(canvas_id: &str, texture: &Texture) -> Result<Painter, JsValue> {
|
pub fn new(canvas_id: &str) -> Result<Painter, JsValue> {
|
||||||
let document = web_sys::window().unwrap().document().unwrap();
|
let document = web_sys::window().unwrap().document().unwrap();
|
||||||
let canvas = document.get_element_by_id(canvas_id).unwrap();
|
let canvas = document.get_element_by_id(canvas_id).unwrap();
|
||||||
let canvas: web_sys::HtmlCanvasElement = canvas.dyn_into::<web_sys::HtmlCanvasElement>()?;
|
let canvas: web_sys::HtmlCanvasElement = canvas.dyn_into::<web_sys::HtmlCanvasElement>()?;
|
||||||
|
@ -46,27 +47,6 @@ impl Painter {
|
||||||
|
|
||||||
let gl_texture = gl.create_texture().unwrap();
|
let gl_texture = gl.create_texture().unwrap();
|
||||||
gl.bind_texture(Gl::TEXTURE_2D, Some(&gl_texture));
|
gl.bind_texture(Gl::TEXTURE_2D, Some(&gl_texture));
|
||||||
|
|
||||||
// TODO: remove once https://github.com/rustwasm/wasm-bindgen/issues/1005 is fixed.
|
|
||||||
let mut pixels: Vec<_> = texture.pixels.iter().cloned().collect();
|
|
||||||
|
|
||||||
let level = 0;
|
|
||||||
let internal_format = Gl::ALPHA;
|
|
||||||
let border = 0;
|
|
||||||
let src_format = Gl::ALPHA;
|
|
||||||
let src_type = Gl::UNSIGNED_BYTE;
|
|
||||||
gl.tex_image_2d_with_i32_and_i32_and_i32_and_format_and_type_and_opt_u8_array(
|
|
||||||
Gl::TEXTURE_2D,
|
|
||||||
level,
|
|
||||||
internal_format as i32,
|
|
||||||
texture.width as i32,
|
|
||||||
texture.height as i32,
|
|
||||||
border,
|
|
||||||
src_format,
|
|
||||||
src_type,
|
|
||||||
Some(&mut pixels),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
gl.tex_parameteri(Gl::TEXTURE_2D, Gl::TEXTURE_WRAP_S, Gl::CLAMP_TO_EDGE as i32);
|
gl.tex_parameteri(Gl::TEXTURE_2D, Gl::TEXTURE_WRAP_S, Gl::CLAMP_TO_EDGE as i32);
|
||||||
gl.tex_parameteri(Gl::TEXTURE_2D, Gl::TEXTURE_WRAP_T, Gl::CLAMP_TO_EDGE as i32);
|
gl.tex_parameteri(Gl::TEXTURE_2D, Gl::TEXTURE_WRAP_T, Gl::CLAMP_TO_EDGE as i32);
|
||||||
// gl.tex_parameteri(Gl::TEXTURE_2D, Gl::TEXTURE_MIN_FILTER, Gl::NEAREST as i32);
|
// gl.tex_parameteri(Gl::TEXTURE_2D, Gl::TEXTURE_MIN_FILTER, Gl::NEAREST as i32);
|
||||||
|
@ -125,11 +105,47 @@ impl Painter {
|
||||||
pos_buffer,
|
pos_buffer,
|
||||||
tc_buffer,
|
tc_buffer,
|
||||||
color_buffer,
|
color_buffer,
|
||||||
tex_size: (texture.width as u16, texture.height as u16),
|
tex_size: (0, 0),
|
||||||
|
current_texture_id: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn paint(&self, frame: &Frame) -> Result<(), JsValue> {
|
fn upload_texture(&mut self, texture: &Texture) {
|
||||||
|
if self.current_texture_id == Some(texture.id) {
|
||||||
|
return; // No change
|
||||||
|
}
|
||||||
|
|
||||||
|
let gl = &self.gl;
|
||||||
|
gl.bind_texture(Gl::TEXTURE_2D, Some(&self.texture));
|
||||||
|
|
||||||
|
// TODO: remove once https://github.com/rustwasm/wasm-bindgen/issues/1005 is fixed.
|
||||||
|
let mut pixels: Vec<_> = texture.pixels.iter().cloned().collect();
|
||||||
|
|
||||||
|
let level = 0;
|
||||||
|
let internal_format = Gl::ALPHA;
|
||||||
|
let border = 0;
|
||||||
|
let src_format = Gl::ALPHA;
|
||||||
|
let src_type = Gl::UNSIGNED_BYTE;
|
||||||
|
gl.tex_image_2d_with_i32_and_i32_and_i32_and_format_and_type_and_opt_u8_array(
|
||||||
|
Gl::TEXTURE_2D,
|
||||||
|
level,
|
||||||
|
internal_format as i32,
|
||||||
|
texture.width as i32,
|
||||||
|
texture.height as i32,
|
||||||
|
border,
|
||||||
|
src_format,
|
||||||
|
src_type,
|
||||||
|
Some(&mut pixels),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
self.tex_size = (texture.width as u16, texture.height as u16);
|
||||||
|
self.current_texture_id = Some(texture.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn paint(&mut self, frame: &Frame, texture: &Texture) -> Result<(), JsValue> {
|
||||||
|
self.upload_texture(texture);
|
||||||
|
|
||||||
let gl = &self.gl;
|
let gl = &self.gl;
|
||||||
|
|
||||||
// --------------------------------------------------------------------
|
// --------------------------------------------------------------------
|
||||||
|
|
Loading…
Reference in a new issue