Various small fixes, added workspace manager, new animations for IconGroup

This commit is contained in:
Tom Beckmann 2014-06-14 19:19:24 +02:00
parent 0af86fdbf9
commit ff48c641fd
10 changed files with 550 additions and 81 deletions

View File

@ -1,5 +1,5 @@
stylesdir = $(pkgdatadir)
styles_DATA = gala.css texture.png
styles_DATA = gala.css texture.png close.png
applicationsdir = $(datadir)/applications
applications_DATA = gala.desktop
@ -22,6 +22,7 @@ EXTRA_DIST = \
gala.css \
gala.desktop \
texture.png \
close.png \
org.pantheon.desktop.gala.gschema.xml.in.in \
$(NULL)

BIN
data/close.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -24,6 +24,8 @@ namespace Gala
static Gee.HashMap<string, Gdk.Pixbuf> icon_pixbuf_cache;
static uint cache_clear_timeout = 0;
static Gdk.Pixbuf? close_pixbuf = null;
static construct
{
xid_pixbuf_cache = new Gee.HashMap<string, Gdk.Pixbuf> ();
@ -72,7 +74,7 @@ namespace Gala
/**
* returns a pixbuf for the application of this window or a default icon
**/
public static Gdk.Pixbuf get_icon_for_window (Meta.Window window, int size)
public static Gdk.Pixbuf? get_icon_for_window (Meta.Window window, int size)
{
Gdk.Pixbuf? result = null;
@ -220,6 +222,49 @@ namespace Gala
screen.get_display ().get_compositor ().flash_screen (screen);
}
/**
* @return the close button pixbuf or null if it failed to load
*/
public static Gdk.Pixbuf? get_close_button_pixbuf ()
{
if (close_pixbuf == null) {
try {
close_pixbuf = new Gdk.Pixbuf.from_file (Config.PKGDATADIR + "/close.png");
} catch (Error e) {
warning (e.message);
return null;
}
}
return close_pixbuf;
}
/**
* Creates a new reactive ClutterActor at 28x28 with the close pixbuf
* @return The close button actor
*/
public static GtkClutter.Texture create_close_button ()
{
var texture = new GtkClutter.Texture ();
var pixbuf = get_close_button_pixbuf ();
texture.reactive = true;
texture.set_size (28, 28);
if (pixbuf != null) {
try {
texture.set_from_pixbuf (pixbuf);
} catch (Error e) {}
} else {
// we'll just make this red so there's at least something as an
// indicator that loading failed. Should never happen and this
// works as good as some weird fallback-image-failed-to-load pixbuf
texture.background_color = { 255, 0, 0, 255 };
}
return texture;
}
/**
* Plank DockTheme
*/

View File

@ -41,6 +41,7 @@ gala_VALASOURCES = \
Settings.vala \
TextShadowEffect.vala \
WindowManager.vala \
WorkspaceManager.vala \
Background/Background.vala \
Background/BackgroundCache.vala \
Background/BackgroundManager.vala \

View File

@ -2,23 +2,184 @@ using Clutter;
namespace Gala
{
class WindowIcon : Actor
{
static Gdk.Pixbuf? app_fallback_icon_64 = null;
static Gdk.Pixbuf? app_fallback_icon_22 = null;
static Gdk.Pixbuf? app_fallback_icon_16 = null;
static bool fallback_attempted_loading = false;
static const string FALLBACK_ICON = "application-default-icon";
public Meta.Window window { get; construct; }
int _icon_size;
public int icon_size {
get {
return _icon_size;
}
set {
if (value == _icon_size)
return;
_icon_size = value;
set_size (_icon_size, _icon_size);
fade_new_icon ();
//get_transition ("size").completed.connect (fade_new_icon);
}
}
bool _temporary;
public bool temporary {
get {
return _temporary;
}
set {
if (_temporary && !value) {
remove_transition ("pulse");
} else if (!_temporary && value) {
var transition = new TransitionGroup ();
transition.duration = 800;
transition.auto_reverse = true;
transition.repeat_count = -1;
transition.progress_mode = AnimationMode.LINEAR;
var opacity_transition = new PropertyTransition ("opacity");
opacity_transition.set_from_value (100);
opacity_transition.set_to_value (255);
opacity_transition.auto_reverse = true;
var scale_x_transition = new PropertyTransition ("scale-x");
scale_x_transition.set_from_value (0.8);
scale_x_transition.set_to_value (1.3);
scale_x_transition.auto_reverse = true;
var scale_y_transition = new PropertyTransition ("scale-y");
scale_y_transition.set_from_value (0.8);
scale_y_transition.set_to_value (1.3);
scale_y_transition.auto_reverse = true;
transition.add_transition (opacity_transition);
transition.add_transition (scale_x_transition);
transition.add_transition (scale_y_transition);
add_transition ("pulse", transition);
}
_temporary = value;
}
}
bool initial = true;
GtkClutter.Texture? icon = null;
GtkClutter.Texture? old_icon = null;
public WindowIcon (Meta.Window window)
{
Object (window: window);
set_pivot_point (0.5f, 0.5f);
set_easing_mode (AnimationMode.EASE_OUT_ELASTIC);
set_easing_duration (800);
if (!fallback_attempted_loading) {
fallback_attempted_loading = true;
try {
app_fallback_icon_64 = Gtk.IconTheme.get_default ().load_icon (FALLBACK_ICON, 64, 0);
app_fallback_icon_22 = Gtk.IconTheme.get_default ().load_icon (FALLBACK_ICON, 22, 0);
app_fallback_icon_16 = Gtk.IconTheme.get_default ().load_icon (FALLBACK_ICON, 16, 0);
} catch (Error e) {
warning (e.message);
}
}
}
public void place (float x, float y, int size)
{
if (initial) {
save_easing_state ();
set_easing_duration (10);
}
set_position (x, y);
icon_size = size;
if (initial) {
restore_easing_state ();
initial = false;
}
}
void fade_new_icon ()
{
var new_icon = new GtkClutter.Texture ();
new_icon.add_constraint (new BindConstraint (this, BindCoordinate.SIZE, 0));
new_icon.opacity = 0;
var pixbuf = Utils.get_icon_for_window (window, icon_size);
if (pixbuf == null) {
switch (icon_size) {
case 16:
pixbuf = app_fallback_icon_16;
break;
case 22:
pixbuf = app_fallback_icon_22;
break;
case 64:
pixbuf = app_fallback_icon_64;
break;
}
}
if (pixbuf != null)
new_icon.set_from_pixbuf (pixbuf);
add_child (new_icon);
new_icon.set_easing_mode (AnimationMode.EASE_OUT_QUAD);
new_icon.set_easing_duration (500);
if (icon == null) {
icon = new_icon;
} else {
old_icon = icon;
}
new_icon.opacity = 255;
if (old_icon != null) {
old_icon.opacity = 0;
var transition = old_icon.get_transition ("opacity");
if (transition != null) {
transition.completed.connect (() => {
old_icon.destroy ();
old_icon = null;
});
} else {
old_icon.destroy ();
old_icon = null;
}
}
icon = new_icon;
}
}
public class IconGroup : Actor
{
const int SIZE = 64;
const string FALLBACK_ICON = "application-default-icon";
static const int PLUS_SIZE = 8;
static const int PLUS_WIDTH = 24;
Gdk.Pixbuf? app_fallback_icon_64 = null;
Gdk.Pixbuf? app_fallback_icon_22 = null;
Gdk.Pixbuf? app_fallback_icon_16 = null;
public signal void selected ();
public Meta.Workspace workspace { get; construct; }
List<Meta.Window> windows;
List<string> windows;
int current_icon_size = -1;
public IconGroup (Meta.Workspace workspace)
{
@ -34,14 +195,6 @@ namespace Gala
canvas.set_size (SIZE, SIZE);
canvas.draw.connect (draw);
content = canvas;
try {
app_fallback_icon_64 = Gtk.IconTheme.get_default ().load_icon (FALLBACK_ICON, 64, 0);
app_fallback_icon_22 = Gtk.IconTheme.get_default ().load_icon (FALLBACK_ICON, 22, 0);
app_fallback_icon_16 = Gtk.IconTheme.get_default ().load_icon (FALLBACK_ICON, 16, 0);
} catch (Error e) {
warning (e.message);
}
}
public override bool button_release_event (ButtonEvent event)
@ -53,21 +206,46 @@ namespace Gala
public void clear ()
{
windows = new List<Meta.Window> ();
destroy_all_children ();
}
public void add_window (Meta.Window window, bool no_redraw = false)
public void add_window (Meta.Window window, bool no_redraw = false, bool temporary = false)
{
windows.append (window);
var new_window = new WindowIcon (window);
new_window.save_easing_state ();
new_window.set_easing_duration (0);
new_window.set_position (32, 32);
new_window.restore_easing_state ();
new_window.temporary = temporary;
add_child (new_window);
if (!no_redraw)
redraw ();
}
public void remove_window (Meta.Window window)
public void remove_window (Meta.Window window, bool animate = true)
{
windows.remove (window);
redraw ();
foreach (var child in get_children ()) {
var w = child as WindowIcon;
if (w.window == window) {
if (animate) {
w.set_easing_mode (AnimationMode.LINEAR);
w.set_easing_duration (200);
w.opacity = 0;
print ("c\n");
w.get_transition ("opacity").completed.connect (() => {
w.destroy ();
redraw ();
});
print ("d\n");
} else
w.destroy ();
break;
}
}
}
public void redraw ()
@ -75,30 +253,27 @@ namespace Gala
content.invalidate ();
}
/**
* Draw the background or plus sign and do layouting. We won't lose performance here
* by relayouting in the same function, as it's only ever called when we invalidate it.
*/
bool draw (Cairo.Context cr)
{
cr.set_operator (Cairo.Operator.CLEAR);
cr.paint ();
cr.set_operator (Cairo.Operator.OVER);
// we never show more than 9, TODO: show an ellipsis when we do cut
var n_windows = uint.min (windows.length (), 9);
var n_windows = get_n_children ();
// single icon => big icon
if (n_windows == 1) {
var pix = Utils.get_icon_for_window (windows.nth_data (0), 64);
if (pix == null) {
if (app_fallback_icon_64 != null)
pix = app_fallback_icon_64;
else
return false;
}
var icon = get_child_at_index (0) as WindowIcon;
icon.place (0, 0, 64);
Gdk.cairo_set_source_pixbuf (cr, pix, 0, 0);
cr.paint ();
return false;
}
// folder
// more than one => we need a folder
Granite.Drawing.Utilities.cairo_rounded_rectangle (cr, 0.5, 0.5, (int)width - 1, (int)height - 1, 5);
cr.set_source_rgba (0, 0, 0, 0.1);
@ -157,8 +332,9 @@ namespace Gala
else
size = 16;
var columns = (int)Math.ceil (Math.sqrt (n_windows));
var rows = (int)Math.ceil (n_windows / (double)columns);
var n_tiled_windows = uint.min (n_windows, 9);
var columns = (int)Math.ceil (Math.sqrt (n_tiled_windows));
var rows = (int)Math.ceil (n_tiled_windows / (double)columns);
const int spacing = 6;
@ -167,28 +343,45 @@ namespace Gala
var x_offset = SIZE / 2 - width / 2;
var y_offset = SIZE / 2 - height / 2;
var show_ellipsis = false;
var n_shown_windows = n_windows;
// make place for an ellipsis
if (n_shown_windows > 9) {
n_shown_windows = 8;
show_ellipsis = true;
}
var x = x_offset;
var y = y_offset;
for (var i = 0; i < n_windows; i++) {
var pix = Utils.get_icon_for_window (windows.nth_data (i), size);
if (pix == null) {
if (size == 22 && app_fallback_icon_22 != null)
pix = app_fallback_icon_22;
else if (size == 16 && app_fallback_icon_16 != null)
pix = app_fallback_icon_16;
else
continue;
var window = get_child_at_index (i) as WindowIcon;
// draw an ellipsis at the 9th position if we need one
if (show_ellipsis && i == 8) {
const int top_offset = 10;
const int left_offset = 2;
const int radius = 2;
const int spacing = 3;
cr.arc (left_offset + x, y + top_offset, radius, 0, 2 * Math.PI);
cr.arc (left_offset + x + radius + spacing, y + top_offset, radius, 0, 2 * Math.PI);
cr.arc (left_offset + x + radius * 2 + spacing * 2, y + top_offset, radius, 0, 2 * Math.PI);
cr.set_source_rgb (0.3, 0.3, 0.3);
cr.fill ();
}
Gdk.cairo_set_source_pixbuf (cr, pix, x, y);
if (i >= n_shown_windows) {
window.visible = false;
continue;
}
window.place (x, y, size);
x += size + spacing;
if (x + size >= SIZE) {
x = x_offset;
y += size + spacing;
}
cr.paint ();
}
return false;

View File

@ -22,6 +22,7 @@ namespace Gala
reactive = true;
workspaces = new Actor ();
workspaces.set_easing_mode (AnimationMode.EASE_OUT_QUAD);
icon_groups = new Actor ();
icon_groups.layout_manager = new BoxLayout ();
@ -35,7 +36,7 @@ namespace Gala
screen.workspace_added.connect (add_workspace);
screen.workspace_removed.connect (remove_workspace);
screen.workspace_switched.connect ((from, to, direction) => {
screen.workspace_switched.connect_after ((from, to, direction) => {
update_positions (opened);
});
}
@ -53,29 +54,25 @@ namespace Gala
return false;
}
void update_positions (bool animate = false, bool closing = false)
void update_positions (bool animate = false)
{
float x = 0;
WorkspaceClone? active = null;
var active_index = screen.get_active_workspace ().index ();
var active_x = 0.0f;
foreach (var child in workspaces.get_children ()) {
var workspace_clone = child as WorkspaceClone;
var index = workspace_clone.workspace.index ();
var dest_x = index * (workspace_clone.width - 150);
if (index == active_index)
active = workspace_clone;
active_x = dest_x;
workspace_clone.x = index * (workspace_clone.width - 150);
workspace_clone.set_easing_duration (animate ? 200 : 0);
workspace_clone.x = dest_x;
}
if (active != null) {
var dest_x = -active.x;
if (animate)
workspaces.animate (AnimationMode.EASE_OUT_QUAD, 300, x: dest_x);
else
workspaces.x = dest_x;
}
workspaces.set_easing_duration (animate ? 300 : 0);
workspaces.x = -active_x;
}
void add_workspace (int num)
@ -88,6 +85,9 @@ namespace Gala
icon_groups.insert_child_at_index (workspace.icon_group, num);
update_positions ();
if (opened)
workspace.open ();
}
void remove_workspace (int num)
@ -111,10 +111,20 @@ namespace Gala
workspace.window_selected.disconnect (window_selected);
workspace.selected.disconnect (activate_workspace);
workspace.icon_group.destroy ();
workspace.icon_group.set_easing_duration (200);
workspace.icon_group.set_easing_mode (AnimationMode.LINEAR);
workspace.icon_group.opacity = 0;
var transition = workspace.icon_group.get_transition ("opacity");
if (transition != null)
transition.completed.connect (() => {
workspace.icon_group.destroy ();
});
else
workspace.icon_group.destroy ();
workspace.destroy ();
update_positions ();
update_positions (opened);
}
void activate_workspace (WorkspaceClone clone, bool close_view)
@ -228,10 +238,10 @@ namespace Gala
/**
* checks if val1 is about the same as val2 with a threshold of 2 by default
*/
private bool about_same (float val1, float val2, float threshold = 2.0f)
/*private bool about_same (float val1, float val2, float threshold = 2.0f)
{
return Math.fabsf (val1 - val2) <= threshold;
}
}*/
}
}

View File

@ -12,10 +12,13 @@ namespace Gala
DragDropAction drag_action;
Clone? clone = null;
Meta.Rectangle? slot = null;
Actor prev_parent = null;
int prev_index = -1;
Actor close_button;
public TiledWindow (Meta.Window window)
{
Object (window: window);
@ -34,6 +37,9 @@ namespace Gala
drag_action.actor_clicked.connect (() => { selected (); });
add_action (drag_action);
close_button = Utils.create_close_button ();
add_child (close_button);
}
~TiledWindow ()
@ -55,24 +61,77 @@ namespace Gala
}
clone = new Clone (actor);
clone.add_constraint (new BindConstraint (this, BindCoordinate.SIZE, 0));
add_child (clone);
set_easing_duration (250);
set_easing_mode (AnimationMode.EASE_OUT_QUAD);
set_position (actor.x, actor.y);
var outer_rect = window.get_outer_rect ();
request_reposition ();
set_position (outer_rect.x, outer_rect.y);
set_size (outer_rect.width, outer_rect.height);
}
public void transition_to_original_state ()
{
var actor = window.get_compositor_private () as Actor;
var outer_rect = window.get_outer_rect ();
set_easing_mode (AnimationMode.EASE_IN_OUT_CUBIC);
set_easing_duration (300);
set_size (actor.width, actor.height);
set_position (actor.x, actor.y);
set_position (outer_rect.x, outer_rect.y);
set_size (outer_rect.width, outer_rect.height);
}
public void take_slot (Meta.Rectangle rect)
{
slot = rect;
set_easing_duration (250);
set_easing_mode (AnimationMode.EASE_OUT_QUAD);
set_size (rect.width, rect.height);
set_position (rect.x, rect.y);
}
public override void allocate (ActorBox box, AllocationFlags flags)
{
base.allocate (box, flags);
foreach (var child in get_children ()) {
if (child != clone)
// child.allocate ({ child.x, child.y, child.x + child.width, child.y + child.height }, flags);
child.allocate_preferred_size (flags);
}
if (clone == null)
return;
var actor = window.get_compositor_private () as WindowActor;
var input_rect = window.get_input_rect ();
var outer_rect = window.get_outer_rect ();
var scale_factor = (float)width / outer_rect.width;
var alloc = ActorBox ();
alloc.set_origin ((input_rect.x - outer_rect.x) * scale_factor,
(input_rect.y - outer_rect.y) * scale_factor);
alloc.set_size (actor.width * scale_factor, actor.height * scale_factor);
clone.allocate (alloc, flags);
}
public void place_widgets (int dest_width, int dest_height)
{
Granite.CloseButtonPosition pos;
Granite.Widgets.Utils.get_default_close_button_position (out pos);
close_button.y = 0;
switch (pos) {
case Granite.CloseButtonPosition.RIGHT:
close_button.x = dest_width;
break;
case Granite.CloseButtonPosition.LEFT:
close_button.x = 0;
break;
}
}
void unmanaged ()
@ -115,8 +174,13 @@ namespace Gala
if (!(destination is IconGroup))
return;
var scale = hovered ? 0.2 : 0.4;
var opacity = hovered ? 100 : 255;
var icon_group = destination as IconGroup;
if (icon_group.workspace == window.get_workspace ())
return;
var scale = hovered ? 0.1 : 0.4;
var opacity = hovered ? 50 : 255;
var mode = hovered ? AnimationMode.EASE_IN_OUT_BACK : AnimationMode.EASE_OUT_ELASTIC;
save_easing_state ();
@ -129,6 +193,12 @@ namespace Gala
set_opacity (opacity);
restore_easing_state ();
if (hovered) {
icon_group.add_window (window, false, true);
} else {
icon_group.remove_window (window);
}
}
void drag_end (Actor destination)
@ -279,8 +349,8 @@ namespace Gala
foreach (var tilable in result) {
var window = (TiledWindow)tilable.id;
window.set_position (tilable.rect.x, tilable.rect.y);
window.set_size (tilable.rect.width, tilable.rect.height);
window.take_slot (tilable.rect);
window.place_widgets (tilable.rect.width, tilable.rect.height);
}
}

View File

@ -90,6 +90,8 @@ namespace Gala
public signal void window_selected (Meta.Window window);
public signal void selected (bool close_view);
bool opened = false;
uint hover_activate_timeout = 0;
public WorkspaceClone (Meta.Workspace workspace, WindowManager wm)
@ -171,8 +173,7 @@ namespace Gala
private void remove_window (Meta.Window window)
{
icon_group.remove_window (window);
// TODO ?
icon_group.remove_window (window, opened);
}
private void shrink_rectangle (ref Meta.Rectangle rect, int amount)
@ -202,9 +203,10 @@ namespace Gala
};
shrink_rectangle (ref area, 32);
icon_group.clear ();
opened = true;
// TODO this can be optimized
icon_group.clear ();
window_container.destroy_all_children ();
window_container.padding_top = TOP_OFFSET;
window_container.padding_left =
@ -226,6 +228,8 @@ namespace Gala
public void close ()
{
opened = false;
background.animate (AnimationMode.EASE_IN_OUT_CUBIC, 300, scale_x: 1.0f, scale_y: 1.0f);
window_container.transition_to_original_state ();

View File

@ -29,6 +29,8 @@ namespace Gala
public Meta.BackgroundGroup background_group { get; protected set; }
public HashTable<int,int> window_stacking_order { get; protected set; }
public WorkspaceManager workspace_manager { get; private set; }
Meta.PluginInfo info;
WindowSwitcher? winswitcher = null;
@ -92,8 +94,7 @@ namespace Gala
var color = BackgroundSettings.get_default ().primary_color;
stage.background_color = Clutter.Color.from_string (color);
if (Prefs.get_dynamic_workspaces ())
screen.override_workspace_layout (ScreenCorner.TOPLEFT, false, 1, -1);
workspace_manager = new WorkspaceManager (screen);
/* our layer structure, copied from gnome-shell (from bottom to top):
* stage

144
src/WorkspaceManager.vala Normal file
View File

@ -0,0 +1,144 @@
//
// Copyright (C) 2014 Tom Beckmann, Rico Tzschichholz
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
using Meta;
namespace Gala
{
public class WorkspaceManager : Object
{
public Screen screen { get; construct; }
public WorkspaceManager (Screen screen)
{
Object (screen: screen);
if (Prefs.get_dynamic_workspaces ())
screen.override_workspace_layout (ScreenCorner.TOPLEFT, false, 1, -1);
for (var i = 0; i < screen.get_n_workspaces (); i++)
workspace_added (screen, i);
Prefs.add_listener (prefs_listener);
screen.workspace_added.connect (workspace_added);
screen.workspace_switched.connect_after (workspace_switched);
// make sure the last workspace has no windows on it
if (Prefs.get_dynamic_workspaces ()
&& Utils.get_n_windows (screen.get_workspace_by_index (screen.get_n_workspaces () - 1)) > 0)
append_workspace ();
}
~WorkspaceManager ()
{
Prefs.remove_listener (prefs_listener);
screen.workspace_added.disconnect (workspace_added);
screen.workspace_switched.disconnect (workspace_switched);
}
void workspace_added (Screen screen, int index)
{
var workspace = screen.get_workspace_by_index (index);
if (workspace == null)
return;
workspace.window_added.connect (window_added);
workspace.window_removed.connect (window_removed);
}
void workspace_switched (Screen screen, int from, int to, MotionDirection direction)
{
if (!Prefs.get_dynamic_workspaces ())
return;
// remove empty workspaces after we switched away from them unless it's the last one
var prev_workspace = screen.get_workspace_by_index (from);
if (Utils.get_n_windows (prev_workspace) < 1
&& from != screen.get_n_workspaces () - 1) {
screen.remove_workspace (prev_workspace, screen.get_display ().get_current_time ());
}
}
void window_added (Workspace workspace, Window window)
{
if (!Prefs.get_dynamic_workspaces ())
return;
if ((window.window_type == WindowType.NORMAL
|| window.window_type == WindowType.DIALOG
|| window.window_type == WindowType.MODAL_DIALOG)
&& workspace.index () == screen.get_n_workspaces () - 1)
append_workspace ();
}
void window_removed (Workspace workspace, Window window)
{
if (!Prefs.get_dynamic_workspaces ())
return;
if (window.window_type != WindowType.NORMAL
&& window.window_type != WindowType.DIALOG
&& window.window_type != WindowType.MODAL_DIALOG)
return;
var index = screen.get_workspaces ().index (workspace);
// has already been removed
if (index < 0)
return;
// remove it right away if it was the active workspace and it's not the very last
if (workspace != screen.get_active_workspace ()
&& Utils.get_n_windows (workspace) < 1
&& index != screen.get_n_workspaces () - 1)
screen.remove_workspace (workspace, screen.get_display ().get_current_time ());
}
void prefs_listener (Meta.Preference pref)
{
if (pref == Preference.DYNAMIC_WORKSPACES && Prefs.get_dynamic_workspaces ()) {
// 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);
}
}*/
}
}
void append_workspace ()
{
screen.append_new_workspace (false, screen.get_display ().get_current_time ());
}
}
}