1
0
mirror of https://github.com/emilk/egui.git synced 2026-09-01 06:10:06 -04:00

Refactor example code

This commit is contained in:
Emil Ernerfeldt
2020-04-12 12:07:51 +02:00
parent 4889296f7a
commit d999962602
13 changed files with 402 additions and 343 deletions

View File

@@ -1,12 +1,9 @@
use std::sync::Arc;
use crate::{
color::WHITE,
label, layout,
layout::{show_popup, Region},
math::{clamp, remap_clamp, vec2},
mesher::{Mesher, Vertex},
style::Style,
layout::Region,
mesher::Mesher,
types::{GuiInput, PaintCmd},
widgets::*,
FontDefinitions, Fonts, Mesh, RawInput, Texture,
@@ -18,98 +15,6 @@ struct Stats {
num_triangles: usize,
}
fn show_style(style: &mut Style, gui: &mut Region) {
if gui.add(Button::new("Reset style")).clicked {
*style = Default::default();
}
gui.add(Slider::f32(&mut style.item_spacing.x, 0.0, 10.0).text("item_spacing.x"));
gui.add(Slider::f32(&mut style.item_spacing.y, 0.0, 10.0).text("item_spacing.y"));
gui.add(Slider::f32(&mut style.window_padding.x, 0.0, 10.0).text("window_padding.x"));
gui.add(Slider::f32(&mut style.window_padding.y, 0.0, 10.0).text("window_padding.y"));
gui.add(Slider::f32(&mut style.indent, 0.0, 100.0).text("indent"));
gui.add(Slider::f32(&mut style.button_padding.x, 0.0, 20.0).text("button_padding.x"));
gui.add(Slider::f32(&mut style.button_padding.y, 0.0, 20.0).text("button_padding.y"));
gui.add(Slider::f32(&mut style.clickable_diameter, 0.0, 60.0).text("clickable_diameter"));
gui.add(Slider::f32(&mut style.start_icon_width, 0.0, 60.0).text("start_icon_width"));
gui.add(Slider::f32(&mut style.line_width, 0.0, 10.0).text("line_width"));
}
fn show_font_definitions(font_definitions: &mut FontDefinitions, gui: &mut Region) {
for (text_style, (_family, size)) in font_definitions {
// TODO: radiobutton for family
gui.add(Slider::f32(size, 4.0, 40.0).text(format!("{:?}", text_style)));
}
}
fn show_font_texture(texture: &Texture, gui: &mut Region) {
gui.add(label!(
"Font texture size: {} x {} (hover to zoom)",
texture.width,
texture.height
));
let mut size = vec2(texture.width as f32, texture.height as f32);
if size.x > gui.width() {
size *= gui.width() / size.x;
}
let interact = gui.reserve_space(size, None);
let rect = interact.rect;
let top_left = Vertex {
pos: rect.min(),
uv: (0, 0),
color: WHITE,
};
let bottom_right = Vertex {
pos: rect.max(),
uv: (texture.width as u16 - 1, texture.height as u16 - 1),
color: WHITE,
};
let mut mesh = Mesh::default();
mesh.add_rect(top_left, bottom_right);
gui.add_paint_cmd(PaintCmd::Mesh(mesh));
if let Some(mouse_pos) = gui.input().mouse_pos {
if interact.hovered {
show_popup(gui.data(), mouse_pos, |gui| {
let zoom_rect = gui.reserve_space(vec2(128.0, 128.0), None).rect;
let u = remap_clamp(
mouse_pos.x,
rect.min().x,
rect.max().x,
0.0,
texture.width as f32 - 1.0,
)
.round();
let v = remap_clamp(
mouse_pos.y,
rect.min().y,
rect.max().y,
0.0,
texture.height as f32 - 1.0,
)
.round();
let texel_radius = 32.0;
let u = clamp(u, texel_radius, texture.width as f32 - 1.0 - texel_radius);
let v = clamp(v, texel_radius, texture.height as f32 - 1.0 - texel_radius);
let top_left = Vertex {
pos: zoom_rect.min(),
uv: ((u - texel_radius) as u16, (v - texel_radius) as u16),
color: WHITE,
};
let bottom_right = Vertex {
pos: zoom_rect.max(),
uv: ((u + texel_radius) as u16, (v + texel_radius) as u16),
color: WHITE,
};
let mut mesh = Mesh::default();
mesh.add_rect(top_left, bottom_right);
gui.add_paint_cmd(PaintCmd::Mesh(mesh));
});
}
}
}
/// Encapsulates input, layout and painting for ease of use.
pub struct Emigui {
pub last_input: RawInput,
@@ -164,18 +69,16 @@ impl Emigui {
mesh
}
pub fn example(&mut self, region: &mut Region) {
region.foldable("Style", |gui| {
let mut style = self.data.style();
show_style(&mut style, gui);
self.data.set_style(style);
pub fn ui(&mut self, region: &mut Region) {
region.foldable("Style", |region| {
self.data.style_ui(region);
});
region.foldable("Fonts", |gui| {
region.foldable("Fonts", |region| {
let old_font_definitions = self.data.fonts.definitions();
let mut new_font_definitions = old_font_definitions.clone();
show_font_definitions(&mut new_font_definitions, gui);
show_font_texture(self.texture(), gui);
font_definitions_ui(&mut new_font_definitions, region);
self.data.fonts.texture().ui(region);
if *old_font_definitions != new_font_definitions {
let mut new_data = (*self.data).clone();
let fonts =
@@ -185,25 +88,39 @@ impl Emigui {
}
});
region.foldable("Stats", |gui| {
gui.add(label!(
region.foldable("Stats", |region| {
region.add(label!(
"Screen size: {} x {} points, pixels_per_point: {}",
gui.input().screen_size.x,
gui.input().screen_size.y,
gui.input().pixels_per_point,
region.input().screen_size.x,
region.input().screen_size.y,
region.input().pixels_per_point,
));
if let Some(mouse_pos) = gui.input().mouse_pos {
gui.add(label!("mouse_pos: {} x {}", mouse_pos.x, mouse_pos.y,));
if let Some(mouse_pos) = region.input().mouse_pos {
region.add(label!("mouse_pos: {} x {}", mouse_pos.x, mouse_pos.y,));
} else {
gui.add(label!("mouse_pos: None"));
region.add(label!("mouse_pos: None"));
}
gui.add(label!(
"gui cursor: {} x {}",
gui.cursor().x,
gui.cursor().y,
region.add(label!(
"region cursor: {} x {}",
region.cursor().x,
region.cursor().y,
));
gui.add(label!("num_vertices: {}", self.stats.num_vertices));
gui.add(label!("num_triangles: {}", self.stats.num_triangles));
region.add(label!("num_vertices: {}", self.stats.num_vertices));
region.add(label!("num_triangles: {}", self.stats.num_triangles));
});
}
}
fn font_definitions_ui(font_definitions: &mut FontDefinitions, region: &mut Region) {
for (text_style, (_family, size)) in font_definitions.iter_mut() {
// TODO: radiobutton for family
region.add(
Slider::f32(size, 4.0, 40.0)
.precision(0)
.text(format!("{:?}", text_style)),
);
}
if region.add(Button::new("Reset fonts")).clicked {
*font_definitions = crate::fonts::default_font_definitions();
}
}

140
emigui/src/example_app.rs Normal file
View File

@@ -0,0 +1,140 @@
use crate::{color::*, label, math::*, widgets::*, Align, Outline, PaintCmd, Region};
/// Showcase some region code
pub struct ExampleApp {
checked: bool,
count: usize,
radio: usize,
size: Vec2,
corner_radius: f32,
stroke_width: f32,
num_boxes: usize,
num_columns: usize,
slider_value: usize,
}
impl Default for ExampleApp {
fn default() -> ExampleApp {
ExampleApp {
checked: true,
radio: 0,
count: 0,
size: vec2(100.0, 50.0),
corner_radius: 5.0,
stroke_width: 2.0,
num_boxes: 1,
num_columns: 2,
slider_value: 100,
}
}
}
impl ExampleApp {
pub fn ui(&mut self, region: &mut Region) {
region.foldable("About Emigui", |region| {
region.add(label!("Emigui is an immediate mode GUI written in Rust, compiled to WebAssembly, rendered with WebGL."));
});
region.foldable("Widget examples", |region| {
region.horizontal(Align::Min, |region| {
region.add(label!("Text can have").text_color(srgba(110, 255, 110, 255)));
region.add(label!("color").text_color(srgba(128, 140, 255, 255)));
region.add(label!("and tooltips (hover me)")).tooltip_text(
"This is a multiline tooltip that demonstrates that you can easily add tooltips to any element.\nThis is the second line.\nThis is the third.",
);
});
region.add(Checkbox::new(&mut self.checked, "checkbox"));
region.horizontal(Align::Min, |region| {
if region.add(radio(self.radio == 0, "First")).clicked {
self.radio = 0;
}
if region.add(radio(self.radio == 1, "Second")).clicked {
self.radio = 1;
}
if region.add(radio(self.radio == 2, "Final")).clicked {
self.radio = 2;
}
});
region.horizontal(Align::Min, |region| {
if region
.add(Button::new("Click me"))
.tooltip_text("This will just increase a counter.")
.clicked
{
self.count += 1;
}
region.add(label!(
"The button have been clicked {} times",
self.count
));
});
});
region.foldable("Layouts", |region| {
region.add(Slider::usize(&mut self.num_columns, 1, 10).text("Columns"));
region.columns(self.num_columns, |cols| {
for (i, col) in cols.iter_mut().enumerate() {
col.add(label!("Column {} out of {}", i + 1, self.num_columns));
if i + 1 == self.num_columns {
if col.add(Button::new("Delete this")).clicked {
self.num_columns -= 1;
}
}
}
});
});
region.foldable("Test box rendering", |region| {
region.add(Slider::f32(&mut self.size.x, 0.0, 500.0).text("width"));
region.add(Slider::f32(&mut self.size.y, 0.0, 500.0).text("height"));
region.add(Slider::f32(&mut self.corner_radius, 0.0, 50.0).text("corner_radius"));
region.add(Slider::f32(&mut self.stroke_width, 0.0, 10.0).text("stroke_width"));
region.add(Slider::usize(&mut self.num_boxes, 0, 5).text("num_boxes"));
let pos = region
.reserve_space(
vec2(self.size.x * (self.num_boxes as f32), self.size.y),
None,
)
.rect
.min();
let mut cmds = vec![];
for i in 0..self.num_boxes {
cmds.push(PaintCmd::Rect {
corner_radius: self.corner_radius,
fill_color: Some(gray(136, 255)),
rect: Rect::from_min_size(
vec2(pos.x + (i as f32) * (self.size.x * 1.1), pos.y),
self.size,
),
outline: Some(Outline {
width: self.stroke_width,
color: gray(255, 255),
}),
});
}
region.add_paint_cmds(cmds);
});
region.foldable("Slider example", |region| {
value_ui(&mut self.slider_value, region);
});
}
}
pub fn value_ui(value: &mut usize, region: &mut Region) {
region.add(Slider::usize(value, 1, 1000));
if region.add(Button::new("Double it")).clicked {
*value *= 2;
}
region.add(label!("Value: {}", value));
}

View File

@@ -26,6 +26,15 @@ pub enum FontFamily {
pub type FontDefinitions = BTreeMap<TextStyle, (FontFamily, f32)>;
pub fn default_font_definitions() -> FontDefinitions {
let mut definitions = FontDefinitions::new();
definitions.insert(TextStyle::Body, (FontFamily::VariableWidth, 16.0));
definitions.insert(TextStyle::Button, (FontFamily::VariableWidth, 18.0));
definitions.insert(TextStyle::Heading, (FontFamily::VariableWidth, 28.0));
definitions.insert(TextStyle::Monospace, (FontFamily::Monospace, 13.0));
definitions
}
pub struct Fonts {
pixels_per_point: f32,
definitions: FontDefinitions,
@@ -35,12 +44,7 @@ pub struct Fonts {
impl Fonts {
pub fn new(pixels_per_point: f32) -> Fonts {
let mut definitions = FontDefinitions::new();
definitions.insert(TextStyle::Body, (FontFamily::VariableWidth, 16.0));
definitions.insert(TextStyle::Button, (FontFamily::VariableWidth, 18.0));
definitions.insert(TextStyle::Heading, (FontFamily::VariableWidth, 28.0));
definitions.insert(TextStyle::Monospace, (FontFamily::Monospace, 13.0));
Fonts::from_definitions(definitions, pixels_per_point)
Fonts::from_definitions(default_font_definitions(), pixels_per_point)
}
pub fn from_definitions(definitions: FontDefinitions, pixels_per_point: f32) -> Fonts {

View File

@@ -192,6 +192,14 @@ impl Data {
}
}
impl Data {
pub fn style_ui(&self, region: &mut Region) {
let mut style = self.style();
style.ui(region);
self.set_style(style);
}
}
/// Show a pop-over window
pub fn show_popup<F>(data: &Arc<Data>, window_pos: Vec2, add_contents: F)
where

View File

@@ -8,6 +8,7 @@ extern crate serde_derive;
pub mod color;
mod emigui;
pub mod example_app;
mod font;
mod fonts;
mod layout;

View File

@@ -86,3 +86,22 @@ impl Style {
(small_icon_rect, big_icon_rect)
}
}
impl Style {
pub fn ui(&mut self, region: &mut crate::Region) {
use crate::widgets::{Button, Slider};
if region.add(Button::new("Reset style")).clicked {
*self = Default::default();
}
region.add(Slider::f32(&mut self.item_spacing.x, 0.0, 10.0).text("item_spacing.x"));
region.add(Slider::f32(&mut self.item_spacing.y, 0.0, 10.0).text("item_spacing.y"));
region.add(Slider::f32(&mut self.window_padding.x, 0.0, 10.0).text("window_padding.x"));
region.add(Slider::f32(&mut self.window_padding.y, 0.0, 10.0).text("window_padding.y"));
region.add(Slider::f32(&mut self.indent, 0.0, 100.0).text("indent"));
region.add(Slider::f32(&mut self.button_padding.x, 0.0, 20.0).text("button_padding.x"));
region.add(Slider::f32(&mut self.button_padding.y, 0.0, 20.0).text("button_padding.y"));
region.add(Slider::f32(&mut self.clickable_diameter, 0.0, 60.0).text("clickable_diameter"));
region.add(Slider::f32(&mut self.start_icon_width, 0.0, 60.0).text("start_icon_width"));
region.add(Slider::f32(&mut self.line_width, 0.0, 10.0).text("line_width"));
}
}

View File

@@ -89,3 +89,79 @@ impl TextureAtlas {
(pos.0 as usize, pos.1 as usize)
}
}
impl Texture {
pub fn ui(&self, region: &mut crate::Region) {
use crate::{
color::WHITE, label, layout::show_popup, math::*, widgets::Label, Mesh, PaintCmd,
Vertex,
};
region.add(label!(
"Texture size: {} x {} (hover to zoom)",
self.width,
self.height
));
let mut size = vec2(self.width as f32, self.height as f32);
if size.x > region.width() {
size *= region.width() / size.x;
}
let interact = region.reserve_space(size, None);
let rect = interact.rect;
let top_left = Vertex {
pos: rect.min(),
uv: (0, 0),
color: WHITE,
};
let bottom_right = Vertex {
pos: rect.max(),
uv: (self.width as u16 - 1, self.height as u16 - 1),
color: WHITE,
};
let mut mesh = Mesh::default();
mesh.add_rect(top_left, bottom_right);
region.add_paint_cmd(PaintCmd::Mesh(mesh));
if let Some(mouse_pos) = region.input().mouse_pos {
if interact.hovered {
show_popup(region.data(), mouse_pos, |region| {
let zoom_rect = region.reserve_space(vec2(128.0, 128.0), None).rect;
let u = remap_clamp(
mouse_pos.x,
rect.min().x,
rect.max().x,
0.0,
self.width as f32 - 1.0,
)
.round();
let v = remap_clamp(
mouse_pos.y,
rect.min().y,
rect.max().y,
0.0,
self.height as f32 - 1.0,
)
.round();
let texel_radius = 32.0;
let u = clamp(u, texel_radius, self.width as f32 - 1.0 - texel_radius);
let v = clamp(v, texel_radius, self.height as f32 - 1.0 - texel_radius);
let top_left = Vertex {
pos: zoom_rect.min(),
uv: ((u - texel_radius) as u16, (v - texel_radius) as u16),
color: WHITE,
};
let bottom_right = Vertex {
pos: zoom_rect.max(),
uv: ((u + texel_radius) as u16, (v + texel_radius) as u16),
color: WHITE,
};
let mut mesh = Mesh::default();
mesh.add_rect(top_left, bottom_right);
region.add_paint_cmd(PaintCmd::Mesh(mesh));
});
}
}
}
}

View File

@@ -19,7 +19,7 @@ pub trait Widget {
pub struct Label {
text: String,
text_style: TextStyle,
text_style: TextStyle, // TODO: Option<TextStyle>, where None means "use the default for the region"
text_color: Option<Color>,
}
@@ -331,6 +331,17 @@ impl<'a> Slider<'a> {
self.precision = precision;
self
}
fn get_value_f32(&mut self) -> f32 {
(self.get_set_value)(None)
}
fn set_value_f32(&mut self, mut value: f32) {
if self.precision == 0 {
value = value.round();
}
(self.get_set_value)(Some(value));
}
}
impl<'a> Widget for Slider<'a> {
@@ -341,12 +352,8 @@ impl<'a> Widget for Slider<'a> {
if let Some(text) = &self.text {
let text_on_top = self.text_on_top.unwrap_or_default();
let text_color = self.text_color;
let full_text = format!(
"{}: {:.*}",
text,
self.precision,
(self.get_set_value)(None)
);
let value = (self.get_set_value)(None);
let full_text = format!("{}: {:.*}", text, self.precision, value);
let id = Some(self.id.unwrap_or_else(|| make_id(text)));
let mut naked = self;
naked.id = id;
@@ -386,19 +393,19 @@ impl<'a> Widget for Slider<'a> {
if let Some(mouse_pos) = region.input().mouse_pos {
if interact.active {
(self.get_set_value)(Some(remap_clamp(
self.set_value_f32(remap_clamp(
mouse_pos.x,
interact.rect.min().x,
interact.rect.max().x,
min,
max,
)));
));
}
}
// Paint it:
{
let value = (self.get_set_value)(None);
let value = self.get_value_f32();
let rect = interact.rect;
let thickness = rect.size().y;