add proper multimonitor support if workspaces-only-on-primary is enabled

This commit is contained in:
Tom Beckmann 2014-06-21 16:04:24 +02:00
parent efa173fda1
commit 933f8d5576
7 changed files with 307 additions and 70 deletions

View File

@ -15,6 +15,8 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
// //
using Clutter;
namespace Gala namespace Gala
{ {
public enum DragDropActionType public enum DragDropActionType
@ -25,6 +27,8 @@ namespace Gala
public class DragDropAction : Clutter.Action public class DragDropAction : Clutter.Action
{ {
static Gee.HashMap<string,Gee.LinkedList<Actor>>? sources = null;
/** /**
* A drag has been started. You have to connect to this signal and * A drag has been started. You have to connect to this signal and
* return an actor that is transformed during the drag operation. * 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 * @param y The global y coordinate where the action was activated
* @return A ClutterActor that serves as handle * @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 * 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 * @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 * The destination has been crossed
@ -61,7 +65,7 @@ namespace Gala
* @param destination The destination actor that has been crossed * @param destination The destination actor that has been crossed
* @param hovered Whether the actor is now hovered or has just been left * @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 * 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 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 * Indicates whether a drag action is currently active
*/ */
@ -93,9 +97,8 @@ namespace Gala
*/ */
public bool allow_bubbling { get; set; default = true; } public bool allow_bubbling { get; set; default = true; }
Clutter.Actor? hovered = null; Actor? hovered = null;
bool clicked = false; bool clicked = false;
bool actor_was_reactive = false;
float last_x; float last_x;
float last_y; float last_y;
@ -110,6 +113,9 @@ namespace Gala
public DragDropAction (DragDropActionType type, string id) public DragDropAction (DragDropActionType type, string id)
{ {
Object (drag_type : type, drag_id : id); Object (drag_type : type, drag_id : id);
if (sources == null)
sources = new Gee.HashMap<string,Gee.LinkedList<Actor>> ();
} }
~DragDropAction () ~DragDropAction ()
@ -118,7 +124,7 @@ namespace Gala
release_actor (actor); release_actor (actor);
} }
public override void set_actor (Clutter.Actor? new_actor) public override void set_actor (Actor? new_actor)
{ {
if (actor != null) { if (actor != null) {
release_actor (actor); release_actor (actor);
@ -131,27 +137,38 @@ namespace Gala
base.set_actor (new_actor); base.set_actor (new_actor);
} }
void release_actor (Clutter.Actor actor) void release_actor (Actor actor)
{ {
if (drag_type == DragDropActionType.SOURCE) { if (drag_type == DragDropActionType.SOURCE) {
actor.button_press_event.disconnect (source_clicked); 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) { if (drag_type == DragDropActionType.SOURCE) {
actor.button_press_event.connect (source_clicked); actor.button_press_event.connect (source_clicked);
var source_list = sources.@get (drag_id);
if (source_list == null) {
source_list = new Gee.LinkedList<Actor> ();
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); get_drag_drop_action (destination).crossed (hovered);
destination_crossed (destination, hovered); destination_crossed (destination, hovered);
} }
bool source_clicked (Clutter.ButtonEvent event) bool source_clicked (ButtonEvent event)
{ {
if (event.button != 1) { if (event.button != 1) {
actor_clicked (event.button); actor_clicked (event.button);
@ -166,12 +183,12 @@ namespace Gala
return true; return true;
} }
bool follow_move (Clutter.Event event) bool follow_move (Event event)
{ {
// still determining if we actually want to start a drag action // still determining if we actually want to start a drag action
if (!dragging) { if (!dragging) {
switch (event.get_type ()) { switch (event.get_type ()) {
case Clutter.EventType.MOTION: case EventType.MOTION:
float x, y; float x, y;
event.get_coords (out x, out y); event.get_coords (out x, out y);
@ -184,14 +201,20 @@ namespace Gala
return false; return false;
} }
actor_was_reactive = handle.reactive;
handle.reactive = false; handle.reactive = false;
clicked = false; clicked = false;
dragging = true; dragging = true;
var source_list = sources.@get (drag_id);
if (source_list != null) {
foreach (var actor in source_list) {
actor.reactive = false;
}
}
} }
return true; return true;
case Clutter.EventType.BUTTON_RELEASE: case EventType.BUTTON_RELEASE:
float x, y, ex, ey; float x, y, ex, ey;
event.get_coords (out ex, out ey); event.get_coords (out ex, out ey);
actor.get_transformed_position (out x, out y); actor.get_transformed_position (out x, out y);
@ -211,12 +234,12 @@ namespace Gala
} }
switch (event.get_type ()) { switch (event.get_type ()) {
case Clutter.EventType.KEY_PRESS: case EventType.KEY_PRESS:
if (event.get_key_code () == Clutter.Key.Escape) { if (event.get_key_code () == Key.Escape) {
cancel (); cancel ();
} }
return true; return true;
case Clutter.EventType.MOTION: case EventType.MOTION:
float x, y; float x, y;
event.get_coords (out x, out y); event.get_coords (out x, out y);
handle.x -= last_x - x; handle.x -= last_x - x;
@ -225,7 +248,7 @@ namespace Gala
last_y = y; last_y = y;
var stage = actor.get_stage (); 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; DragDropAction action = null;
// if we're allowed to bubble and we this actor is not a destination, check its parents // 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) { if (actor != null && (action = get_drag_drop_action (actor)) == null && allow_bubbling) {
@ -259,15 +282,15 @@ namespace Gala
emit_crossed (hovered, true); emit_crossed (hovered, true);
return true; return true;
case Clutter.EventType.BUTTON_RELEASE: case EventType.BUTTON_RELEASE:
if (hovered != null) { if (hovered != null) {
finish (); finish ();
} else { } else {
cancel (); cancel ();
} }
return true; return true;
case Clutter.EventType.ENTER: case EventType.ENTER:
case Clutter.EventType.LEAVE: case EventType.LEAVE:
return true; return true;
} }
@ -280,7 +303,7 @@ namespace Gala
* *
* @return the DragDropAction instance on this actor or NULL * @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; DragDropAction? drop_action = null;
@ -302,12 +325,7 @@ namespace Gala
*/ */
public void cancel () public void cancel ()
{ {
if (dragging) { cleanup ();
actor.get_stage ().captured_event.disconnect (follow_move);
}
dragging = false;
actor.reactive = actor_was_reactive;
drag_canceled (); drag_canceled ();
} }
@ -317,12 +335,24 @@ namespace Gala
// make sure they reset the style or whatever they changed when hovered // make sure they reset the style or whatever they changed when hovered
emit_crossed (hovered, false); emit_crossed (hovered, false);
actor.get_stage ().captured_event.disconnect (follow_move); cleanup ();
dragging = false;
actor.reactive = actor_was_reactive;
drag_end (hovered); 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;
}
} }
} }

View File

@ -23,6 +23,12 @@ namespace Gala
{ {
public class InternalUtils public class InternalUtils
{ {
public static bool workspaces_only_on_primary ()
{
return Prefs.get_dynamic_workspaces ()
&& Prefs.get_workspaces_only_on_primary ();
}
/* /*
* Reload shadow settings * Reload shadow settings
*/ */

View File

@ -55,6 +55,7 @@ gala_VALASOURCES = \
Widgets/WorkspaceThumb.vala \ Widgets/WorkspaceThumb.vala \
Widgets/WorkspaceView.vala \ Widgets/WorkspaceView.vala \
Widgets/MultitaskingView/IconGroup.vala \ Widgets/MultitaskingView/IconGroup.vala \
Widgets/MultitaskingView/MonitorClone.vala \
Widgets/MultitaskingView/MultitaskingView.vala \ Widgets/MultitaskingView/MultitaskingView.vala \
Widgets/MultitaskingView/TiledWindowContainer.vala \ Widgets/MultitaskingView/TiledWindowContainer.vala \
Widgets/MultitaskingView/Workspace.vala \ Widgets/MultitaskingView/Workspace.vala \

View File

@ -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);
}
}
}

View File

@ -11,6 +11,8 @@ namespace Gala
public WindowManager wm { get; construct set; } public WindowManager wm { get; construct set; }
public bool opened { get; private set; default = false; } public bool opened { get; private set; default = false; }
List<MonitorClone> window_containers_monitors;
Actor icon_groups; Actor icon_groups;
Actor workspaces; Actor workspaces;
@ -40,6 +42,67 @@ namespace Gala
screen.workspace_switched.connect_after ((from, to, direction) => { screen.workspace_switched.connect_after ((from, to, direction) => {
update_positions (opened); update_positions (opened);
}); });
window_containers_monitors = new List<MonitorClone> ();
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<Workspace> 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 () public override void key_focus_out ()
@ -221,12 +284,13 @@ namespace Gala
var opening = opened; var opening = opened;
unowned List<Meta.WindowActor> windows = Meta.Compositor.get_window_actors (screen); foreach (var container in window_containers_monitors) {
var primary_monitor = screen.get_primary_monitor (); if (opening) {
var monitor = screen.get_monitor_geometry (primary_monitor); container.visible = true;
container.open ();
set_position (monitor.x, monitor.y); } else
set_size (monitor.width, monitor.height); container.close ();
}
if (opening) { if (opening) {
wm.begin_modal (); wm.begin_modal ();
@ -238,7 +302,7 @@ namespace Gala
show (); show ();
grab_key_focus (); 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 // find active workspace clone and raise it, so there are no overlaps while transitioning
@ -265,6 +329,10 @@ namespace Gala
if (!opening) { if (!opening) {
Timeout.add (290, () => { Timeout.add (290, () => {
foreach (var container in window_containers_monitors) {
container.visible = false;
}
hide (); hide ();
wm.background_group.show (); wm.background_group.show ();

View File

@ -136,11 +136,9 @@ namespace Gala
set_child_above_sibling (close_button, clone); set_child_above_sibling (close_button, clone);
set_child_above_sibling (window_icon, clone); set_child_above_sibling (window_icon, clone);
transition_to_original_state (false);
var outer_rect = window.get_outer_rect (); 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)); add_effect_with_name ("shadow", new ShadowEffect (outer_rect.width, outer_rect.height, 40, 5));
#if HAS_MUTTER312 #if HAS_MUTTER312
window.size_changed.connect (update_shadow_size); window.size_changed.connect (update_shadow_size);
@ -156,6 +154,8 @@ namespace Gala
opacity = 0; opacity = 0;
take_slot (slot); take_slot (slot);
opacity = 255; opacity = 255;
request_reposition ();
} }
} }
@ -171,6 +171,9 @@ namespace Gala
shadow_update_timeout = 0; shadow_update_timeout = 0;
// if there was a size change it makes sense to recalculate the positions
request_reposition ();
return false; return false;
}); });
} }
@ -179,10 +182,16 @@ namespace Gala
{ {
var outer_rect = window.get_outer_rect (); 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_mode (AnimationMode.EASE_IN_OUT_CUBIC);
set_easing_duration (animate ? 300 : 0); 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); set_size (outer_rect.width, outer_rect.height);
window_icon.opacity = 0; window_icon.opacity = 0;
@ -334,7 +343,8 @@ namespace Gala
var icon_group = destination as IconGroup; 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; return;
var scale = hovered ? 0.1 : 0.4; var scale = hovered ? 0.1 : 0.4;
@ -367,15 +377,30 @@ namespace Gala
workspace = (destination as IconGroup).workspace; workspace = (destination as IconGroup).workspace;
} else if (destination is FramedBackground) { } else if (destination is FramedBackground) {
workspace = (destination.get_parent () as WorkspaceClone).workspace; 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 ()) { if (workspace != null && workspace != window.get_workspace ()) {
window.change_workspace (workspace); window.change_workspace (workspace);
did_move = true;
}
if (did_move)
unmanaged (); unmanaged ();
} else { else
// if we're dropped at the place where we came from interpret as cancel // if we're dropped at the place where we came from interpret as cancel
drag_canceled (); drag_canceled ();
}
} }
void drag_canceled () void drag_canceled ()
@ -401,10 +426,10 @@ namespace Gala
{ {
public signal void window_selected (Meta.Window window); public signal void window_selected (Meta.Window window);
public int padding_top { get; set; } public int padding_top { get; set; default = 12; }
public int padding_left { get; set; } public int padding_left { get; set; default = 12; }
public int padding_right { get; set; } public int padding_right { get; set; default = 12; }
public int padding_bottom { get; set; } public int padding_bottom { get; set; default = 12; }
HashTable<int,int> _stacking_order; HashTable<int,int> _stacking_order;
public HashTable<int,int> stacking_order { public HashTable<int,int> stacking_order {
@ -502,6 +527,8 @@ namespace Gala
break; break;
} }
} }
reflow ();
} }
void window_selected_cb (TiledWindow tiled) void window_selected_cb (TiledWindow tiled)

View File

@ -29,10 +29,14 @@ namespace Gala
*/ */
public bool remove_workspace_immediately { get; set; default = false; } public bool remove_workspace_immediately { get; set; default = false; }
Gee.LinkedList<Workspace> workspaces_marked_removed;
public WorkspaceManager (Screen screen) public WorkspaceManager (Screen screen)
{ {
Object (screen: screen); Object (screen: screen);
workspaces_marked_removed = new Gee.LinkedList<Workspace> ();
if (Prefs.get_dynamic_workspaces ()) if (Prefs.get_dynamic_workspaces ())
screen.override_workspace_layout (ScreenCorner.TOPLEFT, false, 1, -1); screen.override_workspace_layout (ScreenCorner.TOPLEFT, false, 1, -1);
@ -41,8 +45,29 @@ namespace Gala
Prefs.add_listener (prefs_listener); Prefs.add_listener (prefs_listener);
screen.workspace_added.connect (workspace_added);
screen.workspace_switched.connect_after (workspace_switched); screen.workspace_switched.connect_after (workspace_switched);
screen.workspace_added.connect (workspace_added);
screen.workspace_removed.connect_after (() => {
unowned List<Workspace> 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 // make sure the last workspace has no windows on it
if (Prefs.get_dynamic_workspaces () 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 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) if (Utils.get_n_windows (screen.get_workspace_by_index (screen.get_n_workspaces () - 1)) > 0)
append_workspace (); 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); 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); screen.remove_workspace (workspace, time);
} }
} }