Add ui.drag_angle helper

This commit is contained in:
Emil Ernerfeldt 2020-08-29 15:09:09 +02:00
parent d4c0197752
commit 5df9bfd514
4 changed files with 57 additions and 8 deletions

View file

@ -416,6 +416,7 @@ struct Widgets {
count: usize, count: usize,
radio: usize, radio: usize,
slider_value: f32, slider_value: f32,
angle: f32,
single_line_text_input: String, single_line_text_input: String,
multiline_text_input: String, multiline_text_input: String,
} }
@ -427,6 +428,7 @@ impl Default for Widgets {
radio: 0, radio: 0,
count: 0, count: 0,
slider_value: 3.4, slider_value: 3.4,
angle: TAU / 8.0,
single_line_text_input: "Hello World!".to_owned(), single_line_text_input: "Hello World!".to_owned(),
multiline_text_input: "Text can both be so wide that it needs a line break, but you can also add manual line break by pressing enter, creating new paragraphs.\nThis is the start of the next paragraph.\n\nClick me to edit me!".to_owned(), multiline_text_input: "Text can both be so wide that it needs a line break, but you can also add manual line break by pressing enter, creating new paragraphs.\nThis is the start of the next paragraph.\n\nClick me to edit me!".to_owned(),
} }
@ -480,6 +482,15 @@ impl Widgets {
} }
} }
ui.separator(); ui.separator();
{
ui.label("An angle stored as radians, but edited in degrees:");
ui.horizontal_centered(|ui| {
ui.style_mut().item_spacing.x = 0.0;
ui.drag_angle(&mut self.angle);
ui.label(format!(" = {} radians", self.angle));
});
}
ui.separator();
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.add(label!("Single line text input:")); ui.add(label!("Single line text input:"));

View file

@ -211,6 +211,7 @@ impl Font {
font.add_char(c); font.add_char(c);
} }
font.add_char(REPLACEMENT_CHAR); font.add_char(REPLACEMENT_CHAR);
font.add_char('°');
font font
} }

View file

@ -506,6 +506,22 @@ impl Ui {
pub fn separator(&mut self) -> GuiResponse { pub fn separator(&mut self) -> GuiResponse {
self.add(Separator::new()) self.add(Separator::new())
} }
/// Modify an angle. The given angle should be in radians, but is shown to the user in degrees.
/// The angle is NOT wrapped, so the user may select, for instance 720° = 2𝞃 = 4π
pub fn drag_angle(&mut self, radians: &mut f32) -> GuiResponse {
#![allow(clippy::float_cmp)]
let mut degrees = radians.to_degrees();
let response = self.add(DragValue::f32(&mut degrees).speed(1.0).suffix("°"));
// only touch `*radians` if we actually changed the degree value
if degrees != radians.to_degrees() {
*radians = degrees.to_radians();
}
response
}
} }
/// # Adding Containers / Sub-uis: /// # Adding Containers / Sub-uis:

View file

@ -552,11 +552,18 @@ impl Widget for Separator {
pub struct DragValue<'a> { pub struct DragValue<'a> {
value: &'a mut f32, value: &'a mut f32,
speed: f32, speed: f32,
prefix: String,
suffix: String,
} }
impl<'a> DragValue<'a> { impl<'a> DragValue<'a> {
pub fn f32(value: &'a mut f32) -> Self { pub fn f32(value: &'a mut f32) -> Self {
DragValue { value, speed: 1.0 } DragValue {
value,
speed: 1.0,
prefix: Default::default(),
suffix: Default::default(),
}
} }
/// How much the value changes when dragged one point (logical pixel). /// How much the value changes when dragged one point (logical pixel).
@ -564,16 +571,30 @@ impl<'a> DragValue<'a> {
self.speed = speed; self.speed = speed;
self self
} }
/// Show a prefix before the number, e.g. "x: "
pub fn prefix(mut self, prefix: impl ToString) -> Self {
self.prefix = prefix.to_string();
self
}
/// Add a suffix to the number, this can be e.g. a unit ("°" or " m")
pub fn suffix(mut self, suffix: impl ToString) -> Self {
self.suffix = suffix.to_string();
self
}
} }
impl<'a> Widget for DragValue<'a> { impl<'a> Widget for DragValue<'a> {
fn ui(self, ui: &mut Ui) -> InteractInfo { fn ui(self, ui: &mut Ui) -> InteractInfo {
let Self { value, speed } = self; let Self {
let speed_in_physical_pixels = speed / ui.input().pixels_per_point; value,
let precision = (1.0 / speed_in_physical_pixels.abs()) speed,
.log10() prefix,
.ceil() suffix,
.max(0.0) as usize; } = self;
let aim_rad = ui.input().physical_pixel_size(); // ui.input().aim_radius(); // TODO
let precision = (aim_rad / speed.abs()).log10().ceil().max(0.0) as usize;
let value_text = format_with_minimum_precision(*value, precision); let value_text = format_with_minimum_precision(*value, precision);
let kb_edit_id = ui.make_position_id().with("edit"); let kb_edit_id = ui.make_position_id().with("edit");
@ -602,7 +623,7 @@ impl<'a> Widget for DragValue<'a> {
} }
response.into() response.into()
} else { } else {
let button = Button::new(value_text) let button = Button::new(format!("{}{}{}", prefix, value_text, suffix))
.sense(Sense::click_and_drag()) .sense(Sense::click_and_drag())
.text_style(TextStyle::Monospace); .text_style(TextStyle::Monospace);
let response = ui.add(button); let response = ui.add(button);