mirror of
https://github.com/elementary/gala.git
synced 2024-09-11 15:15:39 +03:00
Add ability to reorder workspaces (#464)
This commit is contained in:
parent
a790d2d0f8
commit
da85223ff8
@ -19,15 +19,17 @@ using Clutter;
|
|||||||
|
|
||||||
namespace Gala
|
namespace Gala
|
||||||
{
|
{
|
||||||
|
[Flags]
|
||||||
public enum DragDropActionType
|
public enum DragDropActionType
|
||||||
{
|
{
|
||||||
SOURCE = 0,
|
SOURCE,
|
||||||
DESTINATION
|
DESTINATION
|
||||||
}
|
}
|
||||||
|
|
||||||
public class DragDropAction : Clutter.Action
|
public class DragDropAction : Clutter.Action
|
||||||
{
|
{
|
||||||
static Gee.HashMap<string,Gee.LinkedList<Actor>>? sources = null;
|
static Gee.HashMap<string,Gee.LinkedList<Actor>>? sources = null;
|
||||||
|
static Gee.HashMap<string,Gee.LinkedList<Actor>>? destinations = 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
|
||||||
@ -55,9 +57,10 @@ namespace Gala
|
|||||||
/**
|
/**
|
||||||
* The destination has been crossed
|
* The destination has been crossed
|
||||||
*
|
*
|
||||||
|
* @param target the target actor that is crossing the destination
|
||||||
* @param hovered indicates whether the actor is now hovered or not
|
* @param hovered indicates whether the actor is now hovered or not
|
||||||
*/
|
*/
|
||||||
public signal void crossed (bool hovered);
|
public signal void crossed (Actor? target, bool hovered);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emitted on the source when a destination is crossed.
|
* Emitted on the source when a destination is crossed.
|
||||||
@ -97,7 +100,8 @@ namespace Gala
|
|||||||
*/
|
*/
|
||||||
public bool allow_bubbling { get; set; default = true; }
|
public bool allow_bubbling { get; set; default = true; }
|
||||||
|
|
||||||
Actor? hovered = null;
|
public Actor? hovered { private get; set; default = null; }
|
||||||
|
|
||||||
bool clicked = false;
|
bool clicked = false;
|
||||||
float last_x;
|
float last_x;
|
||||||
float last_y;
|
float last_y;
|
||||||
@ -116,6 +120,10 @@ namespace Gala
|
|||||||
|
|
||||||
if (sources == null)
|
if (sources == null)
|
||||||
sources = new Gee.HashMap<string,Gee.LinkedList<Actor>> ();
|
sources = new Gee.HashMap<string,Gee.LinkedList<Actor>> ();
|
||||||
|
|
||||||
|
if (destinations == null)
|
||||||
|
destinations = new Gee.HashMap<string,Gee.LinkedList<Actor>> ();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~DragDropAction ()
|
~DragDropAction ()
|
||||||
@ -139,17 +147,22 @@ namespace Gala
|
|||||||
|
|
||||||
void release_actor (Actor actor)
|
void release_actor (Actor actor)
|
||||||
{
|
{
|
||||||
if (drag_type == DragDropActionType.SOURCE) {
|
if (DragDropActionType.SOURCE in drag_type) {
|
||||||
actor.button_press_event.disconnect (source_clicked);
|
actor.button_press_event.disconnect (source_clicked);
|
||||||
|
|
||||||
var source_list = sources.@get (drag_id);
|
var source_list = sources.@get (drag_id);
|
||||||
source_list.remove (actor);
|
source_list.remove (actor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (DragDropActionType.DESTINATION in drag_type) {
|
||||||
|
var dest_list = destinations[drag_id];
|
||||||
|
dest_list.remove (actor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void connect_actor (Actor actor)
|
void connect_actor (Actor actor)
|
||||||
{
|
{
|
||||||
if (drag_type == DragDropActionType.SOURCE) {
|
if (DragDropActionType.SOURCE in drag_type) {
|
||||||
actor.button_press_event.connect (source_clicked);
|
actor.button_press_event.connect (source_clicked);
|
||||||
|
|
||||||
var source_list = sources.@get (drag_id);
|
var source_list = sources.@get (drag_id);
|
||||||
@ -160,12 +173,22 @@ namespace Gala
|
|||||||
|
|
||||||
source_list.add (actor);
|
source_list.add (actor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (DragDropActionType.DESTINATION in drag_type) {
|
||||||
|
var dest_list = destinations[drag_id];
|
||||||
|
if (dest_list == null) {
|
||||||
|
dest_list = new Gee.LinkedList<Actor> ();
|
||||||
|
destinations[drag_id] = dest_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
dest_list.add (actor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void emit_crossed (Actor destination, bool hovered)
|
void emit_crossed (Actor destination, bool is_hovered)
|
||||||
{
|
{
|
||||||
get_drag_drop_action (destination).crossed (hovered);
|
get_drag_drop_action (destination).crossed (actor, is_hovered);
|
||||||
destination_crossed (destination, hovered);
|
destination_crossed (destination, is_hovered);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool source_clicked (ButtonEvent event)
|
bool source_clicked (ButtonEvent event)
|
||||||
@ -208,7 +231,13 @@ namespace Gala
|
|||||||
|
|
||||||
var source_list = sources.@get (drag_id);
|
var source_list = sources.@get (drag_id);
|
||||||
if (source_list != null) {
|
if (source_list != null) {
|
||||||
|
var dest_list = destinations[drag_id];
|
||||||
foreach (var actor in source_list) {
|
foreach (var actor in source_list) {
|
||||||
|
// Do not unset reactivity on destinations
|
||||||
|
if (actor in dest_list) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
actor.reactive = false;
|
actor.reactive = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -310,7 +339,7 @@ namespace Gala
|
|||||||
foreach (var action in actor.get_actions ()) {
|
foreach (var action in actor.get_actions ()) {
|
||||||
drop_action = action as DragDropAction;
|
drop_action = action as DragDropAction;
|
||||||
if (drop_action == null
|
if (drop_action == null
|
||||||
|| drop_action.drag_type != DragDropActionType.DESTINATION
|
|| !(DragDropActionType.DESTINATION in drop_action.drag_type)
|
||||||
|| drop_action.drag_id != drag_id)
|
|| drop_action.drag_id != drag_id)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -81,8 +81,11 @@ namespace Gala
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DragDropAction drag_action;
|
||||||
|
|
||||||
public Workspace workspace { get; construct; }
|
public Workspace workspace { get; construct; }
|
||||||
|
|
||||||
|
Actor? prev_parent = null;
|
||||||
Actor close_button;
|
Actor close_button;
|
||||||
Actor icon_container;
|
Actor icon_container;
|
||||||
Cogl.Material dummy_material;
|
Cogl.Material dummy_material;
|
||||||
@ -110,15 +113,13 @@ namespace Gala
|
|||||||
|
|
||||||
dummy_material = new Cogl.Material ();
|
dummy_material = new Cogl.Material ();
|
||||||
|
|
||||||
var click = new ClickAction ();
|
drag_action = new DragDropAction (DragDropActionType.SOURCE | DragDropActionType.DESTINATION, "multitaskingview-window");
|
||||||
click.clicked.connect (() => selected ());
|
drag_action.actor_clicked.connect (() => selected ());
|
||||||
// when the actor is pressed, the ClickAction grabs all events, so we won't be
|
drag_action.drag_begin.connect (drag_begin);
|
||||||
// notified when the cursor leaves the actor, which makes our close button stay
|
drag_action.drag_end.connect (drag_end);
|
||||||
// forever. To fix this we hide the button for as long as the actor is pressed.
|
drag_action.drag_canceled.connect (drag_canceled);
|
||||||
click.notify["pressed"].connect (() => {
|
drag_action.notify["dragging"].connect (redraw);
|
||||||
toggle_close_button (!click.pressed && get_has_pointer ());
|
add_action (drag_action);
|
||||||
});
|
|
||||||
add_action (click);
|
|
||||||
|
|
||||||
icon_container = new Actor ();
|
icon_container = new Actor ();
|
||||||
icon_container.width = width;
|
icon_container.width = width;
|
||||||
@ -213,7 +214,7 @@ namespace Gala
|
|||||||
*/
|
*/
|
||||||
public override void paint ()
|
public override void paint ()
|
||||||
{
|
{
|
||||||
if (backdrop_opacity < 1) {
|
if (backdrop_opacity < 1 || drag_action.dragging) {
|
||||||
base.paint ();
|
base.paint ();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -309,6 +310,14 @@ namespace Gala
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a hovered actor for the drag action.
|
||||||
|
*/
|
||||||
|
public void set_hovered_actor (Actor actor)
|
||||||
|
{
|
||||||
|
drag_action.hovered = actor;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Trigger a redraw
|
* Trigger a redraw
|
||||||
*/
|
*/
|
||||||
@ -365,7 +374,13 @@ namespace Gala
|
|||||||
5 * scale
|
5 * scale
|
||||||
);
|
);
|
||||||
|
|
||||||
cr.set_source_rgba (0, 0, 0, 0.1);
|
if (drag_action.dragging) {
|
||||||
|
const double BG_COLOR = 53.0 / 255.0;
|
||||||
|
cr.set_source_rgba (BG_COLOR, BG_COLOR, BG_COLOR, 0.7);
|
||||||
|
} else {
|
||||||
|
cr.set_source_rgba (0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
cr.fill_preserve ();
|
cr.fill_preserve ();
|
||||||
|
|
||||||
cr.set_line_width (1 * scale);
|
cr.set_line_width (1 * scale);
|
||||||
@ -489,5 +504,69 @@ namespace Gala
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Actor drag_begin (float click_x, float click_y)
|
||||||
|
{
|
||||||
|
unowned Screen screen = workspace.get_screen ();
|
||||||
|
if (icon_container.get_n_children () < 1 &&
|
||||||
|
Prefs.get_dynamic_workspaces () &&
|
||||||
|
workspace.index () == screen.get_n_workspaces () - 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
float abs_x, abs_y;
|
||||||
|
float prev_parent_x, prev_parent_y;
|
||||||
|
|
||||||
|
prev_parent = get_parent ();
|
||||||
|
prev_parent.get_transformed_position (out prev_parent_x, out prev_parent_y);
|
||||||
|
|
||||||
|
var stage = get_stage ();
|
||||||
|
var container = prev_parent as IconGroupContainer;
|
||||||
|
if (container != null) {
|
||||||
|
container.remove_group_in_place (this);
|
||||||
|
container.reset_thumbs (0);
|
||||||
|
} else {
|
||||||
|
prev_parent.remove_child (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
stage.add_child (this);
|
||||||
|
|
||||||
|
get_transformed_position (out abs_x, out abs_y);
|
||||||
|
set_position (abs_x + prev_parent_x, abs_y + prev_parent_y);
|
||||||
|
|
||||||
|
close_button.opacity = 0;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void drag_end (Actor destination)
|
||||||
|
{
|
||||||
|
if (destination is WorkspaceInsertThumb) {
|
||||||
|
get_parent ().remove_child (this);
|
||||||
|
|
||||||
|
unowned WorkspaceInsertThumb inserter = (WorkspaceInsertThumb) destination;
|
||||||
|
workspace.get_screen ().reorder_workspace (workspace, inserter.workspace_index);
|
||||||
|
|
||||||
|
restore_group ();
|
||||||
|
} else {
|
||||||
|
drag_canceled ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void drag_canceled ()
|
||||||
|
{
|
||||||
|
get_parent ().remove_child (this);
|
||||||
|
restore_group ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void restore_group ()
|
||||||
|
{
|
||||||
|
var container = prev_parent as IconGroupContainer;
|
||||||
|
if (container != null) {
|
||||||
|
container.add_group (this);
|
||||||
|
container.request_reposition (false);
|
||||||
|
container.reset_thumbs (WorkspaceInsertThumb.EXPAND_DELAY);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ namespace Gala
|
|||||||
public const int SPACING = 48;
|
public const int SPACING = 48;
|
||||||
public const int GROUP_WIDTH = 64;
|
public const int GROUP_WIDTH = 64;
|
||||||
|
|
||||||
public signal void request_reposition ();
|
public signal void request_reposition (bool animate);
|
||||||
|
|
||||||
public Screen screen { get; construct; }
|
public Screen screen { get; construct; }
|
||||||
|
|
||||||
@ -65,9 +65,50 @@ namespace Gala
|
|||||||
update_inserter_indices ();
|
update_inserter_indices ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes an icon group "in place".
|
||||||
|
* When initially dragging an icon group we remove
|
||||||
|
* it and it's previous WorkspaceInsertThumb. This would make
|
||||||
|
* the container immediately reallocate and fill the empty space
|
||||||
|
* with right-most IconGroups.
|
||||||
|
*
|
||||||
|
* We don't want that until the IconGroup
|
||||||
|
* leaves the expanded WorkspaceInsertThumb.
|
||||||
|
*/
|
||||||
|
public void remove_group_in_place (IconGroup group)
|
||||||
|
{
|
||||||
|
var deleted_thumb = (WorkspaceInsertThumb) group.get_previous_sibling ();
|
||||||
|
var deleted_placeholder_thumb = (WorkspaceInsertThumb) group.get_next_sibling ();
|
||||||
|
|
||||||
|
remove_group (group);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We will account for that empty space
|
||||||
|
* by manually expanding the next WorkspaceInsertThumb with the
|
||||||
|
* width we deleted. Because the IconGroup is still hovering over
|
||||||
|
* the expanded thumb, we will also update the drag & drop action
|
||||||
|
* of IconGroup on that.
|
||||||
|
*/
|
||||||
|
float deleted_width = deleted_thumb.get_width () + group.get_width ();
|
||||||
|
deleted_placeholder_thumb.expanded = true;
|
||||||
|
deleted_placeholder_thumb.width += deleted_width;
|
||||||
|
group.set_hovered_actor (deleted_placeholder_thumb);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset_thumbs (int delay)
|
||||||
|
{
|
||||||
|
foreach (var child in get_children ()) {
|
||||||
|
unowned WorkspaceInsertThumb thumb = child as WorkspaceInsertThumb;
|
||||||
|
if (thumb != null) {
|
||||||
|
thumb.delay = delay;
|
||||||
|
thumb.destroy_all_children ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void expanded_changed (ParamSpec param)
|
void expanded_changed (ParamSpec param)
|
||||||
{
|
{
|
||||||
request_reposition ();
|
request_reposition (true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -64,7 +64,7 @@ namespace Gala
|
|||||||
workspaces.set_easing_mode (AnimationMode.EASE_OUT_QUAD);
|
workspaces.set_easing_mode (AnimationMode.EASE_OUT_QUAD);
|
||||||
|
|
||||||
icon_groups = new IconGroupContainer (screen);
|
icon_groups = new IconGroupContainer (screen);
|
||||||
icon_groups.request_reposition.connect (() => reposition_icon_groups (true));
|
icon_groups.request_reposition.connect ((animate) => reposition_icon_groups (animate));
|
||||||
|
|
||||||
dock_clones = new Actor ();
|
dock_clones = new Actor ();
|
||||||
|
|
||||||
@ -77,6 +77,7 @@ namespace Gala
|
|||||||
|
|
||||||
screen.workspace_added.connect (add_workspace);
|
screen.workspace_added.connect (add_workspace);
|
||||||
screen.workspace_removed.connect (remove_workspace);
|
screen.workspace_removed.connect (remove_workspace);
|
||||||
|
screen.workspaces_reordered.connect (() => update_positions (false));
|
||||||
screen.workspace_switched.connect_after ((from, to, direction) => {
|
screen.workspace_switched.connect_after ((from, to, direction) => {
|
||||||
update_positions (opened);
|
update_positions (opened);
|
||||||
});
|
});
|
||||||
@ -311,7 +312,9 @@ namespace Gala
|
|||||||
workspace.window_selected.disconnect (window_selected);
|
workspace.window_selected.disconnect (window_selected);
|
||||||
workspace.selected.disconnect (activate_workspace);
|
workspace.selected.disconnect (activate_workspace);
|
||||||
|
|
||||||
icon_groups.remove_group (workspace.icon_group);
|
if (icon_groups.contains (workspace.icon_group)) {
|
||||||
|
icon_groups.remove_group (workspace.icon_group);
|
||||||
|
}
|
||||||
|
|
||||||
workspace.destroy ();
|
workspace.destroy ();
|
||||||
|
|
||||||
|
@ -590,7 +590,7 @@ namespace Gala
|
|||||||
|
|
||||||
var scale = hovered ? 0.4 : 1.0;
|
var scale = hovered ? 0.4 : 1.0;
|
||||||
var opacity = hovered ? 0 : 255;
|
var opacity = hovered ? 0 : 255;
|
||||||
var duration = hovered && insert_thumb != null ? WorkspaceInsertThumb.EXPAND_DELAY : 100;
|
var duration = hovered && insert_thumb != null ? insert_thumb.delay : 100;
|
||||||
|
|
||||||
window_icon.save_easing_state ();
|
window_icon.save_easing_state ();
|
||||||
|
|
||||||
|
@ -165,7 +165,7 @@ namespace Gala
|
|||||||
|
|
||||||
var background_drop_action = new DragDropAction (DragDropActionType.DESTINATION, "multitaskingview-window");
|
var background_drop_action = new DragDropAction (DragDropActionType.DESTINATION, "multitaskingview-window");
|
||||||
background.add_action (background_drop_action);
|
background.add_action (background_drop_action);
|
||||||
background_drop_action.crossed.connect ((hovered) => {
|
background_drop_action.crossed.connect ((target, hovered) => {
|
||||||
if (!hovered && hover_activate_timeout != 0) {
|
if (!hovered && hover_activate_timeout != 0) {
|
||||||
Source.remove (hover_activate_timeout);
|
Source.remove (hover_activate_timeout);
|
||||||
hover_activate_timeout = 0;
|
hover_activate_timeout = 0;
|
||||||
|
@ -25,7 +25,8 @@ namespace Gala
|
|||||||
public const int EXPAND_DELAY = 300;
|
public const int EXPAND_DELAY = 300;
|
||||||
|
|
||||||
public int workspace_index { get; construct set; }
|
public int workspace_index { get; construct set; }
|
||||||
public bool expanded { get; private set; default = false; }
|
public bool expanded { get; set; default = false; }
|
||||||
|
public int delay { get; set; default = EXPAND_DELAY; }
|
||||||
|
|
||||||
uint expand_timeout = 0;
|
uint expand_timeout = 0;
|
||||||
|
|
||||||
@ -43,8 +44,8 @@ namespace Gala
|
|||||||
x_align = Clutter.ActorAlign.CENTER;
|
x_align = Clutter.ActorAlign.CENTER;
|
||||||
|
|
||||||
var drop = new DragDropAction (DragDropActionType.DESTINATION, "multitaskingview-window");
|
var drop = new DragDropAction (DragDropActionType.DESTINATION, "multitaskingview-window");
|
||||||
drop.crossed.connect ((hovered) => {
|
drop.crossed.connect ((target, hovered) => {
|
||||||
if (!Prefs.get_dynamic_workspaces ())
|
if (!Prefs.get_dynamic_workspaces () && (target != null && target is WindowClone))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!hovered) {
|
if (!hovered) {
|
||||||
@ -55,7 +56,7 @@ namespace Gala
|
|||||||
|
|
||||||
transform (false);
|
transform (false);
|
||||||
} else
|
} else
|
||||||
expand_timeout = Timeout.add (EXPAND_DELAY, expand);
|
expand_timeout = Timeout.add (delay, expand);
|
||||||
});
|
});
|
||||||
|
|
||||||
add_action (drop);
|
add_action (drop);
|
||||||
|
@ -573,6 +573,7 @@ namespace Meta {
|
|||||||
public unowned Meta.Workspace? append_new_workspace (bool activate, uint32 timestamp);
|
public unowned Meta.Workspace? append_new_workspace (bool activate, uint32 timestamp);
|
||||||
public void focus_default_window (uint32 timestamp);
|
public void focus_default_window (uint32 timestamp);
|
||||||
public unowned Meta.Workspace get_active_workspace ();
|
public unowned Meta.Workspace get_active_workspace ();
|
||||||
|
public void reorder_workspace (Meta.Workspace workspace, int new_index);
|
||||||
public int get_active_workspace_index ();
|
public int get_active_workspace_index ();
|
||||||
public int get_current_monitor ();
|
public int get_current_monitor ();
|
||||||
#if !HAS_MUTTER324
|
#if !HAS_MUTTER324
|
||||||
@ -606,6 +607,7 @@ namespace Meta {
|
|||||||
public signal void workspace_added (int object);
|
public signal void workspace_added (int object);
|
||||||
public signal void workspace_removed (int object);
|
public signal void workspace_removed (int object);
|
||||||
public signal void workspace_switched (int object, int p0, Meta.MotionDirection p1);
|
public signal void workspace_switched (int object, int p0, Meta.MotionDirection p1);
|
||||||
|
public signal void workspaces_reordered ();
|
||||||
}
|
}
|
||||||
#if HAS_MUTTER326
|
#if HAS_MUTTER326
|
||||||
[CCode (cheader_filename = "meta/meta-settings.h", has_type_id = false)]
|
[CCode (cheader_filename = "meta/meta-settings.h", has_type_id = false)]
|
||||||
|
Loading…
Reference in New Issue
Block a user