Skip to content

Commit 5860a7b

Browse files
committed
feat: add helper to handle touch tap and scroll
1 parent e757dc2 commit 5860a7b

File tree

4 files changed

+131
-2
lines changed

4 files changed

+131
-2
lines changed

src/components/windowsSuggestions/suggestedWindowPreview.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
GLib,
1212
} from '@gi.ext';
1313
import { logger } from '@utils/logger';
14+
import TouchEventHelper from '@utils/touch';
1415

1516
const WINDOW_OVERLAY_FADE_TIME = 200;
1617

@@ -40,6 +41,7 @@ export default class SuggestedWindowPreview extends Shell.WindowPreview {
4041
private _stackAbove: Clutter.Actor | null;
4142
private _destroyed: boolean;
4243
private _idleHideOverlayId: number;
44+
private _touchHelper: TouchEventHelper;
4345

4446
constructor(metaWindow: Meta.Window) {
4547
super({
@@ -153,6 +155,8 @@ export default class SuggestedWindowPreview extends Shell.WindowPreview {
153155
this._title.ensure_style();
154156
this._icon.ensure_style();
155157
});
158+
159+
this._touchHelper = new TouchEventHelper(this);
156160
}
157161

158162
public get_window_clone(): Clutter.Actor | undefined {
@@ -351,4 +355,11 @@ export default class SuggestedWindowPreview extends Shell.WindowPreview {
351355

352356
this.hideOverlay(true);
353357
}
358+
359+
vfunc_touch_event(event: Clutter.Event): boolean {
360+
if (this._touchHelper.convertTapToButtonPress(event))
361+
return super.vfunc_touch_event(event);
362+
363+
return false;
364+
}
354365
}

src/components/windowsSuggestions/suggestionsTilePreview.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import TilePreview from '../tilepreview/tilePreview';
44
import { buildBlurEffect, widgetOrientation } from '@utils/gnomesupport';
55
import Tile from '@components/layout/Tile';
66
import MasonryLayoutManager from './masonryLayoutManager';
7+
import TouchEventHelper from '@utils/touch';
78

89
const MASONRY_LAYOUT_SPACING = 32;
910
const SCROLLBARS_SHOW_ANIM_DURATION = 100; // ms
@@ -26,6 +27,7 @@ export default class SuggestionsTilePreview extends TilePreview {
2627
private _blur: boolean;
2728
private _container: St.BoxLayout;
2829
private _scrollView: St.ScrollView;
30+
private _touchHelper: TouchEventHelper;
2931

3032
constructor(params: {
3133
parent: Clutter.Actor;
@@ -94,6 +96,8 @@ export default class SuggestionsTilePreview extends TilePreview {
9496
// @ts-expect-error "get_vscroll_bar is valid for GNOME < 48"
9597
this._scrollView.get_vscroll_bar().opacity = 0;
9698
}
99+
100+
this._touchHelper = new TouchEventHelper(this);
97101
}
98102

99103
set blur(value: boolean) {
@@ -127,6 +131,8 @@ export default class SuggestionsTilePreview extends TilePreview {
127131
this.add_effect(effect);
128132

129133
this.add_style_class_name('selection-tile-preview');
134+
135+
this._setupTouchScrolling();
130136
}
131137

132138
_recolor() {
@@ -243,4 +249,13 @@ export default class SuggestionsTilePreview extends TilePreview {
243249
public removeAllWindows() {
244250
this._container.destroy_all_children();
245251
}
252+
253+
private _setupTouchScrolling() {
254+
this.connect('touch-event', (_, event: Clutter.Event) => {
255+
return this._touchHelper.convertPanToScroll(
256+
event,
257+
this._scrollView,
258+
);
259+
});
260+
}
246261
}

src/components/windowsSuggestions/tilingLayoutWithSuggestions.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import SignalHandling from '@utils/signalHandling';
1313
import SuggestionsTilePreview from '@components/windowsSuggestions/suggestionsTilePreview';
1414
import TilingShellWindowManager from '@components/windowManager/tilingShellWindowManager';
1515
import { unmaximizeWindow } from '@utils/gnomesupport';
16+
import TouchEventHelper from '@utils/touch';
1617

1718
const debug = logger('TilingLayoutWithSuggestions');
1819

@@ -25,6 +26,7 @@ export default class TilingLayoutWithSuggestions extends LayoutWidget<Suggestion
2526
private _lastTiledWindow: Meta.Window | null;
2627
private _showing: boolean;
2728
private _oldPreviews: SuggestionsTilePreview[];
29+
private _touchHelper: TouchEventHelper;
2830

2931
constructor(
3032
innerGaps: Clutter.Margin,
@@ -47,6 +49,7 @@ export default class TilingLayoutWithSuggestions extends LayoutWidget<Suggestion
4749
this._showing = false;
4850
this._oldPreviews = [];
4951
this.connect('destroy', () => this._signals.disconnect());
52+
this._touchHelper = new TouchEventHelper(this);
5053
}
5154

5255
protected override buildTile(
@@ -80,7 +83,13 @@ export default class TilingLayoutWithSuggestions extends LayoutWidget<Suggestion
8083
this._recursivelyShowPopup(nontiledWindows, monitorIndex);
8184

8285
this._signals.disconnect();
83-
this._signals.connect(this, 'touch-event', () => this.close());
86+
this._signals.connect(
87+
this,
88+
'touch-event',
89+
(_, event: Clutter.Event) => {
90+
return this._touchHelper.convertTapToButtonPress(event);
91+
},
92+
);
8493
this._signals.connect(this, 'key-focus-out', () => this.close());
8594
this._signals.connect(this, 'button-press-event', () => {
8695
// if a window clone is pressed by a button, it will stop propagating the event
@@ -250,7 +259,6 @@ export default class TilingLayoutWithSuggestions extends LayoutWidget<Suggestion
250259

251260
// when the clone is selected by the user
252261
winClone.connect('button-press-event', onSuggestionPress);
253-
winClone.connect('touch-event', onSuggestionPress);
254262

255263
return winClone;
256264
});

src/utils/touch.ts

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import { Clutter, St } from '@gi.ext';
2+
3+
export default class TouchEventHelper {
4+
private readonly TOUCH_SCROLL_THRESHOLD = 10;
5+
6+
private _touchStartY: number | null = null;
7+
private _touchMoved: boolean = false;
8+
private _scrollStartY: number = 0;
9+
private _isDragging: boolean = false;
10+
11+
private _actor: Clutter.Actor;
12+
13+
constructor(actor: Clutter.Actor) {
14+
this._actor = actor;
15+
}
16+
17+
convertTapToButtonPress(event: Clutter.Event): boolean {
18+
const eventType = event.type();
19+
const [, y] = event.get_coords();
20+
21+
switch (eventType) {
22+
case Clutter.EventType.TOUCH_BEGIN:
23+
this._touchStartY = y;
24+
this._isDragging = false;
25+
return Clutter.EVENT_PROPAGATE;
26+
case Clutter.EventType.TOUCH_UPDATE:
27+
if (this._touchStartY !== null) {
28+
const deltaY = Math.abs(y - this._touchStartY);
29+
if (deltaY > this.TOUCH_SCROLL_THRESHOLD)
30+
this._isDragging = true;
31+
}
32+
return Clutter.EVENT_PROPAGATE;
33+
34+
case Clutter.EventType.TOUCH_END: {
35+
const wasTap = !this._isDragging;
36+
this._touchStartY = null;
37+
this._isDragging = false;
38+
39+
if (wasTap) this._actor.emit('button-press-event', event);
40+
return Clutter.EVENT_STOP;
41+
}
42+
default:
43+
return Clutter.EVENT_PROPAGATE;
44+
}
45+
}
46+
47+
convertPanToScroll(
48+
event: Clutter.Event,
49+
scrollView: St.ScrollView,
50+
): boolean {
51+
const eventType = event.type();
52+
const [, y] = event.get_coords();
53+
54+
switch (eventType) {
55+
case Clutter.EventType.TOUCH_BEGIN:
56+
this._touchStartY = y;
57+
this._scrollStartY = scrollView.vadjustment.value;
58+
this._isDragging = false;
59+
return Clutter.EVENT_STOP;
60+
61+
case Clutter.EventType.TOUCH_UPDATE:
62+
if (this._touchStartY !== null) {
63+
const deltaY = this._touchStartY - y;
64+
const newScrollYValue = this._scrollStartY + deltaY;
65+
const adjustment = scrollView.vadjustment;
66+
const clampedValue = Math.max(
67+
0,
68+
Math.min(
69+
newScrollYValue,
70+
adjustment.upper - adjustment.page_size,
71+
),
72+
);
73+
adjustment.set_value(clampedValue);
74+
75+
if (Math.abs(deltaY) > this.TOUCH_SCROLL_THRESHOLD)
76+
this._isDragging = true;
77+
}
78+
return Clutter.EVENT_STOP;
79+
80+
case Clutter.EventType.TOUCH_END: {
81+
const wasDragging = this._isDragging;
82+
this._touchStartY = null;
83+
this._isDragging = false;
84+
85+
// Propagate touch event if we weren't dragging
86+
return wasDragging
87+
? Clutter.EVENT_STOP
88+
: Clutter.EVENT_PROPAGATE;
89+
}
90+
91+
default:
92+
return Clutter.EVENT_PROPAGATE;
93+
}
94+
}
95+
}

0 commit comments

Comments
 (0)