mirror of
https://github.com/elementary/gala.git
synced 2024-08-16 18:40:27 +03:00
Multi-touch support (#983)
This commit is contained in:
parent
f5725fd1bb
commit
933ca7bf7f
@ -313,4 +313,29 @@
|
|||||||
<summary>Only show corner masks on primary monitor</summary>
|
<summary>Only show corner masks on primary monitor</summary>
|
||||||
</key>
|
</key>
|
||||||
</schema>
|
</schema>
|
||||||
|
|
||||||
|
<schema path="/io/elementary/desktop/wm/gestures/" id="io.elementary.desktop.wm.gestures">
|
||||||
|
<key type="b" name="multitasking-gesture-enabled">
|
||||||
|
<default>true</default>
|
||||||
|
<summary>Multitasking view gesture</summary>
|
||||||
|
<description>If enabled, swipe up with the number of fingers set in io.elementary.desktop.wm.gestures.multitasking-gesture-fingers to show the multitasking view</description>
|
||||||
|
</key>
|
||||||
|
<key type="i" name="multitasking-gesture-fingers">
|
||||||
|
<default>3</default>
|
||||||
|
<range min="3" max="4"/>
|
||||||
|
<summary>Multitasking view gesture fingers</summary>
|
||||||
|
<description>Number of fingers used in the multitasking view gesture</description>
|
||||||
|
</key>
|
||||||
|
<key type="b" name="workspaces-gesture-enabled">
|
||||||
|
<default>true</default>
|
||||||
|
<summary>Switch workspace gesture</summary>
|
||||||
|
<description>If enabled, swipe left/right with the number of fingers set in io.elementary.desktop.wm.gestures.workspaces-gesture-fingers to switch between workspaces</description>
|
||||||
|
</key>
|
||||||
|
<key type="i" name="workspaces-gesture-fingers">
|
||||||
|
<default>3</default>
|
||||||
|
<range min="3" max="4"/>
|
||||||
|
<summary>Switch workspace gesture fingers</summary>
|
||||||
|
<description>Number of fingers used in the switch workspaces gesture</description>
|
||||||
|
</key>
|
||||||
|
</schema>
|
||||||
</schemalist>
|
</schemalist>
|
||||||
|
@ -34,8 +34,11 @@ namespace Gala {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The component was requested to be closed.
|
* The component was requested to be closed.
|
||||||
|
*
|
||||||
|
* @param hints The hashmap may contain special parameters that are useful
|
||||||
|
* to the component.
|
||||||
*/
|
*/
|
||||||
public abstract void close ();
|
public abstract void close (HashTable<string,Variant>? hints = null);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Should return whether the component is currently opened. Used mainly for
|
* Should return whether the component is currently opened. Used mainly for
|
||||||
|
@ -106,6 +106,11 @@ namespace Gala {
|
|||||||
*/
|
*/
|
||||||
public abstract Meta.BackgroundGroup background_group { get; protected set; }
|
public abstract Meta.BackgroundGroup background_group { get; protected set; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* View that allows to see and manage all your windows and desktops.
|
||||||
|
*/
|
||||||
|
public abstract Gala.ActivatableComponent workspace_view { get; protected set; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether animations should be displayed.
|
* Whether animations should be displayed.
|
||||||
*/
|
*/
|
||||||
@ -164,6 +169,7 @@ namespace Gala {
|
|||||||
*
|
*
|
||||||
* @param direction The direction in which to switch
|
* @param direction The direction in which to switch
|
||||||
*/
|
*/
|
||||||
public abstract void switch_to_next_workspace (Meta.MotionDirection direction);
|
public abstract void switch_to_next_workspace (Meta.MotionDirection direction,
|
||||||
|
HashTable<string,Variant>? hints = null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -193,6 +193,7 @@ subdir('daemon')
|
|||||||
subdir('plugins/maskcorners')
|
subdir('plugins/maskcorners')
|
||||||
subdir('plugins/pip')
|
subdir('plugins/pip')
|
||||||
subdir('plugins/template')
|
subdir('plugins/template')
|
||||||
|
subdir('plugins/touchegg')
|
||||||
subdir('plugins/zoom')
|
subdir('plugins/zoom')
|
||||||
if get_option('documentation')
|
if get_option('documentation')
|
||||||
subdir('docs')
|
subdir('docs')
|
||||||
|
200
plugins/touchegg/Client.vala
Normal file
200
plugins/touchegg/Client.vala
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 elementary, Inc (https://elementary.io)
|
||||||
|
* 2020 José Expósito <jose.exposito89@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Gala.Plugins.Touchegg {
|
||||||
|
/**
|
||||||
|
* Daemon event type.
|
||||||
|
*/
|
||||||
|
private enum GestureEventType {
|
||||||
|
UNKNOWN = 0,
|
||||||
|
BEGIN = 1,
|
||||||
|
UPDATE = 2,
|
||||||
|
END = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Daemon event.
|
||||||
|
*/
|
||||||
|
private struct GestureEvent {
|
||||||
|
public uint32 event_size;
|
||||||
|
public GestureEventType event_type;
|
||||||
|
public GestureType type;
|
||||||
|
public GestureDirection direction;
|
||||||
|
public int percentage;
|
||||||
|
public int fingers;
|
||||||
|
public uint64 elapsed_time;
|
||||||
|
public DeviceType performed_on_device_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class connects to the Touchégg daemon to receive touch events.
|
||||||
|
* See: https://github.com/JoseExposito/touchegg
|
||||||
|
*/
|
||||||
|
public class Client : Object {
|
||||||
|
public signal void on_gesture_begin (Gesture gesture);
|
||||||
|
public signal void on_gesture_update (Gesture gesture);
|
||||||
|
public signal void on_gesture_end (Gesture gesture);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum number of reconnection attempts to the daemon.
|
||||||
|
*/
|
||||||
|
private const int MAX_RECONNECTION_ATTEMPTS = 5;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Time to sleep between reconnection attempts.
|
||||||
|
*/
|
||||||
|
private const int RECONNECTION_USLEEP_TIME = 5000000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Socket used to connect to the daemon.
|
||||||
|
*/
|
||||||
|
private Socket? socket = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current number of reconnection attempts.
|
||||||
|
*/
|
||||||
|
private int reconnection_attempts = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Struct to store the received event. It is useful to keep it to be able to finish ongoing
|
||||||
|
* actions in case of disconnection
|
||||||
|
*/
|
||||||
|
private GestureEvent *event = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start receiving gestures.
|
||||||
|
*/
|
||||||
|
public void run () throws IOError {
|
||||||
|
new Thread<void*> (null, receive_events);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop () {
|
||||||
|
if (socket != null) {
|
||||||
|
try {
|
||||||
|
reconnection_attempts = MAX_RECONNECTION_ATTEMPTS;
|
||||||
|
socket.close ();
|
||||||
|
} catch (Error e) {
|
||||||
|
// Ignore this error, the process is being killed as this point
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void* receive_events () {
|
||||||
|
uint8[] event_buffer = new uint8[sizeof (GestureEvent)];
|
||||||
|
|
||||||
|
while (reconnection_attempts < MAX_RECONNECTION_ATTEMPTS) {
|
||||||
|
try {
|
||||||
|
if (socket == null || !socket.is_connected ()) {
|
||||||
|
debug ("Connecting to Touchégg daemon");
|
||||||
|
socket = new Socket (SocketFamily.UNIX, SocketType.STREAM, 0);
|
||||||
|
if (socket == null) {
|
||||||
|
throw new GLib.IOError.CONNECTION_REFUSED (
|
||||||
|
"Error connecting to Touchégg daemon: Can not create socket"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
UnixSocketAddress address = new UnixSocketAddress.as_abstract ("/touchegg", -1);
|
||||||
|
bool connected = socket.connect (address);
|
||||||
|
if (!connected) {
|
||||||
|
throw new GLib.IOError.CONNECTION_REFUSED ("Error connecting to Touchégg daemon");
|
||||||
|
}
|
||||||
|
|
||||||
|
reconnection_attempts = 0;
|
||||||
|
debug ("Connection to Touchégg daemon established");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the event
|
||||||
|
ssize_t bytes_received = socket.receive (event_buffer);
|
||||||
|
if (bytes_received <= 0) {
|
||||||
|
throw new GLib.IOError.CONNECTION_CLOSED ("Error reading socket");
|
||||||
|
}
|
||||||
|
event = (GestureEvent *) event_buffer;
|
||||||
|
|
||||||
|
// The daemon could add events not supported by this plugin yet
|
||||||
|
// Discard any extra data
|
||||||
|
if (bytes_received < event.event_size) {
|
||||||
|
ssize_t pending_bytes = event.event_size - bytes_received;
|
||||||
|
uint8[] discard_buffer = new uint8[pending_bytes];
|
||||||
|
bytes_received = socket.receive (discard_buffer);
|
||||||
|
if (bytes_received <= 0) {
|
||||||
|
throw new GLib.IOError.CONNECTION_CLOSED ("Error reading socket");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
emit_event (event);
|
||||||
|
} catch (Error e) {
|
||||||
|
warning ("Connection to Touchégg daemon lost: %s", e.message);
|
||||||
|
handle_disconnection ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handle_disconnection () {
|
||||||
|
reconnection_attempts++;
|
||||||
|
|
||||||
|
if (event != null
|
||||||
|
&& event.event_type != GestureEventType.UNKNOWN
|
||||||
|
&& event.event_type != GestureEventType.END) {
|
||||||
|
event.event_type = GestureEventType.END;
|
||||||
|
emit_event (event);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (socket != null) {
|
||||||
|
try {
|
||||||
|
socket.close ();
|
||||||
|
} catch (Error e) {
|
||||||
|
// The connection is already closed at this point, ignore this error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reconnection_attempts < MAX_RECONNECTION_ATTEMPTS) {
|
||||||
|
debug ("Reconnecting to Touchégg daemon in 5 seconds");
|
||||||
|
Thread.usleep (RECONNECTION_USLEEP_TIME);
|
||||||
|
} else {
|
||||||
|
warning ("Maximum number of reconnections reached, aborting");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void emit_event (GestureEvent *event) {
|
||||||
|
Gesture gesture = new Gesture () {
|
||||||
|
type = event.type,
|
||||||
|
direction = event.direction,
|
||||||
|
percentage = event.percentage,
|
||||||
|
fingers = event.fingers,
|
||||||
|
elapsed_time = event.elapsed_time,
|
||||||
|
performed_on_device_type = event.performed_on_device_type
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (event.event_type) {
|
||||||
|
case GestureEventType.BEGIN:
|
||||||
|
on_gesture_begin (gesture);
|
||||||
|
break;
|
||||||
|
case GestureEventType.UPDATE:
|
||||||
|
on_gesture_update (gesture);
|
||||||
|
break;
|
||||||
|
case GestureEventType.END:
|
||||||
|
on_gesture_end (gesture);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
54
plugins/touchegg/Gesture.vala
Normal file
54
plugins/touchegg/Gesture.vala
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 elementary, Inc (https://elementary.io)
|
||||||
|
* 2020 José Expósito <jose.exposito89@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Gala.Plugins.Touchegg {
|
||||||
|
public enum GestureType {
|
||||||
|
NOT_SUPPORTED = 0,
|
||||||
|
SWIPE = 1,
|
||||||
|
PINCH = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum GestureDirection {
|
||||||
|
UNKNOWN = 0,
|
||||||
|
|
||||||
|
// GestureType.SWIPE
|
||||||
|
UP = 1,
|
||||||
|
DOWN = 2,
|
||||||
|
LEFT = 3,
|
||||||
|
RIGHT = 4,
|
||||||
|
|
||||||
|
// GestureType.PINCH
|
||||||
|
IN = 5,
|
||||||
|
OUT = 6,
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum DeviceType {
|
||||||
|
UNKNOWN = 0,
|
||||||
|
TOUCHPAD = 1,
|
||||||
|
TOUCHSCREEN = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Gesture {
|
||||||
|
public GestureType type;
|
||||||
|
public GestureDirection direction;
|
||||||
|
public int percentage;
|
||||||
|
public int fingers;
|
||||||
|
public uint64 elapsed_time;
|
||||||
|
public DeviceType performed_on_device_type;
|
||||||
|
}
|
||||||
|
}
|
153
plugins/touchegg/Main.vala
Normal file
153
plugins/touchegg/Main.vala
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 elementary, Inc (https://elementary.io)
|
||||||
|
* 2020 José Expósito <jose.exposito89@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class Gala.Plugins.Touchegg.Plugin : Gala.Plugin {
|
||||||
|
private Gala.WindowManager? wm = null;
|
||||||
|
private Client? client = null;
|
||||||
|
private GLib.Settings gala_settings;
|
||||||
|
private GLib.Settings touchpad_settings;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Percentage of the animation to be completed to apply the action.
|
||||||
|
*/
|
||||||
|
private const int SUCCESS_THRESHOLD = 20;
|
||||||
|
|
||||||
|
public override void initialize (Gala.WindowManager window_manager) {
|
||||||
|
wm = window_manager;
|
||||||
|
gala_settings = new GLib.Settings ("io.elementary.desktop.wm.gestures");
|
||||||
|
touchpad_settings = new GLib.Settings ("org.gnome.desktop.peripherals.touchpad");
|
||||||
|
|
||||||
|
client = new Client ();
|
||||||
|
client.on_gesture_begin.connect ((gesture) => Idle.add (() => {
|
||||||
|
on_handle_gesture (gesture, "begin");
|
||||||
|
return false;
|
||||||
|
}));
|
||||||
|
client.on_gesture_update.connect ((gesture) => Idle.add (() => {
|
||||||
|
on_handle_gesture (gesture, "update");
|
||||||
|
return false;
|
||||||
|
}));
|
||||||
|
client.on_gesture_end.connect ((gesture) => Idle.add (() => {
|
||||||
|
on_handle_gesture (gesture, "end");
|
||||||
|
return false;
|
||||||
|
}));
|
||||||
|
|
||||||
|
try {
|
||||||
|
client.run ();
|
||||||
|
} catch (Error e) {
|
||||||
|
warning ("Error initializing Touchégg client: %s", e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void destroy () {
|
||||||
|
if (client != null) {
|
||||||
|
client.stop ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void on_handle_gesture (Gesture gesture, string event) {
|
||||||
|
// debug (@"Gesture $(event): $(gesture.type) - $(gesture.direction) - $(gesture.fingers) fingers - $(gesture.percentage)% - $(gesture.elapsed_time) - $(gesture.performed_on_device_type)");
|
||||||
|
var hints = build_hints_from_gesture (gesture, event);
|
||||||
|
|
||||||
|
if (is_open_workspace_gesture (gesture)) {
|
||||||
|
wm.workspace_view.open (hints);
|
||||||
|
} else if (is_close_workspace_gesture (gesture)) {
|
||||||
|
wm.workspace_view.close (hints);
|
||||||
|
} else if (is_next_desktop_gesture (gesture)) {
|
||||||
|
if (!wm.workspace_view.is_opened ()) {
|
||||||
|
wm.switch_to_next_workspace (Meta.MotionDirection.RIGHT, hints);
|
||||||
|
}
|
||||||
|
} else if (is_previous_desktop_gesture (gesture)) {
|
||||||
|
if (!wm.workspace_view.is_opened ()) {
|
||||||
|
wm.switch_to_next_workspace (Meta.MotionDirection.LEFT, hints);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private GLib.HashTable<string, Variant> build_hints_from_gesture (Gesture gesture, string event) {
|
||||||
|
var hints = new GLib.HashTable<string, Variant> (str_hash, str_equal);
|
||||||
|
hints.insert ("manual_animation", new Variant.boolean (true));
|
||||||
|
hints.insert ("event", new Variant.string (event));
|
||||||
|
hints.insert ("percentage", new Variant.int32 (gesture.percentage));
|
||||||
|
|
||||||
|
if (event == "end") {
|
||||||
|
hints.insert ("cancel_action", new Variant.boolean (gesture.percentage < SUCCESS_THRESHOLD));
|
||||||
|
}
|
||||||
|
|
||||||
|
return hints;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool is_open_workspace_gesture (Gesture gesture) {
|
||||||
|
bool enabled = gala_settings.get_boolean ("multitasking-gesture-enabled");
|
||||||
|
int fingers = gala_settings.get_int ("multitasking-gesture-fingers");
|
||||||
|
|
||||||
|
return enabled
|
||||||
|
&& gesture.type == GestureType.SWIPE
|
||||||
|
&& gesture.direction == GestureDirection.UP
|
||||||
|
&& gesture.fingers == fingers;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool is_close_workspace_gesture (Gesture gesture) {
|
||||||
|
bool enabled = gala_settings.get_boolean ("multitasking-gesture-enabled");
|
||||||
|
int fingers = gala_settings.get_int ("multitasking-gesture-fingers");
|
||||||
|
|
||||||
|
return enabled
|
||||||
|
&& gesture.type == GestureType.SWIPE
|
||||||
|
&& gesture.direction == GestureDirection.DOWN
|
||||||
|
&& gesture.fingers == fingers;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool is_next_desktop_gesture (Gesture gesture) {
|
||||||
|
bool enabled = gala_settings.get_boolean ("workspaces-gesture-enabled");
|
||||||
|
int fingers = gala_settings.get_int ("workspaces-gesture-fingers");
|
||||||
|
bool natural_scroll = (gesture.performed_on_device_type == DeviceType.TOUCHSCREEN)
|
||||||
|
? true
|
||||||
|
: touchpad_settings.get_boolean ("natural-scroll");
|
||||||
|
var direction = natural_scroll ? GestureDirection.LEFT : GestureDirection.RIGHT;
|
||||||
|
|
||||||
|
return enabled
|
||||||
|
&& gesture.type == GestureType.SWIPE
|
||||||
|
&& gesture.direction == direction
|
||||||
|
&& gesture.fingers == fingers;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool is_previous_desktop_gesture (Gesture gesture) {
|
||||||
|
bool enabled = gala_settings.get_boolean ("workspaces-gesture-enabled");
|
||||||
|
int fingers = gala_settings.get_int ("workspaces-gesture-fingers");
|
||||||
|
bool natural_scroll = (gesture.performed_on_device_type == DeviceType.TOUCHSCREEN)
|
||||||
|
? true
|
||||||
|
: touchpad_settings.get_boolean ("natural-scroll");
|
||||||
|
var direction = natural_scroll ? GestureDirection.RIGHT : GestureDirection.LEFT;
|
||||||
|
|
||||||
|
return enabled
|
||||||
|
&& gesture.type == GestureType.SWIPE
|
||||||
|
&& gesture.direction == direction
|
||||||
|
&& gesture.fingers == fingers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Gala.PluginInfo register_plugin () {
|
||||||
|
return Gala.PluginInfo () {
|
||||||
|
name = "Touchégg",
|
||||||
|
author = "José Expósito <jose.exposito89@gmail.com>",
|
||||||
|
plugin_type = typeof (Gala.Plugins.Touchegg.Plugin),
|
||||||
|
provides = Gala.PluginFunction.ADDITION,
|
||||||
|
load_priority = Gala.LoadPriority.DEFERRED
|
||||||
|
};
|
||||||
|
}
|
15
plugins/touchegg/meson.build
Normal file
15
plugins/touchegg/meson.build
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
gala_touchegg_sources = [
|
||||||
|
'Main.vala',
|
||||||
|
'Client.vala',
|
||||||
|
'Gesture.vala',
|
||||||
|
]
|
||||||
|
|
||||||
|
gala_touchegg_lib = shared_library(
|
||||||
|
'gala-touchegg',
|
||||||
|
gala_touchegg_sources,
|
||||||
|
dependencies: [gala_dep, gala_base_dep],
|
||||||
|
include_directories: config_inc_dir,
|
||||||
|
install: true,
|
||||||
|
install_dir: plugins_dir,
|
||||||
|
install_rpath: mutter_typelib_dir,
|
||||||
|
)
|
84
src/GestureAnimationDirector.vala
Normal file
84
src/GestureAnimationDirector.vala
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 elementary, Inc (https://elementary.io)
|
||||||
|
* 2020 José Expósito <jose.exposito89@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class Gala.GestureAnimationDirector : Object {
|
||||||
|
public bool running { get; set; default = false; }
|
||||||
|
public bool canceling { get; set; default = false; }
|
||||||
|
|
||||||
|
public signal void on_animation_begin (int percentage);
|
||||||
|
public signal void on_animation_update (int percentage);
|
||||||
|
public signal void on_animation_end (int percentage, bool cancel_action);
|
||||||
|
|
||||||
|
public delegate void OnBegin (int percentage);
|
||||||
|
public delegate void OnUpdate (int percentage);
|
||||||
|
public delegate void OnEnd (int percentage, bool cancel_action);
|
||||||
|
|
||||||
|
private Gee.ArrayList<ulong> handlers;
|
||||||
|
|
||||||
|
construct {
|
||||||
|
handlers = new Gee.ArrayList<ulong> ();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void connect_handlers (owned OnBegin? on_begin, owned OnUpdate? on_update, owned OnEnd? on_end) {
|
||||||
|
if (on_begin != null) {
|
||||||
|
ulong handler_id = on_animation_begin.connect ((percentage) => on_begin (percentage));
|
||||||
|
handlers.add (handler_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (on_update != null) {
|
||||||
|
ulong handler_id = on_animation_update.connect ((percentage) => on_update (percentage));
|
||||||
|
handlers.add (handler_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (on_end != null) {
|
||||||
|
ulong handler_id = on_animation_end.connect ((percentage, cancel_action) => on_end (percentage, cancel_action));
|
||||||
|
handlers.add (handler_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void disconnect_all_handlers () {
|
||||||
|
foreach (var handler in handlers) {
|
||||||
|
disconnect (handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
handlers.clear ();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update_animation (HashTable<string,Variant> hints) {
|
||||||
|
string event = hints.get ("event").get_string ();
|
||||||
|
int32 percentage = hints.get ("percentage").get_int32 ();
|
||||||
|
|
||||||
|
switch (event) {
|
||||||
|
case "begin":
|
||||||
|
on_animation_begin (percentage);
|
||||||
|
break;
|
||||||
|
case "update":
|
||||||
|
on_animation_update (percentage);
|
||||||
|
break;
|
||||||
|
case "end":
|
||||||
|
default:
|
||||||
|
var cancel_action = hints.get ("cancel_action").get_boolean ();
|
||||||
|
on_animation_end (percentage, cancel_action);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float animation_value (float initial_value, float target_value, int percentage) {
|
||||||
|
return (((target_value - initial_value) * percentage) / 100) + initial_value;
|
||||||
|
}
|
||||||
|
}
|
@ -35,17 +35,18 @@ namespace Gala {
|
|||||||
public Screen screen { get; construct; }
|
public Screen screen { get; construct; }
|
||||||
#endif
|
#endif
|
||||||
public int monitor { get; construct; }
|
public int monitor { get; construct; }
|
||||||
|
public GestureAnimationDirector gesture_animation_director { get; construct; }
|
||||||
|
|
||||||
WindowCloneContainer window_container;
|
WindowCloneContainer window_container;
|
||||||
BackgroundManager background;
|
BackgroundManager background;
|
||||||
|
|
||||||
#if HAS_MUTTER330
|
#if HAS_MUTTER330
|
||||||
public MonitorClone (Meta.Display display, int monitor) {
|
public MonitorClone (Meta.Display display, int monitor, GestureAnimationDirector gesture_animation_director) {
|
||||||
Object (display: display, monitor: monitor);
|
Object (display: display, monitor: monitor, gesture_animation_director: gesture_animation_director);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
public MonitorClone (Screen screen, int monitor) {
|
public MonitorClone (Screen screen, int monitor, GestureAnimationDirector gesture_animation_director) {
|
||||||
Object (screen: screen, monitor: monitor);
|
Object (screen: screen, monitor: monitor, gesture_animation_director: gesture_animation_director);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -59,7 +60,7 @@ namespace Gala {
|
|||||||
#endif
|
#endif
|
||||||
background.set_easing_duration (MultitaskingView.ANIMATION_DURATION);
|
background.set_easing_duration (MultitaskingView.ANIMATION_DURATION);
|
||||||
|
|
||||||
window_container = new WindowCloneContainer ();
|
window_container = new WindowCloneContainer (gesture_animation_director);
|
||||||
window_container.window_selected.connect ((w) => { window_selected (w); });
|
window_container.window_selected.connect ((w) => { window_selected (w); });
|
||||||
#if HAS_MUTTER330
|
#if HAS_MUTTER330
|
||||||
display.restacked.connect (window_container.restack_windows);
|
display.restacked.connect (window_container.restack_windows);
|
||||||
|
@ -27,6 +27,9 @@ namespace Gala {
|
|||||||
public class MultitaskingView : Actor, ActivatableComponent {
|
public class MultitaskingView : Actor, ActivatableComponent {
|
||||||
public const int ANIMATION_DURATION = 250;
|
public const int ANIMATION_DURATION = 250;
|
||||||
public const AnimationMode ANIMATION_MODE = AnimationMode.EASE_OUT_QUAD;
|
public const AnimationMode ANIMATION_MODE = AnimationMode.EASE_OUT_QUAD;
|
||||||
|
|
||||||
|
public GestureAnimationDirector gesture_animation_director { get; construct; }
|
||||||
|
|
||||||
const int SMOOTH_SCROLL_DELAY = 500;
|
const int SMOOTH_SCROLL_DELAY = 500;
|
||||||
|
|
||||||
public WindowManager wm { get; construct; }
|
public WindowManager wm { get; construct; }
|
||||||
@ -48,8 +51,8 @@ namespace Gala {
|
|||||||
Actor workspaces;
|
Actor workspaces;
|
||||||
Actor dock_clones;
|
Actor dock_clones;
|
||||||
|
|
||||||
public MultitaskingView (WindowManager wm) {
|
public MultitaskingView (WindowManager wm, GestureAnimationDirector gesture_animation_director) {
|
||||||
Object (wm: wm);
|
Object (wm: wm, gesture_animation_director: gesture_animation_director);
|
||||||
}
|
}
|
||||||
|
|
||||||
construct {
|
construct {
|
||||||
@ -63,6 +66,7 @@ namespace Gala {
|
|||||||
#else
|
#else
|
||||||
screen = wm.get_screen ();
|
screen = wm.get_screen ();
|
||||||
#endif
|
#endif
|
||||||
|
gesture_animation_director = new GestureAnimationDirector ();
|
||||||
|
|
||||||
workspaces = new Actor ();
|
workspaces = new Actor ();
|
||||||
workspaces.set_easing_mode (AnimationMode.EASE_OUT_QUAD);
|
workspaces.set_easing_mode (AnimationMode.EASE_OUT_QUAD);
|
||||||
@ -182,7 +186,7 @@ namespace Gala {
|
|||||||
if (monitor == primary)
|
if (monitor == primary)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var monitor_clone = new MonitorClone (display, monitor);
|
var monitor_clone = new MonitorClone (display, monitor, gesture_animation_director);
|
||||||
monitor_clone.window_selected.connect (window_selected);
|
monitor_clone.window_selected.connect (window_selected);
|
||||||
monitor_clone.visible = opened;
|
monitor_clone.visible = opened;
|
||||||
|
|
||||||
@ -200,7 +204,7 @@ namespace Gala {
|
|||||||
if (monitor == primary)
|
if (monitor == primary)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var monitor_clone = new MonitorClone (screen, monitor);
|
var monitor_clone = new MonitorClone (screen, monitor, gesture_animation_director);
|
||||||
monitor_clone.window_selected.connect (window_selected);
|
monitor_clone.window_selected.connect (window_selected);
|
||||||
monitor_clone.visible = opened;
|
monitor_clone.visible = opened;
|
||||||
|
|
||||||
@ -359,9 +363,9 @@ namespace Gala {
|
|||||||
void add_workspace (int num) {
|
void add_workspace (int num) {
|
||||||
#if HAS_MUTTER330
|
#if HAS_MUTTER330
|
||||||
unowned Meta.WorkspaceManager manager = display.get_workspace_manager ();
|
unowned Meta.WorkspaceManager manager = display.get_workspace_manager ();
|
||||||
var workspace = new WorkspaceClone (manager.get_workspace_by_index (num));
|
var workspace = new WorkspaceClone (manager.get_workspace_by_index (num), gesture_animation_director);
|
||||||
#else
|
#else
|
||||||
var workspace = new WorkspaceClone (screen.get_workspace_by_index (num));
|
var workspace = new WorkspaceClone (screen.get_workspace_by_index (num), gesture_animation_director);
|
||||||
#endif
|
#endif
|
||||||
workspace.window_selected.connect (window_selected);
|
workspace.window_selected.connect (window_selected);
|
||||||
workspace.selected.connect (activate_workspace);
|
workspace.selected.connect (activate_workspace);
|
||||||
@ -544,20 +548,40 @@ namespace Gala {
|
|||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public void open (HashTable<string,Variant>? hints = null) {
|
public void open (HashTable<string,Variant>? hints = null) {
|
||||||
if (opened)
|
bool manual_animation = hints != null && hints.get ("manual_animation").get_boolean ();
|
||||||
return;
|
|
||||||
|
|
||||||
toggle ();
|
if (!opened) {
|
||||||
|
if (manual_animation && !animating) {
|
||||||
|
debug ("Starting MultitaskingView manual open animation");
|
||||||
|
gesture_animation_director.running = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
toggle ();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opened && manual_animation && gesture_animation_director.running) {
|
||||||
|
gesture_animation_director.update_animation (hints);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public void close () {
|
public void close (HashTable<string,Variant>? hints = null) {
|
||||||
if (!opened)
|
bool manual_animation = hints != null && hints.get ("manual_animation").get_boolean ();
|
||||||
return;
|
|
||||||
|
|
||||||
toggle ();
|
if (opened) {
|
||||||
|
if (manual_animation && !animating) {
|
||||||
|
debug ("Starting MultitaskingView manual close animation");
|
||||||
|
gesture_animation_director.running = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
toggle ();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!opened && manual_animation && gesture_animation_director.running) {
|
||||||
|
gesture_animation_director.update_animation (hints);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -566,8 +590,9 @@ namespace Gala {
|
|||||||
* to animate to their positions.
|
* to animate to their positions.
|
||||||
*/
|
*/
|
||||||
void toggle () {
|
void toggle () {
|
||||||
if (animating)
|
if (animating) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
animating = true;
|
animating = true;
|
||||||
|
|
||||||
@ -578,8 +603,9 @@ namespace Gala {
|
|||||||
if (opening) {
|
if (opening) {
|
||||||
container.visible = true;
|
container.visible = true;
|
||||||
container.open ();
|
container.open ();
|
||||||
} else
|
} else {
|
||||||
container.close ();
|
container.close ();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opening) {
|
if (opening) {
|
||||||
@ -625,96 +651,167 @@ namespace Gala {
|
|||||||
|
|
||||||
foreach (var child in workspaces.get_children ()) {
|
foreach (var child in workspaces.get_children ()) {
|
||||||
unowned WorkspaceClone workspace = (WorkspaceClone) child;
|
unowned WorkspaceClone workspace = (WorkspaceClone) child;
|
||||||
if (opening)
|
if (opening) {
|
||||||
workspace.open ();
|
workspace.open ();
|
||||||
else
|
} else {
|
||||||
workspace.close ();
|
workspace.close ();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (opening) {
|
||||||
|
show_docks ();
|
||||||
|
} else {
|
||||||
|
hide_docks ();
|
||||||
|
}
|
||||||
|
|
||||||
|
GestureAnimationDirector.OnEnd on_animation_end = (percentage, cancel_action) => {
|
||||||
|
var animation_duration = cancel_action ? 0 : ANIMATION_DURATION;
|
||||||
|
Timeout.add (animation_duration, () => {
|
||||||
|
if (!opening) {
|
||||||
|
foreach (var container in window_containers_monitors) {
|
||||||
|
container.visible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
hide ();
|
||||||
|
|
||||||
|
wm.background_group.show ();
|
||||||
|
wm.window_group.show ();
|
||||||
|
wm.top_window_group.show ();
|
||||||
|
|
||||||
|
dock_clones.destroy_all_children ();
|
||||||
|
|
||||||
|
wm.pop_modal (modal_proxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
animating = false;
|
||||||
|
gesture_animation_director.disconnect_all_handlers ();
|
||||||
|
gesture_animation_director.running = false;
|
||||||
|
gesture_animation_director.canceling = cancel_action;
|
||||||
|
|
||||||
|
if (cancel_action) {
|
||||||
|
toggle ();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!gesture_animation_director.running) {
|
||||||
|
on_animation_end (100, false);
|
||||||
|
} else {
|
||||||
|
gesture_animation_director.connect_handlers (null, null, (owned) on_animation_end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void show_docks () {
|
||||||
float clone_offset_x, clone_offset_y;
|
float clone_offset_x, clone_offset_y;
|
||||||
dock_clones.get_transformed_position (out clone_offset_x, out clone_offset_y);
|
dock_clones.get_transformed_position (out clone_offset_x, out clone_offset_y);
|
||||||
|
|
||||||
if (opening) {
|
|
||||||
#if HAS_MUTTER330
|
#if HAS_MUTTER330
|
||||||
unowned GLib.List<Meta.WindowActor> window_actors = display.get_window_actors ();
|
unowned GLib.List<Meta.WindowActor> window_actors = display.get_window_actors ();
|
||||||
#else
|
#else
|
||||||
unowned GLib.List<Meta.WindowActor> window_actors = screen.get_window_actors ();
|
unowned GLib.List<Meta.WindowActor> window_actors = screen.get_window_actors ();
|
||||||
#endif
|
|
||||||
foreach (unowned Meta.WindowActor actor in window_actors) {
|
|
||||||
const int MAX_OFFSET = 100;
|
|
||||||
|
|
||||||
if (actor.is_destroyed ())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
unowned Meta.Window window = actor.get_meta_window ();
|
|
||||||
var monitor = window.get_monitor ();
|
|
||||||
|
|
||||||
if (window.window_type != WindowType.DOCK)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
#if HAS_MUTTER330
|
|
||||||
if (display.get_monitor_in_fullscreen (monitor))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var monitor_geom = display.get_monitor_geometry (monitor);
|
|
||||||
#else
|
|
||||||
if (screen.get_monitor_in_fullscreen (monitor))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var monitor_geom = screen.get_monitor_geometry (monitor);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
var window_geom = window.get_frame_rect ();
|
foreach (unowned Meta.WindowActor actor in window_actors) {
|
||||||
var top = monitor_geom.y + MAX_OFFSET > window_geom.y;
|
const int MAX_OFFSET = 100;
|
||||||
var bottom = monitor_geom.y + monitor_geom.height - MAX_OFFSET > window_geom.y;
|
|
||||||
|
|
||||||
if (!top && !bottom)
|
if (actor.is_destroyed ())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var clone = new SafeWindowClone (window, true);
|
unowned Meta.Window window = actor.get_meta_window ();
|
||||||
clone.set_position (actor.x - clone_offset_x, actor.y - clone_offset_y);
|
var monitor = window.get_monitor ();
|
||||||
clone.set_easing_duration (ANIMATION_DURATION);
|
|
||||||
|
if (window.window_type != WindowType.DOCK)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
#if HAS_MUTTER330
|
||||||
|
if (display.get_monitor_in_fullscreen (monitor))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var monitor_geom = display.get_monitor_geometry (monitor);
|
||||||
|
#else
|
||||||
|
if (screen.get_monitor_in_fullscreen (monitor))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var monitor_geom = screen.get_monitor_geometry (monitor);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
var window_geom = window.get_frame_rect ();
|
||||||
|
var top = monitor_geom.y + MAX_OFFSET > window_geom.y;
|
||||||
|
var bottom = monitor_geom.y + monitor_geom.height - MAX_OFFSET > window_geom.y;
|
||||||
|
|
||||||
|
if (!top && !bottom)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var initial_x = actor.x - clone_offset_x;
|
||||||
|
var initial_y = actor.y - clone_offset_y;
|
||||||
|
var target_y = (top)
|
||||||
|
? actor.y - actor.height - clone_offset_y
|
||||||
|
: actor.y + actor.height - clone_offset_y;
|
||||||
|
|
||||||
|
var clone = new SafeWindowClone (window, true);
|
||||||
|
dock_clones.add_child (clone);
|
||||||
|
|
||||||
|
GestureAnimationDirector.OnBegin on_animation_begin = () => {
|
||||||
|
clone.set_position (initial_x, initial_y);
|
||||||
|
clone.set_easing_mode (0);
|
||||||
|
};
|
||||||
|
|
||||||
|
GestureAnimationDirector.OnUpdate on_animation_update = (percentage) => {
|
||||||
|
var y = GestureAnimationDirector.animation_value (initial_y, target_y, percentage);
|
||||||
|
clone.y = y;
|
||||||
|
};
|
||||||
|
|
||||||
|
GestureAnimationDirector.OnEnd on_animation_end = (percentage, cancel_action) => {
|
||||||
clone.set_easing_mode (ANIMATION_MODE);
|
clone.set_easing_mode (ANIMATION_MODE);
|
||||||
dock_clones.add_child (clone);
|
|
||||||
|
|
||||||
if (top)
|
if (cancel_action) {
|
||||||
clone.y = actor.y - actor.height - clone_offset_y;
|
return;
|
||||||
else if (bottom)
|
|
||||||
clone.y = actor.y + actor.height - clone_offset_y;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
foreach (var child in dock_clones.get_children ()) {
|
|
||||||
var dock = (Clone) child;
|
|
||||||
|
|
||||||
dock.y = dock.source.y - clone_offset_y;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!opening) {
|
|
||||||
Timeout.add (ANIMATION_DURATION, () => {
|
|
||||||
foreach (var container in window_containers_monitors) {
|
|
||||||
container.visible = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hide ();
|
clone.set_easing_duration (gesture_animation_director.canceling ? 0 : ANIMATION_DURATION);
|
||||||
|
clone.y = target_y;
|
||||||
|
};
|
||||||
|
|
||||||
wm.background_group.show ();
|
if (!gesture_animation_director.running) {
|
||||||
wm.window_group.show ();
|
on_animation_begin (0);
|
||||||
wm.top_window_group.show ();
|
on_animation_end (100, false);
|
||||||
|
} else {
|
||||||
|
gesture_animation_director.connect_handlers ((owned) on_animation_begin, (owned) on_animation_update, (owned) on_animation_end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dock_clones.destroy_all_children ();
|
void hide_docks () {
|
||||||
|
float clone_offset_x, clone_offset_y;
|
||||||
|
dock_clones.get_transformed_position (out clone_offset_x, out clone_offset_y);
|
||||||
|
|
||||||
wm.pop_modal (modal_proxy);
|
foreach (var child in dock_clones.get_children ()) {
|
||||||
|
var dock = (Clone) child;
|
||||||
|
var initial_y = dock.y;
|
||||||
|
var target_y = dock.source.y - clone_offset_y;
|
||||||
|
|
||||||
animating = false;
|
GestureAnimationDirector.OnUpdate on_animation_update = (percentage) => {
|
||||||
|
var y = GestureAnimationDirector.animation_value (initial_y, target_y, percentage);
|
||||||
|
dock.y = y;
|
||||||
|
};
|
||||||
|
|
||||||
return false;
|
GestureAnimationDirector.OnEnd on_animation_end = (percentage, cancel_action) => {
|
||||||
});
|
if (cancel_action) {
|
||||||
} else {
|
return;
|
||||||
Timeout.add (ANIMATION_DURATION, () => {
|
}
|
||||||
animating = false;
|
|
||||||
return false;
|
dock.set_easing_duration (ANIMATION_DURATION);
|
||||||
});
|
dock.set_easing_mode (ANIMATION_MODE);
|
||||||
|
dock.y = target_y;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!gesture_animation_director.running) {
|
||||||
|
on_animation_end (100, false);
|
||||||
|
} else {
|
||||||
|
gesture_animation_director.connect_handlers (null, (owned) on_animation_update, (owned) on_animation_end);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,6 +92,7 @@ namespace Gala {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public bool overview_mode { get; construct; }
|
public bool overview_mode { get; construct; }
|
||||||
|
public GestureAnimationDirector? gesture_animation_director { get; construct; }
|
||||||
|
|
||||||
[CCode (notify = false)]
|
[CCode (notify = false)]
|
||||||
public uint8 shadow_opacity {
|
public uint8 shadow_opacity {
|
||||||
@ -120,8 +121,8 @@ namespace Gala {
|
|||||||
Actor active_shape;
|
Actor active_shape;
|
||||||
Actor window_icon;
|
Actor window_icon;
|
||||||
|
|
||||||
public WindowClone (Meta.Window window, bool overview_mode = false) {
|
public WindowClone (Meta.Window window, GestureAnimationDirector? gesture_animation_director, bool overview_mode = false) {
|
||||||
Object (window: window, overview_mode: overview_mode);
|
Object (window: window, gesture_animation_director: gesture_animation_director, overview_mode: overview_mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
construct {
|
construct {
|
||||||
@ -295,24 +296,67 @@ namespace Gala {
|
|||||||
var offset_x = monitor_geom.x;
|
var offset_x = monitor_geom.x;
|
||||||
var offset_y = monitor_geom.y;
|
var offset_y = monitor_geom.y;
|
||||||
|
|
||||||
save_easing_state ();
|
var initial_x = x;
|
||||||
set_easing_mode (MultitaskingView.ANIMATION_MODE);
|
var initial_y = y;
|
||||||
set_easing_duration (animate ? MultitaskingView.ANIMATION_DURATION : 0);
|
var initial_width = width;
|
||||||
|
var initial_height = height;
|
||||||
|
|
||||||
set_position (outer_rect.x - offset_x, outer_rect.y - offset_y);
|
var target_x = outer_rect.x - offset_x;
|
||||||
set_size (outer_rect.width, outer_rect.height);
|
var target_y = outer_rect.y - offset_y;
|
||||||
|
|
||||||
if (should_fade ())
|
GestureAnimationDirector.OnBegin on_animation_begin = () => {
|
||||||
opacity = 0;
|
window_icon.set_easing_duration (0);
|
||||||
|
};
|
||||||
|
|
||||||
restore_easing_state ();
|
GestureAnimationDirector.OnUpdate on_animation_update = (percentage) => {
|
||||||
|
var x = GestureAnimationDirector.animation_value (initial_x, target_x, percentage);
|
||||||
|
var y = GestureAnimationDirector.animation_value (initial_y, target_y, percentage);
|
||||||
|
var width = GestureAnimationDirector.animation_value (initial_width, outer_rect.width, percentage);
|
||||||
|
var height = GestureAnimationDirector.animation_value (initial_height, outer_rect.height, percentage);
|
||||||
|
var opacity = GestureAnimationDirector.animation_value (255f, 0f, percentage);
|
||||||
|
|
||||||
if (animate)
|
set_size (width, height);
|
||||||
toggle_shadow (false);
|
set_position (x, y);
|
||||||
|
window_icon.opacity = (uint) opacity;
|
||||||
|
window_icon.set_position ((width - WINDOW_ICON_SIZE) / 2,
|
||||||
|
height - (WINDOW_ICON_SIZE * scale_factor) * 0.75f);
|
||||||
|
};
|
||||||
|
|
||||||
window_icon.set_position ((outer_rect.width - WINDOW_ICON_SIZE) / 2, outer_rect.height - (WINDOW_ICON_SIZE * scale_factor) * 0.75f);
|
GestureAnimationDirector.OnEnd on_animation_end = (percentage, cancel_action) => {
|
||||||
window_icon.opacity = 0;
|
window_icon.set_easing_duration (MultitaskingView.ANIMATION_DURATION);
|
||||||
close_button.opacity = 0;
|
|
||||||
|
if (cancel_action) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
save_easing_state ();
|
||||||
|
set_easing_mode (MultitaskingView.ANIMATION_MODE);
|
||||||
|
set_easing_duration (animate ? MultitaskingView.ANIMATION_DURATION : 0);
|
||||||
|
|
||||||
|
set_position (target_x, target_y);
|
||||||
|
set_size (outer_rect.width, outer_rect.height);
|
||||||
|
|
||||||
|
if (should_fade ()) {
|
||||||
|
opacity = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
restore_easing_state ();
|
||||||
|
|
||||||
|
if (animate) {
|
||||||
|
toggle_shadow (false);
|
||||||
|
}
|
||||||
|
|
||||||
|
window_icon.set_position ((outer_rect.width - WINDOW_ICON_SIZE) / 2, outer_rect.height - (WINDOW_ICON_SIZE * scale_factor) * 0.75f);
|
||||||
|
window_icon.opacity = 0;
|
||||||
|
close_button.opacity = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!animate || gesture_animation_director == null || !gesture_animation_director.running) {
|
||||||
|
on_animation_begin (0);
|
||||||
|
on_animation_end (100, false);
|
||||||
|
} else {
|
||||||
|
gesture_animation_director.connect_handlers ((owned) on_animation_begin, (owned) on_animation_update, (owned) on_animation_end);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -320,28 +364,67 @@ namespace Gala {
|
|||||||
*/
|
*/
|
||||||
public void take_slot (Meta.Rectangle rect) {
|
public void take_slot (Meta.Rectangle rect) {
|
||||||
slot = rect;
|
slot = rect;
|
||||||
|
var initial_x = x;
|
||||||
|
var initial_y = y;
|
||||||
|
var initial_width = width;
|
||||||
|
var initial_height = height;
|
||||||
|
|
||||||
save_easing_state ();
|
GestureAnimationDirector.OnBegin on_animation_begin = () => {
|
||||||
set_easing_duration (MultitaskingView.ANIMATION_DURATION);
|
window_icon.opacity = 0;
|
||||||
set_easing_mode (MultitaskingView.ANIMATION_MODE);
|
window_icon.set_easing_duration (0);
|
||||||
|
};
|
||||||
|
|
||||||
set_size (rect.width, rect.height);
|
GestureAnimationDirector.OnUpdate on_animation_update = (percentage) => {
|
||||||
set_position (rect.x, rect.y);
|
var x = GestureAnimationDirector.animation_value (initial_x, rect.x, percentage);
|
||||||
|
var y = GestureAnimationDirector.animation_value (initial_y, rect.y, percentage);
|
||||||
|
var width = GestureAnimationDirector.animation_value (initial_width, rect.width, percentage);
|
||||||
|
var height = GestureAnimationDirector.animation_value (initial_height, rect.height, percentage);
|
||||||
|
var opacity = GestureAnimationDirector.animation_value (0f, 255f, percentage);
|
||||||
|
|
||||||
window_icon.opacity = 255;
|
set_size (width, height);
|
||||||
window_icon.set_position ((rect.width - WINDOW_ICON_SIZE) / 2, rect.height - (WINDOW_ICON_SIZE * scale_factor) * 0.75f);
|
set_position (x, y);
|
||||||
|
window_icon.opacity = (uint) opacity;
|
||||||
|
window_icon.set_position ((width - WINDOW_ICON_SIZE) / 2,
|
||||||
|
height - (WINDOW_ICON_SIZE * scale_factor) * 0.75f);
|
||||||
|
};
|
||||||
|
|
||||||
restore_easing_state ();
|
GestureAnimationDirector.OnEnd on_animation_end = (percentage, cancel_action) => {
|
||||||
|
window_icon.set_easing_duration (MultitaskingView.ANIMATION_DURATION);
|
||||||
|
|
||||||
toggle_shadow (true);
|
if (cancel_action) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (opacity < 255) {
|
|
||||||
save_easing_state ();
|
save_easing_state ();
|
||||||
set_easing_mode (AnimationMode.EASE_OUT_QUAD);
|
set_easing_duration (MultitaskingView.ANIMATION_DURATION);
|
||||||
set_easing_duration (300);
|
set_easing_mode (MultitaskingView.ANIMATION_MODE);
|
||||||
|
|
||||||
|
set_size (rect.width, rect.height);
|
||||||
|
set_position (rect.x, rect.y);
|
||||||
|
|
||||||
|
window_icon.opacity = 255;
|
||||||
|
window_icon.set_position ((rect.width - WINDOW_ICON_SIZE) / 2,
|
||||||
|
rect.height - (WINDOW_ICON_SIZE * scale_factor) * 0.75f);
|
||||||
|
|
||||||
opacity = 255;
|
|
||||||
restore_easing_state ();
|
restore_easing_state ();
|
||||||
|
|
||||||
|
toggle_shadow (true);
|
||||||
|
|
||||||
|
if (opacity < 255) {
|
||||||
|
save_easing_state ();
|
||||||
|
set_easing_mode (AnimationMode.EASE_OUT_QUAD);
|
||||||
|
set_easing_duration (300);
|
||||||
|
|
||||||
|
opacity = 255;
|
||||||
|
restore_easing_state ();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (gesture_animation_director == null || !gesture_animation_director.running) {
|
||||||
|
on_animation_begin (0);
|
||||||
|
on_animation_end (100, false);
|
||||||
|
} else {
|
||||||
|
gesture_animation_director.connect_handlers ((owned) on_animation_begin, (owned) on_animation_update, (owned) on_animation_end);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,6 +30,7 @@ namespace Gala {
|
|||||||
public int padding_right { get; set; default = 12; }
|
public int padding_right { get; set; default = 12; }
|
||||||
public int padding_bottom { get; set; default = 12; }
|
public int padding_bottom { get; set; default = 12; }
|
||||||
|
|
||||||
|
public GestureAnimationDirector? gesture_animation_director { get; construct; }
|
||||||
public bool overview_mode { get; construct; }
|
public bool overview_mode { get; construct; }
|
||||||
|
|
||||||
bool opened;
|
bool opened;
|
||||||
@ -40,8 +41,8 @@ namespace Gala {
|
|||||||
*/
|
*/
|
||||||
WindowClone? current_window;
|
WindowClone? current_window;
|
||||||
|
|
||||||
public WindowCloneContainer (bool overview_mode = false) {
|
public WindowCloneContainer (GestureAnimationDirector? gesture_animation_director, bool overview_mode = false) {
|
||||||
Object (overview_mode: overview_mode);
|
Object (gesture_animation_director: gesture_animation_director, overview_mode: overview_mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
construct {
|
construct {
|
||||||
@ -68,7 +69,7 @@ namespace Gala {
|
|||||||
|
|
||||||
var windows_ordered = display.sort_windows_by_stacking (windows);
|
var windows_ordered = display.sort_windows_by_stacking (windows);
|
||||||
|
|
||||||
var new_window = new WindowClone (window, overview_mode);
|
var new_window = new WindowClone (window, gesture_animation_director, overview_mode);
|
||||||
|
|
||||||
new_window.selected.connect (window_selected_cb);
|
new_window.selected.connect (window_selected_cb);
|
||||||
new_window.destroy.connect (window_destroyed);
|
new_window.destroy.connect (window_destroyed);
|
||||||
@ -337,8 +338,9 @@ namespace Gala {
|
|||||||
* When opened the WindowClones are animated to a tiled layout
|
* When opened the WindowClones are animated to a tiled layout
|
||||||
*/
|
*/
|
||||||
public void open (Window? selected_window = null) {
|
public void open (Window? selected_window = null) {
|
||||||
if (opened)
|
if (opened) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
opened = true;
|
opened = true;
|
||||||
|
|
||||||
@ -361,8 +363,11 @@ namespace Gala {
|
|||||||
|
|
||||||
// make sure our windows are where they belong in case they were moved
|
// make sure our windows are where they belong in case they were moved
|
||||||
// while were closed.
|
// while were closed.
|
||||||
foreach (var window in get_children ())
|
if (gesture_animation_director == null || !gesture_animation_director.canceling) {
|
||||||
((WindowClone) window).transition_to_original_state (false);
|
foreach (var window in get_children ()) {
|
||||||
|
((WindowClone) window).transition_to_original_state (false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
reflow ();
|
reflow ();
|
||||||
}
|
}
|
||||||
@ -372,13 +377,15 @@ namespace Gala {
|
|||||||
* to make them take their original locations again.
|
* to make them take their original locations again.
|
||||||
*/
|
*/
|
||||||
public void close () {
|
public void close () {
|
||||||
if (!opened)
|
if (!opened) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
opened = false;
|
opened = false;
|
||||||
|
|
||||||
foreach (var window in get_children ())
|
foreach (var window in get_children ()) {
|
||||||
((WindowClone) window).transition_to_original_state (true);
|
((WindowClone) window).transition_to_original_state (true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,12 +54,12 @@ namespace Gala {
|
|||||||
#if HAS_MUTTER330
|
#if HAS_MUTTER330
|
||||||
display = wm.get_display ();
|
display = wm.get_display ();
|
||||||
|
|
||||||
display.get_workspace_manager ().workspace_switched.connect (close);
|
display.get_workspace_manager ().workspace_switched.connect (() => { close (); });
|
||||||
display.restacked.connect (restack_windows);
|
display.restacked.connect (restack_windows);
|
||||||
#else
|
#else
|
||||||
screen = wm.get_screen ();
|
screen = wm.get_screen ();
|
||||||
|
|
||||||
screen.workspace_switched.connect (close);
|
screen.workspace_switched.connect (() => { close (); });
|
||||||
screen.restacked.connect (restack_windows);
|
screen.restacked.connect (restack_windows);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -203,7 +203,7 @@ namespace Gala {
|
|||||||
var geometry = screen.get_monitor_geometry (i);
|
var geometry = screen.get_monitor_geometry (i);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
var container = new WindowCloneContainer (true) {
|
var container = new WindowCloneContainer (null, true) {
|
||||||
padding_top = TOP_GAP,
|
padding_top = TOP_GAP,
|
||||||
padding_left = BORDER,
|
padding_left = BORDER,
|
||||||
padding_right = BORDER,
|
padding_right = BORDER,
|
||||||
@ -322,7 +322,7 @@ namespace Gala {
|
|||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public void close () {
|
public void close (HashTable<string,Variant>? hints = null) {
|
||||||
if (!visible || !ready)
|
if (!visible || !ready)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -143,6 +143,7 @@ namespace Gala {
|
|||||||
public signal void selected (bool close_view);
|
public signal void selected (bool close_view);
|
||||||
|
|
||||||
public Workspace workspace { get; construct; }
|
public Workspace workspace { get; construct; }
|
||||||
|
public GestureAnimationDirector gesture_animation_director { get; construct; }
|
||||||
public IconGroup icon_group { get; private set; }
|
public IconGroup icon_group { get; private set; }
|
||||||
public WindowCloneContainer window_container { get; private set; }
|
public WindowCloneContainer window_container { get; private set; }
|
||||||
|
|
||||||
@ -166,8 +167,8 @@ namespace Gala {
|
|||||||
|
|
||||||
uint hover_activate_timeout = 0;
|
uint hover_activate_timeout = 0;
|
||||||
|
|
||||||
public WorkspaceClone (Workspace workspace) {
|
public WorkspaceClone (Workspace workspace, GestureAnimationDirector gesture_animation_director) {
|
||||||
Object (workspace: workspace);
|
Object (workspace: workspace, gesture_animation_director: gesture_animation_director);
|
||||||
}
|
}
|
||||||
|
|
||||||
construct {
|
construct {
|
||||||
@ -192,7 +193,7 @@ namespace Gala {
|
|||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
window_container = new WindowCloneContainer ();
|
window_container = new WindowCloneContainer (gesture_animation_director);
|
||||||
window_container.window_selected.connect ((w) => { window_selected (w); });
|
window_container.window_selected.connect ((w) => { window_selected (w); });
|
||||||
window_container.set_size (monitor_geometry.width, monitor_geometry.height);
|
window_container.set_size (monitor_geometry.width, monitor_geometry.height);
|
||||||
#if HAS_MUTTER330
|
#if HAS_MUTTER330
|
||||||
@ -369,8 +370,9 @@ namespace Gala {
|
|||||||
* if it belongs to this workspace.
|
* if it belongs to this workspace.
|
||||||
*/
|
*/
|
||||||
public void open () {
|
public void open () {
|
||||||
if (opened)
|
if (opened) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
opened = true;
|
opened = true;
|
||||||
|
|
||||||
@ -390,13 +392,33 @@ namespace Gala {
|
|||||||
|
|
||||||
update_size (monitor);
|
update_size (monitor);
|
||||||
|
|
||||||
background.set_pivot_point (0.5f, pivotY);
|
GestureAnimationDirector.OnBegin on_animation_begin = () => {
|
||||||
|
background.set_pivot_point (0.5f, pivotY);
|
||||||
|
};
|
||||||
|
|
||||||
background.save_easing_state ();
|
GestureAnimationDirector.OnUpdate on_animation_update = (percentage) => {
|
||||||
background.set_easing_duration (MultitaskingView.ANIMATION_DURATION);
|
double update_scale = (double)GestureAnimationDirector.animation_value (1.0f, (float)scale, percentage);
|
||||||
background.set_easing_mode (MultitaskingView.ANIMATION_MODE);
|
background.set_scale (update_scale, update_scale);
|
||||||
background.set_scale (scale, scale);
|
};
|
||||||
background.restore_easing_state ();
|
|
||||||
|
GestureAnimationDirector.OnEnd on_animation_end = (percentage, cancel_action) => {
|
||||||
|
if (cancel_action) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
background.save_easing_state ();
|
||||||
|
background.set_easing_duration (MultitaskingView.ANIMATION_DURATION);
|
||||||
|
background.set_easing_mode (MultitaskingView.ANIMATION_MODE);
|
||||||
|
background.set_scale (scale, scale);
|
||||||
|
background.restore_easing_state ();
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!gesture_animation_director.running) {
|
||||||
|
on_animation_begin (0);
|
||||||
|
on_animation_end (100, false);
|
||||||
|
} else {
|
||||||
|
gesture_animation_director.connect_handlers ((owned) on_animation_begin, (owned) on_animation_update, (owned)on_animation_end);
|
||||||
|
}
|
||||||
|
|
||||||
Meta.Rectangle area = {
|
Meta.Rectangle area = {
|
||||||
(int)Math.floorf (monitor.x + monitor.width - monitor.width * scale) / 2,
|
(int)Math.floorf (monitor.x + monitor.width - monitor.width * scale) / 2,
|
||||||
@ -425,16 +447,38 @@ namespace Gala {
|
|||||||
* the windows back to their old locations.
|
* the windows back to their old locations.
|
||||||
*/
|
*/
|
||||||
public void close () {
|
public void close () {
|
||||||
if (!opened)
|
if (!opened) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
opened = false;
|
opened = false;
|
||||||
|
|
||||||
background.save_easing_state ();
|
double initial_scale_x, initial_scale_y;
|
||||||
background.set_easing_duration (MultitaskingView.ANIMATION_DURATION);
|
background.get_scale (out initial_scale_x, out initial_scale_y);
|
||||||
background.set_easing_mode (MultitaskingView.ANIMATION_MODE);
|
|
||||||
background.set_scale (1, 1);
|
GestureAnimationDirector.OnUpdate on_animation_update = (percentage) => {
|
||||||
background.restore_easing_state ();
|
double scale_x = (double) GestureAnimationDirector.animation_value ((float) initial_scale_x, 1.0f, percentage);
|
||||||
|
double scale_y = (double) GestureAnimationDirector.animation_value ((float) initial_scale_y, 1.0f, percentage);
|
||||||
|
background.set_scale (scale_x, scale_y);
|
||||||
|
};
|
||||||
|
|
||||||
|
GestureAnimationDirector.OnEnd on_animation_end = (percentage, cancel_action) => {
|
||||||
|
if (cancel_action) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
background.save_easing_state ();
|
||||||
|
background.set_easing_duration (MultitaskingView.ANIMATION_DURATION);
|
||||||
|
background.set_easing_mode (MultitaskingView.ANIMATION_MODE);
|
||||||
|
background.set_scale (1, 1);
|
||||||
|
background.restore_easing_state ();
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!gesture_animation_director.running) {
|
||||||
|
on_animation_end (100, false);
|
||||||
|
} else {
|
||||||
|
gesture_animation_director.connect_handlers (null, (owned) on_animation_update, (owned) on_animation_end);
|
||||||
|
}
|
||||||
|
|
||||||
window_container.close ();
|
window_container.close ();
|
||||||
}
|
}
|
||||||
|
@ -51,6 +51,11 @@ namespace Gala {
|
|||||||
*/
|
*/
|
||||||
public Meta.BackgroundGroup background_group { get; protected set; }
|
public Meta.BackgroundGroup background_group { get; protected set; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public Gala.ActivatableComponent workspace_view { get; protected set; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
@ -65,7 +70,6 @@ namespace Gala {
|
|||||||
Meta.PluginInfo info;
|
Meta.PluginInfo info;
|
||||||
|
|
||||||
WindowSwitcher? winswitcher = null;
|
WindowSwitcher? winswitcher = null;
|
||||||
ActivatableComponent? workspace_view = null;
|
|
||||||
ActivatableComponent? window_overview = null;
|
ActivatableComponent? window_overview = null;
|
||||||
|
|
||||||
// used to detect which corner was used to trigger an action
|
// used to detect which corner was used to trigger an action
|
||||||
@ -95,7 +99,12 @@ namespace Gala {
|
|||||||
private GLib.Settings animations_settings;
|
private GLib.Settings animations_settings;
|
||||||
private GLib.Settings behavior_settings;
|
private GLib.Settings behavior_settings;
|
||||||
|
|
||||||
|
private bool animating_switch_workspace = false;
|
||||||
|
private GestureAnimationDirector gesture_animation_director;
|
||||||
|
|
||||||
construct {
|
construct {
|
||||||
|
gesture_animation_director = new GestureAnimationDirector ();
|
||||||
|
|
||||||
info = Meta.PluginInfo () {name = "Gala", version = Config.VERSION, author = "Gala Developers",
|
info = Meta.PluginInfo () {name = "Gala", version = Config.VERSION, author = "Gala Developers",
|
||||||
license = "GPLv3", description = "A nice elementary window manager"};
|
license = "GPLv3", description = "A nice elementary window manager"};
|
||||||
|
|
||||||
@ -331,7 +340,7 @@ namespace Gala {
|
|||||||
|
|
||||||
if (plugin_manager.workspace_view_provider == null
|
if (plugin_manager.workspace_view_provider == null
|
||||||
|| (workspace_view = (plugin_manager.get_plugin (plugin_manager.workspace_view_provider) as ActivatableComponent)) == null) {
|
|| (workspace_view = (plugin_manager.get_plugin (plugin_manager.workspace_view_provider) as ActivatableComponent)) == null) {
|
||||||
workspace_view = new MultitaskingView (this);
|
workspace_view = new MultitaskingView (this, gesture_animation_director);
|
||||||
ui_group.add_child ((Clutter.Actor) workspace_view);
|
ui_group.add_child ((Clutter.Actor) workspace_view);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -573,7 +582,23 @@ namespace Gala {
|
|||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
public void switch_to_next_workspace (Meta.MotionDirection direction) {
|
public void switch_to_next_workspace (Meta.MotionDirection direction, HashTable<string,Variant>? hints = null) {
|
||||||
|
if (animating_switch_workspace) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool manual_animation = hints != null && hints.get ("manual_animation").get_boolean ();
|
||||||
|
if (manual_animation) {
|
||||||
|
string event = hints.get ("event").get_string ();
|
||||||
|
|
||||||
|
if (event == "begin") {
|
||||||
|
gesture_animation_director.running = true;
|
||||||
|
} else {
|
||||||
|
gesture_animation_director.update_animation (hints);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#if HAS_MUTTER330
|
#if HAS_MUTTER330
|
||||||
unowned Meta.Display display = get_display ();
|
unowned Meta.Display display = get_display ();
|
||||||
var active_workspace = display.get_workspace_manager ().get_active_workspace ();
|
var active_workspace = display.get_workspace_manager ().get_active_workspace ();
|
||||||
@ -586,29 +611,57 @@ namespace Gala {
|
|||||||
|
|
||||||
if (neighbor != active_workspace) {
|
if (neighbor != active_workspace) {
|
||||||
neighbor.activate (display.get_current_time ());
|
neighbor.activate (display.get_current_time ());
|
||||||
return;
|
if (manual_animation) {
|
||||||
|
gesture_animation_director.update_animation (hints);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// if we didnt switch, show a nudge-over animation if one is not already in progress
|
||||||
|
play_nudge_animation (direction);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// if we didnt switch, show a nudge-over animation if one is not already in progress
|
private void play_nudge_animation (Meta.MotionDirection direction) {
|
||||||
if (ui_group.get_transition ("nudge") != null)
|
int duration = 360;
|
||||||
return;
|
|
||||||
|
|
||||||
var dest = (direction == Meta.MotionDirection.LEFT ? 32.0f : -32.0f);
|
var dest = (direction == Meta.MotionDirection.LEFT ? 32.0f : -32.0f);
|
||||||
|
|
||||||
double[] keyframes = { 0.5 };
|
GestureAnimationDirector.OnUpdate on_animation_update = (percentage) => {
|
||||||
GLib.Value[] x = { dest };
|
var x = GestureAnimationDirector.animation_value (0.0f, dest, percentage);
|
||||||
|
ui_group.x = x;
|
||||||
var nudge = new Clutter.KeyframeTransition ("translation-x") {
|
|
||||||
duration = 360,
|
|
||||||
remove_on_complete = true,
|
|
||||||
progress_mode = Clutter.AnimationMode.EASE_IN_QUAD
|
|
||||||
};
|
};
|
||||||
nudge.set_from_value (0.0f);
|
|
||||||
nudge.set_to_value (0.0f);
|
|
||||||
nudge.set_key_frames (keyframes);
|
|
||||||
nudge.set_values (x);
|
|
||||||
|
|
||||||
ui_group.add_transition ("nudge", nudge);
|
GestureAnimationDirector.OnEnd on_animation_end = (percentage, cancel_action) => {
|
||||||
|
var nudge_gesture = new Clutter.PropertyTransition ("x") {
|
||||||
|
duration = (duration / 2),
|
||||||
|
remove_on_complete = true,
|
||||||
|
progress_mode = Clutter.AnimationMode.LINEAR
|
||||||
|
};
|
||||||
|
nudge_gesture.set_from_value ((float) ui_group.x);
|
||||||
|
nudge_gesture.set_to_value (0.0f);
|
||||||
|
ui_group.add_transition ("nudge", nudge_gesture);
|
||||||
|
|
||||||
|
gesture_animation_director.disconnect_all_handlers ();
|
||||||
|
gesture_animation_director.running = false;
|
||||||
|
gesture_animation_director.canceling = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!gesture_animation_director.running) {
|
||||||
|
double[] keyframes = { 0.5 };
|
||||||
|
GLib.Value[] x = { dest };
|
||||||
|
|
||||||
|
var nudge = new Clutter.KeyframeTransition ("translation-x") {
|
||||||
|
duration = duration,
|
||||||
|
remove_on_complete = true,
|
||||||
|
progress_mode = Clutter.AnimationMode.EASE_IN_QUAD
|
||||||
|
};
|
||||||
|
nudge.set_from_value (0.0f);
|
||||||
|
nudge.set_to_value (0.0f);
|
||||||
|
nudge.set_key_frames (keyframes);
|
||||||
|
nudge.set_values (x);
|
||||||
|
|
||||||
|
ui_group.add_transition ("nudge", nudge);
|
||||||
|
} else {
|
||||||
|
gesture_animation_director.connect_handlers (null, (owned) on_animation_update, (owned) on_animation_end);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if HAS_MUTTER330
|
#if HAS_MUTTER330
|
||||||
@ -1872,11 +1925,11 @@ namespace Gala {
|
|||||||
List<Clutter.Actor>? tmp_actors;
|
List<Clutter.Actor>? tmp_actors;
|
||||||
|
|
||||||
public override void switch_workspace (int from, int to, Meta.MotionDirection direction) {
|
public override void switch_workspace (int from, int to, Meta.MotionDirection direction) {
|
||||||
const int animation_duration = AnimationDuration.WORKSPACE_SWITCH;
|
|
||||||
|
|
||||||
if (!enable_animations
|
if (!enable_animations
|
||||||
|| animation_duration == 0
|
|| AnimationDuration.WORKSPACE_SWITCH == 0
|
||||||
|| (direction != Meta.MotionDirection.LEFT && direction != Meta.MotionDirection.RIGHT)) {
|
|| (direction != Meta.MotionDirection.LEFT && direction != Meta.MotionDirection.RIGHT)
|
||||||
|
|| gesture_animation_director.canceling) {
|
||||||
|
gesture_animation_director.canceling = false;
|
||||||
switch_workspace_completed ();
|
switch_workspace_completed ();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -2075,29 +2128,69 @@ namespace Gala {
|
|||||||
|
|
||||||
var animation_mode = Clutter.AnimationMode.EASE_OUT_CUBIC;
|
var animation_mode = Clutter.AnimationMode.EASE_OUT_CUBIC;
|
||||||
|
|
||||||
out_group.set_easing_mode (animation_mode);
|
GestureAnimationDirector.OnUpdate on_animation_update = (percentage) => {
|
||||||
out_group.set_easing_duration (animation_duration);
|
var x_out = GestureAnimationDirector.animation_value (0.1f, x2, percentage);
|
||||||
in_group.set_easing_mode (animation_mode);
|
var x_in = GestureAnimationDirector.animation_value (-x2, 0.1f, percentage);
|
||||||
in_group.set_easing_duration (animation_duration);
|
|
||||||
wallpaper_clone.set_easing_mode (animation_mode);
|
|
||||||
wallpaper_clone.set_easing_duration (animation_duration);
|
|
||||||
|
|
||||||
wallpaper.save_easing_state ();
|
out_group.x = x_out;
|
||||||
wallpaper.set_easing_mode (animation_mode);
|
in_group.x = x_in;
|
||||||
wallpaper.set_easing_duration (animation_duration);
|
|
||||||
|
|
||||||
out_group.x = x2;
|
wallpaper.x = x_out;
|
||||||
in_group.x = 0.0f;
|
wallpaper_clone.x = x_in;
|
||||||
|
};
|
||||||
|
|
||||||
wallpaper.x = x2;
|
GestureAnimationDirector.OnEnd on_animation_end = (percentage, cancel_action) => {
|
||||||
wallpaper_clone.x = 0.0f;
|
animating_switch_workspace = true;
|
||||||
wallpaper.restore_easing_state ();
|
|
||||||
|
|
||||||
var transition = in_group.get_transition ("x");
|
out_group.set_easing_mode (animation_mode);
|
||||||
if (transition != null)
|
out_group.set_easing_duration (AnimationDuration.WORKSPACE_SWITCH);
|
||||||
transition.completed.connect (end_switch_workspace);
|
in_group.set_easing_mode (animation_mode);
|
||||||
else
|
in_group.set_easing_duration (AnimationDuration.WORKSPACE_SWITCH);
|
||||||
end_switch_workspace ();
|
wallpaper_clone.set_easing_mode (animation_mode);
|
||||||
|
wallpaper_clone.set_easing_duration (AnimationDuration.WORKSPACE_SWITCH);
|
||||||
|
|
||||||
|
wallpaper.save_easing_state ();
|
||||||
|
wallpaper.set_easing_mode (animation_mode);
|
||||||
|
wallpaper.set_easing_duration (AnimationDuration.WORKSPACE_SWITCH);
|
||||||
|
|
||||||
|
out_group.x = cancel_action ? 0.0f : x2;
|
||||||
|
in_group.x = cancel_action ? -x2 : 0.0f;
|
||||||
|
|
||||||
|
wallpaper.x = cancel_action ? 0.0f : x2;
|
||||||
|
wallpaper_clone.x = cancel_action ? -x2 : 0.0f;
|
||||||
|
wallpaper.restore_easing_state ();
|
||||||
|
|
||||||
|
var transition = in_group.get_transition ("x");
|
||||||
|
if (transition != null) {
|
||||||
|
transition.completed.connect (() => {
|
||||||
|
switch_workspace_animation_finished (direction, cancel_action);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
switch_workspace_animation_finished (direction, cancel_action);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!gesture_animation_director.running) {
|
||||||
|
on_animation_end (100, false);
|
||||||
|
} else {
|
||||||
|
gesture_animation_director.connect_handlers (null, (owned) on_animation_update, (owned) on_animation_end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void switch_workspace_animation_finished (Meta.MotionDirection animation_direction,
|
||||||
|
bool cancel_action) {
|
||||||
|
end_switch_workspace ();
|
||||||
|
gesture_animation_director.disconnect_all_handlers ();
|
||||||
|
gesture_animation_director.running = false;
|
||||||
|
gesture_animation_director.canceling = cancel_action;
|
||||||
|
animating_switch_workspace = false;
|
||||||
|
|
||||||
|
if (cancel_action) {
|
||||||
|
var cancel_direction = (animation_direction == Meta.MotionDirection.LEFT)
|
||||||
|
? Meta.MotionDirection.RIGHT
|
||||||
|
: Meta.MotionDirection.LEFT;
|
||||||
|
switch_to_next_workspace (cancel_direction);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void end_switch_workspace () {
|
void end_switch_workspace () {
|
||||||
|
@ -2,6 +2,7 @@ gala_bin_sources = files(
|
|||||||
'DBus.vala',
|
'DBus.vala',
|
||||||
'DBusAccelerator.vala',
|
'DBusAccelerator.vala',
|
||||||
'DockThemeManager.vala',
|
'DockThemeManager.vala',
|
||||||
|
'GestureAnimationDirector.vala',
|
||||||
'InternalUtils.vala',
|
'InternalUtils.vala',
|
||||||
'KeyboardManager.vala',
|
'KeyboardManager.vala',
|
||||||
'NotificationStack.vala',
|
'NotificationStack.vala',
|
||||||
|
Loading…
Reference in New Issue
Block a user