Add rect field to InteractInfo struct

This commit is contained in:
Emil Ernerfeldt 2019-01-14 14:26:02 +01:00
parent 6e8cc8439b
commit 3f84836c20
7 changed files with 73 additions and 89 deletions

View file

@ -19,14 +19,17 @@ pub struct LayoutOptions {
/// Horizontal and vertical padding within a window frame. /// Horizontal and vertical padding within a window frame.
pub window_padding: Vec2, pub window_padding: Vec2,
/// Button size is text size plus this on each side
pub button_padding: Vec2,
/// Horizontal and vertical spacing between widgets /// Horizontal and vertical spacing between widgets
pub item_spacing: Vec2, pub item_spacing: Vec2,
/// Indent foldable regions etc by this much. /// Indent foldable regions etc by this much.
pub indent: f32, pub indent: f32,
/// Button size is text size plus this on each side /// Anything clickable is (at least) this wide.
pub button_padding: Vec2, pub clickable_diameter: f32,
/// Checkboxed, radio button and foldables have an icon at the start. /// Checkboxed, radio button and foldables have an icon at the start.
/// The text starts after this many pixels. /// The text starts after this many pixels.
@ -36,10 +39,11 @@ pub struct LayoutOptions {
impl Default for LayoutOptions { impl Default for LayoutOptions {
fn default() -> Self { fn default() -> Self {
LayoutOptions { LayoutOptions {
item_spacing: vec2(8.0, 4.0),
window_padding: vec2(6.0, 6.0), window_padding: vec2(6.0, 6.0),
indent: 21.0,
button_padding: vec2(5.0, 3.0), button_padding: vec2(5.0, 3.0),
item_spacing: vec2(8.0, 4.0),
indent: 21.0,
clickable_diameter: 10.0,
start_icon_width: 20.0, start_icon_width: 20.0,
} }
} }
@ -58,6 +62,9 @@ pub struct GuiResponse {
/// The mouse is interacting with this thing (e.g. dragging it) /// The mouse is interacting with this thing (e.g. dragging it)
pub active: bool, pub active: bool,
/// The region of the screen we are talking about
pub rect: Rect,
/// Used for showing a popup (if any) /// Used for showing a popup (if any)
data: Arc<Data>, data: Arc<Data>,
} }
@ -308,7 +315,7 @@ impl Region {
let font = &self.fonts()[text_style]; let font = &self.fonts()[text_style];
let (text, text_size) = font.layout_multiline(&text, self.width()); let (text, text_size) = font.layout_multiline(&text, self.width());
let text_cursor = self.cursor + self.options().button_padding; let text_cursor = self.cursor + self.options().button_padding;
let (rect, interact) = self.reserve_space( let interact = self.reserve_space(
vec2( vec2(
self.available_space.x, self.available_space.x,
text_size.y + 2.0 * self.options().button_padding.y, text_size.y + 2.0 * self.options().button_padding.y,
@ -328,11 +335,7 @@ impl Region {
memory.open_foldables.contains(&id) memory.open_foldables.contains(&id)
}; };
self.add_graphic(GuiCmd::FoldableHeader { self.add_graphic(GuiCmd::FoldableHeader { interact, open });
interact,
rect,
open,
});
self.add_text( self.add_text(
text_cursor + vec2(self.options().start_icon_width, 0.0), text_cursor + vec2(self.options().start_icon_width, 0.0),
text_style, text_style,
@ -366,7 +369,7 @@ impl Region {
}; };
add_contents(&mut child_region); add_contents(&mut child_region);
let size = child_region.bounding_size; let size = child_region.bounding_size;
self.reserve_space_inner(indent + size); self.reserve_space_without_padding(indent + size);
} }
/// A horizontally centered region of the given width. /// A horizontally centered region of the given width.
@ -398,7 +401,7 @@ impl Region {
}; };
add_contents(&mut child_region); add_contents(&mut child_region);
let size = child_region.bounding_size; let size = child_region.bounding_size;
self.reserve_space_inner(size); self.reserve_space_without_padding(size);
} }
/// Temporarily split split a vertical layout into several columns. /// Temporarily split split a vertical layout into several columns.
@ -436,7 +439,7 @@ impl Region {
max_height = size.y.max(max_height); max_height = size.y.max(max_height);
} }
self.reserve_space_inner(vec2(self.available_space.x, max_height)); self.reserve_space_without_padding(vec2(self.available_space.x, max_height));
result result
} }
@ -448,16 +451,14 @@ impl Region {
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
pub fn reserve_space( pub fn reserve_space(&mut self, size: Vec2, interaction_id: Option<Id>) -> InteractInfo {
&mut self,
size: Vec2,
interaction_id: Option<Id>,
) -> (Rect, InteractInfo) {
let rect = Rect { let rect = Rect {
pos: self.cursor, pos: self.cursor,
size, size,
}; };
self.reserve_space_inner(size + self.options().item_spacing);
self.reserve_space_without_padding(size + self.options().item_spacing);
let hovered = rect.contains(self.input().mouse_pos); let hovered = 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() {
@ -470,17 +471,17 @@ impl Region {
false false
}; };
let interact = InteractInfo { InteractInfo {
rect,
hovered, hovered,
clicked, clicked,
active, active,
}; }
(rect, interact)
} }
// TODO: Return a Rect // TODO: Return a Rect
/// Reserve this much space and move the cursor. /// Reserve this much space and move the cursor.
pub fn reserve_space_inner(&mut self, size: Vec2) { pub fn reserve_space_without_padding(&mut self, size: Vec2) {
if self.dir == Direction::Horizontal { if self.dir == Direction::Horizontal {
self.cursor.x += size.x; self.cursor.x += size.x;
self.available_space.x -= size.x; self.available_space.x -= size.x;
@ -524,10 +525,12 @@ impl Region {
} }
pub fn response(&mut self, interact: InteractInfo) -> GuiResponse { pub fn response(&mut self, interact: InteractInfo) -> GuiResponse {
// TODO: unify GuiResponse and InteractInfo. They are the same thing!
GuiResponse { GuiResponse {
hovered: interact.hovered, hovered: interact.hovered,
clicked: interact.clicked, clicked: interact.clicked,
active: interact.active, active: interact.active,
rect: interact.rect,
data: self.data.clone(), data: self.data.clone(),
} }
} }

View file

@ -128,6 +128,10 @@ impl Rect {
pub fn max(&self) -> Vec2 { pub fn max(&self) -> Vec2 {
self.pos + self.size self.pos + self.size
} }
pub fn size(&self) -> Vec2 {
self.size
}
} }
pub fn lerp(min: f32, max: f32, t: f32) -> f32 { pub fn lerp(min: f32, max: f32, t: f32) -> f32 {

View file

@ -272,16 +272,15 @@ impl Painter {
corner_radius, corner_radius,
fill_color, fill_color,
outline, outline,
pos, rect,
size,
} => { } => {
path_points.clear(); path_points.clear();
path_normals.clear(); path_normals.clear();
let min = *pos; let min = rect.min();
let max = *pos + *size; let max = rect.max();
let cr = corner_radius.min(size.x * 0.5).min(size.y * 0.5); let cr = corner_radius.min(rect.size.x * 0.5).min(rect.size.y * 0.5);
if cr <= 0.0 { if cr <= 0.0 {
path_points.push(vec2(min.x, min.y)); path_points.push(vec2(min.x, min.y));

View file

@ -72,8 +72,7 @@ fn debug_rect(rect: Rect) -> PaintCmd {
color: srgba(255, 255, 255, 255), color: srgba(255, 255, 255, 255),
width: 1.0, width: 1.0,
}), }),
pos: rect.pos, rect,
size: rect.size,
} }
} }
@ -81,30 +80,24 @@ fn debug_rect(rect: Rect) -> PaintCmd {
fn translate_cmd(out_commands: &mut Vec<PaintCmd>, style: &Style, cmd: GuiCmd) { fn translate_cmd(out_commands: &mut Vec<PaintCmd>, style: &Style, cmd: GuiCmd) {
match cmd { match cmd {
GuiCmd::PaintCommands(mut commands) => out_commands.append(&mut commands), GuiCmd::PaintCommands(mut commands) => out_commands.append(&mut commands),
GuiCmd::Button { interact, rect } => { GuiCmd::Button { interact } => {
out_commands.push(PaintCmd::Rect { out_commands.push(PaintCmd::Rect {
corner_radius: 5.0, corner_radius: 5.0,
fill_color: Some(style.interact_fill_color(&interact)), fill_color: Some(style.interact_fill_color(&interact)),
outline: None, outline: None,
pos: rect.pos, rect: interact.rect,
size: rect.size,
}); });
if style.debug_rects { if style.debug_rects {
out_commands.push(debug_rect(rect)); out_commands.push(debug_rect(interact.rect));
} }
} }
GuiCmd::Checkbox { GuiCmd::Checkbox { checked, interact } => {
checked, let (small_icon_rect, big_icon_rect) = style.icon_rectangles(&interact.rect);
interact,
rect,
} => {
let (small_icon_rect, big_icon_rect) = style.icon_rectangles(&rect);
out_commands.push(PaintCmd::Rect { out_commands.push(PaintCmd::Rect {
corner_radius: 3.0, corner_radius: 3.0,
fill_color: Some(style.interact_fill_color(&interact)), fill_color: Some(style.interact_fill_color(&interact)),
outline: None, outline: None,
pos: big_icon_rect.pos, rect: big_icon_rect,
size: big_icon_rect.size,
}); });
let stroke_color = style.interact_stroke_color(&interact); let stroke_color = style.interact_stroke_color(&interact);
@ -122,14 +115,10 @@ fn translate_cmd(out_commands: &mut Vec<PaintCmd>, style: &Style, cmd: GuiCmd) {
} }
if style.debug_rects { if style.debug_rects {
out_commands.push(debug_rect(rect)); out_commands.push(debug_rect(interact.rect));
} }
} }
GuiCmd::FoldableHeader { GuiCmd::FoldableHeader { interact, open } => {
interact,
open,
rect,
} => {
let fill_color = style.interact_fill_color(&interact); let fill_color = style.interact_fill_color(&interact);
let stroke_color = style.interact_stroke_color(&interact); let stroke_color = style.interact_stroke_color(&interact);
@ -137,13 +126,12 @@ fn translate_cmd(out_commands: &mut Vec<PaintCmd>, style: &Style, cmd: GuiCmd) {
corner_radius: 3.0, corner_radius: 3.0,
fill_color: Some(fill_color), fill_color: Some(fill_color),
outline: None, outline: None,
pos: rect.pos, rect: interact.rect,
size: rect.size,
}); });
// TODO: paint a little triangle or arrow or something instead of this // TODO: paint a little triangle or arrow or something instead of this
let (small_icon_rect, _) = style.icon_rectangles(&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 {
points: vec![ points: vec![
@ -165,15 +153,11 @@ fn translate_cmd(out_commands: &mut Vec<PaintCmd>, style: &Style, cmd: GuiCmd) {
}); });
} }
} }
GuiCmd::RadioButton { GuiCmd::RadioButton { checked, interact } => {
checked,
interact,
rect,
} => {
let fill_color = style.interact_fill_color(&interact); let fill_color = style.interact_fill_color(&interact);
let stroke_color = style.interact_stroke_color(&interact); let stroke_color = style.interact_stroke_color(&interact);
let (small_icon_rect, big_icon_rect) = style.icon_rectangles(&rect); let (small_icon_rect, big_icon_rect) = style.icon_rectangles(&interact.rect);
out_commands.push(PaintCmd::Circle { out_commands.push(PaintCmd::Circle {
center: big_icon_rect.center(), center: big_icon_rect.center(),
@ -192,16 +176,16 @@ fn translate_cmd(out_commands: &mut Vec<PaintCmd>, style: &Style, cmd: GuiCmd) {
} }
if style.debug_rects { if style.debug_rects {
out_commands.push(debug_rect(rect)); out_commands.push(debug_rect(interact.rect));
} }
} }
GuiCmd::Slider { GuiCmd::Slider {
interact, interact,
max, max,
min, min,
rect,
value, value,
} => { } => {
let rect = interact.rect;
let thin_rect = Rect::from_center_size(rect.center(), vec2(rect.size.x, 6.0)); 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_center_x = remap_clamp(value, min, max, rect.min().x, rect.max().x);
@ -214,16 +198,14 @@ fn translate_cmd(out_commands: &mut Vec<PaintCmd>, style: &Style, cmd: GuiCmd) {
corner_radius: 2.0, corner_radius: 2.0,
fill_color: Some(style.background_fill_color()), fill_color: Some(style.background_fill_color()),
outline: None, outline: None,
pos: thin_rect.pos, rect: thin_rect,
size: thin_rect.size,
}); });
out_commands.push(PaintCmd::Rect { out_commands.push(PaintCmd::Rect {
corner_radius: 3.0, corner_radius: 3.0,
fill_color: Some(style.interact_fill_color(&interact)), fill_color: Some(style.interact_fill_color(&interact)),
outline: None, outline: None,
pos: marker_rect.pos, rect: marker_rect,
size: marker_rect.size,
}); });
if style.debug_rects { if style.debug_rects {
@ -253,8 +235,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,
}), }),
pos: rect.pos, rect: rect,
size: rect.size,
}); });
} }
} }

View file

@ -89,6 +89,9 @@ pub struct InteractInfo {
/// The mouse is interacting with this thing (e.g. dragging it) /// The mouse is interacting with this thing (e.g. dragging it)
pub active: bool, pub active: bool,
/// The region of the screen we are talking about
pub rect: Rect,
} }
#[derive(Clone, Debug, Serialize)] #[derive(Clone, Debug, Serialize)]
@ -97,29 +100,24 @@ pub enum GuiCmd {
/// The background for a button /// The background for a button
Button { Button {
interact: InteractInfo, interact: InteractInfo,
rect: Rect,
}, },
Checkbox { Checkbox {
checked: bool, checked: bool,
interact: InteractInfo, interact: InteractInfo,
rect: Rect,
}, },
/// The header button background for a foldable region /// The header button background for a foldable region
FoldableHeader { FoldableHeader {
interact: InteractInfo, interact: InteractInfo,
open: bool, open: bool,
rect: Rect,
}, },
RadioButton { RadioButton {
checked: bool, checked: bool,
interact: InteractInfo, interact: InteractInfo,
rect: Rect,
}, },
Slider { Slider {
interact: InteractInfo, interact: InteractInfo,
max: f32, max: f32,
min: f32, min: f32,
rect: Rect,
value: f32, value: f32,
}, },
/// Paint a single line of mono-space text. /// Paint a single line of mono-space text.
@ -167,8 +165,7 @@ pub enum PaintCmd {
corner_radius: f32, corner_radius: f32,
fill_color: Option<Color>, fill_color: Option<Color>,
outline: Option<Outline>, outline: Option<Outline>,
pos: Vec2, rect: Rect,
size: Vec2,
}, },
/// Paint a single line of text /// Paint a single line of text
Text { Text {

View file

@ -42,7 +42,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());
region.add_text(region.cursor(), self.text_style, text); region.add_text(region.cursor(), self.text_style, text);
let (_, interact) = region.reserve_space(text_size, None); let interact = region.reserve_space(text_size, None);
region.response(interact) region.response(interact)
} }
} }
@ -66,9 +66,9 @@ impl Widget for Button {
let font = &region.fonts()[text_style]; let font = &region.fonts()[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 text_cursor = region.cursor() + region.options().button_padding; let text_cursor = region.cursor() + region.options().button_padding;
let (rect, interact) = let interact =
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));
region.add_graphic(GuiCmd::Button { interact, rect }); region.add_graphic(GuiCmd::Button { interact });
region.add_text(text_cursor, text_style, text); region.add_text(text_cursor, text_style, text);
region.response(interact) region.response(interact)
} }
@ -100,7 +100,7 @@ impl<'a> Widget for Checkbox<'a> {
let text_cursor = region.cursor() let text_cursor = region.cursor()
+ region.options().button_padding + region.options().button_padding
+ vec2(region.options().start_icon_width, 0.0); + vec2(region.options().start_icon_width, 0.0);
let (rect, interact) = region.reserve_space( let interact = region.reserve_space(
region.options().button_padding region.options().button_padding
+ vec2(region.options().start_icon_width, 0.0) + vec2(region.options().start_icon_width, 0.0)
+ text_size + text_size
@ -113,7 +113,6 @@ impl<'a> Widget for Checkbox<'a> {
region.add_graphic(GuiCmd::Checkbox { region.add_graphic(GuiCmd::Checkbox {
checked: *self.checked, checked: *self.checked,
interact, interact,
rect,
}); });
region.add_text(text_cursor, text_style, text); region.add_text(text_cursor, text_style, text);
region.response(interact) region.response(interact)
@ -150,7 +149,7 @@ impl Widget for RadioButton {
let text_cursor = region.cursor() let text_cursor = region.cursor()
+ region.options().button_padding + region.options().button_padding
+ vec2(region.options().start_icon_width, 0.0); + vec2(region.options().start_icon_width, 0.0);
let (rect, interact) = region.reserve_space( let interact = region.reserve_space(
region.options().button_padding region.options().button_padding
+ vec2(region.options().start_icon_width, 0.0) + vec2(region.options().start_icon_width, 0.0)
+ text_size + text_size
@ -160,7 +159,6 @@ impl Widget for RadioButton {
region.add_graphic(GuiCmd::RadioButton { region.add_graphic(GuiCmd::RadioButton {
checked: self.checked, checked: self.checked,
interact, interact,
rect,
}); });
region.add_text(text_cursor, text_style, text); region.add_text(text_cursor, text_style, text);
region.response(interact) region.response(interact)
@ -218,24 +216,28 @@ 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());
region.add_text(region.cursor(), text_style, text); region.add_text(region.cursor(), text_style, text);
region.reserve_space_inner(text_size); region.reserve_space_without_padding(text_size);
naked.add_to(region) naked.add_to(region)
} else { } else {
region.columns(2, |columns| { region.columns(2, |columns| {
columns[1].add(label(full_text)); let response = naked.add_to(&mut columns[0]);
naked.add_to(&mut columns[0]) columns[1].available_space.y = response.rect.size().y;
columns[1].add(label(full_text)); // TODO: centered!
response
}) })
} }
} else { } else {
let height = font.line_spacing().max(region.options().clickable_diameter);
let value = self.value; let value = self.value;
let min = self.min; let min = self.min;
let max = self.max; let max = self.max;
debug_assert!(min <= max); debug_assert!(min <= max);
let id = region.combined_id(self.id); let id = region.combined_id(self.id);
let (slider_rect, interact) = region.reserve_space( let interact = region.reserve_space(
Vec2 { Vec2 {
x: region.available_space.x, x: region.available_space.x,
y: font.line_spacing(), y: height,
}, },
id, id,
); );
@ -243,8 +245,8 @@ impl<'a> Widget for Slider<'a> {
if interact.active { if interact.active {
*value = remap_clamp( *value = remap_clamp(
region.input().mouse_pos.x, region.input().mouse_pos.x,
slider_rect.min().x, interact.rect.min().x,
slider_rect.max().x, interact.rect.max().x,
min, min,
max, max,
); );
@ -254,7 +256,6 @@ impl<'a> Widget for Slider<'a> {
interact, interact,
max, max,
min, min,
rect: slider_rect,
value: *value, value: *value,
}); });

View file

@ -82,8 +82,7 @@ impl App {
gui.add_graphic(GuiCmd::PaintCommands(vec![PaintCmd::Rect { gui.add_graphic(GuiCmd::PaintCommands(vec![PaintCmd::Rect {
corner_radius: self.corner_radius, corner_radius: self.corner_radius,
fill_color: Some(srgba(136, 136, 136, 255)), fill_color: Some(srgba(136, 136, 136, 255)),
pos, rect: Rect::from_min_size(pos, self.size),
size: self.size,
outline: Some(Outline { outline: Some(Outline {
width: self.stroke_width, width: self.stroke_width,
color: srgba(255, 255, 255, 255), color: srgba(255, 255, 255, 255),