Skip to content

Commit c8675ec

Browse files
authored
Merge pull request #492 from kas-gui/push-xxywvsoowlrn
Fix freezes during pan events, add click-click-drag gesture
2 parents cd61756 + 096147c commit c8675ec

File tree

10 files changed

+174
-40
lines changed

10 files changed

+174
-40
lines changed

crates/kas-core/src/event/cx/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ pub struct EventState {
9595
popup_removed: SmallVec<[(Id, WindowId); 16]>,
9696
time_updates: Vec<(Instant, Id, TimerHandle)>,
9797
frame_updates: LinearSet<(Id, TimerHandle)>,
98+
need_frame_update: bool,
9899
// Set of messages awaiting sending
99100
send_queue: VecDeque<(Id, Erased)>,
100101
// Set of futures of messages together with id of sending widget

crates/kas-core/src/event/cx/platform.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ impl EventState {
3535
popup_removed: Default::default(),
3636
time_updates: vec![],
3737
frame_updates: Default::default(),
38+
need_frame_update: false,
3839
send_queue: Default::default(),
3940
fut_messages: vec![],
4041
pending_update: None,
@@ -85,8 +86,8 @@ impl EventState {
8586
self.time_updates.last().map(|time| time.0)
8687
}
8788

88-
pub(crate) fn have_pending(&self) -> bool {
89-
!self.frame_updates.is_empty() || !self.fut_messages.is_empty()
89+
pub(crate) fn need_frame_update(&self) -> bool {
90+
self.need_frame_update || !self.frame_updates.is_empty() || !self.fut_messages.is_empty()
9091
}
9192

9293
/// Construct a [`EventCx`] referring to this state
@@ -205,6 +206,7 @@ impl<'a> EventCx<'a> {
205206
/// This method should be called once per frame as well as after the last
206207
/// frame before a long sleep.
207208
pub(crate) fn frame_update(&mut self, mut widget: Node<'_>) {
209+
self.need_frame_update = false;
208210
log::debug!(target: "kas_core::event", "Processing frame update");
209211
if let Some((target, event)) = self.mouse.frame_update() {
210212
self.send_event(widget.re(), target, event);

crates/kas-core/src/event/cx/press.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ impl EventState {
236236
}
237237
if self
238238
.mouse
239-
.mouse_grab
239+
.grab
240240
.as_ref()
241241
.map(|grab| *w_id == grab.depress)
242242
.unwrap_or(false)
@@ -259,7 +259,7 @@ impl EventState {
259259
/// Get whether the widget is under the mouse cursor
260260
#[inline]
261261
pub fn is_hovered(&self, w_id: &Id) -> bool {
262-
self.mouse.mouse_grab.is_none() && *w_id == self.mouse.hover
262+
self.mouse.grab.is_none() && *w_id == self.mouse.hover
263263
}
264264

265265
/// Set the cursor icon
@@ -298,7 +298,7 @@ impl EventState {
298298
let mut redraw = false;
299299
match source {
300300
PressSource::Mouse(_, _) => {
301-
if let Some(grab) = self.mouse.mouse_grab.as_mut() {
301+
if let Some(grab) = self.mouse.grab.as_mut() {
302302
redraw = grab.depress != target;
303303
old = grab.depress.take();
304304
grab.depress = target.clone();
@@ -324,7 +324,7 @@ impl EventState {
324324
pub fn any_grab_on(&self, id: &Id) -> bool {
325325
if self
326326
.mouse
327-
.mouse_grab
327+
.grab
328328
.as_ref()
329329
.map(|grab| grab.start_id == id)
330330
.unwrap_or(false)
@@ -342,7 +342,7 @@ impl<'a> EventCx<'a> {
342342
/// [`Press::grab`]). The cursor will be reset when the mouse-grab
343343
/// ends.
344344
pub fn set_grab_cursor(&mut self, id: &Id, icon: CursorIcon) {
345-
if let Some(ref grab) = self.mouse.mouse_grab {
345+
if let Some(ref grab) = self.mouse.grab {
346346
if grab.start_id == *id {
347347
self.window.set_cursor_icon(icon);
348348
}

crates/kas-core/src/event/cx/press/mouse.rs

Lines changed: 97 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
//! Event handling: mouse events
77
88
use super::{GrabMode, Press, PressSource};
9-
use crate::event::{Event, EventCx, FocusSource, ScrollDelta};
9+
use crate::event::{Event, EventCx, EventState, FocusSource, ScrollDelta};
1010
use crate::geom::{Coord, DVec2};
1111
use crate::{Action, Id, NavAdvance, Node, Widget, Window};
1212
use cast::{Cast, Conv, ConvApprox};
@@ -19,10 +19,20 @@ const DOUBLE_CLICK_TIMEOUT: Duration = Duration::from_secs(1);
1919

2020
const FAKE_MOUSE_BUTTON: MouseButton = MouseButton::Other(0);
2121

22+
#[derive(Clone, Debug, PartialEq, Eq)]
23+
enum PanMode {
24+
Pan,
25+
Rotate,
26+
Scale,
27+
Full,
28+
}
29+
2230
#[derive(Clone, Debug)]
2331
struct PanDetails {
2432
c0: Coord,
2533
c1: Coord,
34+
moved: bool,
35+
mode: PanMode,
2636
}
2737

2838
#[derive(Clone, Debug)]
@@ -36,6 +46,15 @@ impl GrabDetails {
3646
fn is_pan(&self) -> bool {
3747
matches!(self, GrabDetails::Pan(_))
3848
}
49+
50+
fn pan(coord: Coord, mode: PanMode) -> Self {
51+
GrabDetails::Pan(PanDetails {
52+
c0: coord,
53+
c1: coord,
54+
moved: false,
55+
mode,
56+
})
57+
}
3958
}
4059

4160
#[derive(Clone, Debug)]
@@ -56,7 +75,8 @@ pub(in crate::event::cx) struct Mouse {
5675
last_click_button: MouseButton,
5776
last_click_repetitions: u32,
5877
last_click_timeout: Instant,
59-
pub(super) mouse_grab: Option<MouseGrab>,
78+
last_pin: Option<(Id, Coord)>,
79+
pub(super) grab: Option<MouseGrab>,
6080
}
6181

6282
impl Default for Mouse {
@@ -69,15 +89,16 @@ impl Default for Mouse {
6989
last_click_button: FAKE_MOUSE_BUTTON,
7090
last_click_repetitions: 0,
7191
last_click_timeout: Instant::now(),
72-
mouse_grab: None,
92+
last_pin: None,
93+
grab: None,
7394
}
7495
}
7596
}
7697

7798
impl Mouse {
7899
/// Clear all focus and grabs on `target`
79100
pub(in crate::event::cx) fn cancel_event_focus(&mut self, target: &Id) {
80-
if let Some(grab) = self.mouse_grab.as_mut() {
101+
if let Some(grab) = self.grab.as_mut() {
81102
if grab.start_id == target {
82103
grab.cancel = true;
83104
}
@@ -86,24 +107,51 @@ impl Mouse {
86107

87108
pub(in crate::event::cx) fn update_hover_icon(&mut self) -> Option<CursorIcon> {
88109
let mut icon = None;
89-
if self.hover_icon != self.old_hover_icon && self.mouse_grab.is_none() {
110+
if self.hover_icon != self.old_hover_icon && self.grab.is_none() {
90111
icon = Some(self.hover_icon);
91112
}
92113
self.old_hover_icon = self.hover_icon;
93114
icon
94115
}
95116

96117
pub fn frame_update(&mut self) -> Option<(Id, Event)> {
97-
if let Some(grab) = self.mouse_grab.as_mut() {
118+
if let Some(grab) = self.grab.as_mut() {
98119
if let GrabDetails::Pan(details) = &mut grab.details {
99120
// Terminology: pi are old coordinates, qi are new coords
100121
let (p1, q1) = (DVec2::conv(details.c0), DVec2::conv(details.c1));
101122
details.c0 = details.c1;
102123

103-
let delta = q1 - p1;
104-
if delta != DVec2::ZERO {
124+
let alpha;
125+
let delta;
126+
127+
if details.mode == PanMode::Pan {
128+
alpha = DVec2(1.0, 0.0);
129+
delta = q1 - p1;
130+
} else if let Some((_, coord)) = self.last_pin.as_ref() {
131+
let p2 = DVec2::conv(*coord);
132+
let (pd, qd) = (p2 - p1, p2 - q1);
133+
134+
alpha = match details.mode {
135+
PanMode::Full => qd.complex_div(pd),
136+
PanMode::Scale => DVec2((qd.sum_square() / pd.sum_square()).sqrt(), 0.0),
137+
PanMode::Rotate => {
138+
let a = qd.complex_div(pd);
139+
a / a.sum_square().sqrt()
140+
}
141+
_ => unreachable!(),
142+
};
143+
144+
// Average delta from both movements:
145+
delta = (q1 - alpha.complex_mul(p1) + p2 - alpha.complex_mul(p2)) * 0.5;
146+
} else {
147+
unreachable!()
148+
}
149+
150+
if alpha.is_finite()
151+
&& delta.is_finite()
152+
&& (alpha != DVec2(1.0, 0.0) || delta != DVec2::ZERO)
153+
{
105154
let id = grab.start_id.clone();
106-
let alpha = DVec2(1.0, 0.0);
107155
let event = Event::Pan { alpha, delta };
108156
return Some((id, event));
109157
}
@@ -119,7 +167,7 @@ impl Mouse {
119167

120168
fn update_hover(&mut self) -> (bool, bool) {
121169
let (mut cancel, mut redraw) = (false, false);
122-
if let Some(grab) = self.mouse_grab.as_mut() {
170+
if let Some(grab) = self.grab.as_mut() {
123171
cancel = grab.cancel;
124172
if let GrabDetails::Click = grab.details {
125173
let hover = self.hover.as_ref();
@@ -146,18 +194,19 @@ impl Mouse {
146194
coord: Coord,
147195
mode: GrabMode,
148196
) -> bool {
197+
let have_pin = matches!(&self.last_pin, Some((id2, _)) if id == *id2);
149198
let details = match mode {
150199
GrabMode::Click => GrabDetails::Click,
151200
GrabMode::Grab => GrabDetails::Grab,
201+
GrabMode::PanRotate if have_pin => GrabDetails::pan(coord, PanMode::Rotate),
202+
GrabMode::PanScale if have_pin => GrabDetails::pan(coord, PanMode::Scale),
203+
GrabMode::PanFull if have_pin => GrabDetails::pan(coord, PanMode::Full),
152204
mode => {
153205
assert!(mode.is_pan());
154-
GrabDetails::Pan(PanDetails {
155-
c0: coord,
156-
c1: coord,
157-
})
206+
GrabDetails::pan(coord, PanMode::Pan)
158207
}
159208
};
160-
if let Some(ref mut grab) = self.mouse_grab {
209+
if let Some(ref mut grab) = self.grab {
161210
if grab.start_id != id
162211
|| grab.button != button
163212
|| grab.details.is_pan() != mode.is_pan()
@@ -171,7 +220,7 @@ impl Mouse {
171220
grab.depress = Some(id.clone());
172221
grab.details = details;
173222
} else {
174-
self.mouse_grab = Some(MouseGrab {
223+
self.grab = Some(MouseGrab {
175224
button,
176225
repetitions,
177226
start_id: id.clone(),
@@ -184,6 +233,22 @@ impl Mouse {
184233
}
185234
}
186235

236+
impl EventState {
237+
pub(crate) fn mouse_pin(&self) -> Option<(Coord, bool)> {
238+
if let Some((_, coord)) = self.mouse.last_pin.as_ref() {
239+
let used = self
240+
.mouse
241+
.grab
242+
.as_ref()
243+
.map(|grab| grab.details.is_pan())
244+
.unwrap_or(false);
245+
Some((*coord, used))
246+
} else {
247+
None
248+
}
249+
}
250+
}
251+
187252
impl<'a> EventCx<'a> {
188253
// Clear old hover, set new hover, send events.
189254
// If there is a popup, only permit descendands of that.
@@ -212,17 +277,23 @@ impl<'a> EventCx<'a> {
212277

213278
// Clears mouse grab and pan grab, resets cursor and redraws
214279
fn remove_mouse_grab(&mut self, success: bool) -> Option<(Id, Event)> {
215-
if let Some(grab) = self.mouse.mouse_grab.take() {
280+
if let Some(grab) = self.mouse.grab.take() {
216281
log::trace!(
217282
"remove_mouse_grab: start_id={}, success={success}",
218283
grab.start_id
219284
);
220285
self.window.set_cursor_icon(self.mouse.hover_icon);
221286
self.opt_action(grab.depress.clone(), Action::REDRAW);
222-
if grab.details.is_pan() {
287+
if let GrabDetails::Pan(details) = &grab.details {
288+
if !details.moved {
289+
self.mouse.last_pin = Some((grab.start_id.clone(), self.mouse.last_coord));
290+
} else {
291+
self.mouse.last_pin = None;
292+
}
223293
// Pan grabs do not receive Event::PressEnd
224294
None
225295
} else {
296+
self.mouse.last_pin = None;
226297
let press = Press {
227298
source: PressSource::Mouse(grab.button, grab.repetitions),
228299
id: self.mouse.hover.clone(),
@@ -268,7 +339,7 @@ impl<'a> EventCx<'a> {
268339
let id = win.try_probe(coord);
269340
self.set_hover(win.as_node(data), id.clone());
270341

271-
if let Some(grab) = self.mouse.mouse_grab.as_mut() {
342+
if let Some(grab) = self.mouse.grab.as_mut() {
272343
match &mut grab.details {
273344
GrabDetails::Click => (),
274345
GrabDetails::Grab => {
@@ -284,6 +355,8 @@ impl<'a> EventCx<'a> {
284355
}
285356
GrabDetails::Pan(details) => {
286357
details.c1 = coord;
358+
details.moved = true;
359+
self.need_frame_update = true;
287360
}
288361
}
289362
} else if let Some(popup_id) = self.popups.last().map(|(_, p, _)| p.id.clone()) {
@@ -309,7 +382,7 @@ impl<'a> EventCx<'a> {
309382
pub(in crate::event::cx) fn handle_cursor_left(&mut self, node: Node<'_>) {
310383
self.mouse.last_click_button = FAKE_MOUSE_BUTTON;
311384

312-
if self.mouse.mouse_grab.is_none() {
385+
if self.mouse.grab.is_none() {
313386
// If there's a mouse grab, we will continue to receive
314387
// coordinates; if not, set a fake coordinate off the window
315388
self.mouse.last_coord = Coord(-1, -1);
@@ -360,7 +433,7 @@ impl<'a> EventCx<'a> {
360433

361434
if self
362435
.mouse
363-
.mouse_grab
436+
.grab
364437
.as_ref()
365438
.map(|g| g.button == button)
366439
.unwrap_or(false)
@@ -373,6 +446,9 @@ impl<'a> EventCx<'a> {
373446
if state == ElementState::Pressed {
374447
if let Some(start_id) = self.mouse.hover.clone() {
375448
// No mouse grab but have a hover target
449+
if matches!(self.mouse.last_pin.as_ref(), Some((id, _)) if *id != start_id) {
450+
self.mouse.last_pin = None;
451+
}
376452
if self.config.event().mouse_nav_focus() {
377453
if let Some(id) = self.nav_next(node.re(), Some(&start_id), NavAdvance::None) {
378454
self.set_nav_focus(id, FocusSource::Pointer);

crates/kas-core/src/event/cx/press/touch.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,10 @@ impl<'a> EventCx<'a> {
272272
}
273273

274274
let id = grab.id.clone();
275-
if alpha != DVec2(1.0, 0.0) || delta != DVec2::ZERO {
275+
if alpha.is_finite()
276+
&& delta.is_finite()
277+
&& (alpha != DVec2(1.0, 0.0) || delta != DVec2::ZERO)
278+
{
276279
let event = Event::Pan { alpha, delta };
277280
self.send_event(node.re(), id, event);
278281
}
@@ -341,8 +344,9 @@ impl<'a> EventCx<'a> {
341344
}
342345

343346
if redraw {
344-
self.action(Id::ROOT, Action::REDRAW);
347+
self.window_action(Action::REDRAW);
345348
} else if let Some(pan_grab) = pan_grab {
349+
self.need_frame_update = true;
346350
if usize::conv(pan_grab.1) < MAX_PAN_GRABS {
347351
if let Some(pan) = self.touch.pan_grab.get_mut(usize::conv(pan_grab.0)) {
348352
pan.coords[usize::conv(pan_grab.1)].1 = coord;

0 commit comments

Comments
 (0)