use crate::{math::*, types::*}; #[derive(Clone, Debug)] pub struct Style { /// Show rectangles around each widget pub debug_rects: bool, /// For stuff like check marks in check boxes. pub line_width: f32, } impl Default for Style { fn default() -> Style { Style { debug_rects: false, line_width: 2.0, } } } impl Style { /// e.g. the background of the slider fn background_fill_color(&self) -> Color { srgba(34, 34, 34, 200) } fn text_color(&self) -> Color { srgba(255, 255, 255, 187) } /// Fill color of the interactive part of a component (button, slider grab, checkbox, ...) fn interact_fill_color(&self, interact: &InteractInfo) -> Color { if interact.active { srgba(136, 136, 136, 255) } else if interact.hovered { srgba(100, 100, 100, 255) } else { srgba(68, 68, 68, 220) } } /// Stroke and text color of the interactive part of a component (button, slider grab, checkbox, ...) fn interact_stroke_color(&self, interact: &InteractInfo) -> Color { if interact.active { srgba(255, 255, 255, 255) } else if interact.hovered { srgba(255, 255, 255, 200) } else { srgba(255, 255, 255, 170) } } /// Returns small icon rectangle and big icon rectangle fn icon_rectangles(&self, rect: &Rect) -> (Rect, Rect) { let box_side = 16.0; let big_icon_rect = Rect::from_center_size( vec2(rect.min().x + 4.0 + box_side * 0.5, rect.center().y), vec2(box_side, box_side), ); let small_icon_rect = Rect::from_center_size(big_icon_rect.center(), vec2(10.0, 10.0)); (small_icon_rect, big_icon_rect) } } fn debug_rect(rect: Rect) -> PaintCmd { PaintCmd::Rect { corner_radius: 0.0, fill_color: None, outline: Some(Outline { color: srgba(255, 255, 255, 255), width: 1.0, }), pos: rect.pos, size: rect.size, } } /// TODO: a Style struct which defines colors etc fn translate_cmd(out_commands: &mut Vec, style: &Style, cmd: GuiCmd) { match cmd { GuiCmd::PaintCommands(mut commands) => out_commands.append(&mut commands), GuiCmd::Button { interact, rect } => { out_commands.push(PaintCmd::Rect { corner_radius: 5.0, fill_color: Some(style.interact_fill_color(&interact)), outline: None, pos: rect.pos, size: rect.size, }); if style.debug_rects { out_commands.push(debug_rect(rect)); } } GuiCmd::Checkbox { checked, interact, rect, } => { let (small_icon_rect, big_icon_rect) = style.icon_rectangles(&rect); out_commands.push(PaintCmd::Rect { corner_radius: 3.0, fill_color: Some(style.interact_fill_color(&interact)), outline: None, pos: big_icon_rect.pos, size: big_icon_rect.size, }); let stroke_color = style.interact_stroke_color(&interact); if checked { out_commands.push(PaintCmd::Line { points: vec![ vec2(small_icon_rect.min().x, small_icon_rect.center().y), vec2(small_icon_rect.center().x, small_icon_rect.max().y), vec2(small_icon_rect.max().x, small_icon_rect.min().y), ], color: stroke_color, width: style.line_width, }); } if style.debug_rects { out_commands.push(debug_rect(rect)); } } GuiCmd::FoldableHeader { interact, open, rect, } => { let fill_color = style.interact_fill_color(&interact); let stroke_color = style.interact_stroke_color(&interact); out_commands.push(PaintCmd::Rect { corner_radius: 3.0, fill_color: Some(fill_color), outline: None, pos: rect.pos, size: rect.size, }); // TODO: paint a little triangle or arrow or something instead of this let (small_icon_rect, _) = style.icon_rectangles(&rect); // Draw a minus: out_commands.push(PaintCmd::Line { points: vec![ vec2(small_icon_rect.min().x, small_icon_rect.center().y), vec2(small_icon_rect.max().x, small_icon_rect.center().y), ], color: stroke_color, width: style.line_width, }); if !open { // Draw it as a plus: out_commands.push(PaintCmd::Line { points: vec![ vec2(small_icon_rect.center().x, small_icon_rect.min().y), vec2(small_icon_rect.center().x, small_icon_rect.max().y), ], color: stroke_color, width: style.line_width, }); } } GuiCmd::RadioButton { checked, interact, rect, } => { let fill_color = style.interact_fill_color(&interact); let stroke_color = style.interact_stroke_color(&interact); let (small_icon_rect, big_icon_rect) = style.icon_rectangles(&rect); out_commands.push(PaintCmd::Circle { center: big_icon_rect.center(), fill_color: Some(fill_color), outline: None, radius: big_icon_rect.size.x / 2.0, }); if checked { out_commands.push(PaintCmd::Circle { center: small_icon_rect.center(), fill_color: Some(stroke_color), outline: None, radius: small_icon_rect.size.x / 2.0, }); } if style.debug_rects { out_commands.push(debug_rect(rect)); } } GuiCmd::Slider { interact, max, min, rect, value, } => { let thin_rect = Rect::from_center_size(rect.center(), vec2(rect.size.x, 6.0)); let marker_center_x = remap_clamp(value, min, max, rect.min().x, rect.max().x); let marker_rect = Rect::from_center_size( vec2(marker_center_x, thin_rect.center().y), vec2(16.0, 16.0), ); out_commands.push(PaintCmd::Rect { corner_radius: 2.0, fill_color: Some(style.background_fill_color()), outline: None, pos: thin_rect.pos, size: thin_rect.size, }); out_commands.push(PaintCmd::Rect { corner_radius: 3.0, fill_color: Some(style.interact_fill_color(&interact)), outline: None, pos: marker_rect.pos, size: marker_rect.size, }); if style.debug_rects { out_commands.push(debug_rect(rect)); } } GuiCmd::Text { pos, style: text_style, text, x_offsets, } => { let color = match text_style { TextStyle::Label => style.text_color(), }; out_commands.push(PaintCmd::Text { color, pos, text, x_offsets, }); } GuiCmd::Window { rect } => { out_commands.push(PaintCmd::Rect { corner_radius: 5.0, fill_color: Some(style.background_fill_color()), outline: Some(Outline { color: srgba(255, 255, 255, 255), // TODO width: 1.0, }), pos: rect.pos, size: rect.size, }); } } } pub fn into_paint_commands<'a, GuiCmdIterator>( gui_commands: GuiCmdIterator, style: &Style, ) -> Vec where GuiCmdIterator: Iterator, { let mut paint_commands = vec![]; for gui_cmd in gui_commands { translate_cmd(&mut paint_commands, style, gui_cmd) } paint_commands }