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="none" value="0" />
<value nick="switch-to-workspace" value="1" /> <value nick="switch-to-workspace" value="1" />
<value nick="move-to-workspace" value="2" /> <value nick="move-to-workspace" value="2" />
<value nick="switch-windows" value="3" />
</enum> </enum>
<enum id="GestureSwipeUp"> <enum id="GestureSwipeUp">
<value nick="none" value="0" /> <value nick="none" value="0" />

View File

@ -10,15 +10,20 @@
public class Gala.WindowSwitcher : Clutter.Actor { public class Gala.WindowSwitcher : Clutter.Actor {
public const int ICON_SIZE = 64; public const int ICON_SIZE = 64;
public const int WRAPPER_PADDING = 12; public const int WRAPPER_PADDING = 12;
private const string CAPTION_FONT_NAME = "Inter"; private const string CAPTION_FONT_NAME = "Inter";
private const int MIN_OFFSET = 64; private const int MIN_OFFSET = 64;
private const int ANIMATION_DURATION = 200; private const int ANIMATION_DURATION = 200;
// https://github.com/elementary/gala/issues/1317#issuecomment-982484415
public bool opened { get; private set; default = false; } private const int GESTURE_RANGE_LIMIT = 10;
public Gala.WindowManager? wm { get; construct; } 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 Granite.Settings granite_settings;
private Clutter.Canvas canvas; private Clutter.Canvas canvas;
private Clutter.Actor container; private Clutter.Actor container;
@ -28,8 +33,6 @@ public class Gala.WindowSwitcher : Clutter.Actor {
private Gtk.StyleContext style_context; private Gtk.StyleContext style_context;
private unowned Gtk.CssProvider? dark_style_provider = null; private unowned Gtk.CssProvider? dark_style_provider = null;
private int modifier_mask;
private WindowSwitcherIcon? _current_icon = null; private WindowSwitcherIcon? _current_icon = null;
private WindowSwitcherIcon? current_icon { private WindowSwitcherIcon? current_icon {
get { get {
@ -51,8 +54,11 @@ public class Gala.WindowSwitcher : Clutter.Actor {
private float scaling_factor = 1.0f; private float scaling_factor = 1.0f;
public WindowSwitcher (Gala.WindowManager wm) { public WindowSwitcher (Gala.WindowManager wm, GestureTracker gesture_tracker) {
Object (wm: wm); Object (
wm: wm,
gesture_tracker: gesture_tracker
);
} }
construct { construct {
@ -184,6 +190,10 @@ public class Gala.WindowSwitcher : Clutter.Actor {
Meta.Display display, Meta.Window? window, Meta.Display display, Meta.Window? window,
Clutter.KeyEvent event, Meta.KeyBinding binding Clutter.KeyEvent event, Meta.KeyBinding binding
) { ) {
if (handling_gesture) {
return;
}
var workspace = display.get_workspace_manager ().get_active_workspace (); var workspace = display.get_workspace_manager ().get_active_workspace ();
// copied from gnome-shell, finds the primary modifier in the mask // copied from gnome-shell, finds the primary modifier in the mask
@ -219,6 +229,49 @@ public class Gala.WindowSwitcher : Clutter.Actor {
next_window (backward); 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) { private bool collect_all_windows (Meta.Display display, Meta.Workspace? workspace) {
var windows = display.get_tab_list (Meta.TabList.NORMAL, workspace); var windows = display.get_tab_list (Meta.TabList.NORMAL, workspace);
if (windows == null) { if (windows == null) {
@ -319,11 +372,6 @@ public class Gala.WindowSwitcher : Clutter.Actor {
); );
toggle_display (true); 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) { private void toggle_display (bool show) {
@ -381,7 +429,7 @@ public class Gala.WindowSwitcher : Clutter.Actor {
} }
if (!cancel) { if (!cancel) {
var workspace = window.get_workspace (); unowned var workspace = window.get_workspace ();
if (workspace != wm.get_display ().get_workspace_manager ().get_active_workspace ()) { if (workspace != wm.get_display ().get_workspace_manager ().get_active_workspace ()) {
workspace.activate_with_focus (window, time); workspace.activate_with_focus (window, time);
} else { } else {
@ -431,7 +479,9 @@ public class Gala.WindowSwitcher : Clutter.Actor {
} }
public override void key_focus_out () { 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 #if HAS_MUTTER45
@ -439,16 +489,20 @@ public class Gala.WindowSwitcher : Clutter.Actor {
#else #else
private bool container_motion_event (Clutter.MotionEvent event) { private bool container_motion_event (Clutter.MotionEvent event) {
#endif #endif
if (handling_gesture) {
return Clutter.EVENT_STOP;
}
float x, y; float x, y;
event.get_coords (out x, out y); event.get_coords (out x, out y);
var actor = container.get_stage ().get_actor_at_pos (Clutter.PickMode.ALL, (int)x, (int)y); var actor = container.get_stage ().get_actor_at_pos (Clutter.PickMode.ALL, (int)x, (int)y);
if (actor == null) { if (actor == null) {
return true; return Clutter.EVENT_STOP;
} }
var selected = actor as WindowSwitcherIcon; var selected = actor as WindowSwitcherIcon;
if (selected == null) { if (selected == null) {
return true; return Clutter.EVENT_STOP;
} }
if (current_icon != selected) { if (current_icon != selected) {
@ -463,7 +517,7 @@ public class Gala.WindowSwitcher : Clutter.Actor {
#else #else
private bool container_mouse_release (Clutter.ButtonEvent event) { private bool container_mouse_release (Clutter.ButtonEvent event) {
#endif #endif
if (opened && event.get_button () == Clutter.Button.PRIMARY) { if (opened && event.get_button () == Clutter.Button.PRIMARY && !handling_gesture) {
close_switcher (event.get_time ()); close_switcher (event.get_time ());
} }
@ -475,7 +529,7 @@ public class Gala.WindowSwitcher : Clutter.Actor {
#else #else
public override bool key_release_event (Clutter.KeyEvent event) { public override bool key_release_event (Clutter.KeyEvent event) {
#endif #endif
if ((get_current_modifiers () & modifier_mask) == 0) { if ((get_current_modifiers () & modifier_mask) == 0 && !handling_gesture) {
close_switcher (event.get_time ()); close_switcher (event.get_time ());
} }
@ -489,10 +543,14 @@ public class Gala.WindowSwitcher : Clutter.Actor {
#endif #endif
switch (event.get_key_symbol ()) { switch (event.get_key_symbol ()) {
case Clutter.Key.Right: case Clutter.Key.Right:
next_window (false); if (!handling_gesture) {
next_window (false);
}
return Clutter.EVENT_STOP; return Clutter.EVENT_STOP;
case Clutter.Key.Left: case Clutter.Key.Left:
next_window (true); if (!handling_gesture) {
next_window (true);
}
return Clutter.EVENT_STOP; return Clutter.EVENT_STOP;
case Clutter.Key.Escape: case Clutter.Key.Escape:
close_switcher (event.get_time (), true); close_switcher (event.get_time (), true);

View File

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