From 46a0758d093020125b38c41066b153c9c243fb36 Mon Sep 17 00:00:00 2001 From: Jonathan Johnson Date: Tue, 21 Nov 2023 20:41:56 -0800 Subject: [PATCH] Vertical sliders/progress bars Also fixed checkbox layout after Label no longer pads itself. --- Cargo.lock | 2 +- examples/progress.rs | 12 +++- src/widget.rs | 9 +++ src/widgets/checkbox.rs | 11 +++- src/widgets/progress.rs | 62 ++++++++++--------- src/widgets/slider.rs | 134 ++++++++++++++-------------------------- 6 files changed, 109 insertions(+), 121 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 40c160d..fbb5f19 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -643,7 +643,7 @@ dependencies = [ [[package]] name = "figures" version = "0.1.0" -source = "git+https://github.com/khonsulabs/figures#40045fa1940f6212bc4876a4ef9ae2cd0bd89808" +source = "git+https://github.com/khonsulabs/figures#4de8f5965bc7f9fc12369b2af93068e9da7a2446" dependencies = [ "bytemuck", "euclid", diff --git a/examples/progress.rs b/examples/progress.rs index fd75406..1fcddc8 100644 --- a/examples/progress.rs +++ b/examples/progress.rs @@ -4,6 +4,8 @@ use gooey::widgets::progress::Progressable; use gooey::widgets::slider::Slidable; use gooey::widgets::Checkbox; use gooey::Run; +use kludgine::figures::units::Lp; +use kludgine::figures::Size; fn main() -> gooey::Result { let indeterminant = Dynamic::new(false); @@ -12,10 +14,18 @@ fn main() -> gooey::Result { .map_each(|(&indeterminant, &value)| (!indeterminant).then_some(value)); value + .clone() .slider() - .and(progress.progress_bar()) + .and(progress.clone().progress_bar()) .and(Checkbox::new(indeterminant.clone(), "Indeterminant")) .into_rows() + .fit_horizontally() + .expand() + .and(value.slider()) + .and(progress.progress_bar()) + .into_columns() + .pad() + .size(Size::squared(Lp::inches(3))) .centered() .expand() .run() diff --git a/src/widget.rs b/src/widget.rs index 11dcd40..390f687 100644 --- a/src/widget.rs +++ b/src/widget.rs @@ -708,6 +708,15 @@ pub trait MakeWidget: Sized { Expand::horizontal(self) } + /// Resizes `self` to `size`. + #[must_use] + fn size(self, size: Size) -> Resize + where + T: Into, + { + Resize::to(size, self) + } + /// Resizes `self` to `width`. /// /// `width` can be an any of: diff --git a/src/widgets/checkbox.rs b/src/widgets/checkbox.rs index 56c73a4..a85875a 100644 --- a/src/widgets/checkbox.rs +++ b/src/widgets/checkbox.rs @@ -159,10 +159,15 @@ impl WrapperWidget for CheckboxLabel { ) -> WrappedLayout { let checkbox_size = context.get(&LineHeight).into_px(context.gfx.scale()); // TODO create a component? let padding = context.get(&IntrinsicPadding).into_px(context.gfx.scale()); - let label_inset = checkbox_size + padding; - let size_with_checkbox = Size::new(size.width + label_inset, size.height).into_unsigned(); + let label_inset = checkbox_size + padding * 2; + let effective_height = size.height.max(label_inset); + let size_with_checkbox = + Size::new(size.width + label_inset + padding, effective_height).into_unsigned(); WrappedLayout { - child: Rect::new(Point::new(label_inset, Px::ZERO), size), + child: Rect::new( + Point::new(label_inset, Px::ZERO), + Size::new(size.width, effective_height), + ), size: size_with_checkbox, } } diff --git a/src/widgets/progress.rs b/src/widgets/progress.rs index 58c8ef9..953ead3 100644 --- a/src/widgets/progress.rs +++ b/src/widgets/progress.rs @@ -5,7 +5,7 @@ use std::time::Duration; use kludgine::figures::Ranged; -use crate::animation::easings::EaseInOutSine; +use crate::animation::easings::EaseInOutQuadradic; use crate::animation::{ AnimationHandle, AnimationTarget, IntoAnimate, PercentBetween, Spawn, ZeroToOne, }; @@ -82,37 +82,39 @@ fn update_progress_bar( ) { match progress { Progress::Indeterminant => { - *indeterminant_animation = Some( - end.transition_to(ZeroToOne::new(0.66)) - .over(Duration::from_millis(500)) - .with_easing(EaseInOutSine) - .and_then( - start - .transition_to(ZeroToOne::new(0.33)) - .over(Duration::from_millis(500)) - .with_easing(EaseInOutSine), + if indeterminant_animation.is_none() { + *indeterminant_animation = Some( + ( + start.transition_to(ZeroToOne::ZERO), + end.transition_to(ZeroToOne::ZERO), ) - .and_then( - end.transition_to(ZeroToOne::ONE) - .over(Duration::from_millis(500)) - .with_easing(EaseInOutSine), - ) - .and_then( - start - .transition_to(ZeroToOne::ONE) - .over(Duration::from_millis(500)) - .with_easing(EaseInOutSine), - ) - .and_then( - ( - start.transition_to(ZeroToOne::ZERO), - end.transition_to(ZeroToOne::ZERO), + .over(Duration::ZERO) + .and_then( + end.transition_to(ZeroToOne::new(0.66)) + .over(Duration::from_millis(500)) + .with_easing(EaseInOutQuadradic), ) - .over(Duration::ZERO), - ) - .cycle() - .spawn(), - ); + .and_then( + start + .transition_to(ZeroToOne::new(0.33)) + .over(Duration::from_millis(500)) + .with_easing(EaseInOutQuadradic), + ) + .and_then( + end.transition_to(ZeroToOne::ONE) + .over(Duration::from_millis(500)) + .with_easing(EaseInOutQuadradic), + ) + .and_then( + start + .transition_to(ZeroToOne::ONE) + .over(Duration::from_millis(500)) + .with_easing(EaseInOutQuadradic), + ) + .cycle() + .spawn(), + ); + } } Progress::Percent(value) => { let _stopped_animation = indeterminant_animation.take(); diff --git a/src/widgets/slider.rs b/src/widgets/slider.rs index 4c7e51c..d198ed1 100644 --- a/src/widgets/slider.rs +++ b/src/widgets/slider.rs @@ -9,8 +9,7 @@ use kludgine::app::winit::event::{DeviceId, MouseButton, MouseScrollDelta, Touch use kludgine::app::winit::keyboard::{Key, NamedKey}; use kludgine::figures::units::{Lp, Px, UPx}; use kludgine::figures::{ - FloatConversion, FromComponents, IntoComponents, IntoSigned, Point, Ranged, Rect, Round, - ScreenScale, Size, + FloatConversion, IntoSigned, Point, Ranged, Rect, Round, ScreenScale, Size, }; use kludgine::shapes::{Shape, StrokeOptions}; use kludgine::{Color, DrawableExt, Origin}; @@ -155,7 +154,7 @@ where let half_focus_ring = spec.if_knobbed(|| (Lp::points(2).into_px(context.gfx.scale()) / 2).ceil()); let focus_ring = half_focus_ring * 2; - let track_length = self.rendered_size - spec.if_knobbed(|| spec.knob_size - focus_ring); + let track_length = self.rendered_size - spec.if_knobbed(|| spec.knob_size + focus_ring); let (start, end) = if let Some(end) = spec.end { (track_length * spec.start, track_length * end) } else { @@ -169,10 +168,10 @@ where if start > 0 { context.gfx.draw_shape( Shape::filled_round_rect( - Rect::new( - flipped(!self.horizontal, Point::new(start_inset, start_inset)), - flipped(!self.horizontal, Size::new(start, spec.track_size)), - ), + self.orient_rectangle(Rect::new( + Point::new(start_inset, start_inset), + Size::new(start, spec.track_size), + )), half_track, spec.inactive_track_color, ) @@ -182,19 +181,13 @@ where if end < track_length { context.gfx.draw_shape( Shape::filled_round_rect( - Rect::new( - flipped( - !self.horizontal, - Point::new(end + spec.if_knobbed(|| spec.half_knob), start_inset), + self.orient_rectangle(Rect::new( + Point::new(end + spec.if_knobbed(|| spec.half_knob), start_inset), + Size::new( + track_length - end + spec.if_knobbed(|| half_track), + spec.track_size, ), - flipped( - !self.horizontal, - Size::new( - track_length - end + spec.if_knobbed(|| half_track), - spec.track_size, - ), - ), - ), + )), half_track, spec.inactive_track_color, ) @@ -205,22 +198,16 @@ where if start != end { context.gfx.draw_shape( Shape::filled_round_rect( - Rect::new( - flipped( - !self.horizontal, - Point::new( - start + spec.if_knobbed(|| spec.half_knob - half_track), - start_inset, - ), + self.orient_rectangle(Rect::new( + Point::new( + start + spec.if_knobbed(|| spec.half_knob - half_track), + start_inset, ), - flipped( - !self.horizontal, - Size::new( - end - start + spec.if_knobbed(|| spec.track_size), - spec.track_size, - ), + Size::new( + end - start + spec.if_knobbed(|| spec.track_size), + spec.track_size, ), - ), + )), half_track, spec.track_color, ) @@ -231,14 +218,10 @@ where // Draw the knob if spec.knob_size > 0 { let focus = context.focused().then_some(self.focused_knob).flatten(); - self.draw_knobs( - flipped( - !self.horizontal, - Point::new(end + spec.half_knob, spec.half_knob) + inset, - ), + Self::draw_knobs( + self.flip_pt_if_vertical(Point::new(end + spec.half_knob, spec.half_knob) + inset), spec.end.map(|_| { - flipped( - !self.horizontal, + self.flip_pt_if_vertical( Point::new(start + spec.half_knob, spec.half_knob) + inset, ) }), @@ -247,39 +230,10 @@ where spec, context, ); - // let this_knob_role = if spec.end.is_some() { - // Knob::End - // } else { - // Knob::Start - // }; - // self.draw_knob( - // flipped( - // !self.horizontal, - // Point::new(end + spec.half_knob, spec.half_knob) + inset, - // ), - // focused && self.focused_knob == Some(this_knob_role), - // focus_ring, - // spec, - // context, - // ); - - // if spec.end.is_some() { - // self.draw_knob( - // flipped( - // !self.horizontal, - // Point::new(start + spec.half_knob, spec.half_knob) + inset, - // ), - // focused && matches!(self.focused_knob, Some(Knob::Start)), - // focus_ring, - // spec, - // context, - // ); - // } } } fn draw_knobs( - &mut self, end_knob: Point, start_knob: Option>, focus: Option, @@ -293,14 +247,13 @@ where (None, focus) => (end_knob, focus.is_some(), None), }; - self.draw_knob(a, a_is_focused, focus_ring_width, spec, context); + Self::draw_knob(a, a_is_focused, focus_ring_width, spec, context); if let Some((b, b_is_focused)) = b { - self.draw_knob(b, b_is_focused, focus_ring_width, spec, context); + Self::draw_knob(b, b_is_focused, focus_ring_width, spec, context); } } fn draw_knob( - &mut self, knob_center: Point, is_focused: bool, focus_ring_width: Px, @@ -309,7 +262,7 @@ where ) { context.gfx.draw_shape( Shape::filled_circle(spec.half_knob, spec.knob_color, Origin::Center) - .translate_by(flipped(!self.horizontal, knob_center)), + .translate_by(knob_center), ); if is_focused { @@ -336,7 +289,7 @@ where let position = if self.horizontal { position.x - knob_size / 2 } else { - position.y - knob_size / 2 + self.rendered_size - position.y - knob_size / 2 }; let track_width = self.rendered_size - knob_size; let position = position.clamp(Px::ZERO, track_width); @@ -439,6 +392,27 @@ where self.value.update(T::from_parts(start, end)); } } + + fn orient_rectangle(&self, rect: Rect) -> Rect { + if self.horizontal { + rect + } else { + let (tl, br) = rect.extents(); + + Rect::from_extents( + Point::new(tl.y, self.rendered_size - tl.x), + Point::new(br.y, self.rendered_size - br.x), + ) + } + } + + fn flip_pt_if_vertical(&self, pt: Point) -> Point { + if self.horizontal { + pt + } else { + Point::new(pt.y, self.rendered_size - pt.x) + } + } } impl Widget for Slider @@ -742,18 +716,6 @@ impl TrackSpec { } } -fn flipped(flip: bool, value: T) -> T -where - T: IntoComponents + FromComponents, -{ - if flip { - let (a, b) = value.into_components(); - T::from_components((b, a)) - } else { - value - } -} - define_components! { Slider { /// The size of the track that the knob of a [`Slider`] traversesq.