mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-13 20:08:42 +00:00
Merge pull request #6897 from dotCooCoo/fix/number-wheel-smoothing-tail
Number fields: the rest of a wheel notch a field took no longer scrolls the panel
This commit is contained in:
@@ -1145,6 +1145,234 @@ fn a_frame_with_turns_on_and_off_a_number_field_scrolls_the_panel() {
|
||||
assert_eq!(value.get(), 10.0);
|
||||
}
|
||||
|
||||
/// The rest of a notch that a number field took, which egui spreads over the next frames, scrolls
|
||||
/// no panel after the pointer leaves the field: neither the field's own panel nor a panel drawn
|
||||
/// before it. A notch turned after that scrolls the panel under the pointer by one whole notch.
|
||||
#[test]
|
||||
fn the_rest_of_a_notch_a_number_field_took_scrolls_no_panel_after_the_pointer_leaves() {
|
||||
for to_other_panel in [false, true] {
|
||||
let mut f = Field::new();
|
||||
let (left, right, value, field) = (Cell::new(0.0), Cell::new(0.0), Cell::new(10.0), Cell::new(Rect::NOTHING));
|
||||
let draw = |ui: &mut egui::Ui| {
|
||||
ui.horizontal_top(|ui| {
|
||||
let out = egui::ScrollArea::vertical().id_salt("left").max_height(120.0).show(ui, |ui| {
|
||||
ui.allocate_space(vec2(100.0, 600.0));
|
||||
});
|
||||
left.set(out.state.clamped_offset().y);
|
||||
let out = egui::ScrollArea::vertical().id_salt("right").max_height(120.0).show(ui, |ui| {
|
||||
ui.vertical(|ui| {
|
||||
let v = widgets::plain_field(ui, "f", value.get(), "", 0, 80.0).inspect(|&v| value.set(v));
|
||||
field.set(ui.min_rect());
|
||||
ui.add_space(600.0);
|
||||
v
|
||||
})
|
||||
.inner
|
||||
});
|
||||
right.set(out.state.clamped_offset().y);
|
||||
out.inner
|
||||
})
|
||||
.inner
|
||||
};
|
||||
f.frame(vec![], &draw);
|
||||
f.frame(vec![], &draw);
|
||||
let on = field.get().center();
|
||||
let off = if to_other_panel { Pos2::new(50.0, 60.0) } else { Pos2::new(on.x, field.get().bottom() + 40.0) };
|
||||
assert_eq!(f.frame(vec![Event::PointerMoved(on), notch_down()], &draw), Some(9.0));
|
||||
f.frame(vec![Event::PointerMoved(off)], &draw);
|
||||
for _ in 0..20 {
|
||||
f.frame(vec![], &draw);
|
||||
}
|
||||
assert_eq!((left.get(), right.get(), value.get()), (0.0, 0.0, 9.0), "pointer moved to the other panel: {to_other_panel}");
|
||||
assert_eq!(f.frame(vec![notch_down()], &draw), None);
|
||||
for _ in 0..30 {
|
||||
f.frame(vec![], &draw);
|
||||
}
|
||||
let line = f.ctx.options(|o| o.input_options.line_scroll_speed);
|
||||
let (scrolled, still) = if to_other_panel { (left.get(), right.get()) } else { (right.get(), left.get()) };
|
||||
assert!((scrolled - line).abs() < 0.01 && still == 0.0, "other panel {to_other_panel}: {scrolled} {still}");
|
||||
assert_eq!(value.get(), 9.0);
|
||||
}
|
||||
}
|
||||
|
||||
/// A wheel touch that starts and never ends (some mouse wheels send no end) stops counting as a
|
||||
/// touch once no wheel event comes for a while, as egui stops it, so the rest of a notch a number
|
||||
/// field takes after that still scrolls no panel after the pointer leaves.
|
||||
#[test]
|
||||
fn a_wheel_touch_with_no_end_does_not_let_the_rest_of_a_later_notch_scroll_the_panel() {
|
||||
let mut f = Field::new();
|
||||
let (offset, value, field) = (Cell::new(0.0), Cell::new(10.0), Cell::new(Rect::NOTHING));
|
||||
let draw = |ui: &mut egui::Ui| {
|
||||
let out = egui::ScrollArea::vertical().max_height(120.0).show(ui, |ui| {
|
||||
ui.vertical(|ui| {
|
||||
let v = widgets::plain_field(ui, "f", value.get(), "", 0, 80.0).inspect(|&v| value.set(v));
|
||||
field.set(ui.min_rect());
|
||||
ui.add_space(600.0);
|
||||
v
|
||||
})
|
||||
.inner
|
||||
});
|
||||
offset.set(out.state.clamped_offset().y);
|
||||
out.inner
|
||||
};
|
||||
f.frame(vec![], &draw);
|
||||
f.frame(vec![], &draw);
|
||||
let on = field.get().center();
|
||||
let off = Pos2::new(on.x, field.get().bottom() + 40.0);
|
||||
assert_eq!(f.frame(vec![Event::PointerMoved(on), notch_down()], &draw), Some(9.0));
|
||||
for _ in 0..20 {
|
||||
f.frame(vec![], &draw);
|
||||
}
|
||||
let start = Event::MouseWheel {
|
||||
unit: egui::MouseWheelUnit::Line,
|
||||
delta: egui::Vec2::ZERO,
|
||||
phase: egui::TouchPhase::Start,
|
||||
modifiers: Modifiers::NONE,
|
||||
source: egui::MouseWheelSource::Wheel,
|
||||
};
|
||||
f.frame(vec![start], &draw);
|
||||
for _ in 0..10 {
|
||||
f.frame(vec![], &draw);
|
||||
}
|
||||
assert_eq!(f.frame(vec![notch_down()], &draw), Some(8.0));
|
||||
f.frame(vec![Event::PointerMoved(off)], &draw);
|
||||
for _ in 0..20 {
|
||||
f.frame(vec![], &draw);
|
||||
}
|
||||
assert_eq!((offset.get(), value.get()), (0.0, 8.0));
|
||||
}
|
||||
|
||||
/// A whole wheel touch (start, turn and end) in one pass that a number field takes leaves nothing
|
||||
/// for egui to let out later, so the next notch on the panel scrolls it by one whole notch.
|
||||
#[test]
|
||||
fn a_whole_touch_in_one_pass_on_a_number_field_leaves_the_next_panel_notch_whole() {
|
||||
let mut f = Field::new();
|
||||
let (offset, value, field) = (Cell::new(0.0), Cell::new(10.0), Cell::new(Rect::NOTHING));
|
||||
let draw = |ui: &mut egui::Ui| {
|
||||
let out = egui::ScrollArea::vertical().max_height(120.0).show(ui, |ui| {
|
||||
let v = ui.vertical(|ui| {
|
||||
let v = widgets::plain_field(ui, "f", value.get(), "", 0, 80.0).inspect(|&v| value.set(v));
|
||||
field.set(ui.min_rect());
|
||||
v
|
||||
});
|
||||
ui.add_space(600.0);
|
||||
v.inner
|
||||
});
|
||||
offset.set(out.state.clamped_offset().y);
|
||||
out.inner
|
||||
};
|
||||
f.frame(vec![], &draw);
|
||||
f.frame(vec![], &draw);
|
||||
let on = field.get().center();
|
||||
let off = Pos2::new(on.x, field.get().bottom() + 40.0);
|
||||
assert_eq!(f.frame(vec![Event::PointerMoved(on), notch_down()], &draw), Some(9.0));
|
||||
for _ in 0..20 {
|
||||
f.frame(vec![], &draw);
|
||||
}
|
||||
let touch = |phase| Event::MouseWheel {
|
||||
unit: egui::MouseWheelUnit::Line,
|
||||
delta: if phase == egui::TouchPhase::Move { vec2(0.0, -1.0) } else { egui::Vec2::ZERO },
|
||||
phase,
|
||||
modifiers: Modifiers::NONE,
|
||||
source: egui::MouseWheelSource::Wheel,
|
||||
};
|
||||
let events = vec![touch(egui::TouchPhase::Start), touch(egui::TouchPhase::Move), touch(egui::TouchPhase::End)];
|
||||
assert_eq!(f.frame(events, &draw), Some(8.0));
|
||||
f.frame(vec![Event::PointerMoved(off), notch_down()], &draw);
|
||||
for _ in 0..30 {
|
||||
f.frame(vec![], &draw);
|
||||
}
|
||||
let line = f.ctx.options(|o| o.input_options.line_scroll_speed);
|
||||
assert!((offset.get() - line).abs() < 0.01, "{} {line}", offset.get());
|
||||
assert_eq!(value.get(), 8.0);
|
||||
}
|
||||
|
||||
/// A turn and the end of the wheel stream in one pass that a number field takes leave nothing for
|
||||
/// egui to let out later, so the next notch on the panel scrolls it by one whole notch.
|
||||
#[test]
|
||||
fn a_turn_ended_in_its_own_pass_on_a_number_field_leaves_the_next_panel_notch_whole() {
|
||||
let mut f = Field::new();
|
||||
let (offset, value, field) = (Cell::new(0.0), Cell::new(10.0), Cell::new(Rect::NOTHING));
|
||||
let draw = |ui: &mut egui::Ui| {
|
||||
let out = egui::ScrollArea::vertical().max_height(120.0).show(ui, |ui| {
|
||||
let v = ui.vertical(|ui| {
|
||||
let v = widgets::plain_field(ui, "f", value.get(), "", 0, 80.0).inspect(|&v| value.set(v));
|
||||
field.set(ui.min_rect());
|
||||
v
|
||||
});
|
||||
ui.add_space(600.0);
|
||||
v.inner
|
||||
});
|
||||
offset.set(out.state.clamped_offset().y);
|
||||
out.inner
|
||||
};
|
||||
f.frame(vec![], &draw);
|
||||
f.frame(vec![], &draw);
|
||||
let on = field.get().center();
|
||||
let off = Pos2::new(on.x, field.get().bottom() + 40.0);
|
||||
let end = Event::MouseWheel {
|
||||
unit: egui::MouseWheelUnit::Line,
|
||||
delta: egui::Vec2::ZERO,
|
||||
phase: egui::TouchPhase::End,
|
||||
modifiers: Modifiers::NONE,
|
||||
source: egui::MouseWheelSource::Wheel,
|
||||
};
|
||||
assert_eq!(f.frame(vec![Event::PointerMoved(on), notch_down(), end], &draw), Some(9.0));
|
||||
f.frame(vec![Event::PointerMoved(off), notch_down()], &draw);
|
||||
for _ in 0..30 {
|
||||
f.frame(vec![], &draw);
|
||||
}
|
||||
let line = f.ctx.options(|o| o.input_options.line_scroll_speed);
|
||||
assert!((offset.get() - line).abs() < 0.01, "{} {line}", offset.get());
|
||||
assert_eq!(value.get(), 9.0);
|
||||
}
|
||||
|
||||
/// A small turn the other way on the panel while the rest of a notch a number field took is still
|
||||
/// being let out scrolls the panel by the whole turn.
|
||||
#[test]
|
||||
fn a_turn_the_other_way_while_a_number_field_notch_runs_out_scrolls_the_panel_by_the_whole_turn() {
|
||||
let mut f = Field::new();
|
||||
let (offset, value, field) = (Cell::new(0.0), Cell::new(10.0), Cell::new(Rect::NOTHING));
|
||||
let draw = |ui: &mut egui::Ui| {
|
||||
let out = egui::ScrollArea::vertical().max_height(120.0).show(ui, |ui| {
|
||||
ui.add_space(150.0);
|
||||
let v = ui.vertical(|ui| {
|
||||
let v = widgets::plain_field(ui, "f", value.get(), "", 0, 80.0).inspect(|&v| value.set(v));
|
||||
field.set(ui.min_rect());
|
||||
v
|
||||
});
|
||||
ui.add_space(600.0);
|
||||
v.inner
|
||||
});
|
||||
offset.set(out.state.clamped_offset().y);
|
||||
out.inner
|
||||
};
|
||||
f.frame(vec![], &draw);
|
||||
f.frame(vec![], &draw);
|
||||
let line = f.ctx.options(|o| o.input_options.line_scroll_speed);
|
||||
let above = |field: Rect| Pos2::new(field.center().x, field.top() - 30.0);
|
||||
f.frame(vec![Event::PointerMoved(Pos2::new(field.get().center().x, 10.0)), notch_down(), notch_down()], &draw);
|
||||
for _ in 0..30 {
|
||||
f.frame(vec![], &draw);
|
||||
}
|
||||
let scrolled = offset.get();
|
||||
assert!((scrolled - 2.0 * line).abs() < 0.01, "{scrolled}");
|
||||
f.frame(vec![Event::PointerMoved(field.get().center())], &draw);
|
||||
assert_eq!(f.frame(vec![notch_down()], &draw), Some(9.0));
|
||||
let up = Event::MouseWheel {
|
||||
unit: egui::MouseWheelUnit::Point,
|
||||
delta: vec2(0.0, 10.0),
|
||||
phase: egui::TouchPhase::Move,
|
||||
modifiers: Modifiers::NONE,
|
||||
source: egui::MouseWheelSource::Wheel,
|
||||
};
|
||||
f.frame(vec![Event::PointerMoved(above(field.get())), up], &draw);
|
||||
for _ in 0..30 {
|
||||
f.frame(vec![], &draw);
|
||||
}
|
||||
assert!((offset.get() - (scrolled - 10.0)).abs() < 0.01, "{} {scrolled}", offset.get());
|
||||
assert_eq!(value.get(), 9.0);
|
||||
}
|
||||
|
||||
/// A number field that missed a pass (its panel was hidden) waits for the pointer to move onto it
|
||||
/// again before it takes the wheel, with or without another number field drawn meanwhile.
|
||||
#[test]
|
||||
|
||||
@@ -302,13 +302,29 @@ fn step_value(
|
||||
}
|
||||
// Wheel turns in notches: a line each, or `line` points of a trackpad or a fine wheel; what is
|
||||
// left of a notch carries over to the next turn.
|
||||
let (line, notch_id) = (ui.ctx().options(|o| o.input_options.line_scroll_speed), resp.id.with("wheel"));
|
||||
let (options, notch_id) = (ui.ctx().options(|o| o.input_options), resp.id.with("wheel"));
|
||||
let line = options.line_scroll_speed;
|
||||
let mut notches: f32 = if wheel { ui.data(|d| d.get_temp(notch_id)).unwrap_or(0.0) } else { 0.0 };
|
||||
let tail_key = wheel_tail_id(ui.ctx().viewport_id());
|
||||
let mut tail: WheelTail = if wheel { ui.data(|d| d.get_temp(tail_key)).unwrap_or_default() } else { WheelTail::default() };
|
||||
let pass = ui.ctx().cumulative_pass_nr();
|
||||
let stored: Option<(u64, Vec<(Event, egui::Vec2)>)> = if wheel { ui.data(|d| d.get_temp(wheel_turns_id(tail_key))) } else { None };
|
||||
let mut measured = stored.filter(|(at, _)| *at == pass).map(|(_, turns)| turns);
|
||||
let mut turns = Vec::new();
|
||||
let mut took = egui::Vec2::ZERO;
|
||||
// Each press and each turn of whole notches in the order they came: its modifiers and size from
|
||||
// [`STEPS`], and how many steps up (down when negative).
|
||||
let mut steps: Vec<(egui::Modifiers, f64, f64)> = Vec::new();
|
||||
let mut taken = taken;
|
||||
ui.input_mut(|i| {
|
||||
let (page, dt) = (i.viewport_rect().height(), i.stable_dt);
|
||||
// In a pass [`WheelTailPlugin`] did not run in (it is added in this one), the field
|
||||
// measures the pass's turns itself.
|
||||
turns = match measured.take() {
|
||||
Some(turns) => turns,
|
||||
None if wheel => wheel_turns(&mut tail, &i.events, &options, page, i.time),
|
||||
None => Vec::new(),
|
||||
};
|
||||
i.events.retain(|e| {
|
||||
// Each taken event is matched once by its value, so wheel events that other code adds
|
||||
// or removes before the field runs do not shift which events the field takes.
|
||||
@@ -318,7 +334,15 @@ fn step_value(
|
||||
let n = match e {
|
||||
Event::Key { key: Key::ArrowUp, pressed: true, .. } if focused => 1.0,
|
||||
Event::Key { key: Key::ArrowDown, pressed: true, .. } if focused => -1.0,
|
||||
Event::MouseWheel { unit, delta, .. } if mine => {
|
||||
Event::MouseWheel { unit, delta, phase, .. } if mine => {
|
||||
if *phase == egui::TouchPhase::Move {
|
||||
// Each turn of the pass was measured in the order egui read them; a turn
|
||||
// other code added after that falls back on the touch state.
|
||||
took += match turns.iter().position(|(t, _)| t == e) {
|
||||
Some(at) => turns.remove(at).1,
|
||||
None => unsmoothed_points(&options, page, tail.touch, *unit, *delta, *modifiers),
|
||||
};
|
||||
}
|
||||
// Shift may turn the wheel sideways (the Mac does).
|
||||
let d = if modifiers.shift { delta.x + delta.y } else { delta.y };
|
||||
notches += match unit {
|
||||
@@ -340,9 +364,16 @@ fn step_value(
|
||||
// The rest of a turn egui spreads over the next frames scrolls nothing either.
|
||||
i.smooth_scroll_delta = egui::Vec2::ZERO;
|
||||
}
|
||||
// What egui has not let out of the turns taken here yet stays out of scrolling after the
|
||||
// pointer leaves the field ([`WheelTailPlugin`]).
|
||||
tail.owed += took - tail_release(took, dt);
|
||||
});
|
||||
if wheel {
|
||||
ui.data_mut(|d| d.insert_temp(notch_id, notches));
|
||||
ui.data_mut(|d| {
|
||||
d.insert_temp(notch_id, notches);
|
||||
d.insert_temp(tail_key, tail);
|
||||
d.insert_temp(wheel_turns_id(tail_key), (pass, turns));
|
||||
});
|
||||
}
|
||||
if steps.is_empty() {
|
||||
return None;
|
||||
@@ -442,6 +473,9 @@ fn number_wheel_turns(ui: &Ui, resp: &Response) -> (Vec<egui::Event>, bool) {
|
||||
Some((state.walk, state.before.clone()))
|
||||
});
|
||||
let walked = fresh.map(|(walk, before)| {
|
||||
if ctx.with_plugin(|_: &mut WheelTailPlugin| ()).is_none() {
|
||||
ctx.add_plugin(WheelTailPlugin);
|
||||
}
|
||||
let (events, now) = ui.input(|i| (i.events.clone(), i.time));
|
||||
walk_wheel(ctx, walk, &events, &before, now, pass)
|
||||
});
|
||||
@@ -557,6 +591,137 @@ fn walk_wheel(
|
||||
(walk, taken)
|
||||
}
|
||||
|
||||
/// The wheel turns that number fields took in one viewport and egui has not let out yet (points),
|
||||
/// whether a trackpad touch is running there, and the time and source of the last wheel event.
|
||||
#[derive(Clone, Copy, Default)]
|
||||
struct WheelTail {
|
||||
owed: egui::Vec2,
|
||||
touch: bool,
|
||||
last: f64,
|
||||
trackpad: bool,
|
||||
}
|
||||
|
||||
impl WheelTail {
|
||||
/// Ends a touch that egui ends at `time` because no wheel event came for a while (its
|
||||
/// `WheelState::is_scroll_action_over`): 1 s for a trackpad, 0.15 s for any other source.
|
||||
fn expire_touch(&mut self, time: f64) {
|
||||
let timeout = if self.trackpad { 1.0 } else { 0.150 };
|
||||
if self.touch && timeout < time - self.last {
|
||||
self.touch = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn wheel_tail_id(viewport: egui::ViewportId) -> egui::Id {
|
||||
egui::Id::unique("number-field-wheel-tail").with(viewport)
|
||||
}
|
||||
|
||||
/// Where the wheel turns of a pass are kept with their [`unsmoothed_points`] and the pass number,
|
||||
/// from [`wheel_turns`].
|
||||
fn wheel_turns_id(tail: egui::Id) -> egui::Id {
|
||||
tail.with("turns")
|
||||
}
|
||||
|
||||
/// Reads the wheel events of a pass at `time` in order, as egui does, into `tail`'s touch state
|
||||
/// (a start begins a touch; an end or cancel ends it and drops what is owed; a touch with no event
|
||||
/// for a while ends), and returns each turn with its [`unsmoothed_points`] at that point.
|
||||
fn wheel_turns(tail: &mut WheelTail, events: &[egui::Event], options: &egui::InputOptions, page: f32, time: f64) -> Vec<(egui::Event, egui::Vec2)> {
|
||||
let mut turns = Vec::new();
|
||||
for event in events {
|
||||
if let egui::Event::MouseWheel { unit, delta, phase, modifiers, source } = event {
|
||||
tail.expire_touch(time);
|
||||
tail.last = time;
|
||||
tail.trackpad = *source == egui::MouseWheelSource::Trackpad;
|
||||
match phase {
|
||||
egui::TouchPhase::Start => tail.touch = true,
|
||||
egui::TouchPhase::End | egui::TouchPhase::Cancel => {
|
||||
tail.touch = false;
|
||||
tail.owed = egui::Vec2::ZERO;
|
||||
// egui drops what the pass's earlier turns left as well.
|
||||
turns.iter_mut().for_each(|(_, points)| *points = egui::Vec2::ZERO);
|
||||
}
|
||||
egui::TouchPhase::Move => turns.push((event.clone(), unsmoothed_points(options, page, tail.touch, *unit, *delta, *modifiers))),
|
||||
}
|
||||
}
|
||||
}
|
||||
tail.expire_touch(time);
|
||||
turns
|
||||
}
|
||||
|
||||
/// The points of wheel turn `delta` in `unit` that egui spreads over the next passes instead of
|
||||
/// scrolling at once (its `WheelState::on_wheel_event`): a turn sideways with the horizontal
|
||||
/// scroll modifier, up and down with the vertical one, and nothing for a zoom turn, a touch or a
|
||||
/// turn under 8 points. `page` is the viewport's height. `modifiers` are the turn's own: egui keeps a
|
||||
/// touch's modifiers only inside the touch, where nothing is spread out.
|
||||
fn unsmoothed_points(
|
||||
options: &egui::InputOptions,
|
||||
page: f32,
|
||||
touch: bool,
|
||||
unit: egui::MouseWheelUnit,
|
||||
delta: egui::Vec2,
|
||||
modifiers: egui::Modifiers,
|
||||
) -> egui::Vec2 {
|
||||
let mut d = match unit {
|
||||
egui::MouseWheelUnit::Point => delta,
|
||||
egui::MouseWheelUnit::Line => options.line_scroll_speed * delta,
|
||||
egui::MouseWheelUnit::Page => page * delta,
|
||||
};
|
||||
let (sideways, upright) = (modifiers.matches_any(options.horizontal_scroll_modifier), modifiers.matches_any(options.vertical_scroll_modifier));
|
||||
if sideways && !upright {
|
||||
d = egui::vec2(d.x + d.y, 0.0);
|
||||
}
|
||||
if upright && !sideways {
|
||||
d = egui::vec2(0.0, d.x + d.y);
|
||||
}
|
||||
let smooth = touch || unit == egui::MouseWheelUnit::Point && d.length() < 8.0;
|
||||
if smooth || modifiers.matches_any(options.zoom_modifier) { egui::Vec2::ZERO } else { d }
|
||||
}
|
||||
|
||||
/// What egui lets out in a pass `dt` seconds long of `owed` points of wheel input it has not let
|
||||
/// out yet (its `WheelState::after_events`): all of an axis under a point, otherwise the share that
|
||||
/// lets out 90% in 0.1 s.
|
||||
fn tail_release(owed: egui::Vec2, dt: f32) -> egui::Vec2 {
|
||||
let t = egui::emath::exponential_smooth_factor(0.90, 0.1, dt.min(0.1));
|
||||
let axis = |v: f32| if v.abs() < 1.0 { v } else { t * v };
|
||||
egui::vec2(axis(owed.x), axis(owed.y))
|
||||
}
|
||||
|
||||
/// Takes out of each pass's scrolling what egui lets out of the wheel turns number fields took
|
||||
/// ([`WheelTail`]), before any panel scrolls, so the rest of a turn a field took scrolls nothing
|
||||
/// after the pointer leaves the field. A turn's end or cancel drops what is left. It also measures
|
||||
/// each wheel turn of the pass with the touch state egui had at that turn, for [`step_value`].
|
||||
#[derive(Default)]
|
||||
struct WheelTailPlugin;
|
||||
|
||||
impl egui::Plugin for WheelTailPlugin {
|
||||
fn debug_name(&self) -> &'static str {
|
||||
"number field wheel tail"
|
||||
}
|
||||
|
||||
fn on_begin_pass(&mut self, ui: &mut Ui) {
|
||||
let key = wheel_tail_id(ui.ctx().viewport_id());
|
||||
let (options, pass) = (ui.ctx().options(|o| o.input_options), ui.ctx().cumulative_pass_nr());
|
||||
let mut tail = ui.data(|d| d.get_temp::<WheelTail>(key)).unwrap_or_default();
|
||||
let mut turns = Vec::new();
|
||||
ui.input_mut(|i| {
|
||||
turns = wheel_turns(&mut tail, &i.events, &options, i.viewport_rect().height(), i.time);
|
||||
if tail.owed == egui::Vec2::ZERO {
|
||||
return;
|
||||
}
|
||||
let release = tail_release(tail.owed, i.stable_dt);
|
||||
tail.owed -= release;
|
||||
// A zoom turn takes what egui lets out, so nothing of it reaches scrolling.
|
||||
if i.smooth_scroll_delta != egui::Vec2::ZERO || i.zoom_delta() == 1.0 {
|
||||
i.smooth_scroll_delta -= release;
|
||||
}
|
||||
});
|
||||
ui.data_mut(|d| {
|
||||
d.insert_temp(key, tail);
|
||||
d.insert_temp(wheel_turns_id(key), (pass, turns));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// A [`num_field`] for a width or height (Transform panel, Properties panel, Control bar): Ctrl/Cmd+Enter
|
||||
/// also commits it and ends its editing, as Enter does, and [`enter_modifiers`] reports Command. A
|
||||
/// percentage typed alone (`50%`) is that share of the size shown, as in Illustrator (#2401).
|
||||
|
||||
Reference in New Issue
Block a user