diff --git a/crates/egui/src/widgets/drag_value.rs b/crates/egui/src/widgets/drag_value.rs index 16739a33..08df4d2a 100644 --- a/crates/egui/src/widgets/drag_value.rs +++ b/crates/egui/src/widgets/drag_value.rs @@ -369,15 +369,16 @@ impl<'a> Widget for DragValue<'a> { } = self; let shift = ui.input().modifiers.shift_only(); - let is_slow_speed = shift && ui.memory().is_being_dragged(ui.next_auto_id()); + // The widget has the same ID whether it's in edit or button mode. + let id = ui.next_auto_id(); + let is_slow_speed = shift && ui.memory().is_being_dragged(id); - let kb_edit_id = ui.next_auto_id(); // The following call ensures that when a `DragValue` receives focus, // it is immediately rendered in edit mode, rather than being rendered // in button mode for just one frame. This is important for // screen readers. - ui.memory().interested_in_focus(kb_edit_id); - let is_kb_editing = ui.memory().has_focus(kb_edit_id); + ui.memory().interested_in_focus(id); + let is_kb_editing = ui.memory().has_focus(id); let old_value = get(&mut get_set_value); let mut value = old_value; @@ -388,24 +389,37 @@ impl<'a> Widget for DragValue<'a> { let max_decimals = max_decimals.unwrap_or(auto_decimals + 2); let auto_decimals = auto_decimals.clamp(min_decimals, max_decimals); - if is_kb_editing { + let change = { + let mut change = 0.0; let mut input = ui.input_mut(); - // This deliberately doesn't listen for left and right arrow keys, - // because when editing, these are used to move the caret. - // This behavior is consistent with other editable spinner/stepper - // implementations, such as Chromium's (for HTML5 number input). - // It is also normal for such controls to go directly into edit mode - // when they receive keyboard focus, and some screen readers - // assume this behavior, so having a separate mode for incrementing - // and decrementing, that supports all arrow keys, would be - // problematic. - let change = input.count_and_consume_key(Modifiers::NONE, Key::ArrowUp) as f64 - - input.count_and_consume_key(Modifiers::NONE, Key::ArrowDown) as f64; - if change != 0.0 { - value += speed * change; - value = emath::round_to_decimals(value, auto_decimals); + if is_kb_editing { + // This deliberately doesn't listen for left and right arrow keys, + // because when editing, these are used to move the caret. + // This behavior is consistent with other editable spinner/stepper + // implementations, such as Chromium's (for HTML5 number input). + // It is also normal for such controls to go directly into edit mode + // when they receive keyboard focus, and some screen readers + // assume this behavior, so having a separate mode for incrementing + // and decrementing, that supports all arrow keys, would be + // problematic. + change += input.count_and_consume_key(Modifiers::NONE, Key::ArrowUp) as f64 + - input.count_and_consume_key(Modifiers::NONE, Key::ArrowDown) as f64; } + + #[cfg(feature = "accesskit")] + { + use accesskit::Action; + change += input.num_accesskit_action_requests(id, Action::Increment) as f64 + - input.num_accesskit_action_requests(id, Action::Decrement) as f64; + } + + change + }; + + if change != 0.0 { + value += speed * change; + value = emath::round_to_decimals(value, auto_decimals); } value = clamp_to_range(value, clamp_range.clone()); @@ -436,7 +450,7 @@ impl<'a> Widget for DragValue<'a> { .unwrap_or(value_text); let response = ui.add( TextEdit::singleline(&mut value_text) - .id(kb_edit_id) + .id(id) .desired_width(button_width) .font(TextStyle::Monospace), ); @@ -472,7 +486,7 @@ impl<'a> Widget for DragValue<'a> { } if response.clicked() { - ui.memory().request_focus(kb_edit_id); + ui.memory().request_focus(id); } else if response.dragged() { ui.output().cursor_icon = CursorIcon::ResizeHorizontal; @@ -518,6 +532,7 @@ impl<'a> Widget for DragValue<'a> { #[cfg(feature = "accesskit")] { + use accesskit::Action; let mut node = ui.ctx().accesskit_node(response.id, None); // If either end of the range is unbounded, it's better // to leave the corresponding AccessKit field set to None, @@ -528,6 +543,12 @@ impl<'a> Widget for DragValue<'a> { if clamp_range.end().is_finite() { node.max_numeric_value = Some(*clamp_range.end()); } + if value < *clamp_range.end() { + node.actions |= Action::Increment; + } + if value > *clamp_range.start() { + node.actions |= Action::Decrement; + } // The name field is set to the current value by the button, // but we don't want it set that way on this widget type. node.name = None;