mirror of
https://github.com/elementary/gala.git
synced 2024-08-17 02:50:38 +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
|
||||
{
|
||||
[Flags]
|
||||
public enum DragDropActionType
|
||||
{
|
||||
SOURCE = 0,
|
||||
SOURCE,
|
||||
DESTINATION
|
||||
}
|
||||
|
||||
public class DragDropAction : Clutter.Action
|
||||
{
|
||||
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
|
||||
@ -55,9 +57,10 @@ namespace Gala
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public signal void crossed (bool hovered);
|
||||
public signal void crossed (Actor? target, bool hovered);
|
||||
|
||||
/**
|
||||
* Emitted on the source when a destination is crossed.
|
||||
@ -97,7 +100,8 @@ namespace Gala
|
||||
*/
|
||||
public bool allow_bubbling { get; set; default = true; }
|
||||
|
||||
Actor? hovered = null;
|
||||
public Actor? hovered { private get; set; default = null; }
|
||||
|
||||
bool clicked = false;
|
||||
float last_x;
|
||||
float last_y;
|
||||
@ -116,6 +120,10 @@ namespace Gala
|
||||
|
||||
if (sources == null)
|
||||
sources = new Gee.HashMap<string,Gee.LinkedList<Actor>> ();
|
||||
|
||||
if (destinations == null)
|
||||
destinations = new Gee.HashMap<string,Gee.LinkedList<Actor>> ();
|
||||
|
||||
}
|
||||
|
||||
~DragDropAction ()
|
||||
@ -139,17 +147,22 @@ namespace Gala
|
||||
|
||||
void release_actor (Actor actor)
|
||||
{
|
||||
if (drag_type == DragDropActionType.SOURCE) {
|
||||
if (DragDropActionType.SOURCE in drag_type) {
|
||||
actor.button_press_event.disconnect (source_clicked);
|
||||
|
||||
var source_list = sources.@get (drag_id);
|
||||
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)
|
||||
{
|
||||
if (drag_type == DragDropActionType.SOURCE) {
|
||||
if (DragDropActionType.SOURCE in drag_type) {
|
||||
actor.button_press_event.connect (source_clicked);
|
||||
|
||||
var source_list = sources.@get (drag_id);
|
||||
@ -160,12 +173,22 @@ namespace Gala
|
||||
|
||||
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);
|
||||
destination_crossed (destination, hovered);
|
||||
get_drag_drop_action (destination).crossed (actor, is_hovered);
|
||||
destination_crossed (destination, is_hovered);
|
||||
}
|
||||
|
||||
bool source_clicked (ButtonEvent event)
|
||||
@ -208,7 +231,13 @@ namespace Gala
|
||||
|
||||
var source_list = sources.@get (drag_id);
|
||||
if (source_list != null) {
|
||||
var dest_list = destinations[drag_id];
|
||||
foreach (var actor in source_list) {
|
||||
// Do not unset reactivity on destinations
|
||||
if (actor in dest_list) {
|
||||
continue;
|
||||
}
|
||||
|
||||
actor.reactive = false;
|
||||
}
|
||||
}
|
||||
@ -310,7 +339,7 @@ namespace Gala
|
||||
foreach (var action in actor.get_actions ()) {
|
||||
drop_action = action as DragDropAction;
|
||||
if (drop_action == null
|
||||
|| drop_action.drag_type != DragDropActionType.DESTINATION
|
||||
|| !(DragDropActionType.DESTINATION in drop_action.drag_type)
|
||||
|| drop_action.drag_id != drag_id)
|
||||
continue;
|
||||
|
||||
|
@ -81,8 +81,11 @@ namespace Gala
|
||||
}
|
||||
}
|
||||
|
||||
DragDropAction drag_action;
|
||||
|
||||
public Workspace workspace { get; construct; }
|
||||
|
||||
Actor? prev_parent = null;
|
||||
Actor close_button;
|
||||
Actor icon_container;
|
||||
Cogl.Material dummy_material;
|
||||
@ -110,15 +113,13 @@ namespace Gala
|
||||
|
||||
dummy_material = new Cogl.Material ();
|
||||
|
||||
var click = new ClickAction ();
|
||||
click.clicked.connect (() => selected ());
|
||||
// when the actor is pressed, the ClickAction grabs all events, so we won't be
|
||||
// notified when the cursor leaves the actor, which makes our close button stay
|
||||
// forever. To fix this we hide the button for as long as the actor is pressed.
|
||||
click.notify["pressed"].connect (() => {
|
||||
toggle_close_button (!click.pressed && get_has_pointer ());
|
||||
});
|
||||
add_action (click);
|
||||
drag_action = new DragDropAction (DragDropActionType.SOURCE | DragDropActionType.DESTINATION, "multitaskingview-window");
|
||||
drag_action.actor_clicked.connect (() => selected ());
|
||||
drag_action.drag_begin.connect (drag_begin);
|
||||
drag_action.drag_end.connect (drag_end);
|
||||
drag_action.drag_canceled.connect (drag_canceled);
|
||||
drag_action.notify["dragging"].connect (redraw);
|
||||
add_action (drag_action);
|
||||
|
||||
icon_container = new Actor ();
|
||||
icon_container.width = width;
|
||||
@ -213,7 +214,7 @@ namespace Gala
|
||||
*/
|
||||
public override void paint ()
|
||||
{
|
||||
if (backdrop_opacity < 1) {
|
||||
if (backdrop_opacity < 1 || drag_action.dragging) {
|
||||
base.paint ();
|
||||
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
|
||||
*/
|
||||
@ -365,7 +374,13 @@ namespace Gala
|
||||
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.set_line_width (1 * scale);
|
||||
@ -489,5 +504,69 @@ namespace Gala
|
||||
|
||||
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 GROUP_WIDTH = 64;
|
||||
|
||||
public signal void request_reposition ();
|
||||
public signal void request_reposition (bool animate);
|
||||
|
||||
public Screen screen { get; construct; }
|
||||
|
||||
@ -65,9 +65,50 @@ namespace Gala
|
||||
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)
|
||||
{
|
||||
request_reposition ();
|
||||
request_reposition (true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -64,7 +64,7 @@ namespace Gala
|
||||
workspaces.set_easing_mode (AnimationMode.EASE_OUT_QUAD);
|
||||
|
||||
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 ();
|
||||
|
||||
@ -77,6 +77,7 @@ namespace Gala
|
||||
|
||||
screen.workspace_added.connect (add_workspace);
|
||||
screen.workspace_removed.connect (remove_workspace);
|
||||
screen.workspaces_reordered.connect (() => update_positions (false));
|
||||
screen.workspace_switched.connect_after ((from, to, direction) => {
|
||||
update_positions (opened);
|
||||
});
|
||||
@ -311,7 +312,9 @@ namespace Gala
|
||||
workspace.window_selected.disconnect (window_selected);
|
||||
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 ();
|
||||
|
||||
|
@ -590,7 +590,7 @@ namespace Gala
|
||||
|
||||
var scale = hovered ? 0.4 : 1.0;
|
||||
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 ();
|
||||
|
||||
|
@ -165,7 +165,7 @@ namespace Gala
|
||||
|
||||
var background_drop_action = new DragDropAction (DragDropActionType.DESTINATION, "multitaskingview-window");
|
||||
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) {
|
||||
Source.remove (hover_activate_timeout);
|
||||
hover_activate_timeout = 0;
|
||||
|
@ -25,7 +25,8 @@ namespace Gala
|
||||
public const int EXPAND_DELAY = 300;
|
||||
|
||||
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;
|
||||
|
||||
@ -43,8 +44,8 @@ namespace Gala
|
||||
x_align = Clutter.ActorAlign.CENTER;
|
||||
|
||||
var drop = new DragDropAction (DragDropActionType.DESTINATION, "multitaskingview-window");
|
||||
drop.crossed.connect ((hovered) => {
|
||||
if (!Prefs.get_dynamic_workspaces ())
|
||||
drop.crossed.connect ((target, hovered) => {
|
||||
if (!Prefs.get_dynamic_workspaces () && (target != null && target is WindowClone))
|
||||
return;
|
||||
|
||||
if (!hovered) {
|
||||
@ -55,7 +56,7 @@ namespace Gala
|
||||
|
||||
transform (false);
|
||||
} else
|
||||
expand_timeout = Timeout.add (EXPAND_DELAY, expand);
|
||||
expand_timeout = Timeout.add (delay, expand);
|
||||
});
|
||||
|
||||
add_action (drop);
|
||||
|
@ -573,6 +573,7 @@ namespace Meta {
|
||||
public unowned Meta.Workspace? append_new_workspace (bool activate, uint32 timestamp);
|
||||
public void focus_default_window (uint32 timestamp);
|
||||
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_current_monitor ();
|
||||
#if !HAS_MUTTER324
|
||||
@ -606,6 +607,7 @@ namespace Meta {
|
||||
public signal void workspace_added (int object);
|
||||
public signal void workspace_removed (int object);
|
||||
public signal void workspace_switched (int object, int p0, Meta.MotionDirection p1);
|
||||
public signal void workspaces_reordered ();
|
||||
}
|
||||
#if HAS_MUTTER326
|
||||
[CCode (cheader_filename = "meta/meta-settings.h", has_type_id = false)]
|
||||
|
Loading…
Reference in New Issue
Block a user