Merge branch 'main' into lenemter/search-apps-by-pid-more-cleverly

This commit is contained in:
Leo 2024-12-07 15:09:53 +03:00 committed by GitHub
commit eea8754136
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 415 additions and 483 deletions

View File

@ -400,5 +400,50 @@ namespace Gala {
return texture;
}
private static HashTable<Meta.Window, X.XserverRegion?> regions = new HashTable<Meta.Window, X.XserverRegion?> (null, null);
public static void x11_set_window_pass_through (Meta.Window window) {
unowned var x11_display = window.display.get_x11_display ();
#if HAS_MUTTER46
var x_window = x11_display.lookup_xwindow (window);
#else
var x_window = window.get_xwindow ();
#endif
unowned var xdisplay = x11_display.get_xdisplay ();
regions[window] = X.Fixes.create_region_from_window (xdisplay, x_window, 0);
X.Xrectangle rect = {};
var region = X.Fixes.create_region (xdisplay, {rect});
X.Fixes.set_window_shape_region (xdisplay, x_window, 2, 0, 0, region);
X.Fixes.destroy_region (xdisplay, region);
}
public static void x11_unset_window_pass_through (Meta.Window window) {
unowned var x11_display = window.display.get_x11_display ();
#if HAS_MUTTER46
var x_window = x11_display.lookup_xwindow (window);
#else
var x_window = window.get_xwindow ();
#endif
unowned var xdisplay = x11_display.get_xdisplay ();
var region = regions[window];
if (region == null) {
return;
}
X.Fixes.set_window_shape_region (xdisplay, x_window, 2, 0, 0, region);
regions.remove (window);
X.Fixes.destroy_region (xdisplay, region);
}
}
}

View File

@ -0,0 +1,180 @@
/*
* Copyright 2024 elementary, Inc. (https://elementary.io)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Authored by: Leonhard Kargl <leo.kargl@proton.me>
*/
/**
* A class that will animate a property of a {@link Clutter.Actor} one to one with a gesture or
* with easing without a gesture. Respects the enable animation setting.
*/
public class Gala.GesturePropertyTransition : Object {
public delegate void DoneCallback ();
/**
* The actor whose property will be animated.
*/
public Clutter.Actor actor { get; construct; }
public GestureTracker gesture_tracker { get; construct; }
/**
* The property that will be animated. To be properly animated it has to be marked as
* animatable in the Clutter documentation and should be numeric.
*/
public string property { get; construct; }
/**
* The starting value of the animation or null to use the current value. The value
* has to be of the same type as the property.
*/
public Value? from_value { get; construct set; }
/**
* The value to animate to. It has to be of the same type as the property.
*/
public Value to_value { get; construct set; }
/**
* If not null this can be used to have an intermediate step before animating back to the origin.
* Therefore using this makes mostly sense if {@link to_value} equals {@link from_value}.
* This is mostly used for the nudge animations when trying to switch workspaces where there isn't one anymore.
*/
public Value? intermediate_value { get; construct; }
/**
* This is the from value that's actually used when calculating the animation movement.
* If {@link from_value} isn't null this will be the same, otherwise it will be set to the current
* value of the target property, when calling {@link start}.
*/
private Value actual_from_value;
private DoneCallback? done_callback;
public GesturePropertyTransition (
Clutter.Actor actor,
GestureTracker gesture_tracker,
string property,
Value? from_value,
Value to_value,
Value? intermediate_value = null
) {
Object (
actor: actor,
gesture_tracker: gesture_tracker,
property: property,
from_value: from_value,
to_value: to_value,
intermediate_value: intermediate_value
);
}
/**
* Starts animating the property from {@link from_value} to {@link to_value}. If with_gesture is true
* it will connect to the gesture trackers signals and animate according to the input finishing with an easing
* to the final position. If with_gesture is false it will just ease to the {@link to_value}.
* #this will keep itself alive until the animation finishes so it is safe to immediatly unref it after creation and calling start.
*
* @param done_callback a callback for when the transition finishes. It is guaranteed to be called exactly once.
*/
public void start (bool with_gesture, owned DoneCallback? done_callback = null) {
ref ();
this.done_callback = (owned) done_callback;
Value current_value = {};
actor.get_property (property, ref current_value);
actual_from_value = from_value ?? current_value;
if (actual_from_value.type () != current_value.type ()) {
warning ("from_value of type %s is not of the same type as the property %s which is %s. Can't animate.", from_value.type_name (), property, current_value.type_name ());
finish ();
return;
}
if (current_value.type () != to_value.type ()) {
warning ("to_value of type %s is not of the same type as the property %s which is %s. Can't animate.", to_value.type_name (), property, current_value.type_name ());
finish ();
return;
}
GestureTracker.OnBegin on_animation_begin = () => {
actor.set_property (property, actual_from_value);
};
GestureTracker.OnUpdate on_animation_update = (percentage) => {
var animation_value = GestureTracker.animation_value (value_to_float (actual_from_value), value_to_float (intermediate_value ?? to_value), percentage);
actor.set_property (property, value_from_float (animation_value));
};
GestureTracker.OnEnd on_animation_end = (percentage, cancel_action, calculated_duration) => {
actor.save_easing_state ();
actor.set_easing_mode (EASE_OUT_QUAD);
actor.set_easing_duration (AnimationsSettings.get_animation_duration (calculated_duration));
actor.set_property (property, cancel_action ? actual_from_value : to_value);
actor.restore_easing_state ();
unowned var transition = actor.get_transition (property);
if (transition == null) {
finish ();
} else {
transition.stopped.connect (finish);
}
};
if (with_gesture && AnimationsSettings.get_enable_animations ()) {
gesture_tracker.connect_handlers (on_animation_begin, on_animation_update, on_animation_end);
} else {
on_animation_begin (0);
if (intermediate_value != null) {
actor.save_easing_state ();
actor.set_easing_mode (EASE_OUT_QUAD);
actor.set_easing_duration (AnimationsSettings.get_animation_duration (gesture_tracker.min_animation_duration));
actor.set_property (property, intermediate_value);
actor.restore_easing_state ();
unowned var transition = actor.get_transition (property);
if (transition == null) {
on_animation_end (1, false, gesture_tracker.min_animation_duration);
} else {
transition.stopped.connect (() => on_animation_end (1, false, gesture_tracker.min_animation_duration));
}
} else {
on_animation_end (1, false, gesture_tracker.min_animation_duration);
}
}
}
private void finish () {
if (done_callback != null) {
done_callback ();
}
unref ();
}
private float value_to_float (Value val) {
Value float_val = Value (typeof (float));
if (val.transform (ref float_val)) {
return float_val.get_float ();
}
critical ("Non numeric property specified");
return 0;
}
private Value value_from_float (float f) {
var float_val = Value (typeof (float));
float_val.set_float (f);
var val = Value (actual_from_value.type ());
if (!float_val.transform (ref val)) {
warning ("Failed to transform float to give type");
}
return val;
}
}

View File

@ -243,25 +243,7 @@ namespace Gala {
return;
}
Meta.Side side = TOP;
switch (anchor) {
case TOP:
break;
case BOTTOM:
side = BOTTOM;
break;
case LEFT:
side = LEFT;
break;
case RIGHT:
side = RIGHT;
break;
}
ShellClientsManager.get_instance ().set_anchor (window, side);
ShellClientsManager.get_instance ().set_anchor (window, anchor);
}
internal static void focus_panel (Wl.Client client, Wl.Resource resource) {

View File

@ -1,42 +0,0 @@
/*
* SPDX-License-Identifier: GPL-3.0-or-later
* SPDX-FileCopyrightText: 2024 elementary, Inc. (https://elementary.io)
*/
/* This class is used to workaround https://github.com/elementary/gala/issues/2101 */
public class Gala.DelegateActor : GLib.Object {
public const int OUT_OF_BOUNDS = 1000000;
public Meta.WindowActor actor { get; construct; }
/* Current window actor position or position before the window was moved out of bounds */
public float x { get; private set; default = 0.0f; }
public float y { get; private set; default = 0.0f; }
/* Current window position or position before it was moved out of bounds */
public int actual_x { get; private set; default = 0; }
public int actual_y { get; private set; default = 0; }
public DelegateActor (Meta.WindowActor actor) {
Object (actor: actor);
}
construct {
actor.meta_window.position_changed.connect ((_window) => {
var rect = _window.get_frame_rect ();
if (rect.x != OUT_OF_BOUNDS) {
actual_x = rect.x;
Idle.add_once (() => {
x = actor.x;
});
}
if (rect.y != OUT_OF_BOUNDS) {
actual_y = rect.y;
Idle.add_once (() => {
y = actor.y;
});
}
});
}
}

View File

@ -16,17 +16,7 @@ public class Gala.HideTracker : Object {
public Meta.Display display { get; construct; }
public unowned PanelWindow panel { get; construct; }
private Pantheon.Desktop.HideMode _hide_mode = NEVER;
public Pantheon.Desktop.HideMode hide_mode {
get {
return _hide_mode;
}
set {
_hide_mode = value;
setup_barrier ();
}
}
public Pantheon.Desktop.HideMode hide_mode { get; set; }
private Clutter.PanAction pan_action;
@ -97,6 +87,16 @@ public class Gala.HideTracker : Object {
pan_action.pan.connect (on_pan);
display.get_stage ().add_action_full ("panel-swipe-gesture", CAPTURE, pan_action);
panel.notify["anchor"].connect (setup_barrier);
var monitor_manager = display.get_context ().get_backend ().get_monitor_manager ();
monitor_manager.monitors_changed.connect (() => {
setup_barrier (); //Make sure barriers are still on the primary monitor
schedule_update ();
});
setup_barrier ();
}
#if !HAS_MUTTER45
@ -171,7 +171,7 @@ public class Gala.HideTracker : Object {
continue;
}
if (!panel.get_custom_window_rect ().overlap (window.get_frame_rect ())) {
if (!panel.window.get_frame_rect ().overlap (window.get_frame_rect ())) {
continue;
}

View File

@ -48,8 +48,8 @@ public class Gala.PanelClone : Object {
actor = (Meta.WindowActor) panel.window.get_compositor_private ();
// WindowActor position and Window position aren't necessarily the same.
// The clone needs the actor position
panel.delegate_actor.notify["x"].connect (update_clone_position);
panel.delegate_actor.notify["y"].connect (update_clone_position);
actor.notify["x"].connect (update_clone_position);
actor.notify["y"].connect (update_clone_position);
// Actor visibility might be changed by something else e.g. workspace switch
// but we want to keep it in sync with us
actor.notify["visible"].connect (update_visible);
@ -97,7 +97,7 @@ public class Gala.PanelClone : Object {
switch (panel.anchor) {
case TOP:
case BOTTOM:
return panel.delegate_actor.x;
return actor.x;
default:
return 0;
}
@ -106,9 +106,9 @@ public class Gala.PanelClone : Object {
private float calculate_clone_y (bool hidden) {
switch (panel.anchor) {
case TOP:
return hidden ? panel.delegate_actor.y - actor.height : panel.delegate_actor.y;
return hidden ? actor.y - actor.height : actor.y;
case BOTTOM:
return hidden ? panel.delegate_actor.y + actor.height : panel.delegate_actor.y;
return hidden ? actor.y + actor.height : actor.y;
default:
return 0;
}
@ -128,7 +128,7 @@ public class Gala.PanelClone : Object {
panel_hidden = true;
if (!Meta.Util.is_wayland_compositor ()) {
panel.window.move_frame (false, DelegateActor.OUT_OF_BOUNDS, DelegateActor.OUT_OF_BOUNDS);
Utils.x11_set_window_pass_through (panel.window);
}
if (panel.anchor != TOP && panel.anchor != BOTTOM) {
@ -151,7 +151,7 @@ public class Gala.PanelClone : Object {
}
if (!Meta.Util.is_wayland_compositor ()) {
panel.window.move_frame (false, panel.delegate_actor.actual_x, panel.delegate_actor.actual_y);
Utils.x11_unset_window_pass_through (panel.window);
}
clone.save_easing_state ();

View File

@ -10,35 +10,21 @@ public class Gala.PanelWindow : Object {
public WindowManager wm { get; construct; }
public Meta.Window window { get; construct; }
public Pantheon.Desktop.Anchor anchor { get; construct set; }
public bool hidden { get; private set; default = false; }
private WindowPositioner window_positioner;
public Meta.Side anchor;
public DelegateActor delegate_actor;
private PanelClone clone;
private uint idle_move_id = 0;
private int width = -1;
private int height = -1;
public PanelWindow (WindowManager wm, Meta.Window window, Meta.Side anchor) {
Object (wm: wm, window: window);
// Meta.Side seems to be currently not supported as GLib.Object property ...?
// At least it always crashed for me with some paramspec, g_type_fundamental backtrace
this.anchor = anchor;
public PanelWindow (WindowManager wm, Meta.Window window, Pantheon.Desktop.Anchor anchor) {
Object (wm: wm, window: window, anchor: anchor);
}
construct {
window.size_changed.connect (position_window);
window.unmanaging.connect (() => {
if (idle_move_id != 0) {
Source.remove (idle_move_id);
}
if (window_struts.remove (window)) {
update_struts ();
}
@ -46,16 +32,20 @@ public class Gala.PanelWindow : Object {
window.stick ();
delegate_actor = new DelegateActor ((Meta.WindowActor) window.get_compositor_private ());
clone = new PanelClone (wm, this);
var monitor_manager = wm.get_display ().get_context ().get_backend ().get_monitor_manager ();
monitor_manager.monitors_changed.connect (() => update_anchor (anchor));
monitor_manager.monitors_changed_internal.connect (() => update_anchor (anchor));
unowned var display = wm.get_display ();
var workspace_manager = wm.get_display ().get_workspace_manager ();
unowned var workspace_manager = display.get_workspace_manager ();
workspace_manager.workspace_added.connect (update_strut);
workspace_manager.workspace_removed.connect (update_strut);
window.size_changed.connect (update_strut);
window.position_changed.connect (update_strut);
window_positioner = new WindowPositioner (display, window, WindowPositioner.Position.from_anchor (anchor));
notify["anchor"].connect (() => window_positioner.position = WindowPositioner.Position.from_anchor (anchor));
}
#if HAS_MUTTER45
@ -64,8 +54,6 @@ public class Gala.PanelWindow : Object {
public Meta.Rectangle get_custom_window_rect () {
#endif
var window_rect = window.get_frame_rect ();
window_rect.x = delegate_actor.actual_x;
window_rect.y = delegate_actor.actual_y;
if (width > 0) {
window_rect.width = width;
@ -87,73 +75,9 @@ public class Gala.PanelWindow : Object {
this.width = width;
this.height = height;
position_window ();
set_hide_mode (clone.hide_mode); // Resetup barriers etc.
}
public void update_anchor (Meta.Side anchor) {
this.anchor = anchor;
position_window ();
set_hide_mode (clone.hide_mode); // Resetup barriers etc.
}
private void position_window () {
var display = wm.get_display ();
var monitor_geom = display.get_monitor_geometry (display.get_primary_monitor ());
var window_rect = window.get_frame_rect ();
switch (anchor) {
case TOP:
position_window_top (monitor_geom, window_rect);
break;
case BOTTOM:
position_window_bottom (monitor_geom, window_rect);
break;
default:
warning ("Side not supported yet");
break;
}
update_strut ();
}
#if HAS_MUTTER45
private void position_window_top (Mtk.Rectangle monitor_geom, Mtk.Rectangle window_rect) {
#else
private void position_window_top (Meta.Rectangle monitor_geom, Meta.Rectangle window_rect) {
#endif
var x = monitor_geom.x + (monitor_geom.width - window_rect.width) / 2;
move_window_idle (x, monitor_geom.y);
}
#if HAS_MUTTER45
private void position_window_bottom (Mtk.Rectangle monitor_geom, Mtk.Rectangle window_rect) {
#else
private void position_window_bottom (Meta.Rectangle monitor_geom, Meta.Rectangle window_rect) {
#endif
var x = monitor_geom.x + (monitor_geom.width - window_rect.width) / 2;
var y = monitor_geom.y + monitor_geom.height - window_rect.height;
move_window_idle (x, y);
}
private void move_window_idle (int x, int y) {
if (idle_move_id != 0) {
Source.remove (idle_move_id);
}
idle_move_id = Idle.add (() => {
window.move_frame (false, x, y);
idle_move_id = 0;
return Source.REMOVE;
});
}
public void set_hide_mode (Pantheon.Desktop.HideMode hide_mode) {
clone.hide_mode = hide_mode;
@ -177,7 +101,7 @@ public class Gala.PanelWindow : Object {
Meta.Strut strut = {
rect,
anchor
side_from_anchor (anchor)
};
window_struts[window] = strut;
@ -203,4 +127,20 @@ public class Gala.PanelWindow : Object {
update_struts ();
}
}
private Meta.Side side_from_anchor (Pantheon.Desktop.Anchor anchor) {
switch (anchor) {
case BOTTOM:
return BOTTOM;
case LEFT:
return LEFT;
case RIGHT:
return RIGHT;
default:
return TOP;
}
}
}

View File

@ -143,16 +143,16 @@ public class Gala.ShellClientsManager : Object {
xdisplay.change_property (x_window, atom, (X.Atom) 4, 32, 0, (uchar[]) dock_atom, 1);
}
public void set_anchor (Meta.Window window, Meta.Side side) {
public void set_anchor (Meta.Window window, Pantheon.Desktop.Anchor anchor) {
if (window in panel_windows) {
panel_windows[window].update_anchor (side);
panel_windows[window].anchor = anchor;
return;
}
make_dock (window);
// TODO: Return if requested by window that's not a trusted client?
panel_windows[window] = new PanelWindow (wm, window, side);
panel_windows[window] = new PanelWindow (wm, window, anchor);
// connect_after so we make sure the PanelWindow can destroy its barriers and struts
window.unmanaging.connect_after ((_window) => panel_windows.remove (_window));
@ -226,8 +226,30 @@ public class Gala.ShellClientsManager : Object {
switch (key) {
case "anchor":
int parsed; // Will be used as Meta.Side which is a 4 value bitfield so check bounds for that
if (int.try_parse (val, out parsed) && 0 <= parsed && parsed <= 15) {
int meta_side_parsed; // Will be used as Meta.Side which is a 4 value bitfield so check bounds for that
if (int.try_parse (val, out meta_side_parsed) && 0 <= meta_side_parsed && meta_side_parsed <= 15) {
//FIXME: Next major release change dock and wingpanel calls to get rid of this
Pantheon.Desktop.Anchor parsed = TOP;
switch ((Meta.Side) meta_side_parsed) {
case BOTTOM:
parsed = BOTTOM;
break;
case LEFT:
parsed = LEFT;
break;
case RIGHT:
parsed = RIGHT;
break;
default:
break;
}
set_anchor (window, parsed);
// We need to set a second time because the intention is to call this before the window is shown which it is on wayland
// but on X the window was already shown when we get here so we have to call again to instantly apply it.
set_anchor (window, parsed);
} else {
warning ("Failed to parse %s as anchor", val);

View File

@ -7,13 +7,28 @@
public class Gala.WindowPositioner : Object {
public enum Position {
CENTER
TOP,
BOTTOM,
CENTER;
public static Position from_anchor (Pantheon.Desktop.Anchor anchor) {
if (anchor > 1) {
warning ("Position %s not supported yet", anchor.to_string ());
return CENTER;
}
return (Position) anchor;
}
}
public Meta.Display display { get; construct; }
public Meta.Window window { get; construct; }
public Position position { get; private set; }
public Variant? position_data { get; private set; }
/**
* This may only be set after the window was shown.
* The initial position should only be given in the constructor.
*/
public Position position { get; construct set; }
public Variant? position_data { get; construct set; }
public WindowPositioner (Meta.Display display, Meta.Window window, Position position, Variant? position_data = null) {
Object (display: display, window: window, position: position, position_data: position_data);
@ -29,29 +44,34 @@ public class Gala.WindowPositioner : Object {
unowned var monitor_manager = display.get_context ().get_backend ().get_monitor_manager ();
monitor_manager.monitors_changed.connect (position_window);
monitor_manager.monitors_changed_internal.connect (position_window);
}
/**
* This may only be called after the window was shown.
*/
public void update_position (Position new_position, Variant? new_position_data = null) {
position = new_position;
position_data = new_position_data;
position_window ();
notify["position"].connect (position_window);
notify["position-data"].connect (position_window);
}
private void position_window () {
int x = 0, y = 0;
var window_rect = window.get_frame_rect ();
switch (position) {
case CENTER:
var monitor_geom = display.get_monitor_geometry (display.get_primary_monitor ());
var window_rect = window.get_frame_rect ();
x = monitor_geom.x + (monitor_geom.width - window_rect.width) / 2;
y = monitor_geom.y + (monitor_geom.height - window_rect.height) / 2;
break;
case TOP:
var monitor_geom = display.get_monitor_geometry (display.get_primary_monitor ());
x = monitor_geom.x + (monitor_geom.width - window_rect.width) / 2;
y = monitor_geom.y;
break;
case BOTTOM:
var monitor_geom = display.get_monitor_geometry (display.get_primary_monitor ());
x = monitor_geom.x + (monitor_geom.width - window_rect.width) / 2;
y = monitor_geom.y + monitor_geom.height - window_rect.height;
break;
}
window.move_frame (false, x, y);

View File

@ -383,58 +383,17 @@ namespace Gala {
target_workspace.activate (display.get_current_time ());
}
GestureTracker.OnUpdate on_animation_update = (percentage) => {
var x = GestureTracker.animation_value (initial_x, target_x, percentage, true);
var icon_group_opacity = GestureTracker.animation_value (0.0f, 1.0f, percentage, false);
if (is_nudge_animation) {
x = x.clamp (initial_x - nudge_gap, initial_x + nudge_gap);
}
workspaces.x = x;
if (!is_nudge_animation) {
active_icon_group.backdrop_opacity = 1.0f - icon_group_opacity;
target_icon_group.backdrop_opacity = icon_group_opacity;
}
};
if (is_nudge_animation) {
new GesturePropertyTransition (workspaces, workspace_gesture_tracker, "x", null, initial_x, initial_x + nudge_gap * -relative_dir).start (true);
} else {
new GesturePropertyTransition (workspaces, workspace_gesture_tracker, "x", null, target_x).start (true);
new GesturePropertyTransition (active_icon_group, workspace_gesture_tracker, "backdrop-opacity", 1f, 0f).start (true);
new GesturePropertyTransition (target_icon_group, workspace_gesture_tracker, "backdrop-opacity", 0f, 1f).start (true);
}
GestureTracker.OnEnd on_animation_end = (percentage, cancel_action, calculated_duration) => {
switching_workspace_with_gesture = false;
var duration = is_nudge_animation ?
(uint) (AnimationDuration.NUDGE / 2) :
(uint) calculated_duration;
workspaces.save_easing_state ();
workspaces.set_easing_mode (Clutter.AnimationMode.EASE_OUT_QUAD);
workspaces.set_easing_duration (duration);
workspaces.x = (is_nudge_animation || cancel_action) ? initial_x : target_x;
workspaces.restore_easing_state ();
if (!is_nudge_animation) {
if (AnimationsSettings.get_enable_animations ()) {
var active_transition = new Clutter.PropertyTransition ("backdrop-opacity") {
duration = duration,
remove_on_complete = true
};
active_transition.set_from_value (active_icon_group.backdrop_opacity);
active_transition.set_to_value (cancel_action ? 1.0f : 0.0f);
active_icon_group.add_transition ("backdrop-opacity", active_transition);
var target_transition = new Clutter.PropertyTransition ("backdrop-opacity") {
duration = duration,
remove_on_complete = true
};
target_transition.set_from_value (target_icon_group.backdrop_opacity);
target_transition.set_to_value (cancel_action ? 0.0f : 1.0f);
target_icon_group.add_transition ("backdrop-opacity", target_transition);
} else {
active_icon_group.backdrop_opacity = cancel_action ? 1.0f : 0.0f;
target_icon_group.backdrop_opacity = cancel_action ? 0.0f : 1.0f;
}
}
if (is_nudge_animation || cancel_action) {
active_workspace.activate (display.get_current_time ());
}
@ -443,7 +402,7 @@ namespace Gala {
if (!AnimationsSettings.get_enable_animations ()) {
on_animation_end (1, false, 0);
} else {
workspace_gesture_tracker.connect_handlers (null, (owned) on_animation_update, (owned) on_animation_end);
workspace_gesture_tracker.connect_handlers (null, null, (owned) on_animation_end);
}
}

View File

@ -207,8 +207,6 @@ public class Gala.WindowClone : Clutter.Actor {
set_child_above_sibling (window_icon, clone);
set_child_above_sibling (window_title, clone);
transition_to_original_state (false);
check_shadow_requirements ();
if (should_fade ()) {
@ -221,7 +219,7 @@ public class Gala.WindowClone : Clutter.Actor {
// window was opened, so we stay at our old place.
if (was_waiting && slot != null) {
opacity = 0;
take_slot (slot);
take_slot (slot, true);
opacity = 255;
request_reposition ();
@ -268,7 +266,7 @@ public class Gala.WindowClone : Clutter.Actor {
*
* @param animate Animate the transformation of the placement
*/
public void transition_to_original_state (bool animate, bool with_gesture = false, bool is_cancel_animation = false) {
public void transition_to_original_state (bool with_gesture = false, bool is_cancel_animation = false) {
var outer_rect = window.get_frame_rect ();
unowned var display = window.get_display ();
@ -278,8 +276,6 @@ public class Gala.WindowClone : Clutter.Actor {
var offset_x = monitor_geom.x;
var offset_y = monitor_geom.y;
var initial_x = x;
var initial_y = y;
var initial_width = width;
var initial_height = height;
@ -290,22 +286,23 @@ public class Gala.WindowClone : Clutter.Actor {
in_slot_animation = true;
place_widgets (outer_rect.width, outer_rect.height, initial_scale);
new GesturePropertyTransition (this, gesture_tracker, "x", null, (float) target_x).start (with_gesture);
new GesturePropertyTransition (this, gesture_tracker, "y", null, (float) target_y).start (with_gesture);
new GesturePropertyTransition (this, gesture_tracker, "width", null, (float) outer_rect.width).start (with_gesture);
new GesturePropertyTransition (this, gesture_tracker, "height", null, (float) outer_rect.height).start (with_gesture);
new GesturePropertyTransition (this, gesture_tracker, "shadow-opacity", (uint8) 255, (uint8) 0).start (with_gesture);
new GesturePropertyTransition (window_icon, gesture_tracker, "opacity", 255u, 0u).start (with_gesture, () => {
in_slot_animation = false;
place_widgets (outer_rect.width, outer_rect.height, target_scale);
});
GestureTracker.OnUpdate on_animation_update = (percentage) => {
var x = GestureTracker.animation_value (initial_x, target_x, percentage);
var y = GestureTracker.animation_value (initial_y, target_y, percentage);
var width = GestureTracker.animation_value (initial_width, outer_rect.width, percentage);
var height = GestureTracker.animation_value (initial_height, outer_rect.height, percentage);
var scale = GestureTracker.animation_value (initial_scale, target_scale, percentage);
var opacity = GestureTracker.animation_value (255f, 0f, percentage);
set_size (width, height);
set_position (x, y);
window_icon.opacity = (uint) opacity;
set_window_icon_position (width, height, scale, false);
place_widgets ((int)width, (int)height, scale);
shadow_opacity = (uint8) opacity;
};
GestureTracker.OnEnd on_animation_end = (percentage, cancel_action) => {
@ -313,45 +310,16 @@ public class Gala.WindowClone : Clutter.Actor {
return;
}
var duration = (animate && AnimationsSettings.get_enable_animations ()) ? MultitaskingView.ANIMATION_DURATION : 0;
save_easing_state ();
set_easing_mode (Clutter.AnimationMode.EASE_OUT_QUAD);
set_easing_duration (duration);
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);
}
toggle_shadow (false);
window_icon.save_easing_state ();
window_icon.set_easing_mode (Clutter.AnimationMode.EASE_OUT_QUAD);
window_icon.set_easing_duration (duration);
window_icon.opacity = 0;
window_icon.set_easing_duration (AnimationsSettings.get_animation_duration (MultitaskingView.ANIMATION_DURATION));
set_window_icon_position (outer_rect.width, outer_rect.height, target_scale);
window_icon.restore_easing_state ();
var transition = window_icon.get_transition ("opacity");
if (transition != null) {
transition.completed.connect (() => {
in_slot_animation = false;
place_widgets (outer_rect.width, outer_rect.height, target_scale);
});
} else {
in_slot_animation = false;
place_widgets (outer_rect.width, outer_rect.height, target_scale);
}
};
if (!animate || gesture_tracker == null || !with_gesture || !AnimationsSettings.get_enable_animations ()) {
if (gesture_tracker == null || !with_gesture || !AnimationsSettings.get_enable_animations ()) {
on_animation_end (1, false, 0);
} else {
gesture_tracker.connect_handlers (null, (owned) on_animation_update, (owned) on_animation_end);
@ -362,36 +330,44 @@ public class Gala.WindowClone : Clutter.Actor {
* Animate the window to the given slot
*/
#if HAS_MUTTER45
public void take_slot (Mtk.Rectangle rect, bool with_gesture = false, bool is_cancel_animation = false) {
public void take_slot (Mtk.Rectangle rect, bool from_window_position, bool with_gesture = false, bool is_cancel_animation = false) {
#else
public void take_slot (Meta.Rectangle rect, bool with_gesture = false, bool is_cancel_animation = false) {
public void take_slot (Meta.Rectangle rect, bool from_window_position, bool with_gesture = false, bool is_cancel_animation = false) {
#endif
slot = rect;
var initial_x = x;
var initial_y = y;
var initial_width = width;
var initial_height = height;
in_slot_animation = true;
active = false;
var scale = display.get_monitor_scale (display.get_monitor_index_for_rect (rect));
in_slot_animation = true;
var outer_rect = window.get_frame_rect ();
float initial_width = from_window_position ? outer_rect.width : width;
float initial_height = from_window_position ? outer_rect.height : height;
var monitor_geom = display.get_monitor_geometry (window.get_monitor ());
float intial_x = from_window_position ? outer_rect.x - monitor_geom.x : x;
float intial_y = from_window_position ? outer_rect.y - monitor_geom.y : y;
var scale = display.get_monitor_scale (display.get_monitor_index_for_rect (rect));
place_widgets (rect.width, rect.height, scale);
set_window_icon_position (initial_width, initial_height, scale);
new GesturePropertyTransition (this, gesture_tracker, "x", intial_x, (float) rect.x).start (with_gesture);
new GesturePropertyTransition (this, gesture_tracker, "y", intial_y, (float) rect.y).start (with_gesture);
new GesturePropertyTransition (this, gesture_tracker, "width", (float) initial_width, (float) rect.width).start (with_gesture);
new GesturePropertyTransition (this, gesture_tracker, "height", (float) initial_height, (float) rect.height).start (with_gesture);
new GesturePropertyTransition (this, gesture_tracker, "shadow-opacity", (uint8) 0, (uint8) 255).start (with_gesture);
new GesturePropertyTransition (window_icon, gesture_tracker, "opacity", 0u, 255u).start (with_gesture, () => {
in_slot_animation = false;
place_widgets (rect.width, rect.height, scale);
});
GestureTracker.OnUpdate on_animation_update = (percentage) => {
var x = GestureTracker.animation_value (initial_x, rect.x, percentage);
var y = GestureTracker.animation_value (initial_y, rect.y, percentage);
var width = GestureTracker.animation_value (initial_width, rect.width, percentage);
var height = GestureTracker.animation_value (initial_height, rect.height, percentage);
var opacity = GestureTracker.animation_value (0f, 255f, percentage);
set_size (width, height);
set_position (x, y);
window_icon.opacity = (uint) opacity;
set_window_icon_position (width, height, scale, false);
shadow_opacity = (uint8) opacity;
};
GestureTracker.OnEnd on_animation_end = (percentage, cancel_action) => {
@ -401,34 +377,13 @@ public class Gala.WindowClone : Clutter.Actor {
var duration = AnimationsSettings.get_animation_duration (MultitaskingView.ANIMATION_DURATION);
save_easing_state ();
set_easing_mode (Clutter.AnimationMode.EASE_OUT_QUAD);
set_easing_duration (duration);
set_size (rect.width, rect.height);
set_position (rect.x, rect.y);
opacity = 255;
restore_easing_state ();
window_icon.save_easing_state ();
window_icon.set_easing_mode (Clutter.AnimationMode.EASE_OUT_QUAD);
window_icon.set_easing_duration (duration);
window_icon.opacity = 255;
set_window_icon_position (rect.width, rect.height, scale);
window_icon.restore_easing_state ();
toggle_shadow (true);
var transition = window_icon.get_transition ("opacity");
if (transition != null) {
transition.completed.connect (() => {
in_slot_animation = false;
place_widgets (rect.width, rect.height, scale);
});
} else {
in_slot_animation = false;
place_widgets (rect.width, rect.height, scale);
}
};
if (gesture_tracker == null || !with_gesture || !AnimationsSettings.get_enable_animations ()) {

View File

@ -167,7 +167,7 @@ namespace Gala {
* Recalculate the tiling positions of the windows and animate them to
* the resulting spots.
*/
public void reflow (bool with_gesture = false, bool is_cancel_animation = false) {
public void reflow (bool with_gesture = false, bool is_cancel_animation = false, bool opening = false) {
if (!opened) {
return;
}
@ -206,7 +206,7 @@ namespace Gala {
foreach (var tilable in window_positions) {
unowned var clone = (WindowClone) tilable.id;
clone.take_slot (tilable.rect, with_gesture, is_cancel_animation);
clone.take_slot (tilable.rect, opening, with_gesture, is_cancel_animation);
}
}
@ -393,15 +393,7 @@ namespace Gala {
current_window = null;
}
// make sure our windows are where they belong in case they were moved
// while were closed.
if (gesture_tracker == null || !is_cancel_animation) {
foreach (var window in get_children ()) {
((WindowClone) window).transition_to_original_state (false, with_gesture, is_cancel_animation);
}
}
reflow (with_gesture, is_cancel_animation);
reflow (with_gesture, is_cancel_animation, true);
}
/**
@ -416,7 +408,7 @@ namespace Gala {
opened = false;
foreach (var window in get_children ()) {
((WindowClone) window).transition_to_original_state (true, with_gesture, is_cancel_animation);
((WindowClone) window).transition_to_original_state (with_gesture, is_cancel_animation);
}
}
}

View File

@ -364,46 +364,13 @@ namespace Gala {
var scale = (float)(monitor.height - InternalUtils.scale_to_int (TOP_OFFSET + BOTTOM_OFFSET, scale_factor)) / monitor.height;
var pivot_y = InternalUtils.scale_to_int (TOP_OFFSET, scale_factor) / (monitor.height - monitor.height * scale);
background.set_pivot_point (0.5f, pivot_y);
update_size (monitor);
GestureTracker.OnBegin on_animation_begin = () => {
x = initial_x;
background.set_pivot_point (0.5f, pivot_y);
};
GestureTracker.OnUpdate on_animation_update = (percentage) => {
var x = GestureTracker.animation_value (initial_x, target_x, percentage);
set_x (x);
var update_scale = (double) GestureTracker.animation_value (1.0f, (float)scale, percentage);
background.set_scale (update_scale, update_scale);
};
GestureTracker.OnEnd on_animation_end = (percentage, cancel_action) => {
if (cancel_action) {
return;
}
save_easing_state ();
set_easing_mode (Clutter.AnimationMode.EASE_OUT_QUAD);
set_easing_duration (AnimationsSettings.get_animation_duration (MultitaskingView.ANIMATION_DURATION));
set_x (target_x);
restore_easing_state ();
background.save_easing_state ();
background.set_easing_mode (Clutter.AnimationMode.EASE_OUT_QUAD);
background.set_easing_duration (AnimationsSettings.get_animation_duration (MultitaskingView.ANIMATION_DURATION));
background.set_scale (scale, scale);
background.restore_easing_state ();
};
if (!with_gesture || !AnimationsSettings.get_enable_animations ()) {
on_animation_begin (0);
on_animation_end (1, false, 0);
} else {
gesture_tracker.connect_handlers ((owned) on_animation_begin, (owned) on_animation_update, (owned)on_animation_end);
}
new GesturePropertyTransition (this, gesture_tracker, "x", initial_x, target_x).start (with_gesture);
new GesturePropertyTransition (background, gesture_tracker, "scale-x", 1.0d, (double) scale).start (with_gesture);
new GesturePropertyTransition (background, gesture_tracker, "scale-y", 1.0d, (double) scale).start (with_gesture);
#if HAS_MUTTER45
Mtk.Rectangle area = {
@ -444,41 +411,9 @@ namespace Gala {
var initial_x = is_cancel_animation ? x : multitasking_view_x ();
var target_x = multitasking_view_x () + current_x_overlap ();
double initial_scale_x, initial_scale_y;
background.get_scale (out initial_scale_x, out initial_scale_y);
GestureTracker.OnUpdate on_animation_update = (percentage) => {
var x = GestureTracker.animation_value (initial_x, target_x, percentage);
set_x (x);
double scale_x = (double) GestureTracker.animation_value ((float) initial_scale_x, 1.0f, percentage);
double scale_y = (double) GestureTracker.animation_value ((float) initial_scale_y, 1.0f, percentage);
background.set_scale (scale_x, scale_y);
};
GestureTracker.OnEnd on_animation_end = (percentage, cancel_action) => {
if (cancel_action) {
return;
}
save_easing_state ();
set_easing_mode (Clutter.AnimationMode.EASE_OUT_QUAD);
set_easing_duration (AnimationsSettings.get_animation_duration (MultitaskingView.ANIMATION_DURATION));
set_x (target_x);
restore_easing_state ();
background.save_easing_state ();
background.set_easing_mode (Clutter.AnimationMode.EASE_OUT_QUAD);
background.set_easing_duration (AnimationsSettings.get_animation_duration (MultitaskingView.ANIMATION_DURATION));
background.set_scale (1, 1);
background.restore_easing_state ();
};
if (!with_gesture || !AnimationsSettings.get_enable_animations ()) {
on_animation_end (1, false, 0);
} else {
gesture_tracker.connect_handlers (null, (owned) on_animation_update, (owned) on_animation_end);
}
new GesturePropertyTransition (this, gesture_tracker, "x", initial_x, target_x).start (with_gesture);
new GesturePropertyTransition (background, gesture_tracker, "scale-x", null, 1.0d).start (with_gesture);
new GesturePropertyTransition (background, gesture_tracker, "scale-y", null, 1.0d).start (with_gesture);
window_container.close (with_gesture, is_cancel_animation);
}

View File

@ -662,81 +662,16 @@ namespace Gala {
var monitor_scale = display.get_monitor_scale (display.get_primary_monitor ());
var nudge_gap = InternalUtils.scale_to_int (NUDGE_GAP, monitor_scale);
float dest = 0;
if (!switch_workspace_with_gesture) {
dest = nudge_gap;
} else {
var workspaces_geometry = InternalUtils.get_workspaces_geometry (display);
dest = workspaces_geometry.width;
if (direction == RIGHT) {
nudge_gap *= -1;
}
if (direction == Meta.MotionDirection.RIGHT) {
dest *= -1;
}
var animation_mode = Clutter.AnimationMode.EASE_OUT_CUBIC;
GestureTracker.OnUpdate on_animation_update = (percentage) => {
var x_out = GestureTracker.animation_value (0.0f, dest, percentage, true).clamp (-nudge_gap, nudge_gap);
out_group.x = x_out;
wallpaper.x = x_out;
};
GestureTracker.OnEnd on_animation_end = (percentage, cancel_action, duration) => {
out_group.save_easing_state ();
out_group.set_easing_mode (animation_mode);
out_group.set_easing_duration (AnimationDuration.NUDGE / 2);
wallpaper.save_easing_state ();
wallpaper.set_easing_mode (animation_mode);
wallpaper.set_easing_duration (AnimationDuration.NUDGE / 2);
out_group.x = 0.0f;
out_group.restore_easing_state ();
wallpaper.x = 0.0f;
wallpaper.restore_easing_state ();
unowned var transition = out_group.get_transition ("x");
transition.completed.connect (() => {
switch_workspace_animation_finished (direction, false, true);
animating_switch_workspace = false;
});
};
if (!switch_workspace_with_gesture) {
double[] keyframes = { 0.5 };
GLib.Value[] x = { dest };
var out_group_nudge = new Clutter.KeyframeTransition ("translation-x") {
duration = AnimationDuration.NUDGE,
remove_on_complete = true,
progress_mode = Clutter.AnimationMode.EASE_IN_QUAD
};
out_group_nudge.set_from_value (0.0f);
out_group_nudge.set_to_value (0.0f);
out_group_nudge.set_key_frames (keyframes);
out_group_nudge.set_values (x);
out_group.add_transition ("nudge", out_group_nudge);
var wallpaper_nudge = new Clutter.KeyframeTransition ("translation-x") {
duration = AnimationDuration.NUDGE,
remove_on_complete = true,
progress_mode = Clutter.AnimationMode.EASE_IN_QUAD
};
wallpaper_nudge.set_from_value (0.0f);
wallpaper_nudge.set_to_value (0.0f);
wallpaper_nudge.set_key_frames (keyframes);
wallpaper_nudge.set_values (x);
wallpaper.add_transition ("nudge", wallpaper_nudge);
wallpaper_nudge.completed.connect (() => {
switch_workspace_animation_finished (direction, false, true);
animating_switch_workspace = false;
});
} else {
gesture_tracker.connect_handlers (null, (owned) on_animation_update, (owned) on_animation_end);
}
new GesturePropertyTransition (out_group, gesture_tracker, "x", 0f, 0f, nudge_gap).start (switch_workspace_with_gesture);
new GesturePropertyTransition (wallpaper, gesture_tracker, "x", 0f, 0f, nudge_gap).start (switch_workspace_with_gesture, () => {
switch_workspace_animation_finished (direction, false, true);
animating_switch_workspace = false;
});
}
private void update_input_area () {
@ -2261,6 +2196,11 @@ namespace Gala {
private void switch_workspace_animation_finished (Meta.MotionDirection animation_direction,
bool cancel_action, bool is_nudge_animation = false) {
if (!animating_switch_workspace) {
return;
}
animating_switch_workspace = cancel_action;
if (switch_workspace_window_created_id > 0) {
disconnect (switch_workspace_window_created_id);
switch_workspace_window_created_id = 0;
@ -2269,7 +2209,6 @@ namespace Gala {
if (!is_nudge_animation) {
switch_workspace_completed ();
}
animating_switch_workspace = cancel_action;
if (cancel_action) {
var cancel_direction = (animation_direction == Meta.MotionDirection.LEFT)
@ -2352,7 +2291,8 @@ namespace Gala {
}
public override void kill_switch_workspace () {
end_switch_workspace ();
// We don't care about animation direction, we don't want to cancel it, make it nudge so that it doesn't call switch_workspace_completed ()
switch_workspace_animation_finished (LEFT, false, true);
}
public override void locate_pointer () {

View File

@ -34,6 +34,7 @@ gala_bin_sources = files(
'ColorFilters/FilterManager.vala',
'ColorFilters/MonochromeEffect.vala',
'Gestures/Gesture.vala',
'Gestures/GesturePropertyTransition.vala',
'Gestures/GestureSettings.vala',
'Gestures/GestureTracker.vala',
'Gestures/ScrollBackend.vala',
@ -41,7 +42,6 @@ gala_bin_sources = files(
'HotCorners/Barrier.vala',
'HotCorners/HotCorner.vala',
'HotCorners/HotCornerManager.vala',
'ShellClients/DelegateActor.vala',
'ShellClients/HideTracker.vala',
'ShellClients/ManagedClient.vala',
'ShellClients/NotificationsClient.vala',

View File

@ -6,8 +6,12 @@ namespace X {
namespace Fixes {
[CCode (cheader_filename = "X11/extensions/Xfixes.h", cname = "XFixesCreateRegion")]
public static X.XserverRegion create_region (X.Display display, [CCode (array_length = true)] X.Xrectangle[] rectangles);
[CCode (cheader_filename = "X11/extensions/Xfixes.h", cname = "XFixesCreateRegionFromWindow")]
public static X.XserverRegion create_region_from_window (X.Display display, X.Window window, int shape_kind);
[CCode (cheader_filename = "X11/extensions/Xfixes.h", cname = "XFixesDestroyRegion")]
public static void destroy_region (X.Display display, X.XserverRegion region);
[CCode (cheader_filename = "X11/extensions/Xfixes.h", cname = "XFixesSetWindowShapeRegion")]
public static void set_window_shape_region (X.Display display, X.Window win, int shape_kind, int x_off, int y_off, XserverRegion region);
}
[SimpleType]
[CCode (cheader_filename = "X11/extensions/Xfixes.h", cname = "XserverRegion", has_type_id = false)]