diff --git a/src/DragDropAction.vala b/src/DragDropAction.vala
index 77c59137..a0fd58d7 100644
--- a/src/DragDropAction.vala
+++ b/src/DragDropAction.vala
@@ -15,6 +15,8 @@
// along with this program. If not, see .
//
+using Clutter;
+
namespace Gala
{
public enum DragDropActionType
@@ -25,6 +27,8 @@ namespace Gala
public class DragDropAction : Clutter.Action
{
+ static Gee.HashMap>? sources = null;
+
/**
* A drag has been started. You have to connect to this signal and
* return an actor that is transformed during the drag operation.
@@ -33,7 +37,7 @@ namespace Gala
* @param y The global y coordinate where the action was activated
* @return A ClutterActor that serves as handle
*/
- public signal Clutter.Actor drag_begin (float x, float y);
+ public signal Actor drag_begin (float x, float y);
/**
* A drag has been canceled. You may want to consider cleaning up
@@ -46,7 +50,7 @@ namespace Gala
*
* @param actor The actor on which the drag finished
*/
- public signal void drag_end (Clutter.Actor actor);
+ public signal void drag_end (Actor actor);
/**
* The destination has been crossed
@@ -61,7 +65,7 @@ namespace Gala
* @param destination The destination actor that has been crossed
* @param hovered Whether the actor is now hovered or has just been left
*/
- public signal void destination_crossed (Clutter.Actor destination, bool hovered);
+ public signal void destination_crossed (Actor destination, bool hovered);
/**
* The source has been clicked, but the movement was not larger than
@@ -81,7 +85,7 @@ namespace Gala
*/
public string drag_id { get; construct; }
- public Clutter.Actor handle { get; private set; }
+ public Actor handle { get; private set; }
/**
* Indicates whether a drag action is currently active
*/
@@ -93,9 +97,8 @@ namespace Gala
*/
public bool allow_bubbling { get; set; default = true; }
- Clutter.Actor? hovered = null;
+ Actor? hovered = null;
bool clicked = false;
- bool actor_was_reactive = false;
float last_x;
float last_y;
@@ -110,6 +113,9 @@ namespace Gala
public DragDropAction (DragDropActionType type, string id)
{
Object (drag_type : type, drag_id : id);
+
+ if (sources == null)
+ sources = new Gee.HashMap> ();
}
~DragDropAction ()
@@ -118,7 +124,7 @@ namespace Gala
release_actor (actor);
}
- public override void set_actor (Clutter.Actor? new_actor)
+ public override void set_actor (Actor? new_actor)
{
if (actor != null) {
release_actor (actor);
@@ -131,27 +137,38 @@ namespace Gala
base.set_actor (new_actor);
}
- void release_actor (Clutter.Actor actor)
+ void release_actor (Actor actor)
{
if (drag_type == DragDropActionType.SOURCE) {
actor.button_press_event.disconnect (source_clicked);
+
+ var source_list = sources.@get (drag_id);
+ source_list.remove (actor);
}
}
- void connect_actor (Clutter.Actor actor)
+ void connect_actor (Actor actor)
{
if (drag_type == DragDropActionType.SOURCE) {
actor.button_press_event.connect (source_clicked);
+
+ var source_list = sources.@get (drag_id);
+ if (source_list == null) {
+ source_list = new Gee.LinkedList ();
+ sources.@set (drag_id, source_list);
+ }
+
+ source_list.add (actor);
}
}
- void emit_crossed (Clutter.Actor destination, bool hovered)
+ void emit_crossed (Actor destination, bool hovered)
{
get_drag_drop_action (destination).crossed (hovered);
destination_crossed (destination, hovered);
}
- bool source_clicked (Clutter.ButtonEvent event)
+ bool source_clicked (ButtonEvent event)
{
if (event.button != 1) {
actor_clicked (event.button);
@@ -166,12 +183,12 @@ namespace Gala
return true;
}
- bool follow_move (Clutter.Event event)
+ bool follow_move (Event event)
{
// still determining if we actually want to start a drag action
if (!dragging) {
switch (event.get_type ()) {
- case Clutter.EventType.MOTION:
+ case EventType.MOTION:
float x, y;
event.get_coords (out x, out y);
@@ -184,14 +201,20 @@ namespace Gala
return false;
}
- actor_was_reactive = handle.reactive;
handle.reactive = false;
clicked = false;
dragging = true;
+
+ var source_list = sources.@get (drag_id);
+ if (source_list != null) {
+ foreach (var actor in source_list) {
+ actor.reactive = false;
+ }
+ }
}
return true;
- case Clutter.EventType.BUTTON_RELEASE:
+ case EventType.BUTTON_RELEASE:
float x, y, ex, ey;
event.get_coords (out ex, out ey);
actor.get_transformed_position (out x, out y);
@@ -211,12 +234,12 @@ namespace Gala
}
switch (event.get_type ()) {
- case Clutter.EventType.KEY_PRESS:
- if (event.get_key_code () == Clutter.Key.Escape) {
+ case EventType.KEY_PRESS:
+ if (event.get_key_code () == Key.Escape) {
cancel ();
}
return true;
- case Clutter.EventType.MOTION:
+ case EventType.MOTION:
float x, y;
event.get_coords (out x, out y);
handle.x -= last_x - x;
@@ -225,7 +248,7 @@ namespace Gala
last_y = y;
var stage = actor.get_stage ();
- var actor = actor.get_stage ().get_actor_at_pos (Clutter.PickMode.REACTIVE, (int)x, (int)y);
+ var actor = actor.get_stage ().get_actor_at_pos (PickMode.REACTIVE, (int)x, (int)y);
DragDropAction action = null;
// if we're allowed to bubble and we this actor is not a destination, check its parents
if (actor != null && (action = get_drag_drop_action (actor)) == null && allow_bubbling) {
@@ -259,15 +282,15 @@ namespace Gala
emit_crossed (hovered, true);
return true;
- case Clutter.EventType.BUTTON_RELEASE:
+ case EventType.BUTTON_RELEASE:
if (hovered != null) {
finish ();
} else {
cancel ();
}
return true;
- case Clutter.EventType.ENTER:
- case Clutter.EventType.LEAVE:
+ case EventType.ENTER:
+ case EventType.LEAVE:
return true;
}
@@ -280,7 +303,7 @@ namespace Gala
*
* @return the DragDropAction instance on this actor or NULL
*/
- DragDropAction? get_drag_drop_action (Clutter.Actor actor)
+ DragDropAction? get_drag_drop_action (Actor actor)
{
DragDropAction? drop_action = null;
@@ -302,12 +325,7 @@ namespace Gala
*/
public void cancel ()
{
- if (dragging) {
- actor.get_stage ().captured_event.disconnect (follow_move);
- }
-
- dragging = false;
- actor.reactive = actor_was_reactive;
+ cleanup ();
drag_canceled ();
}
@@ -317,12 +335,24 @@ namespace Gala
// make sure they reset the style or whatever they changed when hovered
emit_crossed (hovered, false);
- actor.get_stage ().captured_event.disconnect (follow_move);
-
- dragging = false;
- actor.reactive = actor_was_reactive;
+ cleanup ();
drag_end (hovered);
}
+
+ void cleanup ()
+ {
+ var source_list = sources.@get (drag_id);
+ if (source_list != null) {
+ foreach (var actor in source_list) {
+ actor.reactive = true;
+ }
+ }
+
+ if (dragging)
+ actor.get_stage ().captured_event.disconnect (follow_move);
+
+ dragging = false;
+ }
}
}
diff --git a/src/InternalUtils.vala b/src/InternalUtils.vala
index d7953c31..c2d18289 100644
--- a/src/InternalUtils.vala
+++ b/src/InternalUtils.vala
@@ -23,6 +23,12 @@ namespace Gala
{
public class InternalUtils
{
+ public static bool workspaces_only_on_primary ()
+ {
+ return Prefs.get_dynamic_workspaces ()
+ && Prefs.get_workspaces_only_on_primary ();
+ }
+
/*
* Reload shadow settings
*/
diff --git a/src/Makefile.am b/src/Makefile.am
index 86c549df..7ba9205a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -55,6 +55,7 @@ gala_VALASOURCES = \
Widgets/WorkspaceThumb.vala \
Widgets/WorkspaceView.vala \
Widgets/MultitaskingView/IconGroup.vala \
+ Widgets/MultitaskingView/MonitorClone.vala \
Widgets/MultitaskingView/MultitaskingView.vala \
Widgets/MultitaskingView/TiledWindowContainer.vala \
Widgets/MultitaskingView/Workspace.vala \
diff --git a/src/Widgets/MultitaskingView/MonitorClone.vala b/src/Widgets/MultitaskingView/MonitorClone.vala
new file mode 100644
index 00000000..fb9d90ee
--- /dev/null
+++ b/src/Widgets/MultitaskingView/MonitorClone.vala
@@ -0,0 +1,90 @@
+using Clutter;
+using Meta;
+
+namespace Gala
+{
+ public class MonitorClone : Actor
+ {
+ public WindowManager wm { get; construct; }
+ public Screen screen { get; construct; }
+ public int monitor { get; construct; }
+
+ public signal void window_selected (Window window);
+
+ TiledWorkspaceContainer window_container;
+ Background background;
+
+ public MonitorClone (WindowManager wm, Screen screen, int monitor)
+ {
+ Object (wm: wm, monitor: monitor, screen: screen);
+
+ reactive = true;
+
+ background = new Background (screen, monitor, BackgroundSettings.get_default ().schema);
+ background.set_easing_duration (300);
+
+ window_container = new TiledWorkspaceContainer (wm.window_stacking_order);
+ window_container.window_selected.connect ((w) => { window_selected (w); });
+
+ wm.windows_restacked.connect (() => {
+ window_container.stacking_order = wm.window_stacking_order;
+ });
+
+ screen.window_entered_monitor.connect (window_entered);
+ screen.window_left_monitor.connect (window_left);
+
+ foreach (var window_actor in Compositor.get_window_actors (screen)) {
+ var window = window_actor.get_meta_window ();
+ if (window.get_monitor () == monitor) {
+ window_entered (monitor, window);
+ }
+ }
+
+ add_child (background);
+ add_child (window_container);
+
+ var drop = new DragDropAction (DragDropActionType.DESTINATION, "multitaskingview-window");
+ add_action (drop);
+
+ update_allocation ();
+ }
+
+ public void update_allocation ()
+ {
+ var monitor_geometry = screen.get_monitor_geometry (monitor);
+
+ set_position (monitor_geometry.x, monitor_geometry.y);
+ set_size (monitor_geometry.width, monitor_geometry.height);
+ window_container.set_size (monitor_geometry.width, monitor_geometry.height);
+ }
+
+ public void open ()
+ {
+ window_container.opened = true;
+ // background.opacity = 0; TODO consider this option
+ }
+
+ public void close ()
+ {
+ window_container.opened = false;
+ background.opacity = 255;
+ }
+
+ void window_left (int window_monitor, Window window)
+ {
+ if (window_monitor != monitor)
+ return;
+
+ window_container.remove_window (window);
+ }
+
+ void window_entered (int window_monitor, Window window)
+ {
+ if (window_monitor != monitor || window.window_type != WindowType.NORMAL)
+ return;
+
+ window_container.add_window (window);
+ }
+ }
+}
+
diff --git a/src/Widgets/MultitaskingView/MultitaskingView.vala b/src/Widgets/MultitaskingView/MultitaskingView.vala
index b042292f..7578a05e 100644
--- a/src/Widgets/MultitaskingView/MultitaskingView.vala
+++ b/src/Widgets/MultitaskingView/MultitaskingView.vala
@@ -11,6 +11,8 @@ namespace Gala
public WindowManager wm { get; construct set; }
public bool opened { get; private set; default = false; }
+ List window_containers_monitors;
+
Actor icon_groups;
Actor workspaces;
@@ -40,6 +42,67 @@ namespace Gala
screen.workspace_switched.connect_after ((from, to, direction) => {
update_positions (opened);
});
+
+ window_containers_monitors = new List ();
+ update_monitors ();
+ screen.monitors_changed.connect (update_monitors);
+
+ Prefs.add_listener ((pref) => {
+ if (pref == Preference.WORKSPACES_ONLY_ON_PRIMARY) {
+ update_monitors ();
+ return;
+ }
+
+ if (Prefs.get_dynamic_workspaces () ||
+ (pref != Preference.DYNAMIC_WORKSPACES && pref != Preference.NUM_WORKSPACES))
+ return;
+
+ Idle.add (() => {
+ unowned List existing_workspaces = screen.get_workspaces ();
+
+ foreach (var child in workspaces.get_children ()) {
+ var workspace_clone = child as WorkspaceClone;
+ if (existing_workspaces.index (workspace_clone.workspace) < 0) {
+ workspace_clone.window_selected.disconnect (window_selected);
+ workspace_clone.selected.disconnect (activate_workspace);
+ workspace_clone.icon_group.destroy ();
+ workspace_clone.destroy ();
+ }
+ }
+
+ update_monitors ();
+ update_positions ();
+
+ return false;
+ });
+ });
+ }
+
+ void update_monitors ()
+ {
+ foreach (var monitor_clone in window_containers_monitors)
+ monitor_clone.destroy ();
+
+ var primary = screen.get_primary_monitor ();
+
+ if (InternalUtils.workspaces_only_on_primary ()) {
+ for (var monitor = 0; monitor < screen.get_n_monitors (); monitor++) {
+ if (monitor == primary)
+ continue;
+
+ var monitor_clone = new MonitorClone (wm, screen, monitor);
+ monitor_clone.window_selected.connect (window_selected);
+ monitor_clone.visible = opened;
+
+ window_containers_monitors.append (monitor_clone);
+ wm.ui_group.add_child (monitor_clone);
+ }
+ }
+
+ var primary_geometry = screen.get_monitor_geometry (primary);
+
+ set_position (primary_geometry.x, primary_geometry.y);
+ set_size (primary_geometry.width, primary_geometry.height);
}
public override void key_focus_out ()
@@ -221,12 +284,13 @@ namespace Gala
var opening = opened;
- unowned List windows = Meta.Compositor.get_window_actors (screen);
- var primary_monitor = screen.get_primary_monitor ();
- var monitor = screen.get_monitor_geometry (primary_monitor);
-
- set_position (monitor.x, monitor.y);
- set_size (monitor.width, monitor.height);
+ foreach (var container in window_containers_monitors) {
+ if (opening) {
+ container.visible = true;
+ container.open ();
+ } else
+ container.close ();
+ }
if (opening) {
wm.begin_modal ();
@@ -238,7 +302,7 @@ namespace Gala
show ();
grab_key_focus ();
- icon_groups.y = monitor.height - WorkspaceClone.BOTTOM_OFFSET + 20;
+ icon_groups.y = height - WorkspaceClone.BOTTOM_OFFSET + 20;
}
// find active workspace clone and raise it, so there are no overlaps while transitioning
@@ -265,6 +329,10 @@ namespace Gala
if (!opening) {
Timeout.add (290, () => {
+ foreach (var container in window_containers_monitors) {
+ container.visible = false;
+ }
+
hide ();
wm.background_group.show ();
diff --git a/src/Widgets/MultitaskingView/TiledWindowContainer.vala b/src/Widgets/MultitaskingView/TiledWindowContainer.vala
index 359c49ca..f2e18089 100644
--- a/src/Widgets/MultitaskingView/TiledWindowContainer.vala
+++ b/src/Widgets/MultitaskingView/TiledWindowContainer.vala
@@ -136,11 +136,9 @@ namespace Gala
set_child_above_sibling (close_button, clone);
set_child_above_sibling (window_icon, clone);
+ transition_to_original_state (false);
+
var outer_rect = window.get_outer_rect ();
-
- set_position (outer_rect.x, outer_rect.y);
- set_size (outer_rect.width, outer_rect.height);
-
add_effect_with_name ("shadow", new ShadowEffect (outer_rect.width, outer_rect.height, 40, 5));
#if HAS_MUTTER312
window.size_changed.connect (update_shadow_size);
@@ -156,6 +154,8 @@ namespace Gala
opacity = 0;
take_slot (slot);
opacity = 255;
+
+ request_reposition ();
}
}
@@ -171,6 +171,9 @@ namespace Gala
shadow_update_timeout = 0;
+ // if there was a size change it makes sense to recalculate the positions
+ request_reposition ();
+
return false;
});
}
@@ -179,10 +182,16 @@ namespace Gala
{
var outer_rect = window.get_outer_rect ();
+ float offset_x = 0, offset_y = 0;
+
+ var parent = get_parent ();
+ if (parent != null)
+ parent.get_transformed_position (out offset_x, out offset_y);
+
set_easing_mode (AnimationMode.EASE_IN_OUT_CUBIC);
set_easing_duration (animate ? 300 : 0);
- set_position (outer_rect.x, outer_rect.y);
+ set_position (outer_rect.x - offset_x, outer_rect.y - offset_y);
set_size (outer_rect.width, outer_rect.height);
window_icon.opacity = 0;
@@ -334,7 +343,8 @@ namespace Gala
var icon_group = destination as IconGroup;
- if (icon_group.workspace == window.get_workspace ())
+ if (icon_group.workspace == window.get_workspace ()
+ && window.get_monitor () == window.get_screen ().get_primary_monitor ())
return;
var scale = hovered ? 0.1 : 0.4;
@@ -367,15 +377,30 @@ namespace Gala
workspace = (destination as IconGroup).workspace;
} else if (destination is FramedBackground) {
workspace = (destination.get_parent () as WorkspaceClone).workspace;
+ } else if (destination is MonitorClone) {
+ window.move_to_monitor ((destination as MonitorClone).monitor);
+ unmanaged ();
+ return;
+ }
+
+ bool did_move = false;
+
+ var primary = window.get_screen ().get_primary_monitor ();
+ if (window.get_monitor () != primary) {
+ window.move_to_monitor (primary);
+ did_move = true;
}
if (workspace != null && workspace != window.get_workspace ()) {
window.change_workspace (workspace);
+ did_move = true;
+ }
+
+ if (did_move)
unmanaged ();
- } else {
+ else
// if we're dropped at the place where we came from interpret as cancel
drag_canceled ();
- }
}
void drag_canceled ()
@@ -401,10 +426,10 @@ namespace Gala
{
public signal void window_selected (Meta.Window window);
- public int padding_top { get; set; }
- public int padding_left { get; set; }
- public int padding_right { get; set; }
- public int padding_bottom { get; set; }
+ public int padding_top { get; set; default = 12; }
+ public int padding_left { get; set; default = 12; }
+ public int padding_right { get; set; default = 12; }
+ public int padding_bottom { get; set; default = 12; }
HashTable _stacking_order;
public HashTable stacking_order {
@@ -502,6 +527,8 @@ namespace Gala
break;
}
}
+
+ reflow ();
}
void window_selected_cb (TiledWindow tiled)
diff --git a/src/WorkspaceManager.vala b/src/WorkspaceManager.vala
index 080a220b..0ade971c 100644
--- a/src/WorkspaceManager.vala
+++ b/src/WorkspaceManager.vala
@@ -29,10 +29,14 @@ namespace Gala
*/
public bool remove_workspace_immediately { get; set; default = false; }
+ Gee.LinkedList workspaces_marked_removed;
+
public WorkspaceManager (Screen screen)
{
Object (screen: screen);
+ workspaces_marked_removed = new Gee.LinkedList ();
+
if (Prefs.get_dynamic_workspaces ())
screen.override_workspace_layout (ScreenCorner.TOPLEFT, false, 1, -1);
@@ -41,8 +45,29 @@ namespace Gala
Prefs.add_listener (prefs_listener);
- screen.workspace_added.connect (workspace_added);
screen.workspace_switched.connect_after (workspace_switched);
+ screen.workspace_added.connect (workspace_added);
+ screen.workspace_removed.connect_after (() => {
+ unowned List existing_workspaces = screen.get_workspaces ();
+
+ var it = workspaces_marked_removed.iterator ();
+ while (it.next ()) {
+ if (existing_workspaces.index (it.@get ()) < 0)
+ it.remove ();
+ }
+ });
+
+ screen.window_left_monitor.connect ((monitor, window) => {
+ if (InternalUtils.workspaces_only_on_primary ()
+ && monitor == screen.get_primary_monitor ())
+ window_removed (window.get_workspace (), window);
+ });
+
+ screen.window_entered_monitor.connect ((monitor, window) => {
+ if (InternalUtils.workspaces_only_on_primary ()
+ && monitor == screen.get_primary_monitor ())
+ window_added (window.get_workspace (), window);
+ });
// make sure the last workspace has no windows on it
if (Prefs.get_dynamic_workspaces ()
@@ -125,23 +150,6 @@ namespace Gala
// if the last workspace has a window, we need to append a new workspace
if (Utils.get_n_windows (screen.get_workspace_by_index (screen.get_n_workspaces () - 1)) > 0)
append_workspace ();
-
- } else if ((pref == Preference.DYNAMIC_WORKSPACES
- || pref == Preference.NUM_WORKSPACES)
- && !Prefs.get_dynamic_workspaces ()) {
-
- var time = screen.get_display ().get_current_time ();
- var n_workspaces = screen.get_n_workspaces ();
-
- /* TODO check if this is still needed
- // only need to listen for the case when workspaces were removed.
- // Any other case will be caught by the workspace_added signal.
- // For some reason workspace_removed is not emitted, when changing the workspace number
- if (Prefs.get_num_workspaces () < n_workspaces) {
- for (int i = Prefs.get_num_workspaces () - 1; i < n_workspaces; i++) {
- screen.remove_workspace (screen.get_workspace_by_index (i), time);
- }
- }*/
}
}
@@ -171,6 +179,13 @@ namespace Gala
next.activate (time);
}
+ // workspace has already been removed
+ if (workspace in workspaces_marked_removed) {
+ return;
+ }
+
+ workspaces_marked_removed.add (workspace);
+
screen.remove_workspace (workspace, time);
}
}