Enable colored text (and other misc fixes)

This commit is contained in:
Emil Ernerfeldt 2019-01-16 09:28:43 -06:00
parent 4dab7a1504
commit 7f83876005
9 changed files with 104 additions and 82 deletions

View file

@ -6,7 +6,7 @@ use crate::{
style, style,
types::GuiInput, types::GuiInput,
widgets::*, widgets::*,
Frame, Painter, RawInput, Frame, RawInput,
}; };
#[derive(Clone, Copy, Default)] #[derive(Clone, Copy, Default)]
@ -42,19 +42,15 @@ pub struct Emigui {
pub last_input: RawInput, pub last_input: RawInput,
pub data: Arc<layout::Data>, pub data: Arc<layout::Data>,
pub style: style::Style, pub style: style::Style,
pub painter: Painter,
stats: Stats, stats: Stats,
} }
impl Emigui { impl Emigui {
pub fn new() -> Emigui { pub fn new() -> Emigui {
let data = Arc::new(layout::Data::new());
let fonts = data.fonts.clone();
Emigui { Emigui {
last_input: Default::default(), last_input: Default::default(),
data, data: Arc::new(layout::Data::new()),
style: Default::default(), style: Default::default(),
painter: Painter::new(fonts),
stats: Default::default(), stats: Default::default(),
} }
} }
@ -90,7 +86,7 @@ impl Emigui {
pub fn paint(&mut self) -> Frame { pub fn paint(&mut self) -> Frame {
let gui_commands = self.data.graphics.lock().unwrap().drain(); let gui_commands = self.data.graphics.lock().unwrap().drain();
let paint_commands = style::into_paint_commands(gui_commands, &self.style); let paint_commands = style::into_paint_commands(gui_commands, &self.style);
let frame = self.painter.paint(&paint_commands); let frame = Frame::paint(&self.data.fonts, &paint_commands);
self.stats.num_vertices = frame.vertices.len(); self.stats.num_vertices = frame.vertices.len();
self.stats.num_triangles = frame.indices.len() / 3; self.stats.num_triangles = frame.indices.len() / 3;
frame frame

View file

@ -51,7 +51,7 @@ impl Default for LayoutOptions {
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// TODO: rename // TODO: rename GuiResponse
pub struct GuiResponse { pub struct GuiResponse {
/// The mouse is hovering above this /// The mouse is hovering above this
pub hovered: bool, pub hovered: bool,
@ -181,7 +181,7 @@ impl Clone for Data {
Data { Data {
options: Mutex::new(self.options()), options: Mutex::new(self.options()),
fonts: self.fonts.clone(), fonts: self.fonts.clone(),
input: self.input.clone(), input: self.input,
memory: Mutex::new(self.memory.lock().unwrap().clone()), memory: Mutex::new(self.memory.lock().unwrap().clone()),
graphics: Mutex::new(self.graphics.lock().unwrap().clone()), graphics: Mutex::new(self.graphics.lock().unwrap().clone()),
} }
@ -358,6 +358,7 @@ impl Region {
text_cursor + vec2(self.options().start_icon_width, 0.0), text_cursor + vec2(self.options().start_icon_width, 0.0),
text_style, text_style,
text, text,
None,
); );
if open { if open {
@ -475,13 +476,15 @@ impl Region {
pub fn reserve_space(&mut self, size: Vec2, interaction_id: Option<Id>) -> InteractInfo { pub fn reserve_space(&mut self, size: Vec2, interaction_id: Option<Id>) -> InteractInfo {
let pos = self.reserve_space_without_padding(size + self.options().item_spacing); let pos = self.reserve_space_without_padding(size + self.options().item_spacing);
let rect = Rect::from_min_size(pos, size); let rect = Rect::from_min_size(pos, size);
let mut memory = self.data.memory.lock().unwrap();
let hovered = rect.contains(self.input().mouse_pos); let is_something_else_active =
memory.active_id.is_some() && memory.active_id != interaction_id;
let hovered = !is_something_else_active && rect.contains(self.input().mouse_pos);
let clicked = hovered && self.input().mouse_clicked; let clicked = hovered && self.input().mouse_clicked;
let active = if interaction_id.is_some() { let active = if interaction_id.is_some() {
let mut memory = self.data.memory.lock().unwrap();
if clicked { if clicked {
memory.active_id = interaction_id; memory.active_id = interaction_id;
} }
@ -543,12 +546,19 @@ impl Region {
}) })
} }
pub fn add_text(&mut self, pos: Vec2, text_style: TextStyle, text: Vec<TextFragment>) { pub fn add_text(
&mut self,
pos: Vec2,
text_style: TextStyle,
text: Vec<TextFragment>,
color: Option<Color>,
) {
for fragment in text { for fragment in text {
self.add_graphic(GuiCmd::Text { self.add_graphic(GuiCmd::Text {
color,
pos: pos + vec2(0.0, fragment.y_offset), pos: pos + vec2(0.0, fragment.y_offset),
text_style,
text: fragment.text, text: fragment.text,
text_style,
x_offsets: fragment.x_offsets, x_offsets: fragment.x_offsets,
}); });
} }

View file

@ -11,7 +11,7 @@ mod font;
mod fonts; mod fonts;
mod layout; mod layout;
pub mod math; pub mod math;
mod painter; mod mesher;
mod style; mod style;
mod texture_atlas; mod texture_atlas;
pub mod types; pub mod types;
@ -21,7 +21,7 @@ pub use crate::{
emigui::Emigui, emigui::Emigui,
fonts::TextStyle, fonts::TextStyle,
layout::{Align, LayoutOptions, Region}, layout::{Align, LayoutOptions, Region},
painter::{Frame, Painter, Vertex}, mesher::{Frame, Vertex},
style::Style, style::Style,
types::RawInput, types::RawInput,
}; };

View file

@ -1,5 +1,3 @@
use std::sync::Arc;
const ANTI_ALIAS: bool = true; const ANTI_ALIAS: bool = true;
const AA_SIZE: f32 = 1.0; const AA_SIZE: f32 = 1.0;
@ -22,7 +20,6 @@ pub struct Vertex {
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
pub struct Frame { pub struct Frame {
pub clear_color: Option<Color>,
/// 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>,
pub vertices: Vec<Vertex>, pub vertices: Vec<Vertex>,
@ -189,19 +186,8 @@ impl Frame {
} }
} }
} }
}
#[derive(Clone)] pub fn paint(fonts: &Fonts, commands: &[PaintCmd]) -> Frame {
pub struct Painter {
fonts: Arc<Fonts>,
}
impl Painter {
pub fn new(fonts: Arc<Fonts>) -> Painter {
Painter { fonts }
}
pub fn paint(&self, commands: &[PaintCmd]) -> Frame {
let mut path_points = Vec::new(); let mut path_points = Vec::new();
let mut path_normals = Vec::new(); let mut path_normals = Vec::new();
@ -238,9 +224,6 @@ impl Painter {
); );
} }
} }
PaintCmd::Clear { fill_color } => {
frame.clear_color = Some(*fill_color);
}
PaintCmd::Line { PaintCmd::Line {
points, points,
color, color,
@ -338,7 +321,7 @@ impl Painter {
text_style, text_style,
x_offsets, x_offsets,
} => { } => {
let font = &self.fonts[*text_style]; let font = &fonts[*text_style];
for (c, x_offset) in text.chars().zip(x_offsets.iter()) { for (c, x_offset) in text.chars().zip(x_offsets.iter()) {
if let Some(glyph) = font.uv_rect(c) { if let Some(glyph) = font.uv_rect(c) {
let mut top_left = Vertex { let mut top_left = Vertex {

View file

@ -129,8 +129,6 @@ fn translate_cmd(out_commands: &mut Vec<PaintCmd>, style: &Style, cmd: GuiCmd) {
rect: interact.rect, rect: interact.rect,
}); });
// TODO: paint a little triangle or arrow or something instead of this
let (small_icon_rect, _) = style.icon_rectangles(&interact.rect); let (small_icon_rect, _) = style.icon_rectangles(&interact.rect);
// Draw a minus: // Draw a minus:
out_commands.push(PaintCmd::Line { out_commands.push(PaintCmd::Line {
@ -215,12 +213,13 @@ fn translate_cmd(out_commands: &mut Vec<PaintCmd>, style: &Style, cmd: GuiCmd) {
} }
} }
GuiCmd::Text { GuiCmd::Text {
color,
pos, pos,
text_style,
text, text,
text_style,
x_offsets, x_offsets,
} => { } => {
let color = style.text_color(); let color = color.unwrap_or_else(|| style.text_color());
out_commands.push(PaintCmd::Text { out_commands.push(PaintCmd::Text {
color, color,
text_style, text_style,
@ -237,7 +236,7 @@ fn translate_cmd(out_commands: &mut Vec<PaintCmd>, style: &Style, cmd: GuiCmd) {
color: srgba(255, 255, 255, 255), // TODO color: srgba(255, 255, 255, 255), // TODO
width: 1.0, width: 1.0,
}), }),
rect: rect, rect,
}); });
} }
} }

View file

@ -13,8 +13,8 @@ pub struct TextureAtlas {
impl TextureAtlas { impl TextureAtlas {
pub fn new(width: usize, height: usize) -> Self { pub fn new(width: usize, height: usize) -> Self {
TextureAtlas { TextureAtlas {
width: width, width,
height: height, height,
pixels: vec![0; width * height], pixels: vec![0; width * height],
..Default::default() ..Default::default()
} }

View file

@ -120,13 +120,12 @@ pub enum GuiCmd {
min: f32, min: f32,
value: f32, value: f32,
}, },
/// Paint a single line of mono-space text. /// A string of text with a position for each character.
/// The text should start at the given position and flow to the right.
/// The text should be vertically centered at the given position.
Text { Text {
color: Option<Color>,
pos: Vec2, pos: Vec2,
text_style: TextStyle,
text: String, text: String,
text_style: TextStyle,
/// Start each character in the text, as offset from pos. /// Start each character in the text, as offset from pos.
x_offsets: Vec<f32>, x_offsets: Vec<f32>,
}, },
@ -153,9 +152,6 @@ pub enum PaintCmd {
outline: Option<Outline>, outline: Option<Outline>,
radius: f32, radius: f32,
}, },
Clear {
fill_color: Color,
},
Line { Line {
points: Vec<Vec2>, points: Vec<Vec2>,
color: Color, color: Color,

View file

@ -17,6 +17,7 @@ pub trait Widget {
pub struct Label { pub struct Label {
text: String, text: String,
text_style: TextStyle, text_style: TextStyle,
text_color: Option<Color>,
} }
impl Label { impl Label {
@ -24,6 +25,7 @@ impl Label {
Label { Label {
text: text.into(), text: text.into(),
text_style: TextStyle::Body, text_style: TextStyle::Body,
text_color: None,
} }
} }
@ -31,6 +33,11 @@ impl Label {
self.text_style = text_style; self.text_style = text_style;
self self
} }
pub fn text_color(mut self, text_color: Color) -> Self {
self.text_color = Some(text_color);
self
}
} }
pub fn label<S: Into<String>>(text: S) -> Label { pub fn label<S: Into<String>>(text: S) -> Label {
@ -42,7 +49,7 @@ impl Widget for Label {
let font = &region.fonts()[self.text_style]; let font = &region.fonts()[self.text_style];
let (text, text_size) = font.layout_multiline(&self.text, region.width()); let (text, text_size) = font.layout_multiline(&self.text, region.width());
let interact = region.reserve_space(text_size, None); let interact = region.reserve_space(text_size, None);
region.add_text(interact.rect.min(), self.text_style, text); region.add_text(interact.rect.min(), self.text_style, text, self.text_color);
region.response(interact) region.response(interact)
} }
} }
@ -51,11 +58,20 @@ impl Widget for Label {
pub struct Button { pub struct Button {
text: String, text: String,
text_color: Option<Color>,
} }
impl Button { impl Button {
pub fn new<S: Into<String>>(text: S) -> Self { pub fn new<S: Into<String>>(text: S) -> Self {
Button { text: text.into() } Button {
text: text.into(),
text_color: None,
}
}
pub fn text_color(mut self, text_color: Color) -> Self {
self.text_color = Some(text_color);
self
} }
} }
@ -69,7 +85,7 @@ impl Widget for Button {
region.reserve_space(text_size + 2.0 * region.options().button_padding, Some(id)); region.reserve_space(text_size + 2.0 * region.options().button_padding, Some(id));
let text_cursor = interact.rect.min() + region.options().button_padding; let text_cursor = interact.rect.min() + region.options().button_padding;
region.add_graphic(GuiCmd::Button { interact }); region.add_graphic(GuiCmd::Button { interact });
region.add_text(text_cursor, text_style, text); region.add_text(text_cursor, text_style, text, self.text_color);
region.response(interact) region.response(interact)
} }
} }
@ -80,6 +96,7 @@ impl Widget for Button {
pub struct Checkbox<'a> { pub struct Checkbox<'a> {
checked: &'a mut bool, checked: &'a mut bool,
text: String, text: String,
text_color: Option<Color>,
} }
impl<'a> Checkbox<'a> { impl<'a> Checkbox<'a> {
@ -87,8 +104,14 @@ impl<'a> Checkbox<'a> {
Checkbox { Checkbox {
checked, checked,
text: text.into(), text: text.into(),
text_color: None,
} }
} }
pub fn text_color(mut self, text_color: Color) -> Self {
self.text_color = Some(text_color);
self
}
} }
impl<'a> Widget for Checkbox<'a> { impl<'a> Widget for Checkbox<'a> {
@ -114,7 +137,7 @@ impl<'a> Widget for Checkbox<'a> {
checked: *self.checked, checked: *self.checked,
interact, interact,
}); });
region.add_text(text_cursor, text_style, text); region.add_text(text_cursor, text_style, text, self.text_color);
region.response(interact) region.response(interact)
} }
} }
@ -125,6 +148,7 @@ impl<'a> Widget for Checkbox<'a> {
pub struct RadioButton { pub struct RadioButton {
checked: bool, checked: bool,
text: String, text: String,
text_color: Option<Color>,
} }
impl RadioButton { impl RadioButton {
@ -132,8 +156,14 @@ impl RadioButton {
RadioButton { RadioButton {
checked, checked,
text: text.into(), text: text.into(),
text_color: None,
} }
} }
pub fn text_color(mut self, text_color: Color) -> Self {
self.text_color = Some(text_color);
self
}
} }
pub fn radio<S: Into<String>>(checked: bool, text: S) -> RadioButton { pub fn radio<S: Into<String>>(checked: bool, text: S) -> RadioButton {
@ -160,7 +190,7 @@ impl Widget for RadioButton {
checked: self.checked, checked: self.checked,
interact, interact,
}); });
region.add_text(text_cursor, text_style, text); region.add_text(text_cursor, text_style, text, self.text_color);
region.response(interact) region.response(interact)
} }
} }
@ -174,6 +204,7 @@ pub struct Slider<'a> {
max: f32, max: f32,
id: Option<Id>, id: Option<Id>,
text: Option<String>, text: Option<String>,
text_color: Option<Color>,
text_on_top: Option<bool>, text_on_top: Option<bool>,
} }
@ -186,6 +217,7 @@ impl<'a> Slider<'a> {
id: None, id: None,
text: None, text: None,
text_on_top: None, text_on_top: None,
text_color: None,
} }
} }
@ -198,6 +230,11 @@ impl<'a> Slider<'a> {
self.text = Some(text.into()); self.text = Some(text.into());
self self
} }
pub fn text_color(mut self, text_color: Color) -> Self {
self.text_color = Some(text_color);
self
}
} }
impl<'a> Widget for Slider<'a> { impl<'a> Widget for Slider<'a> {
@ -207,8 +244,9 @@ impl<'a> Widget for Slider<'a> {
if let Some(text) = &self.text { if let Some(text) = &self.text {
let text_on_top = self.text_on_top.unwrap_or_default(); let text_on_top = self.text_on_top.unwrap_or_default();
let text_color = self.text_color;
let full_text = format!("{}: {:.3}", text, self.value); let full_text = format!("{}: {:.3}", text, self.value);
let id = Some(self.id.unwrap_or(make_id(text))); let id = Some(self.id.unwrap_or_else(|| make_id(text)));
let mut naked = self; let mut naked = self;
naked.id = id; naked.id = id;
naked.text = None; naked.text = None;
@ -216,7 +254,7 @@ impl<'a> Widget for Slider<'a> {
if text_on_top { if text_on_top {
let (text, text_size) = font.layout_multiline(&full_text, region.width()); let (text, text_size) = font.layout_multiline(&full_text, region.width());
let pos = region.reserve_space_without_padding(text_size); let pos = region.reserve_space_without_padding(text_size);
region.add_text(pos, text_style, text); region.add_text(pos, text_style, text, text_color);
naked.add_to(region) naked.add_to(region)
} else { } else {
region.columns(2, |columns| { region.columns(2, |columns| {

View file

@ -3,7 +3,7 @@ use emigui::{math::*, types::*, widgets::*, Align, Region, TextStyle};
pub struct App { pub struct App {
checked: bool, checked: bool,
count: i32, count: i32,
selected_alternative: i32, radio: i32,
size: Vec2, size: Vec2,
corner_radius: f32, corner_radius: f32,
@ -14,7 +14,7 @@ impl Default for App {
fn default() -> App { fn default() -> App {
App { App {
checked: true, checked: true,
selected_alternative: 0, radio: 0,
count: 0, count: 0,
size: vec2(100.0, 50.0), size: vec2(100.0, 50.0),
corner_radius: 5.0, corner_radius: 5.0,
@ -35,6 +35,11 @@ impl App {
gui.input().screen_size.y, gui.input().screen_size.y,
))); )));
gui.horizontal(Align::Min, |gui| {
gui.add(label("Text can have").text_color(srgba(110, 255, 110, 255)));
gui.add(label("color").text_color(srgba(128, 140, 255, 255)));
});
gui.add(label("Hover me")).tooltip_text( gui.add(label("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.", "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.",
); );
@ -42,26 +47,18 @@ impl App {
gui.add(Checkbox::new(&mut self.checked, "checkbox")); gui.add(Checkbox::new(&mut self.checked, "checkbox"));
gui.horizontal(Align::Min, |gui| { gui.horizontal(Align::Min, |gui| {
if gui if gui.add(radio(self.radio == 0, "First")).clicked {
.add(radio(self.selected_alternative == 0, "First")) self.radio = 0;
.clicked
{
self.selected_alternative = 0;
} }
if gui if gui.add(radio(self.radio == 1, "Second")).clicked {
.add(radio(self.selected_alternative == 1, "Second")) self.radio = 1;
.clicked
{
self.selected_alternative = 1;
} }
if gui if gui.add(radio(self.radio == 2, "Final")).clicked {
.add(radio(self.selected_alternative == 2, "Final")) self.radio = 2;
.clicked
{
self.selected_alternative = 2;
} }
}); });
gui.horizontal(Align::Min, |gui| {
if gui if gui
.add(Button::new("Click me")) .add(Button::new("Click me"))
.tooltip_text("This will just increase a counter.") .tooltip_text("This will just increase a counter.")
@ -69,8 +66,11 @@ impl App {
{ {
self.count += 1; self.count += 1;
} }
gui.add(label(format!(
gui.add(label(format!("This is a multiline label.\nThe button have been clicked {} times.\nBelow are more options.", self.count))); "The button have been clicked {} times",
self.count
)));
});
gui.foldable("Test box rendering", |gui| { gui.foldable("Test box rendering", |gui| {
gui.add(Slider::new(&mut self.size.x, 0.0, 500.0).text("width")); gui.add(Slider::new(&mut self.size.x, 0.0, 500.0).text("width"));