Add "Switch windows" gesture (#1802)

This commit is contained in:
Leo 2023-12-05 02:46:39 +09:00 committed by GitHub
parent 72b9e012f3
commit 2699c5a453
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 98 additions and 35 deletions

View File

@ -355,6 +355,7 @@
<value nick="none" value="0" />
<value nick="switch-to-workspace" value="1" />
<value nick="move-to-workspace" value="2" />
<value nick="switch-windows" value="3" />
</enum>
<enum id="GestureSwipeUp">
<value nick="none" value="0" />

View File

@ -10,15 +10,20 @@
public class Gala.WindowSwitcher : Clutter.Actor {
public const int ICON_SIZE = 64;
public const int WRAPPER_PADDING = 12;
private const string CAPTION_FONT_NAME = "Inter";
private const int MIN_OFFSET = 64;
private const int ANIMATION_DURATION = 200;
public bool opened { get; private set; default = false; }
// https://github.com/elementary/gala/issues/1317#issuecomment-982484415
private const int GESTURE_RANGE_LIMIT = 10;
public Gala.WindowManager? wm { get; construct; }
private Gala.ModalProxy modal_proxy = null;
public GestureTracker gesture_tracker { get; construct; }
public bool opened { get; private set; default = false; }
private bool handling_gesture = false;
private int modifier_mask;
private Gala.ModalProxy modal_proxy = null;
private Granite.Settings granite_settings;
private Clutter.Canvas canvas;
private Clutter.Actor container;
@ -28,8 +33,6 @@ public class Gala.WindowSwitcher : Clutter.Actor {
private Gtk.StyleContext style_context;
private unowned Gtk.CssProvider? dark_style_provider = null;
private int modifier_mask;
private WindowSwitcherIcon? _current_icon = null;
private WindowSwitcherIcon? current_icon {
get {
@ -51,8 +54,11 @@ public class Gala.WindowSwitcher : Clutter.Actor {
private float scaling_factor = 1.0f;
public WindowSwitcher (Gala.WindowManager wm) {
Object (wm: wm);
public WindowSwitcher (Gala.WindowManager wm, GestureTracker gesture_tracker) {
Object (
wm: wm,
gesture_tracker: gesture_tracker
);
}
construct {
@ -184,6 +190,10 @@ public class Gala.WindowSwitcher : Clutter.Actor {
Meta.Display display, Meta.Window? window,
Clutter.KeyEvent event, Meta.KeyBinding binding
) {
if (handling_gesture) {
return;
}
var workspace = display.get_workspace_manager ().get_active_workspace ();
// copied from gnome-shell, finds the primary modifier in the mask
@ -219,6 +229,49 @@ public class Gala.WindowSwitcher : Clutter.Actor {
next_window (backward);
}
public void handle_gesture (GestureDirection direction) {
handling_gesture = true;
unowned var display = wm.get_display ();
unowned var workspace_manager = display.get_workspace_manager ();
unowned var active_workspace = workspace_manager.get_active_workspace ();
var windows_exist = collect_all_windows (display, active_workspace);
if (!windows_exist) {
return;
}
open_switcher ();
// if direction == LEFT we need to move to the end of the list first, thats why last_window_index is set to -1
var last_window_index = direction == RIGHT ? 0 : -1;
GestureTracker.OnUpdate on_animation_update = (percentage) => {
var window_index = GestureTracker.animation_value (0, GESTURE_RANGE_LIMIT, percentage, true);
if (window_index >= container.get_n_children ()) {
return;
}
if (window_index > last_window_index) {
while (last_window_index < window_index) {
next_window (direction == LEFT);
last_window_index++;
}
} else if (window_index < last_window_index) {
while (last_window_index > window_index) {
next_window (direction == RIGHT);
last_window_index--;
}
}
};
GestureTracker.OnEnd on_animation_end = (percentage, cancel_action, calculated_duration) => {
handling_gesture = false;
close_switcher (wm.get_display ().get_current_time ());
};
gesture_tracker.connect_handlers (null, (owned) on_animation_update, (owned) on_animation_end);
}
private bool collect_all_windows (Meta.Display display, Meta.Workspace? workspace) {
var windows = display.get_tab_list (Meta.TabList.NORMAL, workspace);
if (windows == null) {
@ -319,11 +372,6 @@ public class Gala.WindowSwitcher : Clutter.Actor {
);
toggle_display (true);
// if we did not have the grab before the key was released, close immediately
if ((get_current_modifiers () & modifier_mask) == 0) {
close_switcher (wm.get_display ().get_current_time ());
}
}
private void toggle_display (bool show) {
@ -381,7 +429,7 @@ public class Gala.WindowSwitcher : Clutter.Actor {
}
if (!cancel) {
var workspace = window.get_workspace ();
unowned var workspace = window.get_workspace ();
if (workspace != wm.get_display ().get_workspace_manager ().get_active_workspace ()) {
workspace.activate_with_focus (window, time);
} else {
@ -431,7 +479,9 @@ public class Gala.WindowSwitcher : Clutter.Actor {
}
public override void key_focus_out () {
close_switcher (wm.get_display ().get_current_time ());
if (!handling_gesture) {
close_switcher (wm.get_display ().get_current_time ());
}
}
#if HAS_MUTTER45
@ -439,16 +489,20 @@ public class Gala.WindowSwitcher : Clutter.Actor {
#else
private bool container_motion_event (Clutter.MotionEvent event) {
#endif
if (handling_gesture) {
return Clutter.EVENT_STOP;
}
float x, y;
event.get_coords (out x, out y);
var actor = container.get_stage ().get_actor_at_pos (Clutter.PickMode.ALL, (int)x, (int)y);
if (actor == null) {
return true;
return Clutter.EVENT_STOP;
}
var selected = actor as WindowSwitcherIcon;
if (selected == null) {
return true;
return Clutter.EVENT_STOP;
}
if (current_icon != selected) {
@ -463,7 +517,7 @@ public class Gala.WindowSwitcher : Clutter.Actor {
#else
private bool container_mouse_release (Clutter.ButtonEvent event) {
#endif
if (opened && event.get_button () == Clutter.Button.PRIMARY) {
if (opened && event.get_button () == Clutter.Button.PRIMARY && !handling_gesture) {
close_switcher (event.get_time ());
}
@ -475,7 +529,7 @@ public class Gala.WindowSwitcher : Clutter.Actor {
#else
public override bool key_release_event (Clutter.KeyEvent event) {
#endif
if ((get_current_modifiers () & modifier_mask) == 0) {
if ((get_current_modifiers () & modifier_mask) == 0 && !handling_gesture) {
close_switcher (event.get_time ());
}
@ -489,10 +543,14 @@ public class Gala.WindowSwitcher : Clutter.Actor {
#endif
switch (event.get_key_symbol ()) {
case Clutter.Key.Right:
next_window (false);
if (!handling_gesture) {
next_window (false);
}
return Clutter.EVENT_STOP;
case Clutter.Key.Left:
next_window (true);
if (!handling_gesture) {
next_window (true);
}
return Clutter.EVENT_STOP;
case Clutter.Key.Escape:
close_switcher (event.get_time (), true);

View File

@ -71,7 +71,7 @@ namespace Gala {
private Meta.PluginInfo info;
private WindowSwitcher? winswitcher = null;
private WindowSwitcher? window_switcher = null;
private ActivatableComponent? window_overview = null;
public ScreenSaverManager? screensaver { get; private set; }
@ -332,15 +332,15 @@ namespace Gala {
});
if (plugin_manager.window_switcher_provider == null) {
winswitcher = new WindowSwitcher (this);
ui_group.add_child (winswitcher);
window_switcher = new WindowSwitcher (this, gesture_tracker);
ui_group.add_child (window_switcher);
Meta.KeyBinding.set_custom_handler ("switch-applications", (Meta.KeyHandlerFunc) winswitcher.handle_switch_windows);
Meta.KeyBinding.set_custom_handler ("switch-applications-backward", (Meta.KeyHandlerFunc) winswitcher.handle_switch_windows);
Meta.KeyBinding.set_custom_handler ("switch-windows", (Meta.KeyHandlerFunc) winswitcher.handle_switch_windows);
Meta.KeyBinding.set_custom_handler ("switch-windows-backward", (Meta.KeyHandlerFunc) winswitcher.handle_switch_windows);
Meta.KeyBinding.set_custom_handler ("switch-group", (Meta.KeyHandlerFunc) winswitcher.handle_switch_windows);
Meta.KeyBinding.set_custom_handler ("switch-group-backward", (Meta.KeyHandlerFunc) winswitcher.handle_switch_windows);
Meta.KeyBinding.set_custom_handler ("switch-applications", (Meta.KeyHandlerFunc) window_switcher.handle_switch_windows);
Meta.KeyBinding.set_custom_handler ("switch-applications-backward", (Meta.KeyHandlerFunc) window_switcher.handle_switch_windows);
Meta.KeyBinding.set_custom_handler ("switch-windows", (Meta.KeyHandlerFunc) window_switcher.handle_switch_windows);
Meta.KeyBinding.set_custom_handler ("switch-windows-backward", (Meta.KeyHandlerFunc) window_switcher.handle_switch_windows);
Meta.KeyBinding.set_custom_handler ("switch-group", (Meta.KeyHandlerFunc) window_switcher.handle_switch_windows);
Meta.KeyBinding.set_custom_handler ("switch-group-backward", (Meta.KeyHandlerFunc) window_switcher.handle_switch_windows);
}
if (plugin_manager.window_overview_provider == null
@ -530,12 +530,8 @@ namespace Gala {
return;
}
var can_handle_swipe = (
gesture.type == Clutter.EventType.TOUCHPAD_SWIPE &&
(gesture.direction == GestureDirection.LEFT || gesture.direction == GestureDirection.RIGHT)
);
if (!can_handle_swipe) {
if (gesture.type != Clutter.EventType.TOUCHPAD_SWIPE ||
(gesture.direction != GestureDirection.LEFT && gesture.direction != GestureDirection.RIGHT)) {
return;
}
@ -550,6 +546,9 @@ namespace Gala {
var three_fingers_move_to_workspace = fingers == 3 && three_finger_swipe_horizontal == "move-to-workspace";
var four_fingers_move_to_workspace = fingers == 4 && four_finger_swipe_horizontal == "move-to-workspace";
var three_fingers_switch_windows = fingers == 3 && three_finger_swipe_horizontal == "switch-windows";
var four_fingers_switch_windows = fingers == 4 && four_finger_swipe_horizontal == "switch-windows";
switch_workspace_with_gesture = three_fingers_switch_to_workspace || four_fingers_switch_to_workspace;
if (switch_workspace_with_gesture) {
var direction = gesture_tracker.settings.get_natural_scroll_direction (gesture);
@ -572,6 +571,11 @@ namespace Gala {
switch_to_next_workspace (direction);
return;
}
var switch_windows = three_fingers_switch_windows || four_fingers_switch_windows;
if (switch_windows && !window_switcher.opened) {
window_switcher.handle_gesture (gesture.direction);
}
}
/**