Skip to content

Commit 44fc4ae

Browse files
authored
Merge branch 'main' into leolost/explicit-override-window-group
2 parents fc73670 + 11ad1cc commit 44fc4ae

9 files changed

Lines changed: 139 additions & 37 deletions

File tree

lib/Gestures/GestureController.vala

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ public class Gala.GestureController : Object {
133133
running = true;
134134
}
135135

136-
timeline = null;
136+
remove_timeline ();
137137
}
138138

139139
private bool gesture_detected (GestureBackend backend, Gesture gesture, uint32 timestamp) {
@@ -268,19 +268,36 @@ public class Gala.GestureController : Object {
268268
}
269269

270270
var spring = new SpringTimeline (target.actor, progress, clamped_to, velocity, 1, 1, 500);
271-
spring.progress.connect ((value) => progress = value);
272-
spring.stopped.connect_after (finished);
271+
spring.progress.connect (on_timeline_progress);
272+
spring.stopped.connect_after (on_timeline_stopped);
273273

274274
timeline = spring;
275275

276276
target.propagate (COMMIT, action, clamped_to);
277277
}
278278

279-
private void finished (bool is_finished = true) requires (is_finished) {
279+
private void on_timeline_progress (double value) {
280+
progress = value;
281+
}
282+
283+
private void on_timeline_stopped (bool is_finished) {
284+
assert (is_finished);
285+
finished ();
286+
}
287+
288+
private void finished () {
280289
assert (running);
281-
target.propagate (END, action, progress);
282290
running = false;
283-
timeline = null;
291+
remove_timeline ();
292+
target.propagate (END, action, progress);
293+
}
294+
295+
private void remove_timeline () {
296+
if (timeline != null) {
297+
timeline.progress.disconnect (on_timeline_progress);
298+
timeline.stopped.disconnect (on_timeline_stopped);
299+
timeline = null;
300+
}
284301
}
285302

286303
/**
@@ -302,6 +319,18 @@ public class Gala.GestureController : Object {
302319
finish ((to > progress ? 1 : -1) * 1, to);
303320
}
304321

322+
public void jump (double to) {
323+
if (running && !recognizing) {
324+
/* We are animating to a snap point so stop the animation */
325+
finished ();
326+
}
327+
328+
var clamped_to = to.clamp ((int) overshoot_lower_clamp, (int) overshoot_upper_clamp);
329+
330+
target?.propagate (COMMIT, action, clamped_to);
331+
progress = clamped_to;
332+
}
333+
305334
public void cancel_gesture () {
306335
if (recognizing) {
307336
recognizing_backend.cancel_gesture ();

lib/Gestures/Triggers/SwipeTrigger.vala

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,14 @@
1111
* It enables touchpad and (once supported) touchscreen backends for the given actor.
1212
*/
1313
public class Gala.SwipeTrigger : Object, GestureTrigger {
14-
public Clutter.Actor actor { get; construct; }
1514
public Clutter.Orientation orientation { get; construct; }
15+
private weak Clutter.Actor actor;
1616

1717
public SwipeTrigger (Clutter.Actor actor, Clutter.Orientation orientation) {
18-
Object (actor: actor, orientation: orientation);
18+
Object (orientation: orientation);
19+
20+
this.actor = actor;
21+
actor.add_weak_pointer (&this.actor);
1922
}
2023

2124
internal bool triggers (Gesture gesture) {
@@ -25,7 +28,7 @@ public class Gala.SwipeTrigger : Object, GestureTrigger {
2528
);
2629
}
2730

28-
internal void enable_backends (GestureController controller) {
31+
internal void enable_backends (GestureController controller) requires (actor != null) {
2932
controller.enable_backend (new ScrollBackend (actor, orientation, new GestureSettings ()));
3033
}
3134
}

lib/Widgets/AbstractSwitcher.vala

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ public abstract class Gala.AbstractSwitcher : CanvasActor {
3131
style_manager = Drawing.StyleManager.get_instance ();
3232

3333
container = new Clutter.Actor () {
34-
reactive = true,
3534
#if HAS_MUTTER46
3635
layout_manager = new Clutter.FlowLayout (Clutter.Orientation.HORIZONTAL)
3736
#else
@@ -89,24 +88,35 @@ public abstract class Gala.AbstractSwitcher : CanvasActor {
8988
caption.margin_bottom = margin;
9089
}
9190

92-
protected override void get_preferred_width (float for_height, out float min_width, out float natural_width) {
93-
min_width = 0;
94-
95-
float preferred_nat_width;
96-
base.get_preferred_width (for_height, null, out preferred_nat_width);
97-
91+
public override void allocate (Clutter.ActorBox box) {
9892
unowned var display = wm.get_display ();
9993
var geom = display.get_monitor_geometry (display.get_current_monitor ());
10094

101-
float container_nat_width;
102-
container.get_preferred_size (null, null, out container_nat_width, null);
95+
float nat_container_width;
96+
container.get_preferred_width (-1, null, out nat_container_width);
10397

104-
var max_width = float.min (
105-
geom.width - Utils.scale_to_int (MIN_OFFSET * 2, monitor_scale), // Don't overflow the monitor
106-
container_nat_width // Ellipsize the label if it's longer than the icons
107-
);
98+
var max_width = geom.width - Utils.scale_to_int (MIN_OFFSET * 2, monitor_scale);
99+
var real_width = (int) float.min (nat_container_width, max_width);
100+
101+
float nat_container_height;
102+
container.get_preferred_height (real_width, null, out nat_container_height);
103+
104+
float nat_caption_height;
105+
caption.get_preferred_height (real_width, null, out nat_caption_height);
106+
107+
var horizontal_margin = (geom.width - real_width) / 2;
108+
109+
var children_height = nat_container_height + nat_caption_height;
110+
var vertical_margin = (geom.height - children_height) / 2;
111+
112+
box = {
113+
geom.x + horizontal_margin,
114+
geom.y + vertical_margin,
115+
geom.width - horizontal_margin,
116+
geom.height - vertical_margin
117+
};
108118

109-
natural_width = float.min (max_width, preferred_nat_width);
119+
base.allocate (box);
110120
}
111121

112122
protected override void draw (Cairo.Context ctx, int width, int height) {

src/Widgets/MultitaskingView/MultitaskingView.vala

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ public class Gala.MultitaskingView : Root, RootTarget, ActivatableComponent {
9292
add_child (StaticWindowContainer.get_instance (display));
9393

9494
unowned var manager = display.get_workspace_manager ();
95+
manager.workspace_added.connect (sync_active_workspace);
9596
manager.workspace_removed.connect (sync_active_workspace);
9697
manager.workspaces_reordered.connect (sync_active_workspace);
9798
manager.workspace_switched.connect (on_workspace_switched);
@@ -302,8 +303,12 @@ public class Gala.MultitaskingView : Root, RootTarget, ActivatableComponent {
302303
}
303304

304305
private void sync_active_workspace () {
305-
unowned var manager = display.get_workspace_manager ();
306-
workspaces_gesture_controller.progress = -manager.get_active_workspace_index ();
306+
if (workspaces_gesture_controller.recognizing) {
307+
return;
308+
}
309+
310+
var target = -display.get_workspace_manager ().get_active_workspace_index ();
311+
workspaces_gesture_controller.jump (target);
307312
}
308313

309314
private void on_workspace_switched (int from, int to) {

src/Widgets/WindowSwitcher/WindowSwitcher.vala

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -243,10 +243,6 @@ public class Gala.WindowSwitcher : AbstractSwitcher, GestureTarget, RootTarget {
243243
return;
244244
}
245245

246-
//Although we are setting visible via the opacity notify handler anyway
247-
//we have to set it here manually otherwise the size gotten via get_preferred_size is wrong
248-
visible = true;
249-
250246
float width, height;
251247
get_preferred_size (null, null, out width, out height);
252248

src/WindowManager.vala

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1373,14 +1373,6 @@ namespace Gala {
13731373
yield builder.run ();
13741374
break;
13751375

1376-
case Meta.WindowType.MENU:
1377-
case Meta.WindowType.DROPDOWN_MENU:
1378-
case Meta.WindowType.POPUP_MENU:
1379-
var builder = new TransitionBuilder (actor, AnimationDuration.MENU_MAP, EASE_OUT_QUAD);
1380-
builder.add_property_with_from ("opacity", 0U, 255U);
1381-
yield builder.run ();
1382-
break;
1383-
13841376
case Meta.WindowType.MODAL_DIALOG:
13851377
case Meta.WindowType.DIALOG:
13861378
dim_parent_window (window);

tests/lib/GestureControllerTest.vala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,10 @@ public class Gala.GestureControllerTest : MutterTestCase {
164164
target.assert_and_remove_propagation (COMMIT, CUSTOM, 1);
165165
target.assert_and_remove_propagation (END, CUSTOM, 1);
166166
target.assert_no_propagations ();
167+
168+
assert_finalize_object (ref target);
169+
assert_finalize_object (ref controller);
170+
assert_finalize_object (ref backend);
167171
}
168172

169173
/**

tests/lib/SwipeTriggerTest.vala

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright 2026 elementary, Inc. (https://elementary.io)
3+
* SPDX-License-Identifier: GPL-3.0-or-later
4+
*
5+
* Authored by: Leonhard Kargl <leo.kargl@proton.me>
6+
*/
7+
8+
public class Gala.SwipeTriggerTest : MutterTestCase {
9+
private Clutter.Actor? actor;
10+
private SwipeTrigger? trigger;
11+
12+
public SwipeTriggerTest () {
13+
Object (name: "SwipeTriggerTest");
14+
}
15+
16+
construct {
17+
add_test ("Test finalize trigger first", test_finalize_trigger_first);
18+
add_test ("Test finalize actor first", test_finalize_actor_first);
19+
add_test ("Test finalize actor and enable backend", test_finalize_actor_and_enable_backend);
20+
}
21+
22+
public override void set_up () {
23+
actor = new Clutter.Actor ();
24+
trigger = new SwipeTrigger (actor, Clutter.Orientation.HORIZONTAL);
25+
}
26+
27+
public override void tear_down () {
28+
trigger = null;
29+
actor = null;
30+
}
31+
32+
private void test_finalize_trigger_first () {
33+
assert_finalize_object (ref trigger);
34+
assert_finalize_object (ref actor);
35+
}
36+
37+
private void test_finalize_actor_first () {
38+
// We can finalize the actor first because the swipe trigger only holds a weak reference to it
39+
assert_finalize_object (ref actor);
40+
assert_finalize_object (ref trigger);
41+
}
42+
43+
private void test_finalize_actor_and_enable_backend () {
44+
// We can finalize the actor first because the swipe trigger only holds a weak reference to it
45+
assert_finalize_object (ref actor);
46+
47+
// Enabling the backend after the actor has been finalized should not cause a crash
48+
// but print a warning
49+
Test.expect_message (null, LEVEL_CRITICAL, "*assertion 'actor != null' failed");
50+
51+
var controller = new GestureController (CUSTOM);
52+
trigger.enable_backends (controller);
53+
54+
Test.assert_expected_messages ();
55+
56+
assert_finalize_object (ref trigger);
57+
}
58+
}
59+
60+
public int main (string[] args) {
61+
return new Gala.SwipeTriggerTest ().run (args);
62+
}

tests/lib/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ tests = [
22
'GestureControllerTest',
33
'PropertyTargetTest',
44
'SetupTest',
5+
'SwipeTriggerTest',
56
]
67

78
foreach test : tests

0 commit comments

Comments
 (0)