From bd9cb6313b8eb139d6c66e8448d86fb4300578cd Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Thu, 10 Aug 2023 10:10:22 +0200 Subject: [PATCH 1/2] Replace uses of `RangeInclusive` with `emath::Rangef` --- crates/egui/src/containers/panel.rs | 71 ++++++++++++----------- crates/egui/src/containers/scroll_area.rs | 3 +- crates/egui/src/frame_state.rs | 4 +- crates/egui/src/lib.rs | 4 +- crates/egui/src/painter.rs | 7 +-- crates/egui/src/ui.rs | 20 ++++--- crates/egui/src/widgets/slider.rs | 39 ++++++------- crates/egui_extras/src/sizing.rs | 42 ++++++-------- crates/egui_extras/src/table.rs | 54 +++++++++-------- crates/emath/src/align.rs | 17 ++---- crates/emath/src/lib.rs | 15 ++++- crates/emath/src/range.rs | 50 +++++++++++++++- crates/emath/src/rect.rs | 22 +++---- crates/epaint/src/shape.rs | 11 ++-- 14 files changed, 198 insertions(+), 161 deletions(-) diff --git a/crates/egui/src/containers/panel.rs b/crates/egui/src/containers/panel.rs index d829ac219dec..f5267e0f0c7a 100644 --- a/crates/egui/src/containers/panel.rs +++ b/crates/egui/src/containers/panel.rs @@ -15,8 +15,6 @@ //! //! Add your [`Window`]:s after any top-level panels. -use std::ops::RangeInclusive; - use crate::*; /// State regarding panels. @@ -99,7 +97,7 @@ pub struct SidePanel { resizable: bool, show_separator_line: bool, default_width: f32, - width_range: RangeInclusive, + width_range: Rangef, } impl SidePanel { @@ -122,7 +120,7 @@ impl SidePanel { resizable: true, show_separator_line: true, default_width: 200.0, - width_range: 96.0..=f32::INFINITY, + width_range: Rangef::new(96.0, f32::INFINITY), } } @@ -153,26 +151,29 @@ impl SidePanel { /// The initial wrapping width of the [`SidePanel`]. pub fn default_width(mut self, default_width: f32) -> Self { self.default_width = default_width; - self.width_range = self.width_range.start().at_most(default_width) - ..=self.width_range.end().at_least(default_width); + self.width_range = Rangef::new( + self.width_range.min.at_most(default_width), + self.width_range.max.at_least(default_width), + ); self } /// Minimum width of the panel. pub fn min_width(mut self, min_width: f32) -> Self { - self.width_range = min_width..=self.width_range.end().at_least(min_width); + self.width_range = Rangef::new(min_width, self.width_range.max.at_least(min_width)); self } /// Maximum width of the panel. pub fn max_width(mut self, max_width: f32) -> Self { - self.width_range = self.width_range.start().at_most(max_width)..=max_width; + self.width_range = Rangef::new(self.width_range.min.at_most(max_width), max_width); self } /// The allowable width range for the panel. - pub fn width_range(mut self, width_range: RangeInclusive) -> Self { - self.default_width = clamp_to_range(self.default_width, width_range.clone()); + pub fn width_range(mut self, width_range: impl Into) -> Self { + let width_range = width_range.into(); + self.default_width = clamp_to_range(self.default_width, width_range); self.width_range = width_range; self } @@ -180,7 +181,7 @@ impl SidePanel { /// Enforce this exact width. pub fn exact_width(mut self, width: f32) -> Self { self.default_width = width; - self.width_range = width..=width; + self.width_range = Rangef::point(width); self } @@ -224,7 +225,7 @@ impl SidePanel { if let Some(state) = PanelState::load(ui.ctx(), id) { width = state.rect.width(); } - width = clamp_to_range(width, width_range.clone()).at_most(available_rect.width()); + width = clamp_to_range(width, width_range).at_most(available_rect.width()); side.set_rect_width(&mut panel_rect, width); ui.ctx().check_for_id_clash(id, panel_rect, "SidePanel"); } @@ -241,7 +242,7 @@ impl SidePanel { let resize_x = side.opposite().side_x(panel_rect); let mouse_over_resize_line = we_are_on_top - && panel_rect.y_range().contains(&pointer.y) + && panel_rect.y_range().contains(pointer.y) && (resize_x - pointer.x).abs() <= ui.style().interaction.resize_grab_radius_side; @@ -253,8 +254,7 @@ impl SidePanel { is_resizing = ui.memory(|mem| mem.is_being_dragged(resize_id)); if is_resizing { let width = (pointer.x - side.side_x(panel_rect)).abs(); - let width = - clamp_to_range(width, width_range.clone()).at_most(available_rect.width()); + let width = clamp_to_range(width, width_range).at_most(available_rect.width()); side.set_rect_width(&mut panel_rect, width); } @@ -273,7 +273,7 @@ impl SidePanel { let frame = frame.unwrap_or_else(|| Frame::side_top_panel(ui.style())); let inner_response = frame.show(&mut panel_ui, |ui| { ui.set_min_height(ui.max_rect().height()); // Make sure the frame fills the full height - ui.set_min_width(*width_range.start()); + ui.set_min_width(width_range.min); add_contents(ui) }); @@ -544,7 +544,7 @@ pub struct TopBottomPanel { resizable: bool, show_separator_line: bool, default_height: Option, - height_range: RangeInclusive, + height_range: Rangef, } impl TopBottomPanel { @@ -567,7 +567,7 @@ impl TopBottomPanel { resizable: false, show_separator_line: true, default_height: None, - height_range: 20.0..=f32::INFINITY, + height_range: Rangef::new(20.0, f32::INFINITY), } } @@ -599,28 +599,31 @@ impl TopBottomPanel { /// Defaults to [`style::Spacing::interact_size`].y. pub fn default_height(mut self, default_height: f32) -> Self { self.default_height = Some(default_height); - self.height_range = self.height_range.start().at_most(default_height) - ..=self.height_range.end().at_least(default_height); + self.height_range = Rangef::new( + self.height_range.min.at_most(default_height), + self.height_range.max.at_least(default_height), + ); self } /// Minimum height of the panel. pub fn min_height(mut self, min_height: f32) -> Self { - self.height_range = min_height..=self.height_range.end().at_least(min_height); + self.height_range = Rangef::new(min_height, self.height_range.max.at_least(min_height)); self } /// Maximum height of the panel. pub fn max_height(mut self, max_height: f32) -> Self { - self.height_range = self.height_range.start().at_most(max_height)..=max_height; + self.height_range = Rangef::new(self.height_range.min.at_most(max_height), max_height); self } /// The allowable height range for the panel. - pub fn height_range(mut self, height_range: RangeInclusive) -> Self { + pub fn height_range(mut self, height_range: impl Into) -> Self { + let height_range = height_range.into(); self.default_height = self .default_height - .map(|default_height| clamp_to_range(default_height, height_range.clone())); + .map(|default_height| clamp_to_range(default_height, height_range)); self.height_range = height_range; self } @@ -628,7 +631,7 @@ impl TopBottomPanel { /// Enforce this exact height. pub fn exact_height(mut self, height: f32) -> Self { self.default_height = Some(height); - self.height_range = height..=height; + self.height_range = Rangef::point(height); self } @@ -673,7 +676,7 @@ impl TopBottomPanel { } else { default_height.unwrap_or_else(|| ui.style().spacing.interact_size.y) }; - height = clamp_to_range(height, height_range.clone()).at_most(available_rect.height()); + height = clamp_to_range(height, height_range).at_most(available_rect.height()); side.set_rect_height(&mut panel_rect, height); ui.ctx() .check_for_id_clash(id, panel_rect, "TopBottomPanel"); @@ -692,7 +695,7 @@ impl TopBottomPanel { let resize_y = side.opposite().side_y(panel_rect); let mouse_over_resize_line = we_are_on_top - && panel_rect.x_range().contains(&pointer.x) + && panel_rect.x_range().contains(pointer.x) && (resize_y - pointer.y).abs() <= ui.style().interaction.resize_grab_radius_side; @@ -704,8 +707,8 @@ impl TopBottomPanel { is_resizing = ui.memory(|mem| mem.interaction.drag_id == Some(resize_id)); if is_resizing { let height = (pointer.y - side.side_y(panel_rect)).abs(); - let height = clamp_to_range(height, height_range.clone()) - .at_most(available_rect.height()); + let height = + clamp_to_range(height, height_range).at_most(available_rect.height()); side.set_rect_height(&mut panel_rect, height); } @@ -724,7 +727,7 @@ impl TopBottomPanel { let frame = frame.unwrap_or_else(|| Frame::side_top_panel(ui.style())); let inner_response = frame.show(&mut panel_ui, |ui| { ui.set_min_width(ui.max_rect().width()); // Make the frame fill full width - ui.set_min_height(*height_range.start()); + ui.set_min_height(height_range.min); add_contents(ui) }); @@ -1056,9 +1059,7 @@ impl CentralPanel { } } -fn clamp_to_range(x: f32, range: RangeInclusive) -> f32 { - x.clamp( - range.start().min(*range.end()), - range.start().max(*range.end()), - ) +fn clamp_to_range(x: f32, range: Rangef) -> f32 { + let range = range.as_positive(); + x.clamp(range.min, range.max) } diff --git a/crates/egui/src/containers/scroll_area.rs b/crates/egui/src/containers/scroll_area.rs index 341e61f2fc7c..b75a16ab2a5a 100644 --- a/crates/egui/src/containers/scroll_area.rs +++ b/crates/egui/src/containers/scroll_area.rs @@ -640,8 +640,7 @@ impl Prepared { let min = content_ui.min_rect().min[d]; let clip_rect = content_ui.clip_rect(); let visible_range = min..=min + clip_rect.size()[d]; - let start = *scroll.start(); - let end = *scroll.end(); + let (start, end) = (scroll.min, scroll.max); let clip_start = clip_rect.min[d]; let clip_end = clip_rect.max[d]; let mut spacing = ui.spacing().item_spacing[d]; diff --git a/crates/egui/src/frame_state.rs b/crates/egui/src/frame_state.rs index 0d7106843ee4..287b35c56e51 100644 --- a/crates/egui/src/frame_state.rs +++ b/crates/egui/src/frame_state.rs @@ -1,5 +1,3 @@ -use std::ops::RangeInclusive; - use crate::{id::IdSet, *}; #[derive(Clone, Copy, Debug)] @@ -46,7 +44,7 @@ pub(crate) struct FrameState { pub(crate) scroll_delta: Vec2, // TODO(emilk): move to `InputState` ? /// horizontal, vertical - pub(crate) scroll_target: [Option<(RangeInclusive, Option)>; 2], + pub(crate) scroll_target: [Option<(Rangef, Option)>; 2], #[cfg(feature = "accesskit")] pub(crate) accesskit_state: Option, diff --git a/crates/egui/src/lib.rs b/crates/egui/src/lib.rs index f7eef71a9433..aab9049b6646 100644 --- a/crates/egui/src/lib.rs +++ b/crates/egui/src/lib.rs @@ -335,7 +335,9 @@ pub use epaint::emath; #[cfg(feature = "color-hex")] pub use ecolor::hex_color; pub use ecolor::{Color32, Rgba}; -pub use emath::{lerp, pos2, remap, remap_clamp, vec2, Align, Align2, NumExt, Pos2, Rect, Vec2}; +pub use emath::{ + lerp, pos2, remap, remap_clamp, vec2, Align, Align2, NumExt, Pos2, Rangef, Rect, Vec2, +}; pub use epaint::{ mutex, text::{FontData, FontDefinitions, FontFamily, FontId, FontTweak}, diff --git a/crates/egui/src/painter.rs b/crates/egui/src/painter.rs index 214bbe175504..baf3b9212594 100644 --- a/crates/egui/src/painter.rs +++ b/crates/egui/src/painter.rs @@ -1,8 +1,7 @@ -use std::ops::RangeInclusive; use std::sync::Arc; use crate::{ - emath::{Align2, Pos2, Rect, Vec2}, + emath::{Align2, Pos2, Rangef, Rect, Vec2}, layers::{LayerId, PaintList, ShapeIdx}, Color32, Context, FontId, }; @@ -263,12 +262,12 @@ impl Painter { } /// Paints a horizontal line. - pub fn hline(&self, x: RangeInclusive, y: f32, stroke: impl Into) { + pub fn hline(&self, x: impl Into, y: f32, stroke: impl Into) { self.add(Shape::hline(x, y, stroke)); } /// Paints a vertical line. - pub fn vline(&self, x: f32, y: RangeInclusive, stroke: impl Into) { + pub fn vline(&self, x: f32, y: impl Into, stroke: impl Into) { self.add(Shape::vline(x, y, stroke)); } diff --git a/crates/egui/src/ui.rs b/crates/egui/src/ui.rs index 4e4b4559dd45..83713e487794 100644 --- a/crates/egui/src/ui.rs +++ b/crates/egui/src/ui.rs @@ -517,15 +517,17 @@ impl Ui { } /// `ui.set_width_range(min..=max);` is equivalent to `ui.set_min_width(min); ui.set_max_width(max);`. - pub fn set_width_range(&mut self, width: std::ops::RangeInclusive) { - self.set_min_width(*width.start()); - self.set_max_width(*width.end()); + pub fn set_width_range(&mut self, width: impl Into) { + let width = width.into(); + self.set_min_width(width.min); + self.set_max_width(width.max); } /// `ui.set_height_range(min..=max);` is equivalent to `ui.set_min_height(min); ui.set_max_height(max);`. - pub fn set_height_range(&mut self, height: std::ops::RangeInclusive) { - self.set_min_height(*height.start()); - self.set_max_height(*height.end()); + pub fn set_height_range(&mut self, height: impl Into) { + let height = height.into(); + self.set_min_height(height.min); + self.set_max_height(height.max); } /// Set both the minimum and maximum width. @@ -978,7 +980,7 @@ impl Ui { /// ``` pub fn scroll_to_rect(&self, rect: Rect, align: Option) { for d in 0..2 { - let range = rect.min[d]..=rect.max[d]; + let range = Rangef::new(rect.min[d], rect.max[d]); self.ctx() .frame_state_mut(|state| state.scroll_target[d] = Some((range, align))); } @@ -1008,9 +1010,9 @@ impl Ui { pub fn scroll_to_cursor(&self, align: Option) { let target = self.next_widget_position(); for d in 0..2 { - let target = target[d]; + let target = Rangef::point(target[d]); self.ctx() - .frame_state_mut(|state| state.scroll_target[d] = Some((target..=target, align))); + .frame_state_mut(|state| state.scroll_target[d] = Some((target, align))); } } diff --git a/crates/egui/src/widgets/slider.rs b/crates/egui/src/widgets/slider.rs index 9ebcaf493ebe..f298a7831b00 100644 --- a/crates/egui/src/widgets/slider.rs +++ b/crates/egui/src/widgets/slider.rs @@ -524,12 +524,12 @@ impl<'a> Slider<'a> { } /// For instance, `position` is the mouse position and `position_range` is the physical location of the slider on the screen. - fn value_from_position(&self, position: f32, position_range: RangeInclusive) -> f64 { + fn value_from_position(&self, position: f32, position_range: Rangef) -> f64 { let normalized = remap_clamp(position, position_range, 0.0..=1.0) as f64; value_from_normalized(normalized, self.range(), &self.spec) } - fn position_from_value(&self, value: f64, position_range: RangeInclusive) -> f32 { + fn position_from_value(&self, value: f64, position_range: Rangef) -> f32 { let normalized = normalized_from_value(value, self.range(), &self.spec); lerp(position_range, normalized as f32) } @@ -555,11 +555,11 @@ impl<'a> Slider<'a> { let new_value = if self.smart_aim { let aim_radius = ui.input(|i| i.aim_radius()); emath::smart_aim::best_in_range_f64( - self.value_from_position(position - aim_radius, position_range.clone()), - self.value_from_position(position + aim_radius, position_range.clone()), + self.value_from_position(position - aim_radius, position_range), + self.value_from_position(position + aim_radius, position_range), ) } else { - self.value_from_position(position, position_range.clone()) + self.value_from_position(position, position_range) }; self.set_value(new_value); } @@ -594,18 +594,18 @@ impl<'a> Slider<'a> { if kb_step != 0.0 { let prev_value = self.get_value(); - let prev_position = self.position_from_value(prev_value, position_range.clone()); + let prev_position = self.position_from_value(prev_value, position_range); let new_position = prev_position + kb_step; let new_value = match self.step { Some(step) => prev_value + (kb_step as f64 * step), None if self.smart_aim => { let aim_radius = ui.input(|i| i.aim_radius()); emath::smart_aim::best_in_range_f64( - self.value_from_position(new_position - aim_radius, position_range.clone()), - self.value_from_position(new_position + aim_radius, position_range.clone()), + self.value_from_position(new_position - aim_radius, position_range), + self.value_from_position(new_position + aim_radius, position_range), ) } - _ => self.value_from_position(new_position, position_range.clone()), + _ => self.value_from_position(new_position, position_range), }; self.set_value(new_value); } @@ -686,15 +686,11 @@ impl<'a> Slider<'a> { } } - fn position_range(&self, rect: &Rect) -> RangeInclusive { + fn position_range(&self, rect: &Rect) -> Rangef { let handle_radius = self.handle_radius(rect); match self.orientation { - SliderOrientation::Horizontal => { - (rect.left() + handle_radius)..=(rect.right() - handle_radius) - } - SliderOrientation::Vertical => { - (rect.bottom() - handle_radius)..=(rect.top() + handle_radius) - } + SliderOrientation::Horizontal => rect.x_range().shrink(handle_radius), + SliderOrientation::Vertical => rect.y_range().shrink(handle_radius), } } @@ -726,7 +722,7 @@ impl<'a> Slider<'a> { } } - fn value_ui(&mut self, ui: &mut Ui, position_range: RangeInclusive) -> Response { + fn value_ui(&mut self, ui: &mut Ui, position_range: Rangef) -> Response { // If [`DragValue`] is controlled from the keyboard and `step` is defined, set speed to `step` let change = ui.input(|input| { input.num_presses(Key::ArrowUp) as i32 + input.num_presses(Key::ArrowRight) as i32 @@ -740,7 +736,7 @@ impl<'a> Slider<'a> { step } else { self.drag_value_speed - .unwrap_or_else(|| self.current_gradient(&position_range)) + .unwrap_or_else(|| self.current_gradient(position_range)) }; let mut value = self.get_value(); @@ -767,12 +763,11 @@ impl<'a> Slider<'a> { } /// delta(value) / delta(points) - fn current_gradient(&mut self, position_range: &RangeInclusive) -> f64 { + fn current_gradient(&mut self, position_range: Rangef) -> f64 { // TODO(emilk): handle clamping let value = self.get_value(); - let value_from_pos = - |position: f32| self.value_from_position(position, position_range.clone()); - let pos_from_value = |value: f64| self.position_from_value(value, position_range.clone()); + let value_from_pos = |position: f32| self.value_from_position(position, position_range); + let pos_from_value = |value: f64| self.position_from_value(value, position_range); let left_value = value_from_pos(pos_from_value(value) - 0.5); let right_value = value_from_pos(pos_from_value(value) + 0.5); right_value - left_value diff --git a/crates/egui_extras/src/sizing.rs b/crates/egui_extras/src/sizing.rs index 8a58302d5c87..c6ae9d6daaef 100644 --- a/crates/egui_extras/src/sizing.rs +++ b/crates/egui_extras/src/sizing.rs @@ -1,14 +1,16 @@ +use egui::Rangef; + /// Size hint for table column/strip cell. #[derive(Clone, Debug, Copy)] pub enum Size { /// Absolute size in points, with a given range of allowed sizes to resize within. - Absolute { initial: f32, range: (f32, f32) }, + Absolute { initial: f32, range: Rangef }, /// Relative size relative to all available space. - Relative { fraction: f32, range: (f32, f32) }, + Relative { fraction: f32, range: Rangef }, /// Multiple remainders each get the same space. - Remainder { range: (f32, f32) }, + Remainder { range: Rangef }, } impl Size { @@ -16,7 +18,7 @@ impl Size { pub fn exact(points: f32) -> Self { Self::Absolute { initial: points, - range: (points, points), + range: Rangef::new(points, points), } } @@ -24,7 +26,7 @@ impl Size { pub fn initial(points: f32) -> Self { Self::Absolute { initial: points, - range: (0.0, f32::INFINITY), + range: Rangef::new(0.0, f32::INFINITY), } } @@ -33,14 +35,14 @@ impl Size { egui::egui_assert!(0.0 <= fraction && fraction <= 1.0); Self::Relative { fraction, - range: (0.0, f32::INFINITY), + range: Rangef::new(0.0, f32::INFINITY), } } /// Multiple remainders each get the same space. pub fn remainder() -> Self { Self::Remainder { - range: (0.0, f32::INFINITY), + range: Rangef::new(0.0, f32::INFINITY), } } @@ -50,7 +52,7 @@ impl Size { Self::Absolute { range, .. } | Self::Relative { range, .. } | Self::Remainder { range, .. } => { - range.0 = minimum; + range.min = minimum; } } self @@ -62,14 +64,14 @@ impl Size { Self::Absolute { range, .. } | Self::Relative { range, .. } | Self::Remainder { range, .. } => { - range.1 = maximum; + range.max = maximum; } } self } /// Allowed range of movement (in points), if in a resizable [`Table`](crate::table::Table). - pub fn range(self) -> (f32, f32) { + pub fn range(self) -> Rangef { match self { Self::Absolute { range, .. } | Self::Relative { range, .. } @@ -99,12 +101,9 @@ impl Sizing { .iter() .map(|&size| match size { Size::Absolute { initial, .. } => initial, - Size::Relative { - fraction, - range: (min, max), - } => { + Size::Relative { fraction, range } => { assert!(0.0 <= fraction && fraction <= 1.0); - (length * fraction).clamp(min, max) + range.clamp(length * fraction) } Size::Remainder { .. } => { remainders += 1; @@ -120,9 +119,9 @@ impl Sizing { let mut remainder_length = length - sum_non_remainder; let avg_remainder_length = 0.0f32.max(remainder_length / remainders as f32).floor(); self.sizes.iter().for_each(|&size| { - if let Size::Remainder { range: (min, _max) } = size { - if avg_remainder_length < min { - remainder_length -= min; + if let Size::Remainder { range } = size { + if avg_remainder_length < range.min { + remainder_length -= range.min; remainders -= 1; } } @@ -138,11 +137,8 @@ impl Sizing { .iter() .map(|&size| match size { Size::Absolute { initial, .. } => initial, - Size::Relative { - fraction, - range: (min, max), - } => (length * fraction).clamp(min, max), - Size::Remainder { range: (min, max) } => avg_remainder_length.clamp(min, max), + Size::Relative { fraction, range } => range.clamp(length * fraction), + Size::Remainder { range } => range.clamp(avg_remainder_length), }) .collect() } diff --git a/crates/egui_extras/src/table.rs b/crates/egui_extras/src/table.rs index 2283ab4d7fd6..9966a8b99283 100644 --- a/crates/egui_extras/src/table.rs +++ b/crates/egui_extras/src/table.rs @@ -3,7 +3,7 @@ //! | fixed size | all available space/minimum | 30% of available width | fixed size | //! Takes all available height, so if you want something below the table, put it in a strip. -use egui::{Align, NumExt as _, Rect, Response, ScrollArea, Ui, Vec2}; +use egui::{Align, NumExt as _, Rangef, Rect, Response, ScrollArea, Ui, Vec2}; use crate::{ layout::{CellDirection, CellSize}, @@ -28,7 +28,7 @@ enum InitialColumnSize { #[derive(Clone, Copy, Debug, PartialEq)] pub struct Column { initial_width: InitialColumnSize, - width_range: (f32, f32), + width_range: Rangef, /// Clip contents if too narrow? clip: bool, @@ -78,7 +78,7 @@ impl Column { fn new(initial_width: InitialColumnSize) -> Self { Self { initial_width, - width_range: (0.0, f32::INFINITY), + width_range: Rangef::new(0.0, f32::INFINITY), resizable: None, clip: false, } @@ -110,7 +110,7 @@ impl Column { /// /// Default: 0.0 pub fn at_least(mut self, minimum: f32) -> Self { - self.width_range.0 = minimum; + self.width_range.min = minimum; self } @@ -118,13 +118,13 @@ impl Column { /// /// Default: [`f32::INFINITY`] pub fn at_most(mut self, maximum: f32) -> Self { - self.width_range.1 = maximum; + self.width_range.max = maximum; self } /// Allowed range of movement (in points), if in a resizable [`Table`](crate::table::Table). - pub fn range(mut self, range: std::ops::RangeInclusive) -> Self { - self.width_range = (*range.start(), *range.end()); + pub fn range(mut self, range: impl Into) -> Self { + self.width_range = range.into(); self } @@ -146,8 +146,8 @@ fn to_sizing(columns: &[Column]) -> crate::sizing::Sizing { InitialColumnSize::Automatic(suggested_width) => Size::initial(suggested_width), InitialColumnSize::Remainder => Size::remainder(), } - .at_least(column.width_range.0) - .at_most(column.width_range.1); + .at_least(column.width_range.min) + .at_most(column.width_range.max); sizing.add(size); } sizing @@ -598,13 +598,13 @@ impl<'a> Table<'a> { if scroll_to_row.is_some() && scroll_to_y_range.is_none() { // TableBody::row didn't find the right row, so scroll to the bottom: - scroll_to_y_range = Some((f32::INFINITY, f32::INFINITY)); + scroll_to_y_range = Some(Rangef::new(f32::INFINITY, f32::INFINITY)); } }); - if let Some((min_y, max_y)) = scroll_to_y_range { + if let Some(y_range) = scroll_to_y_range { let x = 0.0; // ignored, we only have vertical scrolling - let rect = egui::Rect::from_min_max(egui::pos2(x, min_y), egui::pos2(x, max_y)); + let rect = egui::Rect::from_x_y_ranges(x..=x, y_range); let align = scroll_to_row.and_then(|(_, a)| a); ui.scroll_to_rect(rect, align); } @@ -617,14 +617,14 @@ impl<'a> Table<'a> { for (i, column_width) in state.column_widths.iter_mut().enumerate() { let column = &columns[i]; let column_is_resizable = column.resizable.unwrap_or(resizable); - let (min_width, max_width) = column.width_range; + let width_range = column.width_range; if !column.clip { // Unless we clip we don't want to shrink below the // size that was actually used: *column_width = column_width.at_least(max_used_widths[i]); } - *column_width = column_width.clamp(min_width, max_width); + *column_width = width_range.clamp(*column_width); let is_last_column = i + 1 == columns.len(); @@ -633,7 +633,7 @@ impl<'a> Table<'a> { let eps = 0.1; // just to avoid some rounding errors. *column_width = available_width - eps; *column_width = column_width.at_least(max_used_widths[i]); - *column_width = column_width.clamp(min_width, max_width); + *column_width = width_range.clamp(*column_width); break; } @@ -641,7 +641,7 @@ impl<'a> Table<'a> { if column.is_auto() && (first_frame_auto_size_columns || !column_is_resizable) { *column_width = max_used_widths[i]; - *column_width = column_width.clamp(min_width, max_width); + *column_width = width_range.clamp(*column_width); } else if column_is_resizable { let column_resize_id = ui.id().with("resize_column").with(i); @@ -656,7 +656,7 @@ impl<'a> Table<'a> { if resize_response.double_clicked() { // Resize to the minimum of what is needed. - *column_width = max_used_widths[i].clamp(min_width, max_width); + *column_width = width_range.clamp(max_used_widths[i]); } else if resize_response.dragged() { if let Some(pointer) = ui.ctx().pointer_latest_pos() { let mut new_width = *column_width + pointer.x - x; @@ -671,7 +671,7 @@ impl<'a> Table<'a> { new_width = new_width.at_least(max_used_widths[i] - max_shrinkage_per_frame); } - new_width = new_width.clamp(min_width, max_width); + new_width = width_range.clamp(new_width); let x = x - *column_width + new_width; (p0.x, p1.x) = (x, x); @@ -731,7 +731,7 @@ pub struct TableBody<'a> { /// If we find the correct row to scroll to, /// this is set to the y-range of the row. - scroll_to_y_range: &'a mut Option<(f32, f32)>, + scroll_to_y_range: &'a mut Option, } impl<'a> TableBody<'a> { @@ -779,7 +779,7 @@ impl<'a> TableBody<'a> { let bottom_y = self.layout.cursor.y; if Some(self.row_nr) == self.scroll_to_row { - *self.scroll_to_y_range = Some((top_y, bottom_y)); + *self.scroll_to_y_range = Some(Rangef::new(top_y, bottom_y)); } self.row_nr += 1; @@ -819,7 +819,7 @@ impl<'a> TableBody<'a> { if let Some(scroll_to_row) = self.scroll_to_row { let scroll_to_row = scroll_to_row.at_most(total_rows.saturating_sub(1)) as f32; - *self.scroll_to_y_range = Some(( + *self.scroll_to_y_range = Some(Rangef::new( self.layout.cursor.y + scroll_to_row * row_height_with_spacing, self.layout.cursor.y + (scroll_to_row + 1.0) * row_height_with_spacing, )); @@ -909,7 +909,7 @@ impl<'a> TableBody<'a> { cursor_y += (row_height + spacing.y) as f64; if Some(row_index) == self.scroll_to_row { - *self.scroll_to_y_range = Some(( + *self.scroll_to_y_range = Some(Rangef::new( (scroll_to_y_range_offset + old_cursor_y) as f32, (scroll_to_y_range_offset + cursor_y) as f32, )); @@ -953,7 +953,7 @@ impl<'a> TableBody<'a> { cursor_y += (row_height + spacing.y) as f64; if Some(row_index) == self.scroll_to_row { - *self.scroll_to_y_range = Some(( + *self.scroll_to_y_range = Some(Rangef::new( (scroll_to_y_range_offset + top_y) as f32, (scroll_to_y_range_offset + cursor_y) as f32, )); @@ -972,7 +972,7 @@ impl<'a> TableBody<'a> { let top_y = cursor_y; cursor_y += (row_height + spacing.y) as f64; if Some(row_index) == self.scroll_to_row { - *self.scroll_to_y_range = Some(( + *self.scroll_to_y_range = Some(Rangef::new( (scroll_to_y_range_offset + top_y) as f32, (scroll_to_y_range_offset + cursor_y) as f32, )); @@ -981,10 +981,8 @@ impl<'a> TableBody<'a> { if self.scroll_to_row.is_some() && self.scroll_to_y_range.is_none() { // Catch desire to scroll past the end: - *self.scroll_to_y_range = Some(( - (scroll_to_y_range_offset + cursor_y) as f32, - (scroll_to_y_range_offset + cursor_y) as f32, - )); + *self.scroll_to_y_range = + Some(Rangef::point((scroll_to_y_range_offset + cursor_y) as f32)); } if height_below_visible > 0.0 { diff --git a/crates/emath/src/align.rs b/crates/emath/src/align.rs index 9d7ccf81e4d8..13075190cd7f 100644 --- a/crates/emath/src/align.rs +++ b/crates/emath/src/align.rs @@ -110,29 +110,24 @@ impl Align { /// assert_eq!(Max .align_size_within_range(INFINITY, NEG_INFINITY..=20.0), NEG_INFINITY..=20.0); /// ``` #[inline] - pub fn align_size_within_range( - self, - size: f32, - range: RangeInclusive, - ) -> RangeInclusive { - let min = *range.start(); - let max = *range.end(); + pub fn align_size_within_range(self, size: f32, range: Rangef) -> Rangef { + let Rangef { min, max } = range; if max - min == f32::INFINITY && size == f32::INFINITY { return range; } match self { - Self::Min => min..=min + size, + Self::Min => Rangef::new(min, min + size), Self::Center => { if size == f32::INFINITY { - f32::NEG_INFINITY..=f32::INFINITY + Rangef::new(f32::NEG_INFINITY, f32::INFINITY) } else { let left = (min + max) / 2.0 - size / 2.0; - left..=left + size + Rangef::new(left, left + size) } } - Self::Max => max - size..=max, + Self::Max => Rangef::new(max - size, max), } } } diff --git a/crates/emath/src/lib.rs b/crates/emath/src/lib.rs index 89b2db209789..25b3b9eac8d0 100644 --- a/crates/emath/src/lib.rs +++ b/crates/emath/src/lib.rs @@ -99,11 +99,12 @@ impl Real for f64 {} /// assert_eq!(lerp(1.0..=5.0, 2.0), 9.0); /// ``` #[inline(always)] -pub fn lerp(range: RangeInclusive, t: T) -> R +pub fn lerp(range: impl Into>, t: T) -> R where T: Real + Mul, R: Copy + Add, { + let range = range.into(); (T::one() - t) * *range.start() + t * *range.end() } @@ -138,20 +139,28 @@ where /// Linearly remap a value from one range to another, /// so that when `x == from.start()` returns `to.start()` /// and when `x == from.end()` returns `to.end()`. -pub fn remap(x: T, from: RangeInclusive, to: RangeInclusive) -> T +pub fn remap(x: T, from: impl Into>, to: impl Into>) -> T where T: Real, { + let from = from.into(); + let to = to.into(); crate::emath_assert!(from.start() != from.end()); let t = (x - *from.start()) / (*from.end() - *from.start()); lerp(to, t) } /// Like [`remap`], but also clamps the value so that the returned value is always in the `to` range. -pub fn remap_clamp(x: T, from: RangeInclusive, to: RangeInclusive) -> T +pub fn remap_clamp( + x: T, + from: impl Into>, + to: impl Into>, +) -> T where T: Real, { + let from = from.into(); + let to = to.into(); if from.end() < from.start() { return remap_clamp(x, *from.end()..=*from.start(), *to.end()..=*to.start()); } diff --git a/crates/emath/src/range.rs b/crates/emath/src/range.rs index ae3e3f0e59f3..559738851ebc 100644 --- a/crates/emath/src/range.rs +++ b/crates/emath/src/range.rs @@ -36,14 +36,60 @@ impl Rangef { } #[inline] - pub fn span(&self) -> f32 { + pub fn point(min_and_max: f32) -> Self { + Self { + min: min_and_max, + max: min_and_max, + } + } + + /// The length of the range, i.e. `max - min`. + #[inline] + pub fn span(self) -> f32 { self.max - self.min } #[inline] - pub fn contains(&self, x: f32) -> bool { + #[must_use] + pub fn contains(self, x: f32) -> bool { self.min <= x && x <= self.max } + + /// Equivalent to `x.clamp(min, max)` + #[inline] + #[must_use] + pub fn clamp(self, x: f32) -> f32 { + x.clamp(self.min, self.max) + } + + /// Flip `min` and `max` if needed, so that `min <= max` after. + #[inline] + pub fn as_positive(self) -> Self { + Rangef { + min: self.min.min(self.max), + max: self.min.max(self.max), + } + } + + /// Shrink by this much on each side, keeping the center + #[inline] + #[must_use] + pub fn shrink(self, amnt: f32) -> Self { + Self { + min: self.min + amnt, + max: self.max - amnt, + } + } + + /// Expand by this much on each side, keeping the center + #[inline] + #[must_use] + pub fn expand(self, amnt: f32) -> Self { + Self { + min: self.min - amnt, + max: self.max + amnt, + } + } } impl From for RangeInclusive { diff --git a/crates/emath/src/rect.rs b/crates/emath/src/rect.rs index b8c6085a224b..769e75e3467d 100644 --- a/crates/emath/src/rect.rs +++ b/crates/emath/src/rect.rs @@ -1,5 +1,4 @@ use std::f32::INFINITY; -use std::ops::RangeInclusive; use crate::*; @@ -82,15 +81,12 @@ impl Rect { } #[inline(always)] - pub fn from_x_y_ranges( - x_range: impl Into>, - y_range: impl Into>, - ) -> Self { + pub fn from_x_y_ranges(x_range: impl Into, y_range: impl Into) -> Self { let x_range = x_range.into(); let y_range = y_range.into(); Rect { - min: pos2(*x_range.start(), *y_range.start()), - max: pos2(*x_range.end(), *y_range.end()), + min: pos2(x_range.min, y_range.min), + max: pos2(x_range.max, y_range.max), } } @@ -389,18 +385,18 @@ impl Rect { } #[inline(always)] - pub fn x_range(&self) -> RangeInclusive { - self.min.x..=self.max.x + pub fn x_range(&self) -> Rangef { + Rangef::new(self.min.x, self.max.x) } #[inline(always)] - pub fn y_range(&self) -> RangeInclusive { - self.min.y..=self.max.y + pub fn y_range(&self) -> Rangef { + Rangef::new(self.min.y, self.max.y) } #[inline(always)] - pub fn bottom_up_range(&self) -> RangeInclusive { - self.max.y..=self.min.y + pub fn bottom_up_range(&self) -> Rangef { + Rangef::new(self.max.y, self.min.y) } /// `width < 0 || height < 0` diff --git a/crates/epaint/src/shape.rs b/crates/epaint/src/shape.rs index 04621566157e..401f51d8ae29 100644 --- a/crates/epaint/src/shape.rs +++ b/crates/epaint/src/shape.rs @@ -1,6 +1,5 @@ //! The different shapes that can be painted. -use std::ops::RangeInclusive; use std::{any::Any, sync::Arc}; use crate::{ @@ -94,17 +93,19 @@ impl Shape { } /// A horizontal line. - pub fn hline(x: RangeInclusive, y: f32, stroke: impl Into) -> Self { + pub fn hline(x: impl Into, y: f32, stroke: impl Into) -> Self { + let x = x.into(); Shape::LineSegment { - points: [pos2(*x.start(), y), pos2(*x.end(), y)], + points: [pos2(x.min, y), pos2(x.max, y)], stroke: stroke.into(), } } /// A vertical line. - pub fn vline(x: f32, y: RangeInclusive, stroke: impl Into) -> Self { + pub fn vline(x: f32, y: impl Into, stroke: impl Into) -> Self { + let y = y.into(); Shape::LineSegment { - points: [pos2(x, *y.start()), pos2(x, *y.end())], + points: [pos2(x, y.min), pos2(x, y.max)], stroke: stroke.into(), } } From 9571383cc1bd4043de1918887e586714ed9fb40b Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Thu, 10 Aug 2023 10:56:40 +0200 Subject: [PATCH 2/2] Fix doc-test --- crates/emath/src/align.rs | 3 ++- crates/emath/src/range.rs | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/crates/emath/src/align.rs b/crates/emath/src/align.rs index 13075190cd7f..ef07cc1d328f 100644 --- a/crates/emath/src/align.rs +++ b/crates/emath/src/align.rs @@ -110,7 +110,8 @@ impl Align { /// assert_eq!(Max .align_size_within_range(INFINITY, NEG_INFINITY..=20.0), NEG_INFINITY..=20.0); /// ``` #[inline] - pub fn align_size_within_range(self, size: f32, range: Rangef) -> Rangef { + pub fn align_size_within_range(self, size: f32, range: impl Into) -> Rangef { + let range = range.into(); let Rangef { min, max } = range; if max - min == f32::INFINITY && size == f32::INFINITY { diff --git a/crates/emath/src/range.rs b/crates/emath/src/range.rs index 559738851ebc..11459763df84 100644 --- a/crates/emath/src/range.rs +++ b/crates/emath/src/range.rs @@ -154,3 +154,17 @@ impl From> for Rangef { Self::new(f32::NEG_INFINITY, range.end) } } + +impl PartialEq> for Rangef { + #[inline] + fn eq(&self, other: &RangeInclusive) -> bool { + self.min == *other.start() && self.max == *other.end() + } +} + +impl PartialEq for RangeInclusive { + #[inline] + fn eq(&self, other: &Rangef) -> bool { + *self.start() == other.min && *self.end() == other.max + } +}