Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/Gestures/GesturePropertyTransition.vala
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,11 @@ public class Gala.GesturePropertyTransition : Object {
if (transition == null) {
on_animation_end (1, 1, gesture_tracker.min_animation_duration);
} else {
transition.stopped.connect (() => on_animation_end (1, 1, gesture_tracker.min_animation_duration));
transition.stopped.connect ((is_finished) => {
if (is_finished) {
on_animation_end (0, 0, gesture_tracker.min_animation_duration);
}
});
}
} else {
on_animation_end (1, 1, gesture_tracker.min_animation_duration);
Expand Down
2 changes: 1 addition & 1 deletion src/Gestures/GestureTracker.vala
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public class Gala.GestureTracker : Object {
*/
public bool enabled { get; set; default = true; }

public bool recognizing { get; private set; }
public bool recognizing { get; private set; default = false; }

/**
* Emitted when a new gesture is detected.
Expand Down
35 changes: 35 additions & 0 deletions src/Widgets/DesktopWorkspaceSwitcher/DesktopWorkspaceClone.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@

public class Gala.DesktopWorkspaceClone : Clutter.Actor {
public unowned DesktopWorkspaceSwitcher switcher { get; construct; }
public Meta.Workspace workspace { get; construct; }

public DesktopWorkspaceClone (DesktopWorkspaceSwitcher switcher, Meta.Workspace workspace) {
Object (switcher: switcher, workspace: workspace);
}

construct {
unowned var display = workspace.get_display ();
var monitor = display.get_primary_monitor ();
var monitor_geom = display.get_monitor_geometry (monitor);

width = monitor_geom.width;
height = monitor_geom.height;

unowned var background = switcher.background_group.get_child_at_index (monitor);
add_child (new Clutter.Clone (background));

foreach (var window in workspace.list_windows ()) { //TODO: sort by stacking.
if (switcher.is_static (window)) {
continue;
}

var window_actor = (Meta.WindowActor) window.get_compositor_private ();

var clone = new Clutter.Clone (window_actor);
clone.x = window_actor.x - monitor_geom.x;
clone.y = window_actor.y - monitor_geom.y;

add_child (clone);
}
}
}
220 changes: 220 additions & 0 deletions src/Widgets/DesktopWorkspaceSwitcher/DesktopWorkspaceSwitcher.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@

// TODO: We might need to hand the old active index aka from to animate_workspace_switch etc. if the workspace_manager.get_active_workspace_index () is already updated.
public class Gala.DesktopWorkspaceSwitcher : Clutter.Actor {
private const int WORKSPACE_GAP = 24;

public Meta.Display display { get; construct; }
public Clutter.Actor background_group { get; construct; }

private GestureTracker gesture_tracker;

private WindowGrabTracker window_grab_tracker;
private Meta.Window? moving;

private Clutter.Actor workspaces;
private GesturePropertyTransition x_transition;

private Clutter.Actor static_windows;

// This is the index of the workspace that from our POV is the active one (we're animating towards it).
// This is not necessarily the same as the index of the workspace that is actually active.
// (e.g. when the gesture animation finishes but the workspace wasn't activated yet)
private int active_index;

public DesktopWorkspaceSwitcher (Meta.Display display, Clutter.Actor background_group) {
Object (display: display, background_group: background_group);
}

construct {
background_color = { 0x2e, 0x34, 0x36, 0xff };
active_index = display.get_workspace_manager ().get_active_workspace_index ();

clip_to_allocation = true;
visible = false;

workspaces = new Clutter.Actor () {
layout_manager = new Clutter.BoxLayout () {
orientation = HORIZONTAL,
spacing = WORKSPACE_GAP,
}
};
add_child (workspaces);

static_windows = new Clutter.Actor ();
add_child (static_windows);

gesture_tracker = new GestureTracker (AnimationDuration.WORKSPACE_SWITCH_MIN, AnimationDuration.WORKSPACE_SWITCH);
gesture_tracker.enable_touchpad ();
gesture_tracker.on_gesture_detected.connect (on_gesture_detected);
gesture_tracker.on_gesture_handled.connect (on_gesture_handled);

x_transition = new GesturePropertyTransition (workspaces, gesture_tracker, "x", null, 0f);

window_grab_tracker = new WindowGrabTracker (display);
}

/**
* Moves a window to the given workspace and activates it.
*/
public void move_window (Meta.Window? window, Meta.Workspace workspace, uint32 timestamp) {
if (window == null) {
return;
}

unowned var manager = display.get_workspace_manager ();

unowned var active = manager.get_active_workspace ();

// don't allow empty workspaces to be created by moving, if we have dynamic workspaces
if (Utils.get_n_windows (active) == 1 && workspace.index () == manager.n_workspaces - 1) {
InternalUtils.bell_notify (display);
return;
}

if (active == workspace) {
InternalUtils.bell_notify (display);
return;
}

moving = window;

workspace.activate (timestamp);
}

private bool on_gesture_detected (Gesture gesture) {
var action = GestureSettings.get_action (gesture);
return action == SWITCH_WORKSPACE || action == MOVE_TO_WORKSPACE;
}

private double on_gesture_handled (Gesture gesture, uint32 timestamp) {
var direction = gesture_tracker.settings.get_natural_scroll_direction (gesture);
var relative_dir = direction == LEFT ? -1 : 1;

var workspace_manager = display.get_workspace_manager ();
var target_index = active_index + relative_dir;

if (GestureSettings.get_action (gesture) == MOVE_TO_WORKSPACE) {
moving = display.focus_window;
}

var initial_progress = animate_workspace_switch (target_index, true);

gesture_tracker.add_end_callback (true, (percentage, completions, calculated_duration) => {
/* We can just use the active index here because it was already updated before us by the animate_workspace_switch */
workspace_manager.get_workspace_by_index (active_index).activate (display.get_current_time ());
});

return initial_progress;
}

public double animate_workspace_switch (int target_index, bool with_gesture) {
if (active_index == target_index || gesture_tracker.recognizing) { // active_index == target_index implies we've already animated e.g. via the one to one gesture
return 0;
}

visible = true;

if (workspaces.get_n_children () == 0) { // This might be > 0 if we interrupt an animation by starting a new gesture
build_workspace_row ();
}

unowned var workspace_manager = display.get_workspace_manager ();
var n_workspaces = workspace_manager.n_workspaces;

x_transition.overshoot_lower_clamp = (active_index < target_index) ? - (active_index + 0.1) : - (n_workspaces - active_index - 0.9);
x_transition.overshoot_upper_clamp = (active_index > target_index) ? (active_index + 0.1) : (n_workspaces - active_index - 0.9);
x_transition.to_value = calculate_x (target_index);
var initial_progress = x_transition.start (with_gesture, end_animation);

gesture_tracker.add_end_callback (with_gesture, (percentage, completions, calculated_duration) => {
completions = completions.clamp ((int) x_transition.overshoot_lower_clamp, (int) x_transition.overshoot_upper_clamp);
active_index += completions * (target_index - active_index);

if (moving != null && !moving.is_on_all_workspaces ()) {
moving.change_workspace_by_index (active_index, false);
moving = null;
}
});

return initial_progress;
}

private void build_workspace_row () {
background_group.get_child_at_index (display.get_primary_monitor ()).visible = false;

unowned var workspace_manager = display.get_workspace_manager ();
for (int i = 0; i < workspace_manager.n_workspaces; i++) {
var workspace = workspace_manager.get_workspace_by_index (i);

var workspace_clone = new DesktopWorkspaceClone (this, workspace);
workspaces.add_child (workspace_clone);
}

workspaces.x = calculate_x (active_index);

var monitor_geom = display.get_monitor_geometry (display.get_primary_monitor ());
foreach (var window_actor in display.get_window_actors ()) {
var window = window_actor.meta_window;

if (!window.is_on_primary_monitor ()) {
continue;
}

if (!is_static (window)) {
continue;
}

if (ShellClientsManager.get_instance ().is_positioned_window (window)) {
continue;
}

var clone = new Clutter.Clone (window_actor);
clone.x = window_actor.x - monitor_geom.x;
clone.y = window_actor.y - monitor_geom.y;

static_windows.add_child (clone);
}
}

private inline float calculate_x (int index) {
var monitor_geom = display.get_monitor_geometry (display.get_primary_monitor ());
return -index * (monitor_geom.width + WORKSPACE_GAP);
}

public void end_animation () {
workspaces.remove_all_children ();
static_windows.remove_all_children ();

background_group.get_child_at_index (display.get_primary_monitor ()).visible = true;

visible = false;
}

public bool is_static (Meta.Window window) {
if (window.window_type == DESKTOP || window.window_type == DOCK) {
return true;
}

if (window.is_on_all_workspaces ()) {
return true;
}

if (window == moving) {
return true;
}

if (ShellClientsManager.get_instance ().is_positioned_window (window)) {
return true;
}

return window == window_grab_tracker.current_window;
}

public override void get_preferred_width (float for_height, out float min_width, out float natural_width) {
min_width = natural_width = display.get_monitor_geometry (display.get_primary_monitor ()).width;
}

public override void get_preferred_height (float for_width, out float min_height, out float natural_height) {
min_height = natural_height = display.get_monitor_geometry (display.get_primary_monitor ()).height;
}
}
Loading
Loading