Skip to content

Commit da85223

Browse files
donadigodanirabbit
authored andcommitted
Add ability to reorder workspaces (#464)
1 parent a790d2d commit da85223

File tree

8 files changed

+185
-30
lines changed

8 files changed

+185
-30
lines changed

src/DragDropAction.vala

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,17 @@ using Clutter;
1919

2020
namespace Gala
2121
{
22+
[Flags]
2223
public enum DragDropActionType
2324
{
24-
SOURCE = 0,
25+
SOURCE,
2526
DESTINATION
2627
}
2728

2829
public class DragDropAction : Clutter.Action
2930
{
3031
static Gee.HashMap<string,Gee.LinkedList<Actor>>? sources = null;
32+
static Gee.HashMap<string,Gee.LinkedList<Actor>>? destinations = null;
3133

3234
/**
3335
* A drag has been started. You have to connect to this signal and
@@ -55,9 +57,10 @@ namespace Gala
5557
/**
5658
* The destination has been crossed
5759
*
60+
* @param target the target actor that is crossing the destination
5861
* @param hovered indicates whether the actor is now hovered or not
5962
*/
60-
public signal void crossed (bool hovered);
63+
public signal void crossed (Actor? target, bool hovered);
6164

6265
/**
6366
* Emitted on the source when a destination is crossed.
@@ -97,7 +100,8 @@ namespace Gala
97100
*/
98101
public bool allow_bubbling { get; set; default = true; }
99102

100-
Actor? hovered = null;
103+
public Actor? hovered { private get; set; default = null; }
104+
101105
bool clicked = false;
102106
float last_x;
103107
float last_y;
@@ -116,6 +120,10 @@ namespace Gala
116120

117121
if (sources == null)
118122
sources = new Gee.HashMap<string,Gee.LinkedList<Actor>> ();
123+
124+
if (destinations == null)
125+
destinations = new Gee.HashMap<string,Gee.LinkedList<Actor>> ();
126+
119127
}
120128

121129
~DragDropAction ()
@@ -139,17 +147,22 @@ namespace Gala
139147

140148
void release_actor (Actor actor)
141149
{
142-
if (drag_type == DragDropActionType.SOURCE) {
150+
if (DragDropActionType.SOURCE in drag_type) {
143151
actor.button_press_event.disconnect (source_clicked);
144152

145153
var source_list = sources.@get (drag_id);
146154
source_list.remove (actor);
155+
}
156+
157+
if (DragDropActionType.DESTINATION in drag_type) {
158+
var dest_list = destinations[drag_id];
159+
dest_list.remove (actor);
147160
}
148161
}
149162

150163
void connect_actor (Actor actor)
151164
{
152-
if (drag_type == DragDropActionType.SOURCE) {
165+
if (DragDropActionType.SOURCE in drag_type) {
153166
actor.button_press_event.connect (source_clicked);
154167

155168
var source_list = sources.@get (drag_id);
@@ -160,12 +173,22 @@ namespace Gala
160173

161174
source_list.add (actor);
162175
}
176+
177+
if (DragDropActionType.DESTINATION in drag_type) {
178+
var dest_list = destinations[drag_id];
179+
if (dest_list == null) {
180+
dest_list = new Gee.LinkedList<Actor> ();
181+
destinations[drag_id] = dest_list;
182+
}
183+
184+
dest_list.add (actor);
185+
}
163186
}
164187

165-
void emit_crossed (Actor destination, bool hovered)
188+
void emit_crossed (Actor destination, bool is_hovered)
166189
{
167-
get_drag_drop_action (destination).crossed (hovered);
168-
destination_crossed (destination, hovered);
190+
get_drag_drop_action (destination).crossed (actor, is_hovered);
191+
destination_crossed (destination, is_hovered);
169192
}
170193

171194
bool source_clicked (ButtonEvent event)
@@ -208,7 +231,13 @@ namespace Gala
208231

209232
var source_list = sources.@get (drag_id);
210233
if (source_list != null) {
234+
var dest_list = destinations[drag_id];
211235
foreach (var actor in source_list) {
236+
// Do not unset reactivity on destinations
237+
if (actor in dest_list) {
238+
continue;
239+
}
240+
212241
actor.reactive = false;
213242
}
214243
}
@@ -310,7 +339,7 @@ namespace Gala
310339
foreach (var action in actor.get_actions ()) {
311340
drop_action = action as DragDropAction;
312341
if (drop_action == null
313-
|| drop_action.drag_type != DragDropActionType.DESTINATION
342+
|| !(DragDropActionType.DESTINATION in drop_action.drag_type)
314343
|| drop_action.drag_id != drag_id)
315344
continue;
316345

src/Widgets/IconGroup.vala

Lines changed: 90 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,11 @@ namespace Gala
8181
}
8282
}
8383

84+
DragDropAction drag_action;
85+
8486
public Workspace workspace { get; construct; }
8587

88+
Actor? prev_parent = null;
8689
Actor close_button;
8790
Actor icon_container;
8891
Cogl.Material dummy_material;
@@ -110,15 +113,13 @@ namespace Gala
110113

111114
dummy_material = new Cogl.Material ();
112115

113-
var click = new ClickAction ();
114-
click.clicked.connect (() => selected ());
115-
// when the actor is pressed, the ClickAction grabs all events, so we won't be
116-
// notified when the cursor leaves the actor, which makes our close button stay
117-
// forever. To fix this we hide the button for as long as the actor is pressed.
118-
click.notify["pressed"].connect (() => {
119-
toggle_close_button (!click.pressed && get_has_pointer ());
120-
});
121-
add_action (click);
116+
drag_action = new DragDropAction (DragDropActionType.SOURCE | DragDropActionType.DESTINATION, "multitaskingview-window");
117+
drag_action.actor_clicked.connect (() => selected ());
118+
drag_action.drag_begin.connect (drag_begin);
119+
drag_action.drag_end.connect (drag_end);
120+
drag_action.drag_canceled.connect (drag_canceled);
121+
drag_action.notify["dragging"].connect (redraw);
122+
add_action (drag_action);
122123

123124
icon_container = new Actor ();
124125
icon_container.width = width;
@@ -213,7 +214,7 @@ namespace Gala
213214
*/
214215
public override void paint ()
215216
{
216-
if (backdrop_opacity < 1) {
217+
if (backdrop_opacity < 1 || drag_action.dragging) {
217218
base.paint ();
218219
return;
219220
}
@@ -309,6 +310,14 @@ namespace Gala
309310
}
310311
}
311312

313+
/**
314+
* Sets a hovered actor for the drag action.
315+
*/
316+
public void set_hovered_actor (Actor actor)
317+
{
318+
drag_action.hovered = actor;
319+
}
320+
312321
/**
313322
* Trigger a redraw
314323
*/
@@ -365,7 +374,13 @@ namespace Gala
365374
5 * scale
366375
);
367376

368-
cr.set_source_rgba (0, 0, 0, 0.1);
377+
if (drag_action.dragging) {
378+
const double BG_COLOR = 53.0 / 255.0;
379+
cr.set_source_rgba (BG_COLOR, BG_COLOR, BG_COLOR, 0.7);
380+
} else {
381+
cr.set_source_rgba (0, 0, 0, 0.1);
382+
}
383+
369384
cr.fill_preserve ();
370385

371386
cr.set_line_width (1 * scale);
@@ -489,5 +504,69 @@ namespace Gala
489504

490505
return false;
491506
}
507+
508+
Actor drag_begin (float click_x, float click_y)
509+
{
510+
unowned Screen screen = workspace.get_screen ();
511+
if (icon_container.get_n_children () < 1 &&
512+
Prefs.get_dynamic_workspaces () &&
513+
workspace.index () == screen.get_n_workspaces () - 1) {
514+
return null;
515+
}
516+
517+
float abs_x, abs_y;
518+
float prev_parent_x, prev_parent_y;
519+
520+
prev_parent = get_parent ();
521+
prev_parent.get_transformed_position (out prev_parent_x, out prev_parent_y);
522+
523+
var stage = get_stage ();
524+
var container = prev_parent as IconGroupContainer;
525+
if (container != null) {
526+
container.remove_group_in_place (this);
527+
container.reset_thumbs (0);
528+
} else {
529+
prev_parent.remove_child (this);
530+
}
531+
532+
stage.add_child (this);
533+
534+
get_transformed_position (out abs_x, out abs_y);
535+
set_position (abs_x + prev_parent_x, abs_y + prev_parent_y);
536+
537+
close_button.opacity = 0;
538+
539+
return this;
540+
}
541+
542+
void drag_end (Actor destination)
543+
{
544+
if (destination is WorkspaceInsertThumb) {
545+
get_parent ().remove_child (this);
546+
547+
unowned WorkspaceInsertThumb inserter = (WorkspaceInsertThumb) destination;
548+
workspace.get_screen ().reorder_workspace (workspace, inserter.workspace_index);
549+
550+
restore_group ();
551+
} else {
552+
drag_canceled ();
553+
}
554+
}
555+
556+
void drag_canceled ()
557+
{
558+
get_parent ().remove_child (this);
559+
restore_group ();
560+
}
561+
562+
void restore_group ()
563+
{
564+
var container = prev_parent as IconGroupContainer;
565+
if (container != null) {
566+
container.add_group (this);
567+
container.request_reposition (false);
568+
container.reset_thumbs (WorkspaceInsertThumb.EXPAND_DELAY);
569+
}
570+
}
492571
}
493572
}

src/Widgets/IconGroupContainer.vala

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ namespace Gala
3030
public const int SPACING = 48;
3131
public const int GROUP_WIDTH = 64;
3232

33-
public signal void request_reposition ();
33+
public signal void request_reposition (bool animate);
3434

3535
public Screen screen { get; construct; }
3636

@@ -65,9 +65,50 @@ namespace Gala
6565
update_inserter_indices ();
6666
}
6767

68+
/**
69+
* Removes an icon group "in place".
70+
* When initially dragging an icon group we remove
71+
* it and it's previous WorkspaceInsertThumb. This would make
72+
* the container immediately reallocate and fill the empty space
73+
* with right-most IconGroups.
74+
*
75+
* We don't want that until the IconGroup
76+
* leaves the expanded WorkspaceInsertThumb.
77+
*/
78+
public void remove_group_in_place (IconGroup group)
79+
{
80+
var deleted_thumb = (WorkspaceInsertThumb) group.get_previous_sibling ();
81+
var deleted_placeholder_thumb = (WorkspaceInsertThumb) group.get_next_sibling ();
82+
83+
remove_group (group);
84+
85+
/**
86+
* We will account for that empty space
87+
* by manually expanding the next WorkspaceInsertThumb with the
88+
* width we deleted. Because the IconGroup is still hovering over
89+
* the expanded thumb, we will also update the drag & drop action
90+
* of IconGroup on that.
91+
*/
92+
float deleted_width = deleted_thumb.get_width () + group.get_width ();
93+
deleted_placeholder_thumb.expanded = true;
94+
deleted_placeholder_thumb.width += deleted_width;
95+
group.set_hovered_actor (deleted_placeholder_thumb);
96+
}
97+
98+
public void reset_thumbs (int delay)
99+
{
100+
foreach (var child in get_children ()) {
101+
unowned WorkspaceInsertThumb thumb = child as WorkspaceInsertThumb;
102+
if (thumb != null) {
103+
thumb.delay = delay;
104+
thumb.destroy_all_children ();
105+
}
106+
}
107+
}
108+
68109
void expanded_changed (ParamSpec param)
69110
{
70-
request_reposition ();
111+
request_reposition (true);
71112
}
72113

73114
/**

src/Widgets/MultitaskingView.vala

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ namespace Gala
6464
workspaces.set_easing_mode (AnimationMode.EASE_OUT_QUAD);
6565

6666
icon_groups = new IconGroupContainer (screen);
67-
icon_groups.request_reposition.connect (() => reposition_icon_groups (true));
67+
icon_groups.request_reposition.connect ((animate) => reposition_icon_groups (animate));
6868

6969
dock_clones = new Actor ();
7070

@@ -77,6 +77,7 @@ namespace Gala
7777

7878
screen.workspace_added.connect (add_workspace);
7979
screen.workspace_removed.connect (remove_workspace);
80+
screen.workspaces_reordered.connect (() => update_positions (false));
8081
screen.workspace_switched.connect_after ((from, to, direction) => {
8182
update_positions (opened);
8283
});
@@ -311,7 +312,9 @@ namespace Gala
311312
workspace.window_selected.disconnect (window_selected);
312313
workspace.selected.disconnect (activate_workspace);
313314

314-
icon_groups.remove_group (workspace.icon_group);
315+
if (icon_groups.contains (workspace.icon_group)) {
316+
icon_groups.remove_group (workspace.icon_group);
317+
}
315318

316319
workspace.destroy ();
317320

src/Widgets/WindowClone.vala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -590,7 +590,7 @@ namespace Gala
590590

591591
var scale = hovered ? 0.4 : 1.0;
592592
var opacity = hovered ? 0 : 255;
593-
var duration = hovered && insert_thumb != null ? WorkspaceInsertThumb.EXPAND_DELAY : 100;
593+
var duration = hovered && insert_thumb != null ? insert_thumb.delay : 100;
594594

595595
window_icon.save_easing_state ();
596596

src/Widgets/WorkspaceClone.vala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ namespace Gala
165165

166166
var background_drop_action = new DragDropAction (DragDropActionType.DESTINATION, "multitaskingview-window");
167167
background.add_action (background_drop_action);
168-
background_drop_action.crossed.connect ((hovered) => {
168+
background_drop_action.crossed.connect ((target, hovered) => {
169169
if (!hovered && hover_activate_timeout != 0) {
170170
Source.remove (hover_activate_timeout);
171171
hover_activate_timeout = 0;

0 commit comments

Comments
 (0)