diff --git a/.vimspector.json b/.vimspector.json index 1e3c2a3..9c63577 100644 --- a/.vimspector.json +++ b/.vimspector.json @@ -5,8 +5,10 @@ "configuration": { "type": "lldb", "request": "launch", - "sourceLanguages": ["rust"], - "program": "${workspaceRoot}/target/debug/examples/windows", + "sourceLanguages": [ + "rust" + ], + "program": "${workspaceRoot}/target/debug/examples/gui", "MIMode": "gdb", "setupCommands": [ { diff --git a/examples/gui.rs b/examples/gui.rs index 821f9c5..ef6e487 100644 --- a/examples/gui.rs +++ b/examples/gui.rs @@ -19,6 +19,7 @@ struct Gui { advanced_slider: f32, select_box: usize, select_list: usize, + collapsing_header_list: usize, font_size: u32, font_family: usize, theme: usize, @@ -46,6 +47,7 @@ impl Gui { advanced_slider: 0.5, select_box: 0, select_list: 0, + collapsing_header_list: 0, font_size: 12, font_family: 0, theme: 0, @@ -287,7 +289,7 @@ impl Gui { s.collapsing_header("Collapsing Header", |s: &mut PixState| { s.next_width(300); let items = ["Item 1", "Item 2", "Item 3", "Item 4"]; - s.select_list("Some list", &mut self.select_list, &items, 3)?; + s.select_list("Some list", &mut self.collapsing_header_list, &items, 3)?; Ok(()) })?; diff --git a/src/gui/layout.rs b/src/gui/layout.rs index f1bebc6..0124846 100644 --- a/src/gui/layout.rs +++ b/src/gui/layout.rs @@ -180,7 +180,7 @@ impl PixState { // Calculate tab size let (width, height) = s.text_size(tab_label)?; - let tab = rect![pos + fpad.x(), width, height].offset_size(2 * ipad); + let tab = rect![pos + fpad, width, height].offset_size(2 * ipad); // Check hover/active/keyboard focus let hovered = s.ui.try_hover(id, &tab); @@ -247,8 +247,8 @@ impl PixState { let fpad = s.theme.spacing.frame_pad; s.push(); s.stroke(colors.disabled()); - let y = pos.y() + 1; - let line_width = s.ui_width()? - fpad.x(); + let y = pos.y() + fpad.y() + 1; + let line_width = s.ui_width()?; s.line(line_![fpad.x(), y, line_width, y])?; s.pop(); s.advance_cursor([line_width, fpad.y()]); diff --git a/src/gui/state.rs b/src/gui/state.rs index 89f6158..a33fb96 100644 --- a/src/gui/state.rs +++ b/src/gui/state.rs @@ -792,7 +792,7 @@ impl PixState { self.ui.cursor = point![padx + offset_x, pos.y() + line_height + pady]; self.ui.pline_height = line_height; self.ui.line_height = 0; - self.ui.last_size = Some(rect![self.ui.cursor, size.x(), size.y()]); + self.ui.last_size = Some(rect![pos, size.x(), size.y()]); } /// Get or create a UI texture to render to diff --git a/src/gui/widgets.rs b/src/gui/widgets.rs index 81b1c13..135c433 100644 --- a/src/gui/widgets.rs +++ b/src/gui/widgets.rs @@ -367,6 +367,7 @@ impl PixState { } /// Render an arrow aligned with the current font size. + /// /// # Errors /// /// If the renderer fails to draw to the current render target, then an error is returned. diff --git a/src/gui/widgets/select.rs b/src/gui/widgets/select.rs index 38dbb99..ddbce0b 100644 --- a/src/gui/widgets/select.rs +++ b/src/gui/widgets/select.rs @@ -26,7 +26,6 @@ use crate::{ ops::clamp_size, prelude::*, }; -use anyhow::bail; use std::cmp; /// The maximum number of select elements that can be displayed at once. @@ -63,7 +62,7 @@ impl PixState { label: S, selected: &mut usize, items: &[I], - displayed_count: usize, + mut displayed_count: usize, ) -> PixResult where S: AsRef, @@ -72,13 +71,10 @@ impl PixState { let label = label.as_ref(); if displayed_count > MAX_DISPLAYED { - bail!("displayed_count exceeds maximum of: {}", MAX_DISPLAYED); - } else if *selected > items.len() { - bail!( - "selected out of bounds: the len is {} but the value is {}", - items.len(), - *selected - ); + displayed_count = MAX_DISPLAYED; + } + if *selected >= items.len() { + *selected = items.len() - 1; } let s = self; @@ -140,10 +136,14 @@ impl PixState { if arrow_x + arrow_width - fpad.x() <= select_box.right() { s.no_stroke(); s.fill(fg); + s.clip(arrow_box)?; s.arrow( - [arrow_x + fpad.y(), select_y + fpad.y()], + [ + arrow_x + fpad.y(), + select_y + arrow_box.height() / 2 - arrow_width / 4, + ], Direction::Down, - 1.0, + fpad.y() as Scalar / 8.0, )?; } @@ -221,7 +221,7 @@ impl PixState { label: S, selected: &mut usize, items: &[I], - displayed_count: usize, + mut displayed_count: usize, ) -> PixResult where S: AsRef, @@ -230,13 +230,10 @@ impl PixState { let label = label.as_ref(); if displayed_count > MAX_DISPLAYED { - bail!("displayed_count exceeds maximum of: {}", MAX_DISPLAYED); - } else if *selected > items.len() { - bail!( - "selected out of bounds: the len is {} but the value is {}", - items.len(), - *selected - ); + displayed_count = MAX_DISPLAYED; + } + if *selected >= items.len() { + *selected = items.len() - 1; } let s = self; diff --git a/src/gui/widgets/text.rs b/src/gui/widgets/text.rs index 6fda3da..97b5947 100644 --- a/src/gui/widgets/text.rs +++ b/src/gui/widgets/text.rs @@ -194,73 +194,71 @@ impl PixState { angle_radians = angle.map(Scalar::to_radians); } - let mut render_text = |mut color: Color, outline: u8| -> PixResult<(u32, u32)> { - s.push(); + let mut render_text = + |pos: PointI2, mut color: Color, outline: u8| -> PixResult> { + s.push(); - // Make sure to offset the text if an outline was drawn - if stroke.is_some() && stroke_weight > 0 && outline == 0 { - pos += i32::from(stroke_weight); - } - if disabled { - color = color.blended(colors.background, 0.38); - } - - let (w, h) = if wrap_width.is_some() { - s.renderer.text( - pos, - text, - wrap_width, - angle, - center, - flipped, - Some(color), - outline, - )? - } else { - let mut x = pos.x(); - let mut y = pos.y(); - let (mut total_width, mut total_height) = (0, 0); - for line in text.split('\n') { - let (line_width, line_height) = s.renderer.size_of(line, wrap_width)?; - let rect = rect![0, 0, clamp_size(line_width), clamp_size(line_height)]; - let bounding_box = - angle_radians.map_or(rect, |angle| rect.rotated(angle, center)); - x -= bounding_box.x(); - y -= bounding_box.y(); + if disabled { + color = color.blended(colors.background, 0.38); + } + let (w, h) = if wrap_width.is_some() { s.renderer.text( - point![x, y], - line, + pos, + text, wrap_width, angle, center, flipped, Some(color), outline, - )?; - total_width += bounding_box.width() as u32; - total_height += bounding_box.height() as u32; - y += bounding_box.height(); - } - (total_width, total_height) + )? + } else { + let mut x = pos.x(); + let mut y = pos.y(); + let (mut total_width, mut total_height) = (0, 0); + for line in text.split('\n') { + let (line_width, line_height) = s.renderer.size_of(line, wrap_width)?; + let rect = rect![0, 0, clamp_size(line_width), clamp_size(line_height)]; + let bounding_box = + angle_radians.map_or(rect, |angle| rect.rotated(angle, center)); + x -= bounding_box.x(); + y -= bounding_box.y(); + s.renderer.text( + point![x, y], + line, + wrap_width, + angle, + center, + flipped, + Some(color), + outline, + )?; + total_width += bounding_box.width() as u32; + total_height += bounding_box.height() as u32; + y += bounding_box.height(); + } + (total_width, total_height) + }; + let rect = rect![pos, clamp_size(w), clamp_size(h)]; + + s.pop(); + Ok(rect) }; - let rect = rect![pos, clamp_size(w), clamp_size(h)]; - // Only advance the cursor if we're not drawing a text outline - if outline == 0 { - s.advance_cursor(rect.size()); + let rect = { + let stroke_rect = match stroke { + Some(stroke) if stroke_weight > 0 => Some(render_text(pos, stroke, stroke_weight)?), + _ => None, + }; + if stroke_rect.is_some() { + pos += i32::from(stroke_weight); } - - s.pop(); - Ok((w, h)) - }; - - let stroke_size = match stroke { - Some(stroke) if stroke_weight > 0 => Some(render_text(stroke, stroke_weight)?), - _ => None, + let text_rect = render_text(pos, fill, 0)?; + stroke_rect.unwrap_or(text_rect) }; - let size = render_text(fill, 0)?; + s.advance_cursor(rect.size()); - Ok(stroke_size.unwrap_or(size)) + Ok((rect.width() as u32, rect.height() as u32)) } /// Draw bulleted text to the current canvas. diff --git a/src/gui/widgets/tooltip.rs b/src/gui/widgets/tooltip.rs index ce8ef3c..a4d026a 100644 --- a/src/gui/widgets/tooltip.rs +++ b/src/gui/widgets/tooltip.rs @@ -268,7 +268,7 @@ impl PixState { } if rect.bottom() > win_height { let offset = (rect.bottom() - win_height) + pad.y(); - rect = rect.offset([-offset, 0]); + rect = rect.offset([0, -offset]); let mpos = s.mouse_pos(); if rect.contains_point(mpos) { rect.set_bottom(mpos.y() - pad.y());