From 8ae315e229fb7f733afb034148f3e88d19ec2316 Mon Sep 17 00:00:00 2001 From: Jonathan Johnson Date: Fri, 17 Nov 2023 08:07:37 -0800 Subject: [PATCH] Drawing refactor from Kludgine --- Cargo.lock | 6 ++-- examples/canvas.rs | 17 ++++----- examples/login.rs | 21 ++++++----- examples/tilemap.rs | 9 +++-- examples/todo.rs | 60 +++++++++++++++++++++++++++++++ src/context.rs | 3 +- src/graphics.rs | 80 +++++++++++++++-------------------------- src/lib.rs | 16 ++++----- src/widgets/align.rs | 8 ++--- src/widgets/button.rs | 4 +-- src/widgets/checkbox.rs | 24 ++++--------- src/widgets/expand.rs | 4 +-- src/widgets/input.rs | 46 ++++++++++-------------- src/widgets/label.rs | 4 +-- src/widgets/resize.rs | 8 ++--- src/widgets/scroll.rs | 42 +++++++++------------- src/widgets/slider.rs | 69 +++++++++++++++-------------------- src/widgets/stack.rs | 40 ++++++++++----------- src/window.rs | 8 ++--- 19 files changed, 229 insertions(+), 240 deletions(-) create mode 100644 examples/todo.rs diff --git a/Cargo.lock b/Cargo.lock index fc44016..c71d68f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -588,9 +588,9 @@ dependencies = [ [[package]] name = "etagere" -version = "0.2.8" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcf22f748754352918e082e0039335ee92454a5d62bcaf69b5e8daf5907d9644" +checksum = "306960881d6c46bd0dd6b7f07442a441418c08d0d3e63d8d080b0f64c6343e4e" dependencies = [ "euclid", "svg_fmt", @@ -1089,7 +1089,7 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" [[package]] name = "kludgine" version = "0.1.0" -source = "git+https://github.com/khonsulabs/kludgine#220df4ed07fcf86459a34591a7923d7ddb25e67e" +source = "git+https://github.com/khonsulabs/kludgine#74db2e6be9c79f384eafbe57af8fa92de8b8d03c" dependencies = [ "ahash", "alot", diff --git a/examples/canvas.rs b/examples/canvas.rs index f66e592..1c1db1b 100644 --- a/examples/canvas.rs +++ b/examples/canvas.rs @@ -4,7 +4,7 @@ use kludgine::figures::units::Px; use kludgine::figures::{Angle, IntoSigned, Point, Rect, Size}; use kludgine::shapes::Shape; use kludgine::text::{Text, TextOrigin}; -use kludgine::Color; +use kludgine::{Color, DrawableExt}; fn main() -> gooey::Result<()> { let mut angle = Angle::degrees(0); @@ -14,19 +14,16 @@ fn main() -> gooey::Result<()> { let center = Point::from(context.gfx.size()).into_signed() / 2; context.gfx.draw_text( Text::new("Canvas exposes the full power of Kludgine", Color::WHITE) - .origin(TextOrigin::Center), - center - Point::new(Px(0), Px(100)), - None, - None, + .origin(TextOrigin::Center) + .translate_by(center - Point::new(Px(0), Px(100))), ); context.gfx.draw_shape( - &Shape::filled_rect( + Shape::filled_rect( Rect::new(Point::new(Px(-50), Px(-50)), Size::new(Px(100), Px(100))), Color::RED, - ), - center, - Some(angle), - None, + ) + .translate_by(center) + .rotate_by(angle), ) }) .tick(Tick::redraws_per_second(60)) diff --git a/examples/login.rs b/examples/login.rs index 3a61f04..81d0d98 100644 --- a/examples/login.rs +++ b/examples/login.rs @@ -15,16 +15,18 @@ fn main() -> gooey::Result { (&username, &password).map_each(|(username, password)| validate(username, password)); // TODO this should be a grid layout to ensure proper visual alignment. - let username_row = "Username" - .and(username.clone().into_input().expand()) - .into_columns(); + let username_field = "Username" + .align_left() + .and(username.clone().into_input()) + .into_rows(); - let password_row = "Password" + let password_field = "Password" + .align_left() .and( // TODO secure input - password.clone().into_input().expand(), + password.clone().into_input(), ) - .into_columns(); + .into_rows(); let buttons = "Cancel" .into_button() @@ -46,13 +48,14 @@ fn main() -> gooey::Result { ) .into_columns(); - username_row + username_field .pad() - .and(password_row.pad()) + .and(password_field.pad()) .and(buttons.pad()) .into_rows() .contain() - .width(Lp::points(300)..Lp::points(600)) + .width(Lp::inches(3)..Lp::inches(6)) + .pad() .scroll() .centered() .expand() diff --git a/examples/tilemap.rs b/examples/tilemap.rs index 54f7ad7..770ed45 100644 --- a/examples/tilemap.rs +++ b/examples/tilemap.rs @@ -8,6 +8,7 @@ use gooey::kludgine::Color; use gooey::value::Dynamic; use gooey::widgets::TileMap; use gooey::{Run, Tick}; +use kludgine::DrawableExt; const PLAYER_SIZE: Px = Px(16); @@ -83,16 +84,14 @@ impl Object for Player { fn render(&self, center: Point, zoom: f32, context: &mut Renderer<'_, '_>) { let zoomed_size = PLAYER_SIZE * zoom; context.draw_shape( - &Shape::filled_rect( + Shape::filled_rect( Rect::new( Point::new(-zoomed_size / 2, -zoomed_size / 2), Size::squared(zoomed_size), ), self.color, - ), - center, - None, - None, + ) + .translate_by(center), ) } } diff --git a/examples/todo.rs b/examples/todo.rs new file mode 100644 index 0000000..acaf82d --- /dev/null +++ b/examples/todo.rs @@ -0,0 +1,60 @@ +use gooey::value::Dynamic; +use gooey::widget::{Children, MakeWidget, WidgetInstance}; +use gooey::widgets::input::InputValue; +use gooey::widgets::{Button, Label, Scroll, Stack}; +use gooey::Run; + +fn main() -> gooey::Result { + let tasks = Dynamic::default(); + let children = Dynamic::default(); + tasks.for_each(children.with_clone(|children| { + move |tasks: &Vec| { + update_task_widgets(tasks, &mut children.lock()); + } + })); + + let task_text = Dynamic::default(); + let valid = task_text.map_each(|text: &String| !text.is_empty()); + let task_form = Stack::columns( + task_text.clone().into_input().expand().and( + Button::new("Add Task") + .on_click(move |_| { + tasks.lock().push(Task::new(task_text.take())); + }) + .into_default() + .with_enabled(valid), + ), + ); + let tasks = Scroll::vertical(Stack::rows(children)); + + Stack::rows(task_form.and(tasks)).expand().run() +} + +struct Task { + widget: WidgetInstance, +} + +impl Task { + pub fn new(text: String) -> Self { + let widget = Label::new(Dynamic::new(text)).align_left().make_widget(); + + Self { widget } + } +} + +fn update_task_widgets(tasks: &[Task], children: &mut Children) { + 'tasks: for (index, task) in tasks.iter().enumerate() { + for child in index..children.len() { + if children[child].id() == task.widget.id() { + if child != index { + children.swap(child, index); + } + continue 'tasks; + } + } + + children.insert(index, task.widget.clone()); + } + + children.truncate(tasks.len()); +} diff --git a/src/context.rs b/src/context.rs index 6c81f7c..077d9ed 100644 --- a/src/context.rs +++ b/src/context.rs @@ -527,8 +527,7 @@ impl<'context, 'window, 'clip, 'gfx, 'pass> GraphicsContext<'context, 'window, ' let visible_rect = Rect::from(self.gfx.region().size - (Px(1), Px(1))); let focus_ring = Shape::stroked_rect(visible_rect, color, options.into_px(self.gfx.scale())); - self.gfx - .draw_shape(&focus_ring, Point::default(), None, None); + self.gfx.draw_shape(&focus_ring); } /// Renders the default focus ring for this widget. diff --git a/src/graphics.rs b/src/graphics.rs index 21264a5..d2d6136 100644 --- a/src/graphics.rs +++ b/src/graphics.rs @@ -2,14 +2,13 @@ use std::ops::{Deref, DerefMut}; use kludgine::figures::units::{Px, UPx}; use kludgine::figures::{ - self, Angle, Fraction, IntoSigned, IntoUnsigned, IsZero, Point, Rect, ScreenScale, ScreenUnit, - Size, + self, Fraction, IntoSigned, IntoUnsigned, IsZero, Point, Rect, ScreenScale, ScreenUnit, Size, }; use kludgine::render::Renderer; use kludgine::shapes::Shape; use kludgine::text::{MeasuredText, Text, TextOrigin}; use kludgine::{ - cosmic_text, ClipGuard, Color, Kludgine, ShaderScalable, ShapeSource, TextureSource, + cosmic_text, ClipGuard, Color, Drawable, Kludgine, ShaderScalable, ShapeSource, TextureSource, }; /// A 2d graphics context @@ -141,28 +140,18 @@ impl<'clip, 'gfx, 'pass> Graphics<'clip, 'gfx, 'pass> { pub fn fill(&mut self, color: Color) { if color.alpha() > 0 { let rect = Rect::from(self.region.size); - self.draw_shape( - &Shape::filled_rect(rect, color), - Point::default(), - None, - None, - ); + self.draw_shape(&Shape::filled_rect(rect, color)); } } /// Draws a shape at the origin, rotating and scaling as needed. - pub fn draw_shape( - &mut self, - shape: &Shape, - origin: Point, - rotation_rads: Option, - scale: Option, - ) where + pub fn draw_shape<'a, Unit>(&mut self, shape: impl Into, Unit>>) + where Unit: IsZero + ShaderScalable + figures::ScreenUnit + Copy, { - let translate = origin + Point::::from_px(self.translation(), self.scale()); - self.renderer - .draw_shape(shape, translate, rotation_rads, scale); + let mut shape = shape.into(); + shape.translation += Point::::from_px(self.translation(), self.scale()); + self.renderer.draw_shape(shape); } /// Draws `texture` at `destination`, scaling as necessary. @@ -177,20 +166,18 @@ impl<'clip, 'gfx, 'pass> Graphics<'clip, 'gfx, 'pass> { /// Draws a shape that was created with texture coordinates, applying the /// provided texture. - pub fn draw_textured_shape( + pub fn draw_textured_shape<'shape, Unit, Shape>( &mut self, - shape: &impl ShapeSource, + shape: impl Into>, texture: &impl TextureSource, - origin: Point, - rotation: Option, - scale: Option, ) where Unit: IsZero + ShaderScalable + figures::ScreenUnit + Copy, i32: From<::Signed>, + Shape: ShapeSource + 'shape, { - let translate = origin + Point::::from_px(self.translation(), self.scale()); - self.renderer - .draw_textured_shape(shape, texture, translate, rotation, scale); + let mut shape = shape.into(); + shape.translation += Point::::from_px(self.translation(), self.scale()); + self.renderer.draw_textured_shape(shape, texture); } /// Measures `text` using the current text settings. @@ -204,17 +191,13 @@ impl<'clip, 'gfx, 'pass> Graphics<'clip, 'gfx, 'pass> { } /// Draws `text` using the current text settings. - pub fn draw_text<'a, Unit>( - &mut self, - text: impl Into>, - translate: Point, - rotation: Option, - scale: Option, - ) where + pub fn draw_text<'a, Unit>(&mut self, text: impl Into, Unit>>) + where Unit: ScreenUnit, { - let translate = translate + Point::::from_px(self.translation(), self.scale()); - self.renderer.draw_text(text, translate, rotation, scale); + let mut text = text.into(); + text.translation += Point::::from_px(self.translation(), self.scale()); + self.renderer.draw_text(text); } /// Prepares the text layout contained in `buffer` to be rendered. @@ -224,20 +207,18 @@ impl<'clip, 'gfx, 'pass> Graphics<'clip, 'gfx, 'pass> { /// /// `origin` allows controlling how the text will be drawn relative to the /// coordinate provided in [`render()`](kludgine::PreparedGraphic::render). - pub fn draw_text_buffer( + pub fn draw_text_buffer<'a, Unit>( &mut self, - buffer: &cosmic_text::Buffer, + buffer: impl Into>, default_color: Color, origin: TextOrigin, - translate: Point, - rotation: Option, - scale: Option, ) where Unit: ScreenUnit, { - let translate = translate + Point::::from_px(self.translation(), self.scale()); + let mut buffer = buffer.into(); + buffer.translation += Point::::from_px(self.translation(), self.scale()); self.renderer - .draw_text_buffer(buffer, default_color, origin, translate, rotation, scale); + .draw_text_buffer(buffer, default_color, origin); } /// Measures `buffer` and caches the results using `default_color` when @@ -260,19 +241,16 @@ impl<'clip, 'gfx, 'pass> Graphics<'clip, 'gfx, 'pass> { /// /// `origin` allows controlling how the text will be drawn relative to the /// coordinate provided in [`render()`](kludgine::PreparedGraphic::render). - pub fn draw_measured_text( + pub fn draw_measured_text<'a, Unit>( &mut self, - text: &MeasuredText, + text: impl Into, Unit>>, origin: TextOrigin, - translate: Point, - rotation: Option, - scale: Option, ) where Unit: ScreenUnit, { - let translate = translate + Point::::from_px(self.translation(), self.scale()); - self.renderer - .draw_measured_text(text, origin, translate, rotation, scale); + let mut text = text.into(); + text.translation += Point::::from_px(self.translation(), self.scale()); + self.renderer.draw_measured_text(text, origin); } } diff --git a/src/lib.rs b/src/lib.rs index 908878f..bf7f7cc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -36,9 +36,9 @@ pub use self::tick::{InputState, Tick}; #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum ConstraintLimit { /// The widget is expected to occupy a known size. - Known(UPx), + Fill(UPx), /// The widget is expected to resize itself to fit within the size provided. - ClippedAfter(UPx), + SizeToFit(UPx), } impl ConstraintLimit { @@ -46,7 +46,7 @@ impl ConstraintLimit { #[must_use] pub fn max(self) -> UPx { match self { - ConstraintLimit::Known(v) | ConstraintLimit::ClippedAfter(v) => v, + ConstraintLimit::Fill(v) | ConstraintLimit::SizeToFit(v) => v, } } @@ -62,8 +62,8 @@ impl ConstraintLimit { { let measured = measured.into_upx(scale); match self { - ConstraintLimit::Known(size) => size.max(measured), - ConstraintLimit::ClippedAfter(_) => measured, + ConstraintLimit::Fill(size) => size.max(measured), + ConstraintLimit::SizeToFit(_) => measured, } } } @@ -73,10 +73,8 @@ impl Sub for ConstraintLimit { fn sub(self, rhs: UPx) -> Self::Output { match self { - ConstraintLimit::Known(px) => ConstraintLimit::Known(px.saturating_sub(rhs)), - ConstraintLimit::ClippedAfter(px) => { - ConstraintLimit::ClippedAfter(px.saturating_sub(rhs)) - } + ConstraintLimit::Fill(px) => ConstraintLimit::Fill(px.saturating_sub(rhs)), + ConstraintLimit::SizeToFit(px) => ConstraintLimit::SizeToFit(px.saturating_sub(rhs)), } } } diff --git a/src/widgets/align.rs b/src/widgets/align.rs index 5ee0b72..276e2b3 100644 --- a/src/widgets/align.rs +++ b/src/widgets/align.rs @@ -141,15 +141,15 @@ impl FrameInfo { // into ClippedAfter mode to make the widget attempt to size the // content to fit. (Some(one), None) | (None, Some(one)) => { - ConstraintLimit::ClippedAfter(available.max() - one) + ConstraintLimit::SizeToFit(available.max() - one) } - (None, None) => ConstraintLimit::ClippedAfter(available.max()), + (None, None) => ConstraintLimit::SizeToFit(available.max()), } } fn measure(&self, available: ConstraintLimit, content: UPx) -> (UPx, UPx, UPx) { match available { - ConstraintLimit::Known(size) => { + ConstraintLimit::Fill(size) => { let remaining = size.saturating_sub(content); let (a, b) = match (self.a, self.b) { (Some(a), Some(b)) => (a, b), @@ -164,7 +164,7 @@ impl FrameInfo { (a, b, size - a - b) } - ConstraintLimit::ClippedAfter(_) => ( + ConstraintLimit::SizeToFit(_) => ( self.a.unwrap_or_default(), self.b.unwrap_or_default(), content, diff --git a/src/widgets/button.rs b/src/widgets/button.rs index c8ae9ee..5ee81d0 100644 --- a/src/widgets/button.rs +++ b/src/widgets/button.rs @@ -369,9 +369,7 @@ impl Widget for Button { color, two_lp_stroke, ); - context - .gfx - .draw_shape(&focus_ring, Point::default(), None, None); + context.gfx.draw_shape(&focus_ring); } else if context.is_default() { context.stroke_outline(context.get(&OutlineColor), two_lp_stroke); } else { diff --git a/src/widgets/checkbox.rs b/src/widgets/checkbox.rs index ffa9144..a943469 100644 --- a/src/widgets/checkbox.rs +++ b/src/widgets/checkbox.rs @@ -178,12 +178,9 @@ impl WrapperWidget for CheckboxLabel { match self.value.get_tracking_refresh(context) { state @ (CheckboxState::Checked | CheckboxState::Indeterminant) => { let color = context.get(&WidgetAccentColor); - context.gfx.draw_shape( - &Shape::filled_rect(checkbox_rect, color), - Point::default(), - None, - None, - ); + context + .gfx + .draw_shape(&Shape::filled_rect(checkbox_rect, color)); let icon_area = checkbox_rect.inset(Lp::points(3).into_px(context.gfx.scale())); let text_color = context.get(&TextColor); let center = icon_area.origin + icon_area.size / 2; @@ -200,9 +197,6 @@ impl WrapperWidget for CheckboxLabel { )) .build() .stroke(text_color, stroke_options), - Point::default(), - None, - None, ); } else { context.gfx.draw_shape( @@ -213,20 +207,14 @@ impl WrapperWidget for CheckboxLabel { )) .build() .stroke(text_color, stroke_options), - Point::default(), - None, - None, ); } } CheckboxState::Unchecked => { let color = context.get(&OutlineColor); - context.gfx.draw_shape( - &Shape::stroked_rect(checkbox_rect, color, stroke_options), - Point::default(), - None, - None, - ); + context + .gfx + .draw_shape(&Shape::stroked_rect(checkbox_rect, color, stroke_options)); } } } diff --git a/src/widgets/expand.rs b/src/widgets/expand.rs index b654f52..820c10c 100644 --- a/src/widgets/expand.rs +++ b/src/widgets/expand.rs @@ -103,8 +103,8 @@ impl WrapperWidget for Expand { context: &mut LayoutContext<'_, '_, '_, '_, '_>, ) -> WrappedLayout { let available_space = Size::new( - ConstraintLimit::Known(available_space.width.max()), - ConstraintLimit::Known(available_space.height.max()), + ConstraintLimit::Fill(available_space.width.max()), + ConstraintLimit::Fill(available_space.height.max()), ); let child = self.child.mounted(&mut context.as_event_context()); let size = context.for_other(&child).layout(available_space); diff --git a/src/widgets/input.rs b/src/widgets/input.rs index c9c291d..499bc16 100644 --- a/src/widgets/input.rs +++ b/src/widgets/input.rs @@ -19,7 +19,7 @@ use kludgine::figures::{ }; use kludgine::shapes::{Shape, StrokeOptions}; use kludgine::text::{MeasuredText, Text, TextOrigin}; -use kludgine::Color; +use kludgine::{Color, DrawableExt}; use unicode_segmentation::UnicodeSegmentation; use zeroize::Zeroizing; @@ -849,55 +849,47 @@ where // Single line selection let width = end_position.x - start_position.x; context.gfx.draw_shape( - &Shape::filled_rect( + Shape::filled_rect( Rect::new(start_position, Size::new(width, cache.measured.line_height)), highlight, - ), - padding, - None, - None, + ) + .translate_by(padding), ); } else { // Draw from start to end of line, let width = size.width.into_signed() - start_position.x; context.gfx.draw_shape( - &Shape::filled_rect( + Shape::filled_rect( Rect::new(start_position, Size::new(width, cache.measured.line_height)), highlight, - ), - padding, - None, - None, + ) + .translate_by(padding), ); // Fill region between let bottom_of_first_line = start_position.y + cache.measured.line_height; let distance_between = end_position.y - bottom_of_first_line; if distance_between > 0 { context.gfx.draw_shape( - &Shape::filled_rect( + Shape::filled_rect( Rect::new( Point::new(Px(0), bottom_of_first_line), Size::new(size.width.into_signed(), distance_between), ), highlight, - ), - padding, - None, - None, + ) + .translate_by(padding), ); } // Draw from 0 to end + width context.gfx.draw_shape( - &Shape::filled_rect( + Shape::filled_rect( Rect::new( Point::new(Px(0), end_position.y), Size::new(end_position.x + end_width, cache.measured.line_height), ), highlight, - ), - padding, - None, - None, + ) + .translate_by(padding), ); } } else { @@ -906,16 +898,14 @@ where if window_focused && cursor_state.visible { let cursor_width = Lp::points(2).into_px(context.gfx.scale()); context.gfx.draw_shape( - &Shape::filled_rect( + Shape::filled_rect( Rect::new( Point::new(location.x - cursor_width / 2, location.y), Size::new(cursor_width, cache.measured.line_height), ), - highlight, // TODO cursor should be a bold color, highlight probably not. This should have its own color. - ), - padding, - None, - None, + highlight, + ) + .translate_by(padding), ); } if window_focused { @@ -931,7 +921,7 @@ where context .gfx - .draw_measured_text(cache.measured, TextOrigin::TopLeft, padding, None, None); + .draw_measured_text(cache.measured.translate_by(padding), TextOrigin::TopLeft); } fn layout( diff --git a/src/widgets/label.rs b/src/widgets/label.rs index a488902..633ed58 100644 --- a/src/widgets/label.rs +++ b/src/widgets/label.rs @@ -3,7 +3,7 @@ use kludgine::figures::units::{Px, UPx}; use kludgine::figures::{Point, ScreenScale, Size}; use kludgine::text::{MeasuredText, Text, TextOrigin}; -use kludgine::Color; +use kludgine::{Color, DrawableExt}; use crate::context::{GraphicsContext, LayoutContext}; use crate::styles::components::{IntrinsicPadding, TextColor}; @@ -71,7 +71,7 @@ impl Widget for Label { context .gfx - .draw_measured_text(prepared_text, TextOrigin::Center, center, None, None); + .draw_measured_text(prepared_text.translate_by(center), TextOrigin::Center); } fn layout( diff --git a/src/widgets/resize.rs b/src/widgets/resize.rs index eb51cfc..ed9a050 100644 --- a/src/widgets/resize.rs +++ b/src/widgets/resize.rs @@ -124,10 +124,10 @@ fn override_constraint( scale: Fraction, ) -> ConstraintLimit { match constraint { - ConstraintLimit::Known(size) => ConstraintLimit::Known(range.clamp(size, scale)), - ConstraintLimit::ClippedAfter(clipped_after) => match (range.minimum(), range.maximum()) { - (Some(min), Some(max)) if min == max => ConstraintLimit::Known(min.into_upx(scale)), - _ => ConstraintLimit::ClippedAfter(range.clamp(clipped_after, scale)), + ConstraintLimit::Fill(size) => ConstraintLimit::Fill(range.clamp(size, scale)), + ConstraintLimit::SizeToFit(clipped_after) => match (range.minimum(), range.maximum()) { + (Some(min), Some(max)) if min == max => ConstraintLimit::Fill(min.into_upx(scale)), + _ => ConstraintLimit::SizeToFit(range.clamp(clipped_after, scale)), }, } } diff --git a/src/widgets/scroll.rs b/src/widgets/scroll.rs index 91f848e..31278a0 100644 --- a/src/widgets/scroll.rs +++ b/src/widgets/scroll.rs @@ -129,33 +129,23 @@ impl Widget for Scroll { let size = context.gfx.region().size; if self.horizontal_bar.amount_hidden > 0 { - context.gfx.draw_shape( - &Shape::filled_rect( - Rect::new( - Point::new(self.horizontal_bar.offset, size.height - self.bar_width), - Size::new(self.horizontal_bar.size, self.bar_width), - ), - Color::new_f32(1.0, 1.0, 1.0, *self.scrollbar_opacity.get()), + context.gfx.draw_shape(&Shape::filled_rect( + Rect::new( + Point::new(self.horizontal_bar.offset, size.height - self.bar_width), + Size::new(self.horizontal_bar.size, self.bar_width), ), - Point::default(), - None, - None, - ); + Color::new_f32(1.0, 1.0, 1.0, *self.scrollbar_opacity.get()), + )); } if self.vertical_bar.amount_hidden > 0 { - context.gfx.draw_shape( - &Shape::filled_rect( - Rect::new( - Point::new(size.width - self.bar_width, self.vertical_bar.offset), - Size::new(self.bar_width, self.vertical_bar.size), - ), - Color::new_f32(1.0, 1.0, 1.0, *self.scrollbar_opacity.get()), + context.gfx.draw_shape(&Shape::filled_rect( + Rect::new( + Point::new(size.width - self.bar_width, self.vertical_bar.offset), + Size::new(self.bar_width, self.vertical_bar.size), ), - Point::default(), - None, - None, - ); + Color::new_f32(1.0, 1.0, 1.0, *self.scrollbar_opacity.get()), + )); } } @@ -175,12 +165,12 @@ impl Widget for Scroll { Size::new(available_space.width.max(), available_space.height.max()).into_signed(); let max_extents = Size::new( if self.enabled.x { - ConstraintLimit::ClippedAfter((control_size.width).into_unsigned()) + ConstraintLimit::SizeToFit((control_size.width).into_unsigned()) } else { available_space.width }, if self.enabled.y { - ConstraintLimit::ClippedAfter((control_size.height).into_unsigned()) + ConstraintLimit::SizeToFit((control_size.height).into_unsigned()) } else { available_space.height }, @@ -285,8 +275,8 @@ impl Widget for Scroll { fn constrain_child(constraint: ConstraintLimit, measured: Px) -> UPx { let measured = measured.into_unsigned(); match constraint { - ConstraintLimit::Known(size) => size.min(measured), - ConstraintLimit::ClippedAfter(_) => measured, + ConstraintLimit::Fill(size) => size.min(measured), + ConstraintLimit::SizeToFit(_) => measured, } } diff --git a/src/widgets/slider.rs b/src/widgets/slider.rs index a1fc92d..0af7a0b 100644 --- a/src/widgets/slider.rs +++ b/src/widgets/slider.rs @@ -9,7 +9,7 @@ use kludgine::figures::{ Size, }; use kludgine::shapes::Shape; -use kludgine::{Color, Origin}; +use kludgine::{Color, DrawableExt, Origin}; use crate::animation::{LinearInterpolate, PercentBetween}; use crate::context::{EventContext, GraphicsContext, LayoutContext}; @@ -85,53 +85,42 @@ impl Slider { let half_track = spec.track_size / 2; // Draw the track if value_location > spec.half_knob { - context.gfx.draw_shape( - &Shape::filled_rect( - Rect::new( - flipped( - !self.horizontal, - Point::new(spec.half_knob, spec.half_knob - half_track), - ), - flipped(!self.horizontal, Size::new(value_location, spec.track_size)), + context.gfx.draw_shape(&Shape::filled_rect( + Rect::new( + flipped( + !self.horizontal, + Point::new(spec.half_knob, spec.half_knob - half_track), ), - spec.track_color, + flipped(!self.horizontal, Size::new(value_location, spec.track_size)), ), - Point::default(), - None, - None, - ); + spec.track_color, + )); } if value_location < track_length { - context.gfx.draw_shape( - &Shape::filled_rect( - Rect::new( - flipped( - !self.horizontal, - Point::new(value_location, spec.half_knob - half_track), - ), - flipped( - !self.horizontal, - Size::new( - track_length - value_location + spec.half_knob, - spec.track_size, - ), + context.gfx.draw_shape(&Shape::filled_rect( + Rect::new( + flipped( + !self.horizontal, + Point::new(value_location, spec.half_knob - half_track), + ), + flipped( + !self.horizontal, + Size::new( + track_length - value_location + spec.half_knob, + spec.track_size, ), ), - spec.inactive_track_color, ), - Point::default(), - None, - None, - ); + spec.inactive_track_color, + )); } // Draw the knob context.gfx.draw_shape( - &Shape::filled_circle(spec.half_knob, spec.knob_color, Origin::Center), - flipped(!self.horizontal, Point::new(value_location, spec.half_knob)), - None, - None, + Shape::filled_circle(spec.half_knob, spec.knob_color, Origin::Center).translate_by( + flipped(!self.horizontal, Point::new(value_location, spec.half_knob)), + ), ); } } @@ -229,7 +218,7 @@ where .into_upx(context.gfx.scale()); match (available_space.width, available_space.height) { - (ConstraintLimit::Known(width), ConstraintLimit::Known(height)) => { + (ConstraintLimit::Fill(width), ConstraintLimit::Fill(height)) => { // This comparison is done such that if width == height, we end // up with a horizontal slider. if width < height { @@ -240,13 +229,13 @@ where Size::new(width.max(minimum_size), self.knob_size) } } - (ConstraintLimit::Known(width), ConstraintLimit::ClippedAfter(_)) => { + (ConstraintLimit::Fill(width), ConstraintLimit::SizeToFit(_)) => { Size::new(width.max(minimum_size), self.knob_size) } - (ConstraintLimit::ClippedAfter(_), ConstraintLimit::Known(height)) => { + (ConstraintLimit::SizeToFit(_), ConstraintLimit::Fill(height)) => { Size::new(self.knob_size, height.max(minimum_size)) } - (ConstraintLimit::ClippedAfter(width), ConstraintLimit::ClippedAfter(_)) => { + (ConstraintLimit::SizeToFit(width), ConstraintLimit::SizeToFit(_)) => { // When we have no limit on our, we still want to be draggable. // Since we have no limit in both directions, we have to make a // choice: horizontal or vertical. It seems to @ecton at the diff --git a/src/widgets/stack.rs b/src/widgets/stack.rs index 06b733a..d5ba11d 100644 --- a/src/widgets/stack.rs +++ b/src/widgets/stack.rs @@ -409,7 +409,7 @@ impl Layout { // widgets an opportunity to lay themselves out in the full area. This // requires one extra layout call, so we avoid persisting layouts during // the first loop if this is the case. - let needs_final_layout = !matches!(other_constraint, ConstraintLimit::Known(_)); + let needs_final_layout = !matches!(other_constraint, ConstraintLimit::Fill(_)); // Measure the children that fit their content self.other = UPx(0); @@ -418,7 +418,7 @@ impl Layout { let (measured, other) = self.orientation.split_size(measure( index, self.orientation - .make_size(ConstraintLimit::ClippedAfter(remaining), other_constraint), + .make_size(ConstraintLimit::SizeToFit(remaining), other_constraint), !needs_final_layout, )); self.layouts[index].size = measured; @@ -432,7 +432,7 @@ impl Layout { let (_, other) = self.orientation.split_size(measure( index, self.orientation.make_size( - ConstraintLimit::Known(self.layouts[index].size), + ConstraintLimit::Fill(self.layouts[index].size), other_constraint, ), !needs_final_layout, @@ -469,7 +469,7 @@ impl Layout { let (_, measured) = self.orientation.split_size(measure( index, self.orientation.make_size( - ConstraintLimit::Known(self.layouts[index].size.into_upx(scale)), + ConstraintLimit::Fill(self.layouts[index].size.into_upx(scale)), other_constraint, ), !needs_final_layout, @@ -479,8 +479,8 @@ impl Layout { } self.other = match other_constraint { - ConstraintLimit::Known(max) => self.other.max(max), - ConstraintLimit::ClippedAfter(clip_limit) => self.other.min(clip_limit), + ConstraintLimit::Fill(max) => self.other.max(max), + ConstraintLimit::SizeToFit(clip_limit) => self.other.min(clip_limit), }; // Finally, compute the offsets of all of the widgets. @@ -492,8 +492,8 @@ impl Layout { self.orientation.split_size(measure( index, self.orientation.make_size( - ConstraintLimit::Known(self.layouts[index].size.into_upx(scale)), - ConstraintLimit::Known(self.other), + ConstraintLimit::Fill(self.layouts[index].size.into_upx(scale)), + ConstraintLimit::Fill(self.other), ), true, )); @@ -635,8 +635,8 @@ mod tests { fn size_to_fit() { assert_measured_children( &[Child::new(3, 1), Child::new(3, 1), Child::new(3, 1)], - ConstraintLimit::ClippedAfter(UPx(10)), - ConstraintLimit::ClippedAfter(UPx(10)), + ConstraintLimit::SizeToFit(UPx(10)), + ConstraintLimit::SizeToFit(UPx(10)), &[UPx(3), UPx(3), UPx(3)], UPx(9), UPx(1), @@ -660,8 +660,8 @@ mod tests { Child::new(3, 1).weighted(1), Child::new(3, 1).weighted(1), ], - ConstraintLimit::Known(UPx(10)), - ConstraintLimit::ClippedAfter(UPx(10)), + ConstraintLimit::Fill(UPx(10)), + ConstraintLimit::SizeToFit(UPx(10)), &[UPx(4), UPx(3), UPx(3)], UPx(10), UPx(7), // 20 / 3 = 6.666, rounded up is 7 @@ -674,8 +674,8 @@ mod tests { Child::new(3, 1).weighted(1), Child::new(3, 1).weighted(1), ], - ConstraintLimit::Known(UPx(11)), - ConstraintLimit::ClippedAfter(UPx(11)), + ConstraintLimit::Fill(UPx(11)), + ConstraintLimit::SizeToFit(UPx(11)), &[UPx(5), UPx(3), UPx(3)], UPx(11), UPx(7), // 20 / 3 = 6.666, rounded up is 7 @@ -687,8 +687,8 @@ mod tests { Child::new(3, 1).weighted(1), Child::new(3, 1).weighted(1), ], - ConstraintLimit::Known(UPx(12)), - ConstraintLimit::ClippedAfter(UPx(12)), + ConstraintLimit::Fill(UPx(12)), + ConstraintLimit::SizeToFit(UPx(12)), &[UPx(6), UPx(3), UPx(3)], UPx(12), UPx(4), // 20 / 6 = 3.666, rounded up is 4 @@ -701,8 +701,8 @@ mod tests { Child::new(3, 1).weighted(1), Child::new(3, 1).weighted(1), ], - ConstraintLimit::Known(UPx(13)), - ConstraintLimit::ClippedAfter(UPx(13)), + ConstraintLimit::Fill(UPx(13)), + ConstraintLimit::SizeToFit(UPx(13)), &[UPx(6), UPx(3), UPx(4)], UPx(13), UPx(4), // 20 / 6 = 3.666, rounded up is 4 @@ -717,8 +717,8 @@ mod tests { Child::new(3, 1).weighted(1), Child::new(3, 1).weighted(1), ], - ConstraintLimit::Known(UPx(15)), - ConstraintLimit::ClippedAfter(UPx(15)), + ConstraintLimit::Fill(UPx(15)), + ConstraintLimit::SizeToFit(UPx(15)), &[UPx(7), UPx(4), UPx(4)], UPx(15), UPx(1), diff --git a/src/window.rs b/src/window.rs index fdbc0f2..73ad2c0 100644 --- a/src/window.rs +++ b/src/window.rs @@ -567,13 +567,13 @@ where let actual_size = layout_context.layout(if is_expanded { Size::new( - ConstraintLimit::Known(window_size.width), - ConstraintLimit::Known(window_size.height), + ConstraintLimit::Fill(window_size.width), + ConstraintLimit::Fill(window_size.height), ) } else { Size::new( - ConstraintLimit::ClippedAfter(window_size.width), - ConstraintLimit::ClippedAfter(window_size.height), + ConstraintLimit::SizeToFit(window_size.width), + ConstraintLimit::SizeToFit(window_size.height), ) }); let render_size = actual_size.min(window_size);