From f0287fb86dcaca6d0a9e0c00b15119bc91dd8e6a Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Thu, 27 Aug 2020 21:38:07 +0200 Subject: [PATCH] [slider] refactor to break up complex ui code --- egui/src/widgets/slider.rs | 189 ++++++++++++++++++++----------------- 1 file changed, 100 insertions(+), 89 deletions(-) diff --git a/egui/src/widgets/slider.rs b/egui/src/widgets/slider.rs index 48eacd12..3f72217f 100644 --- a/egui/src/widgets/slider.rs +++ b/egui/src/widgets/slider.rs @@ -14,7 +14,6 @@ pub struct Slider<'a> { text: Option, precision: usize, text_color: Option, - text_on_top: Option, id: Option, } @@ -28,7 +27,6 @@ impl<'a> Slider<'a> { range, text: None, precision: 3, - text_on_top: None, text_color: None, id: None, } @@ -105,103 +103,116 @@ impl<'a> Slider<'a> { } } +fn handle_radius(rect: &Rect) -> f32 { + rect.height() / 2.5 +} + +fn x_range(rect: &Rect) -> RangeInclusive { + let handle_radius = handle_radius(rect); + (rect.left() + handle_radius)..=(rect.right() - handle_radius) +} + +impl<'a> Slider<'a> { + /// Just the slider, no text + fn allocate_slide_space(&self, ui: &mut Ui, height: f32) -> InteractInfo { + let id = self.id.unwrap_or_else(|| ui.make_position_id()); + let desired_size = vec2(ui.available().width(), height); + let rect = ui.allocate_space(desired_size); + ui.interact(rect, id, Sense::click_and_drag()) + } + + /// Just the slider, no text + fn slider_ui(&mut self, ui: &mut Ui, interact: InteractInfo) -> InteractInfo { + let rect = &interact.rect; + let x_range = x_range(rect); + + let range = self.range.clone(); + debug_assert!(range.start() <= range.end()); + + if let Some(mouse_pos) = ui.input().mouse.pos { + if interact.active { + let aim_radius = ui.input().aim_radius(); + let new_value = crate::math::smart_aim::best_in_range_f32( + self.value_from_point(mouse_pos.x - aim_radius, x_range.clone()), + self.value_from_point(mouse_pos.x + aim_radius, x_range.clone()), + ); + self.set_value_f32(new_value); + } + } + + // Paint it: + { + let value = self.get_value_f32(); + + let rail_radius = ui.painter().round_to_pixel((rect.height() / 8.0).max(2.0)); + let rail_rect = Rect::from_min_max( + pos2(rect.left(), rect.center().y - rail_radius), + pos2(rect.right(), rect.center().y + rail_radius), + ); + let marker_center_x = remap_clamp(value, range, x_range); + + ui.painter().add(PaintCmd::Rect { + rect: rail_rect, + corner_radius: rail_radius, + fill: Some(ui.style().background_fill), + outline: Some(LineStyle::new(1.0, color::gray(200, 255))), // TODO + }); + + ui.painter().add(PaintCmd::Circle { + center: pos2(marker_center_x, rail_rect.center().y), + radius: handle_radius(rect), + fill: Some(ui.style().interact(&interact).fill), + outline: Some(LineStyle::new( + ui.style().interact(&interact).stroke_width, + ui.style().interact(&interact).stroke_color, + )), + }); + } + + interact + } + + /// Just the text label + fn text_ui(&mut self, ui: &mut Ui) { + if let Some(text) = &self.text { + let text_color = self.text_color.unwrap_or_else(|| ui.style().text_color); + let value = (self.get_set_value)(None); + let full_text = format!("{}: {:.*}", text, self.precision, value); + ui.add( + Label::new(full_text) + .multiline(false) + .text_color(text_color), + ); + } + } +} + impl<'a> Widget for Slider<'a> { fn ui(mut self, ui: &mut Ui) -> InteractInfo { let text_style = TextStyle::Button; let font = &ui.fonts()[text_style]; + let height = font.line_spacing().max(ui.style().clickable_diameter); if let Some(text) = &self.text { - if self.id.is_none() { - self.id = Some(ui.make_unique_child_id(text)); - } + self.id = self.id.or_else(|| Some(ui.make_unique_child_id(text))); - let text_on_top = self.text_on_top.unwrap_or_default(); - let text_color = self.text_color.unwrap_or_else(|| ui.style().text_color); - let value = (self.get_set_value)(None); - let full_text = format!("{}: {:.*}", text, self.precision, value); + ui.columns(2, |columns| { + let slider_ui = &mut columns[0]; + let interact = self.allocate_slide_space(slider_ui, height); + let slider_interact = self.slider_ui(slider_ui, interact); - let slider_sans_text = Slider { text: None, ..self }; + // Place the text in line with the slider on the left: + let text_ui = &mut columns[1]; + text_ui.set_desired_height(slider_interact.rect.height()); + text_ui.inner_layout(Layout::horizontal(Align::Center), |ui| { + self.text_ui(ui); + }); - if text_on_top { - let galley = font.layout_single_line(full_text); - let pos = ui.allocate_space(galley.size).min; - ui.painter().galley(pos, galley, text_style, text_color); - slider_sans_text.ui(ui) - } else { - ui.columns(2, |columns| { - // Slider on the left: - let slider_response = columns[0].add(slider_sans_text); - - // Place the text in line with the slider on the left: - columns[1].set_desired_height(slider_response.rect.height()); - columns[1].inner_layout(Layout::horizontal(Align::Center), |ui| { - ui.add(Label::new(full_text).multiline(false)); - }); - - slider_response.into() - }) - } + slider_interact + }) } else { - let height = font.line_spacing().max(ui.style().clickable_diameter); - let handle_radius = height / 2.5; - - let id = self.id.unwrap_or_else(|| ui.make_position_id()); - - let size = Vec2 { - x: ui.available().width(), - y: height, - }; - let rect = ui.allocate_space(size); - let interact = ui.interact(rect, id, Sense::click_and_drag()); - - let left = interact.rect.left() + handle_radius; - let right = interact.rect.right() - handle_radius; - - let range = self.range.clone(); - debug_assert!(range.start() <= range.end()); - - if let Some(mouse_pos) = ui.input().mouse.pos { - if interact.active { - let aim_radius = ui.input().aim_radius(); - let new_value = crate::math::smart_aim::best_in_range_f32( - self.value_from_point(mouse_pos.x - aim_radius, left..=right), - self.value_from_point(mouse_pos.x + aim_radius, left..=right), - ); - self.set_value_f32(new_value); - } - } - - // Paint it: - { - let value = self.get_value_f32(); - - let rect = interact.rect; - let rail_radius = ui.painter().round_to_pixel((height / 8.0).max(2.0)); - let rail_rect = Rect::from_min_max( - pos2(interact.rect.left(), rect.center().y - rail_radius), - pos2(interact.rect.right(), rect.center().y + rail_radius), - ); - let marker_center_x = remap_clamp(value, range, left..=right); - - ui.painter().add(PaintCmd::Rect { - rect: rail_rect, - corner_radius: rail_radius, - fill: Some(ui.style().background_fill), - outline: Some(LineStyle::new(1.0, color::gray(200, 255))), // TODO - }); - - ui.painter().add(PaintCmd::Circle { - center: pos2(marker_center_x, rail_rect.center().y), - radius: handle_radius, - fill: Some(ui.style().interact(&interact).fill), - outline: Some(LineStyle::new( - ui.style().interact(&interact).stroke_width, - ui.style().interact(&interact).stroke_color, - )), - }); - } - - interact + let interact = self.allocate_slide_space(ui, height); + self.slider_ui(ui, interact) } } }