mirror of
https://github.com/elementary/gala.git
synced 2024-12-18 23:02:14 +03:00
Merge dynamic workspaces
This commit is contained in:
commit
40f15e1487
@ -53,6 +53,7 @@ vala_precompile(VALA_C
|
||||
src/TextShadowEffect.vala
|
||||
src/Widgets/WindowSwitcher.vala
|
||||
src/Widgets/WorkspaceView.vala
|
||||
src/Widgets/WorkspaceThumb.vala
|
||||
${CMAKE_BINARY_DIR}/src/Config.vala
|
||||
PACKAGES
|
||||
granite
|
||||
|
@ -48,10 +48,11 @@ namespace Gala
|
||||
var screen = get_screen ();
|
||||
|
||||
var stage = Compositor.get_stage_for_screen (screen);
|
||||
screen.override_workspace_layout (ScreenCorner.TOPLEFT, false, 4, -1);
|
||||
screen.override_workspace_layout (ScreenCorner.TOPLEFT, false, 1, -1);
|
||||
|
||||
workspace_view = new WorkspaceView (this);
|
||||
workspace_view.visible = false;
|
||||
|
||||
winswitcher = new WindowSwitcher (this);
|
||||
|
||||
stage.add_child (workspace_view);
|
||||
@ -113,6 +114,7 @@ namespace Gala
|
||||
stage.add_child (hot_corner);
|
||||
|
||||
update_input_area ();
|
||||
|
||||
BehaviorSettings.get_default ().notify["enable-manager-corner"].connect (update_input_area);
|
||||
}
|
||||
|
||||
@ -133,13 +135,15 @@ namespace Gala
|
||||
var screen = get_screen ();
|
||||
var display = screen.get_display ();
|
||||
|
||||
var idx = screen.get_active_workspace ().index () + (reverse ? -1 : 1);
|
||||
var active = screen.get_active_workspace ();
|
||||
var idx = active.index () + (reverse ? -1 : 1);
|
||||
|
||||
if (idx < 0 || idx >= screen.n_workspaces)
|
||||
if (idx < 0 || idx >= screen.n_workspaces ||
|
||||
(active.n_windows == 1 && idx == screen.n_workspaces-1)) //dont allow empty workspaces to be created by moving
|
||||
return;
|
||||
|
||||
if (!window.is_on_all_workspaces ())
|
||||
window.change_workspace_by_index (idx, false, display.get_current_time ());
|
||||
window.change_workspace_by_index (idx, true, display.get_current_time ());
|
||||
|
||||
moving = window;
|
||||
screen.get_workspace_by_index (idx).activate_with_focus (window, display.get_current_time ());
|
||||
@ -166,13 +170,14 @@ namespace Gala
|
||||
|
||||
public void dim_window (Window window, bool dim)
|
||||
{
|
||||
var win = window.get_compositor_private () as Clutter.Actor;
|
||||
/*FIXME we need a super awesome blureffect here, the one from clutter is just... bah!
|
||||
var win = window.get_compositor_private () as WindowActor;
|
||||
if (dim) {
|
||||
if (win.has_effects ())
|
||||
return;
|
||||
win.add_effect_with_name ("darken", new Clutter.ColorizeEffect ({180, 180, 180, 255}));
|
||||
win.add_effect_with_name ("darken", new Clutter.BlurEffect ());
|
||||
} else
|
||||
win.clear_effects ();
|
||||
win.clear_effects ();*/
|
||||
}
|
||||
|
||||
/*
|
||||
@ -440,10 +445,8 @@ namespace Gala
|
||||
|
||||
public override void kill_window_effects (WindowActor actor)
|
||||
{
|
||||
if (end_animation (ref mapping, actor)) {
|
||||
if (end_animation (ref mapping, actor))
|
||||
map_completed (actor);
|
||||
print ("KILLED MAPPING ONE\n");
|
||||
}
|
||||
if (end_animation (ref minimizing, actor))
|
||||
minimize_completed (actor);
|
||||
if (end_animation (ref maximizing, actor))
|
||||
@ -473,14 +476,12 @@ namespace Gala
|
||||
get_screen ().get_size (out w, out h);
|
||||
|
||||
var x2 = 0.0f; var y2 = 0.0f;
|
||||
if (direction == MotionDirection.UP ||
|
||||
direction == MotionDirection.UP_LEFT ||
|
||||
direction == MotionDirection.UP_RIGHT)
|
||||
if (direction == MotionDirection.LEFT)
|
||||
x2 = w;
|
||||
else if (direction == MotionDirection.DOWN ||
|
||||
direction == MotionDirection.DOWN_LEFT ||
|
||||
direction == MotionDirection.DOWN_RIGHT)
|
||||
else if (direction == MotionDirection.RIGHT)
|
||||
x2 = -w;
|
||||
else
|
||||
return;
|
||||
|
||||
var group = Compositor.get_window_group_for_screen (get_screen ());
|
||||
var wallpaper = Compositor.get_background_actor_for_screen (get_screen ());
|
||||
|
@ -124,4 +124,20 @@ namespace Gala.Utils
|
||||
var xregion = X.Fixes.create_region (display.get_xdisplay (), {rect});
|
||||
Util.set_stage_input_region (screen, xregion);
|
||||
}
|
||||
|
||||
/**
|
||||
* get the number of toplevel windows on a workspace
|
||||
**/
|
||||
public uint get_n_windows (Workspace workspace)
|
||||
{
|
||||
var n = 0;
|
||||
foreach (var window in workspace.list_windows ()) {
|
||||
if (window.window_type == WindowType.NORMAL ||
|
||||
window.window_type == WindowType.DIALOG ||
|
||||
window.window_type == WindowType.MODAL_DIALOG)
|
||||
n ++;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
392
src/Widgets/WorkspaceThumb.vala
Normal file
392
src/Widgets/WorkspaceThumb.vala
Normal file
@ -0,0 +1,392 @@
|
||||
//
|
||||
// Copyright (C) 2012 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;
|
||||
using Clutter;
|
||||
|
||||
namespace Gala
|
||||
{
|
||||
public class WorkspaceThumb : Clutter.Actor
|
||||
{
|
||||
|
||||
static const int INDICATOR_BORDER = 5;
|
||||
static const int APP_ICON_SIZE = 32;
|
||||
static const float THUMBNAIL_HEIGHT = 80.0f;
|
||||
static const uint CLOSE_BUTTON_DELAY = 500;
|
||||
|
||||
public signal void clicked ();
|
||||
public signal void closed ();
|
||||
public signal void window_on_last ();
|
||||
|
||||
public unowned Workspace? workspace { get; set; }
|
||||
|
||||
unowned Screen screen;
|
||||
|
||||
static GtkClutter.Texture? plus = null;
|
||||
|
||||
Clone wallpaper;
|
||||
Clutter.Actor windows;
|
||||
Clutter.Actor icons;
|
||||
CairoTexture indicator;
|
||||
GtkClutter.Texture close_button;
|
||||
|
||||
uint hover_timer = 0;
|
||||
|
||||
public WorkspaceThumb (Workspace _workspace)
|
||||
{
|
||||
workspace = _workspace;
|
||||
screen = workspace.get_screen ();
|
||||
|
||||
screen.workspace_switched.connect (handle_workspace_switched);
|
||||
screen.workspace_added.connect (workspace_added);
|
||||
|
||||
workspace.window_added.connect (handle_window_added);
|
||||
workspace.window_removed.connect (handle_window_removed);
|
||||
|
||||
int swidth, sheight;
|
||||
screen.get_size (out swidth, out sheight);
|
||||
|
||||
var width = Math.floorf ((THUMBNAIL_HEIGHT / sheight) * swidth);
|
||||
|
||||
reactive = true;
|
||||
|
||||
indicator = new Clutter.CairoTexture ((uint)width + 2 * INDICATOR_BORDER, (uint)THUMBNAIL_HEIGHT + 2 * INDICATOR_BORDER);
|
||||
indicator.draw.connect (draw_indicator);
|
||||
indicator.auto_resize = true;
|
||||
indicator.opacity = 0;
|
||||
handle_workspace_switched (-1, screen.get_active_workspace_index (), MotionDirection.LEFT);
|
||||
|
||||
// FIXME find a nice way to draw a border around it, maybe combinable with the indicator using a ShaderEffect
|
||||
wallpaper = new Clone (Compositor.get_background_actor_for_screen (screen));
|
||||
wallpaper.x = INDICATOR_BORDER;
|
||||
wallpaper.y = INDICATOR_BORDER;
|
||||
wallpaper.height = THUMBNAIL_HEIGHT;
|
||||
wallpaper.width = width;
|
||||
|
||||
close_button = new GtkClutter.Texture ();
|
||||
try {
|
||||
close_button.set_from_pixbuf (Granite.Widgets.get_close_pixbuf ());
|
||||
} catch (Error e) { warning (e.message); }
|
||||
close_button.x = -12.0f;
|
||||
close_button.y = -10.0f;
|
||||
close_button.reactive = true;
|
||||
close_button.scale_gravity = Clutter.Gravity.CENTER;
|
||||
close_button.scale_x = 0;
|
||||
close_button.scale_y = 0;
|
||||
|
||||
icons = new Actor ();
|
||||
icons.layout_manager = new BoxLayout ();
|
||||
(icons.layout_manager as Clutter.BoxLayout).spacing = 6;
|
||||
icons.height = APP_ICON_SIZE;
|
||||
|
||||
windows = new Actor ();
|
||||
windows.x = INDICATOR_BORDER;
|
||||
windows.y = INDICATOR_BORDER;
|
||||
windows.height = THUMBNAIL_HEIGHT;
|
||||
windows.width = width;
|
||||
windows.clip_to_allocation = true;
|
||||
|
||||
add_child (indicator);
|
||||
add_child (wallpaper);
|
||||
add_child (windows);
|
||||
add_child (icons);
|
||||
add_child (close_button);
|
||||
|
||||
//kill the workspace
|
||||
close_button.button_release_event.connect (close_workspace);
|
||||
|
||||
if (plus == null) {
|
||||
var css = new Gtk.CssProvider ();
|
||||
var img = new Gtk.Image ();
|
||||
try {
|
||||
css.load_from_data ("*{text-shadow:0 1 #f00;color:alpha(#fff, 0.8);}", -1);
|
||||
} catch (Error e) { warning(e.message); }
|
||||
img.get_style_context ().add_provider (css, 20000);
|
||||
|
||||
plus = new GtkClutter.Texture ();
|
||||
try {
|
||||
var pix = Gtk.IconTheme.get_default ().choose_icon ({"list-add-symbolic", "list-add"}, (int)THUMBNAIL_HEIGHT / 2, 0).
|
||||
load_symbolic_for_context (img.get_style_context ());
|
||||
plus.set_from_pixbuf (pix);
|
||||
} catch (Error e) { warning (e.message); }
|
||||
|
||||
plus.x = wallpaper.x + wallpaper.width / 2 - plus.width / 2;
|
||||
plus.y = wallpaper.y + wallpaper.height / 2 - plus.height / 2;
|
||||
}
|
||||
|
||||
check_last_workspace ();
|
||||
|
||||
visible = false;
|
||||
}
|
||||
|
||||
~WorkspaceThumb ()
|
||||
{
|
||||
screen.workspace_switched.disconnect (handle_workspace_switched);
|
||||
screen.workspace_added.disconnect (workspace_added);
|
||||
}
|
||||
|
||||
bool close_workspace (Clutter.ButtonEvent event)
|
||||
{
|
||||
workspace.list_windows ().foreach ((w) => {
|
||||
if (w.window_type != WindowType.DOCK) {
|
||||
w.delete (event.time);
|
||||
}
|
||||
});
|
||||
|
||||
GLib.Timeout.add (250, () => {
|
||||
//wait for confirmation dialogs to popup
|
||||
if (Utils.get_n_windows (workspace) == 0) {
|
||||
workspace.window_added.disconnect (handle_window_added);
|
||||
workspace.window_removed.disconnect (handle_window_removed);
|
||||
|
||||
animate (Clutter.AnimationMode.LINEAR, 250, width : 0.0f, opacity : 0);
|
||||
|
||||
closed ();
|
||||
} else
|
||||
workspace.activate (workspace.get_screen ().get_display ().get_current_time ());
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool draw_indicator (Cairo.Context cr)
|
||||
{
|
||||
Granite.Drawing.Utilities.cairo_rounded_rectangle (cr, 0, 0, indicator.width, indicator.height, 8);
|
||||
|
||||
cr.set_source_rgb (0.35, 0.75, 1.0);
|
||||
cr.fill_preserve ();
|
||||
cr.set_line_width (1);
|
||||
cr.set_source_rgba (0.0, 0.0, 0.0, 0.8);
|
||||
cr.stroke ();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void workspace_added (int index)
|
||||
{
|
||||
check_last_workspace ();
|
||||
}
|
||||
|
||||
void update_windows ()
|
||||
{
|
||||
windows.remove_all_children ();
|
||||
|
||||
if (workspace == null)
|
||||
return;
|
||||
|
||||
int swidth, sheight;
|
||||
screen.get_size (out swidth, out sheight);
|
||||
|
||||
// add window thumbnails
|
||||
var aspect = windows.width / swidth;
|
||||
Compositor.get_window_actors (screen).foreach ((w) => {
|
||||
var meta_window = w.get_meta_window ();
|
||||
var type = meta_window.window_type;
|
||||
|
||||
if ((!(w.get_workspace () == workspace.index ()) &&
|
||||
!meta_window.is_on_all_workspaces ()) ||
|
||||
meta_window.minimized ||
|
||||
(type != WindowType.NORMAL &&
|
||||
type != WindowType.DIALOG &&
|
||||
type != WindowType.MODAL_DIALOG))
|
||||
return;
|
||||
|
||||
var clone = new Clone (w.get_texture ());
|
||||
clone.width = aspect * clone.width;
|
||||
clone.height = aspect * clone.height;
|
||||
clone.x = aspect * w.x;
|
||||
clone.y = aspect * w.y;
|
||||
|
||||
windows.add_child (clone);
|
||||
});
|
||||
}
|
||||
|
||||
void update_icons ()
|
||||
{
|
||||
icons.remove_all_children ();
|
||||
|
||||
if (workspace == null)
|
||||
return;
|
||||
|
||||
//show each icon only once, so log the ones added
|
||||
var shown_applications = new List<Bamf.Application> ();
|
||||
|
||||
workspace.list_windows ().foreach ((w) => {
|
||||
if (w.window_type != Meta.WindowType.NORMAL || w.minimized)
|
||||
return;
|
||||
|
||||
var app = Bamf.Matcher.get_default ().get_application_for_xid ((uint32)w.get_xwindow ());
|
||||
if (shown_applications.index (app) != -1)
|
||||
return;
|
||||
|
||||
if (app != null)
|
||||
shown_applications.append (app);
|
||||
|
||||
var icon = new GtkClutter.Texture ();
|
||||
try {
|
||||
icon.set_from_pixbuf (Utils.get_icon_for_window (w, APP_ICON_SIZE));
|
||||
} catch (Error e) { warning (e.message); }
|
||||
|
||||
icon.reactive = true;
|
||||
icon.button_release_event.connect ( () => {
|
||||
workspace.activate_with_focus (w, workspace.get_screen ().get_display ().get_current_time ());
|
||||
return false;
|
||||
});
|
||||
|
||||
icons.add_child (icon);
|
||||
});
|
||||
|
||||
icons.x = Math.floorf (wallpaper.x + wallpaper.width / 2 - icons.width / 2);
|
||||
icons.y = Math.floorf (wallpaper.y + wallpaper.height - 5);
|
||||
}
|
||||
|
||||
void check_last_workspace ()
|
||||
{
|
||||
//last workspace, show plus button and so on
|
||||
//give the last one a different style
|
||||
|
||||
var index = screen.get_workspaces ().index (workspace);
|
||||
if (index < 0) {
|
||||
closed ();
|
||||
return;
|
||||
}
|
||||
|
||||
if (index == screen.n_workspaces - 1) {
|
||||
wallpaper.opacity = 127;
|
||||
if (plus.get_parent () == null)
|
||||
add_child (plus);
|
||||
} else {
|
||||
wallpaper.opacity = 255;
|
||||
if (contains (plus))
|
||||
remove_child (plus);
|
||||
}
|
||||
}
|
||||
|
||||
void handle_workspace_switched (int index_old, int index_new, Meta.MotionDirection direction)
|
||||
{
|
||||
if (index_old == index_new)
|
||||
return;
|
||||
|
||||
if (workspace == null)
|
||||
return;
|
||||
|
||||
if (workspace.index () == index_old)
|
||||
indicator.animate (Clutter.AnimationMode.EASE_OUT_QUAD, 200, opacity : 0);
|
||||
else if (workspace.index () == index_new)
|
||||
indicator.animate (Clutter.AnimationMode.EASE_OUT_QUAD, 200, opacity : 255);
|
||||
}
|
||||
|
||||
void handle_window_added (Meta.Window window)
|
||||
{
|
||||
if (workspace != null && workspace.index () == screen.n_workspaces - 1 && Utils.get_n_windows (workspace) > 0)
|
||||
window_on_last ();
|
||||
}
|
||||
|
||||
void handle_window_removed (Meta.Window window)
|
||||
{
|
||||
|
||||
//dont remove workspaces when for example slingshot was closed
|
||||
if (window.window_type != WindowType.NORMAL &&
|
||||
window.window_type != WindowType.DIALOG &&
|
||||
window.window_type != WindowType.MODAL_DIALOG)
|
||||
return;
|
||||
|
||||
if (workspace != null && Utils.get_n_windows (workspace) == 0) {
|
||||
workspace.window_added.disconnect (handle_window_added);
|
||||
workspace.window_removed.disconnect (handle_window_removed);
|
||||
|
||||
closed ();
|
||||
}
|
||||
}
|
||||
|
||||
public override void hide ()
|
||||
{
|
||||
base.hide ();
|
||||
|
||||
icons.remove_all_children ();
|
||||
windows.remove_all_children ();
|
||||
}
|
||||
|
||||
public override void show ()
|
||||
{
|
||||
check_last_workspace ();
|
||||
|
||||
update_icons ();
|
||||
update_windows ();
|
||||
|
||||
base.show ();
|
||||
}
|
||||
|
||||
public override bool button_release_event (ButtonEvent event)
|
||||
{
|
||||
if (workspace == null)
|
||||
return true;
|
||||
|
||||
workspace.activate (screen.get_display ().get_current_time ());
|
||||
|
||||
clicked ();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool enter_event (CrossingEvent event)
|
||||
{
|
||||
if (workspace == null)
|
||||
return true;
|
||||
|
||||
if (workspace.index () == screen.n_workspaces - 1) {
|
||||
wallpaper.animate (AnimationMode.EASE_OUT_QUAD, 300, opacity : 210);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (hover_timer > 0)
|
||||
GLib.Source.remove (hover_timer);
|
||||
|
||||
hover_timer = Timeout.add (CLOSE_BUTTON_DELAY, () => {
|
||||
close_button.visible = true;
|
||||
close_button.animate (AnimationMode.EASE_OUT_ELASTIC, 400, scale_x : 1.0f, scale_y : 1.0f);
|
||||
return false;
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool leave_event (CrossingEvent event)
|
||||
{
|
||||
if (contains (event.related))
|
||||
return false;
|
||||
|
||||
if (hover_timer > 0) {
|
||||
GLib.Source.remove (hover_timer);
|
||||
hover_timer = 0;
|
||||
}
|
||||
|
||||
if (workspace == null)
|
||||
return false;
|
||||
|
||||
if (workspace.index () == screen.n_workspaces - 1)
|
||||
wallpaper.animate (AnimationMode.EASE_OUT_QUAD, 400, opacity : 127);
|
||||
else
|
||||
close_button.animate (AnimationMode.EASE_IN_QUAD, 400, scale_x : 0.0f, scale_y : 0.0f)
|
||||
.completed.connect (() => close_button.visible = false );
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -21,161 +21,72 @@ namespace Gala
|
||||
{
|
||||
public class WorkspaceView : Clutter.Actor
|
||||
{
|
||||
Gala.Plugin plugin;
|
||||
static const float VIEW_HEIGHT = 140.0f;
|
||||
|
||||
Clutter.Actor workspaces;
|
||||
Clutter.CairoTexture bg;
|
||||
Gala.Plugin plugin;
|
||||
Screen screen;
|
||||
|
||||
Clutter.Actor thumbnails;
|
||||
Clutter.CairoTexture background;
|
||||
Clutter.CairoTexture scroll;
|
||||
|
||||
bool animating; // delay closing the popup
|
||||
|
||||
Gdk.Pixbuf? background_pix;
|
||||
Clutter.CairoTexture workspace_thumb;
|
||||
Clutter.CairoTexture current_workspace;
|
||||
|
||||
float last_workspace_x = 0;
|
||||
int _workspace;
|
||||
int workspace {
|
||||
get {
|
||||
return _workspace;
|
||||
}
|
||||
set {
|
||||
if (_workspace == value)
|
||||
return;
|
||||
|
||||
_workspace = value;
|
||||
|
||||
if ((int) workspaces.get_children ().nth_data (_workspace).x != 0 || _workspace == 0)
|
||||
last_workspace_x = workspaces.get_children ().nth_data (_workspace).x;
|
||||
current_workspace.animate (Clutter.AnimationMode.EASE_IN_OUT_SINE, 400,
|
||||
x : workspaces.x + last_workspace_x - 5);
|
||||
}
|
||||
}
|
||||
|
||||
const string CURRENT_WORKSPACE_STYLE = """
|
||||
* {
|
||||
border-style: solid;
|
||||
border-width: 1px 1px 1px 1px;
|
||||
-unico-inner-stroke-width: 1px 0 1px 0;
|
||||
border-radius: 8px;
|
||||
|
||||
background-image: -gtk-gradient (linear,
|
||||
left top,
|
||||
left bottom,
|
||||
from (shade (@selected_bg_color, 1.4)),
|
||||
to (shade (@selected_bg_color, 0.98)));
|
||||
|
||||
-unico-border-gradient: -gtk-gradient (linear,
|
||||
left top, left bottom,
|
||||
from (alpha (#000, 0.5)),
|
||||
to (alpha (#000, 0.6)));
|
||||
|
||||
-unico-inner-stroke-gradient: -gtk-gradient (linear,
|
||||
left top, left bottom,
|
||||
from (alpha (#fff, 0.90)),
|
||||
to (alpha (#fff, 0.06)));
|
||||
}
|
||||
""";
|
||||
Gtk.Menu current_workspace_style; //dummy item for drawing
|
||||
|
||||
public WorkspaceView (Gala.Plugin _plugin)
|
||||
{
|
||||
plugin = _plugin;
|
||||
screen = plugin.get_screen ();
|
||||
|
||||
height = 128;
|
||||
height = VIEW_HEIGHT;
|
||||
opacity = 0;
|
||||
reactive = true;
|
||||
|
||||
workspaces = new Clutter.Actor ();
|
||||
var box_layout = new Clutter.BoxLayout ();
|
||||
box_layout.spacing = 12;
|
||||
workspaces.set_layout_manager (box_layout);
|
||||
thumbnails = new Clutter.Actor ();
|
||||
thumbnails.layout_manager = new Clutter.BoxLayout ();
|
||||
(thumbnails.layout_manager as Clutter.BoxLayout).spacing = 12;
|
||||
(thumbnails.layout_manager as Clutter.BoxLayout).homogeneous = true;
|
||||
|
||||
bg = new Clutter.CairoTexture (500, (uint)height);
|
||||
bg.auto_resize = true;
|
||||
bg.add_constraint (new Clutter.BindConstraint (this, Clutter.BindCoordinate.WIDTH, 0));
|
||||
bg.add_constraint (new Clutter.BindConstraint (this, Clutter.BindCoordinate.HEIGHT, 0));
|
||||
bg.draw.connect (draw_background);
|
||||
background = new Clutter.CairoTexture (500, (uint)height);
|
||||
background.auto_resize = true;
|
||||
background.add_constraint (new Clutter.BindConstraint (this, Clutter.BindCoordinate.WIDTH, 0));
|
||||
background.add_constraint (new Clutter.BindConstraint (this, Clutter.BindCoordinate.HEIGHT, 0));
|
||||
background.draw.connect (draw_background);
|
||||
|
||||
leave_event.connect ((e) => {
|
||||
if (!contains (e.related))
|
||||
hide ();
|
||||
|
||||
return false;
|
||||
});
|
||||
scroll = new Clutter.CairoTexture (100, 12);
|
||||
scroll.height = 12;
|
||||
scroll.auto_resize = true;
|
||||
scroll.draw.connect (draw_scroll);
|
||||
|
||||
int width, height;
|
||||
var area = plugin.get_screen ().get_monitor_geometry (plugin.get_screen ().get_primary_monitor ());
|
||||
width = area.width;
|
||||
height = area.height;
|
||||
add_child (background);
|
||||
add_child (thumbnails);
|
||||
add_child (scroll);
|
||||
|
||||
workspace_thumb = new Clutter.CairoTexture (120, 120);
|
||||
workspace_thumb.height = 80;
|
||||
workspace_thumb.width = (workspace_thumb.height / height) * width;
|
||||
workspace_thumb.auto_resize = true;
|
||||
workspace_thumb.draw.connect (draw_workspace_thumb);
|
||||
screen.workareas_changed.connect (initial_configuration);
|
||||
}
|
||||
|
||||
//method that waits for the workspaces to be configured on first run
|
||||
void initial_configuration ()
|
||||
{
|
||||
screen.workareas_changed.disconnect (initial_configuration);
|
||||
|
||||
current_workspace_style = new Gtk.Menu ();
|
||||
var provider = new Gtk.CssProvider ();
|
||||
try {
|
||||
provider.load_from_data (CURRENT_WORKSPACE_STYLE, -1);
|
||||
} catch (Error e) { warning (e.message); }
|
||||
current_workspace_style.get_style_context ().add_provider (provider, 20000);
|
||||
//remove everything except for the first
|
||||
for (var i=1;i<screen.get_workspaces ().length ();i++) {
|
||||
screen.remove_workspace (screen.get_workspaces ().nth_data (i), screen.get_display ().get_current_time ());
|
||||
}
|
||||
|
||||
current_workspace = new Clutter.CairoTexture (120, 120);
|
||||
current_workspace.height = workspace_thumb.height + 10;
|
||||
current_workspace.width = workspace_thumb.width + 10;
|
||||
current_workspace.auto_resize = true;
|
||||
current_workspace.draw.connect (draw_current_workspace);
|
||||
var thumb = new WorkspaceThumb (screen.get_workspaces ().nth_data (0));
|
||||
thumb.clicked.connect (hide);
|
||||
thumb.closed.connect (remove_workspace);
|
||||
thumb.window_on_last.connect (add_workspace);
|
||||
|
||||
var settings = new GLib.Settings ("org.gnome.desktop.background");
|
||||
thumbnails.add_child (thumb);
|
||||
|
||||
settings.changed.connect ((key) => {
|
||||
if (key == "picture-uri") {
|
||||
var path = File.new_for_uri (settings.get_string ("picture-uri")).get_path ();
|
||||
try {
|
||||
background_pix = new Gdk.Pixbuf.from_file (path).scale_simple
|
||||
((int)workspace_thumb.width, (int)workspace_thumb.height, Gdk.InterpType.HYPER);
|
||||
} catch (Error e) { warning (e.message); }
|
||||
//do a second run if necessary
|
||||
if (screen.n_workspaces != 1) {
|
||||
for (var i=1;i<screen.get_workspaces ().length ();i++) {
|
||||
screen.remove_workspace (screen.get_workspaces ().nth_data (i), screen.get_display ().get_current_time ());
|
||||
}
|
||||
});
|
||||
|
||||
var path = File.new_for_uri (settings.get_string ("picture-uri")).get_path ();
|
||||
try {
|
||||
background_pix = new Gdk.Pixbuf.from_file (path).scale_simple
|
||||
((int)workspace_thumb.width, (int)workspace_thumb.height, Gdk.InterpType.HYPER);
|
||||
} catch (Error e) { warning (e.message); }
|
||||
|
||||
add_child (workspace_thumb);
|
||||
add_child (bg);
|
||||
add_child (current_workspace);
|
||||
add_child (workspaces);
|
||||
|
||||
workspace_thumb.visible = false; //will only be used for cloning
|
||||
}
|
||||
|
||||
bool draw_current_workspace (Cairo.Context cr)
|
||||
{
|
||||
current_workspace_style.get_style_context ().render_activity (cr, 0, 0,
|
||||
current_workspace.width, current_workspace.height);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool draw_workspace_thumb (Cairo.Context cr)
|
||||
{
|
||||
Granite.Drawing.Utilities.cairo_rounded_rectangle (cr, 0, 0,
|
||||
workspace_thumb.width, workspace_thumb.height, 5);
|
||||
if (background_pix != null)
|
||||
Gdk.cairo_set_source_pixbuf (cr, background_pix, 0, 0);
|
||||
else
|
||||
cr.set_source_rgb (0, 0, 0);
|
||||
cr.fill_preserve ();
|
||||
|
||||
cr.set_line_width (1);
|
||||
cr.set_source_rgba (0, 0, 0, 1);
|
||||
cr.stroke_preserve ();
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool draw_background (Cairo.Context cr)
|
||||
@ -201,19 +112,83 @@ namespace Gala
|
||||
return false;
|
||||
}
|
||||
|
||||
bool draw_scroll (Cairo.Context cr)
|
||||
{
|
||||
Granite.Drawing.Utilities.cairo_rounded_rectangle (cr, 4, 4, scroll.width-32, 4, 2);
|
||||
cr.set_source_rgba (1, 1, 1, 0.8);
|
||||
cr.fill ();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void add_workspace ()
|
||||
{
|
||||
var screen = plugin.get_screen ();
|
||||
var wp = screen.append_new_workspace (false, screen.get_display ().get_current_time ());
|
||||
if (wp == null)
|
||||
return;
|
||||
|
||||
var thumb = new WorkspaceThumb (wp);
|
||||
thumb.clicked.connect (hide);
|
||||
thumb.closed.connect (remove_workspace);
|
||||
thumb.window_on_last.connect (add_workspace);
|
||||
|
||||
thumbnails.add_child (thumb);
|
||||
|
||||
check_scrollbar ();
|
||||
}
|
||||
|
||||
void remove_workspace (WorkspaceThumb thumb)
|
||||
{
|
||||
thumb.clicked.disconnect (hide);
|
||||
thumb.closed.disconnect (remove_workspace);
|
||||
thumb.window_on_last.disconnect (add_workspace);
|
||||
|
||||
var workspace = thumb.workspace;
|
||||
|
||||
if (workspace != null && workspace.index () > -1) { //dont remove non existing workspaces
|
||||
var screen = workspace.get_screen ();
|
||||
screen.remove_workspace (workspace, screen.get_display ().get_current_time ());
|
||||
}
|
||||
|
||||
thumb.workspace = null;
|
||||
|
||||
thumbnails.remove_child (thumb);
|
||||
thumb.destroy ();
|
||||
check_scrollbar ();
|
||||
}
|
||||
|
||||
void check_scrollbar ()
|
||||
{
|
||||
scroll.visible = thumbnails.width > width;
|
||||
|
||||
if (scroll.visible) {
|
||||
if (thumbnails.x + thumbnails.width < width)
|
||||
thumbnails.x = width - thumbnails.width;
|
||||
scroll.width = width / thumbnails.width * width;
|
||||
} else {
|
||||
thumbnails.animate (Clutter.AnimationMode.EASE_OUT_QUAD, 400, x : width / 2 - thumbnails.width / 2);
|
||||
}
|
||||
}
|
||||
|
||||
void switch_to_next_workspace (bool reverse)
|
||||
{
|
||||
var screen = plugin.get_screen ();
|
||||
var display = screen.get_display ();
|
||||
|
||||
var idx = screen.get_active_workspace_index () + (reverse ? -1 : 1);
|
||||
var neighbor = screen.get_active_workspace ().get_neighbor (reverse ? MotionDirection.LEFT : MotionDirection.RIGHT);
|
||||
|
||||
if (idx < 0 || idx >= screen.n_workspaces)
|
||||
if (neighbor == null)
|
||||
return;
|
||||
|
||||
screen.get_workspace_by_index (idx).activate (display.get_current_time ());
|
||||
if (workspaces.x != 0)
|
||||
workspace = idx;
|
||||
neighbor.activate (display.get_current_time ());
|
||||
}
|
||||
|
||||
public override bool leave_event (Clutter.CrossingEvent event) {
|
||||
if (!contains (event.related))
|
||||
hide ();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool key_press_event (Clutter.KeyEvent event)
|
||||
@ -245,7 +220,21 @@ namespace Gala
|
||||
return false;
|
||||
}
|
||||
|
||||
//FIXME move all this positioning stuff to a separate function which is only called by screen size changes
|
||||
const float scroll_speed = 30.0f;
|
||||
public override bool scroll_event (Clutter.ScrollEvent event)
|
||||
{
|
||||
if ((event.direction == Clutter.ScrollDirection.DOWN || event.direction == Clutter.ScrollDirection.RIGHT)
|
||||
&& thumbnails.width + thumbnails.x > width) { //left
|
||||
thumbnails.x -= scroll_speed;
|
||||
} else if ((event.direction == Clutter.ScrollDirection.UP || event.direction == Clutter.ScrollDirection.LEFT)
|
||||
&& thumbnails.x < 0) { //right
|
||||
thumbnails.x += scroll_speed;
|
||||
}
|
||||
scroll.x = Math.floorf (width / thumbnails.width * thumbnails.x);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public new void show ()
|
||||
{
|
||||
if (visible)
|
||||
@ -256,92 +245,34 @@ namespace Gala
|
||||
Utils.set_input_area (screen, Utils.InputArea.FULLSCREEN);
|
||||
plugin.begin_modal ();
|
||||
|
||||
animating = true;
|
||||
|
||||
var area = screen.get_monitor_geometry (screen.get_primary_monitor ());
|
||||
|
||||
y = area.height;
|
||||
width = area.width;
|
||||
|
||||
/*get the workspaces together*/
|
||||
workspaces.remove_all_children ();
|
||||
thumbnails.get_children ().foreach ((thumb) => {
|
||||
thumb.show ();
|
||||
});
|
||||
|
||||
for (var i = 0; i < screen.n_workspaces; i++) {
|
||||
var space = screen.get_workspace_by_index (i);
|
||||
|
||||
var group = new Clutter.Actor ();
|
||||
var icons = new Clutter.Actor ();
|
||||
icons.set_layout_manager (new Clutter.BoxLayout ());
|
||||
var backg = new Clutter.Clone (workspace_thumb);
|
||||
|
||||
var shown_applications = new List<Bamf.Application> ();
|
||||
|
||||
space.list_windows ().foreach ((w) => {
|
||||
if (w.window_type != Meta.WindowType.NORMAL || w.minimized)
|
||||
return;
|
||||
|
||||
var app = Bamf.Matcher.get_default ().get_application_for_xid ((uint32)w.get_xwindow ());
|
||||
if (shown_applications.index (app) != -1)
|
||||
return;
|
||||
|
||||
if (app != null)
|
||||
shown_applications.append (app);
|
||||
|
||||
var pix = Utils.get_icon_for_window (w, 32);
|
||||
var icon = new GtkClutter.Texture ();
|
||||
try {
|
||||
icon.set_from_pixbuf (pix);
|
||||
} catch (Error e) { warning (e.message); }
|
||||
|
||||
icon.reactive = true;
|
||||
icon.button_release_event.connect ( () => {
|
||||
space.activate_with_focus (w, screen.get_display ().get_current_time ());
|
||||
hide ();
|
||||
return false;
|
||||
});
|
||||
|
||||
icons.add_child (icon);
|
||||
});
|
||||
|
||||
group.add_child (backg);
|
||||
group.add_child (icons);
|
||||
|
||||
icons.y = Math.floorf (backg.height - 16);
|
||||
icons.x = Math.floorf (group.width / 2 - icons.width / 2);
|
||||
(icons.layout_manager as Clutter.BoxLayout).spacing = 6;
|
||||
|
||||
group.height = 160;
|
||||
|
||||
group.reactive = true;
|
||||
group.button_release_event.connect (() => {
|
||||
space.activate (plugin.get_screen ().get_display ().get_current_time ());
|
||||
workspace = plugin.get_screen ().get_active_workspace ().index ();
|
||||
hide ();
|
||||
return true;
|
||||
});
|
||||
|
||||
workspaces.add_child (group);
|
||||
thumbnails.x = width / 2 - thumbnails.width / 2;
|
||||
thumbnails.y = 15;
|
||||
|
||||
scroll.visible = thumbnails.width > width;
|
||||
if (scroll.visible) {
|
||||
scroll.y = height - 12;
|
||||
scroll.x = 0.0f;
|
||||
scroll.width = width / thumbnails.width * width;
|
||||
thumbnails.x = 4.0f;
|
||||
}
|
||||
|
||||
var new_idx = screen.get_active_workspace ().index ();
|
||||
|
||||
bool recalc = current_workspace.x == 0;
|
||||
workspaces.x = width / 2 - workspaces.width / 2;
|
||||
workspaces.y = 25;
|
||||
|
||||
current_workspace.y = workspaces.y - 5;
|
||||
animating = true;
|
||||
Timeout.add (50, () => {
|
||||
animating = false;
|
||||
return false;
|
||||
}); //catch hot corner hiding problem and indicator placement
|
||||
|
||||
visible = true;
|
||||
grab_key_focus ();
|
||||
Timeout.add (50, () => animating = false ); //catch hot corner hiding problem
|
||||
animate (Clutter.AnimationMode.EASE_OUT_QUAD, 250, y : area.height - height, opacity : 255)
|
||||
.completed.connect (() => {
|
||||
});
|
||||
|
||||
if (recalc)
|
||||
current_workspace.x = width / 2 - workspaces.width / 2 + (workspaces.get_children ().nth_data (0).width+12)*new_idx - 5;
|
||||
else
|
||||
workspace = new_idx;
|
||||
animate (Clutter.AnimationMode.EASE_OUT_QUAD, 250, y : area.height - height, opacity : 255);
|
||||
}
|
||||
|
||||
public new void hide ()
|
||||
@ -355,8 +286,10 @@ namespace Gala
|
||||
plugin.end_modal ();
|
||||
plugin.update_input_area ();
|
||||
|
||||
animate (Clutter.AnimationMode.EASE_OUT_QUAD, 500, y : height)
|
||||
.completed.connect ( () => {
|
||||
animate (Clutter.AnimationMode.EASE_OUT_QUAD, 500, y : height).completed.connect (() => {
|
||||
thumbnails.get_children ().foreach ((thumb) => {
|
||||
thumb.hide ();
|
||||
});
|
||||
visible = false;
|
||||
});
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user