mirror of
https://github.com/elementary/gala.git
synced 2024-10-26 07:51:41 +03:00
add first implementation of multitaskingview to replace workspaceview
This commit is contained in:
parent
913721161f
commit
af8f3608dc
@ -95,6 +95,9 @@ vala_precompile(VALA_C
|
||||
src/Widgets/WindowThumb.vala
|
||||
src/Widgets/WorkspaceThumb.vala
|
||||
src/Widgets/WorkspaceView.vala
|
||||
src/Widgets/MultitaskingView/IconGroup.vala
|
||||
src/Widgets/MultitaskingView/MultitaskingView.vala
|
||||
src/Widgets/MultitaskingView/Workspace.vala
|
||||
${CMAKE_BINARY_DIR}/src/Config.vala
|
||||
PACKAGES
|
||||
granite
|
||||
@ -108,6 +111,7 @@ PACKAGES
|
||||
xfixes-4.0
|
||||
OPTIONS
|
||||
-g
|
||||
--enable-deprecated
|
||||
--vapidir=${CMAKE_CURRENT_SOURCE_DIR}/vapi/
|
||||
${MUTTER36_FLAGS}
|
||||
${MUTTER38_FLAGS}
|
||||
|
BIN
data/texture.png
BIN
data/texture.png
Binary file not shown.
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 7.6 KiB |
@ -45,6 +45,7 @@ namespace Gala
|
||||
WorkspaceView workspace_view;
|
||||
Zooming zooming;
|
||||
WindowOverview window_overview;
|
||||
MultitaskingView multitasking_view;
|
||||
|
||||
// used to detect which corner was used to trigger an action
|
||||
Clutter.Actor? last_hotcorner;
|
||||
@ -52,6 +53,8 @@ namespace Gala
|
||||
|
||||
#if HAS_MUTTER38
|
||||
public Meta.BackgroundGroup background_group { get; private set; }
|
||||
public Clutter.Actor window_group { get; private set; }
|
||||
public Clutter.Actor top_window_group { get; private set; }
|
||||
public Clutter.Actor ui_group { get; private set; }
|
||||
#endif
|
||||
|
||||
@ -131,7 +134,7 @@ namespace Gala
|
||||
ui_group.reactive = true;
|
||||
stage.add_child (ui_group);
|
||||
|
||||
var window_group = Compositor.get_window_group_for_screen (screen);
|
||||
window_group = Compositor.get_window_group_for_screen (screen);
|
||||
stage.remove_child (window_group);
|
||||
ui_group.add_child (window_group);
|
||||
|
||||
@ -143,6 +146,8 @@ namespace Gala
|
||||
workspace_view = new WorkspaceView (this);
|
||||
workspace_view.visible = false;
|
||||
|
||||
multitasking_view = new MultitaskingView (this);
|
||||
|
||||
winswitcher = new WindowSwitcher (this);
|
||||
|
||||
zooming = new Zooming (this);
|
||||
@ -152,8 +157,9 @@ namespace Gala
|
||||
ui_group.add_child (workspace_view);
|
||||
ui_group.add_child (winswitcher);
|
||||
ui_group.add_child (window_overview);
|
||||
ui_group.add_child (multitasking_view);
|
||||
|
||||
var top_window_group = Compositor.get_top_window_group_for_screen (screen);
|
||||
top_window_group = Compositor.get_top_window_group_for_screen (screen);
|
||||
stage.remove_child (top_window_group);
|
||||
ui_group.add_child (top_window_group);
|
||||
#else
|
||||
@ -223,7 +229,8 @@ namespace Gala
|
||||
});
|
||||
|
||||
KeyBinding.set_custom_handler ("show-desktop", () => {
|
||||
workspace_view.show (true);
|
||||
//workspace_view.show (true);
|
||||
multitasking_view.toggle ();
|
||||
});
|
||||
|
||||
#if HAS_MUTTER38
|
||||
|
160
src/Widgets/MultitaskingView/IconGroup.vala
Normal file
160
src/Widgets/MultitaskingView/IconGroup.vala
Normal file
@ -0,0 +1,160 @@
|
||||
using Clutter;
|
||||
|
||||
namespace Gala
|
||||
{
|
||||
public class IconGroup : Actor
|
||||
{
|
||||
List<Meta.Window> windows;
|
||||
|
||||
const int SIZE = 64;
|
||||
|
||||
static const int PLUS_SIZE = 8;
|
||||
static const int PLUS_WIDTH = 24;
|
||||
|
||||
public signal void selected ();
|
||||
|
||||
public IconGroup ()
|
||||
{
|
||||
clear ();
|
||||
|
||||
width = SIZE;
|
||||
height = SIZE;
|
||||
reactive = true;
|
||||
|
||||
var canvas = new Canvas ();
|
||||
canvas.set_size (SIZE, SIZE);
|
||||
canvas.draw.connect (draw);
|
||||
content = canvas;
|
||||
}
|
||||
|
||||
public override bool button_release_event (ButtonEvent event)
|
||||
{
|
||||
selected ();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void clear ()
|
||||
{
|
||||
windows = new List<Meta.Window> ();
|
||||
}
|
||||
|
||||
public void add_window (Meta.Window window, bool no_redraw = false)
|
||||
{
|
||||
windows.append (window);
|
||||
|
||||
if (!no_redraw)
|
||||
redraw ();
|
||||
}
|
||||
|
||||
public void redraw ()
|
||||
{
|
||||
content.invalidate ();
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (n_windows == 1) {
|
||||
var pix = Utils.get_icon_for_window (windows.nth_data (0), 64);
|
||||
Gdk.cairo_set_source_pixbuf (cr, pix, 0, 0);
|
||||
cr.paint ();
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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);
|
||||
cr.fill_preserve ();
|
||||
|
||||
cr.set_line_width (1);
|
||||
|
||||
var grad = new Cairo.Pattern.linear (0, 0, 0, height);
|
||||
grad.add_color_stop_rgba (0.8, 0, 0, 0, 0);
|
||||
grad.add_color_stop_rgba (1.0, 1, 1, 1, 0.1);
|
||||
|
||||
cr.set_source (grad);
|
||||
cr.stroke ();
|
||||
|
||||
Granite.Drawing.Utilities.cairo_rounded_rectangle (cr, 1.5, 1.5, (int)width - 3, (int)height - 3, 5);
|
||||
|
||||
cr.set_source_rgba (0, 0, 0, 0.3);
|
||||
cr.stroke ();
|
||||
|
||||
// the only workspace that can be empty is the last one, so we draw our
|
||||
// plus here
|
||||
if (n_windows < 1) {
|
||||
var buffer = new Granite.Drawing.BufferSurface (SIZE, SIZE);
|
||||
var offset = SIZE / 2 - PLUS_WIDTH / 2;
|
||||
|
||||
buffer.context.rectangle (PLUS_WIDTH / 2 - PLUS_SIZE / 2 + 0.5 + offset,
|
||||
0.5 + offset,
|
||||
PLUS_SIZE - 1,
|
||||
PLUS_WIDTH - 1);
|
||||
|
||||
buffer.context.rectangle (0.5 + offset,
|
||||
PLUS_WIDTH / 2 - PLUS_SIZE / 2 + 0.5 + offset,
|
||||
PLUS_WIDTH - 1,
|
||||
PLUS_SIZE - 1);
|
||||
|
||||
buffer.context.set_source_rgb (0, 0, 0);
|
||||
buffer.context.fill_preserve ();
|
||||
buffer.exponential_blur (5);
|
||||
|
||||
buffer.context.set_source_rgb (1, 1, 1);
|
||||
buffer.context.set_line_width (1);
|
||||
buffer.context.stroke_preserve ();
|
||||
|
||||
buffer.context.set_source_rgb (0.8, 0.8, 0.8);
|
||||
buffer.context.fill ();
|
||||
|
||||
cr.set_source_surface (buffer.surface, 0, 0);
|
||||
cr.paint ();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int size;
|
||||
if (n_windows < 5)
|
||||
size = 22;
|
||||
else
|
||||
size = 16;
|
||||
|
||||
var columns = (int)Math.ceil (Math.sqrt (n_windows));
|
||||
var rows = (int)Math.ceil (n_windows / (double)columns);
|
||||
|
||||
const int spacing = 6;
|
||||
|
||||
var width = columns * size + (columns - 1) * spacing;
|
||||
var height = rows * size + (rows - 1) * spacing;
|
||||
var x_offset = SIZE / 2 - width / 2;
|
||||
var y_offset = SIZE / 2 - height / 2;
|
||||
|
||||
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);
|
||||
|
||||
Gdk.cairo_set_source_pixbuf (cr, pix, x, y);
|
||||
|
||||
x += size + spacing;
|
||||
if (x + size >= SIZE) {
|
||||
x = x_offset;
|
||||
y += size + spacing;
|
||||
}
|
||||
|
||||
cr.paint ();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
237
src/Widgets/MultitaskingView/MultitaskingView.vala
Normal file
237
src/Widgets/MultitaskingView/MultitaskingView.vala
Normal file
@ -0,0 +1,237 @@
|
||||
using Clutter;
|
||||
|
||||
namespace Gala
|
||||
{
|
||||
public class MultitaskingView : Actor
|
||||
{
|
||||
public Meta.Screen screen { get; construct set; }
|
||||
public Plugin plugin { get; construct set; }
|
||||
|
||||
Actor icon_groups;
|
||||
Actor workspaces;
|
||||
|
||||
public bool opened { get; private set; default = false; }
|
||||
|
||||
const int HIDING_DURATION = 300;
|
||||
|
||||
public MultitaskingView (Plugin plugin)
|
||||
{
|
||||
Object (plugin: plugin, screen: plugin.get_screen ());
|
||||
|
||||
visible = false;
|
||||
reactive = true;
|
||||
|
||||
workspaces = new Actor ();
|
||||
|
||||
icon_groups = new Actor ();
|
||||
icon_groups.layout_manager = new BoxLayout ();
|
||||
(icon_groups.layout_manager as BoxLayout).spacing = 48;
|
||||
|
||||
add_child (icon_groups);
|
||||
add_child (workspaces);
|
||||
|
||||
foreach (var workspace in screen.get_workspaces ())
|
||||
add_workspace (workspace.index ());
|
||||
|
||||
screen.workspace_added.connect (add_workspace);
|
||||
screen.workspace_removed.connect (remove_workspace);
|
||||
screen.workspace_switched.connect ((from, to, direction) => {
|
||||
update_positions (opened);
|
||||
});
|
||||
}
|
||||
|
||||
public override bool scroll_event (ScrollEvent event)
|
||||
{
|
||||
var active_workspace = screen.get_active_workspace ();
|
||||
var new_workspace = active_workspace.get_neighbor (
|
||||
event.direction == ScrollDirection.LEFT ||
|
||||
event.direction == ScrollDirection.UP ?
|
||||
Meta.MotionDirection.LEFT : Meta.MotionDirection.RIGHT);
|
||||
if (active_workspace != new_workspace)
|
||||
new_workspace.activate (screen.get_display ().get_current_time ());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void update_positions (bool animate = false, bool closing = false)
|
||||
{
|
||||
float x = 0;
|
||||
WorkspaceClone? active = null;
|
||||
var active_index = screen.get_active_workspace ().index ();;
|
||||
|
||||
foreach (var child in workspaces.get_children ()) {
|
||||
var workspace_clone = child as WorkspaceClone;
|
||||
var index = workspace_clone.workspace.index ();
|
||||
|
||||
if (index == active_index)
|
||||
active = workspace_clone;
|
||||
|
||||
workspace_clone.x = index * (workspace_clone.width - 150);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void add_workspace (int num)
|
||||
{
|
||||
var workspace = new WorkspaceClone (screen.get_workspace_by_index (num));
|
||||
workspace.window_selected.connect (window_selected);
|
||||
workspace.selected.connect (activate_workspace);
|
||||
|
||||
workspaces.insert_child_at_index (workspace, num);
|
||||
icon_groups.insert_child_at_index (workspace.icon_group, num);
|
||||
|
||||
update_positions ();
|
||||
}
|
||||
|
||||
void remove_workspace (int num)
|
||||
{
|
||||
WorkspaceClone? workspace = null;
|
||||
|
||||
// FIXME is there a better way to get the removed workspace?
|
||||
unowned List<Meta.Workspace> existing_workspaces = screen.get_workspaces ();
|
||||
|
||||
foreach (var child in workspaces.get_children ()) {
|
||||
var clone = child as WorkspaceClone;
|
||||
|
||||
if (existing_workspaces.index (clone.workspace) < 0) {
|
||||
workspace = clone;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (workspace == null)
|
||||
return;
|
||||
|
||||
workspace.window_selected.disconnect (window_selected);
|
||||
workspace.selected.disconnect (activate_workspace);
|
||||
workspace.icon_group.destroy ();
|
||||
workspace.destroy ();
|
||||
|
||||
update_positions ();
|
||||
}
|
||||
|
||||
void activate_workspace (WorkspaceClone clone, bool close_view)
|
||||
{
|
||||
close_view = close_view && screen.get_active_workspace () == clone.workspace;
|
||||
|
||||
clone.workspace.activate (screen.get_display ().get_current_time ());
|
||||
|
||||
if (close_view)
|
||||
toggle ();
|
||||
}
|
||||
|
||||
public override bool key_press_event (Clutter.KeyEvent event)
|
||||
{
|
||||
if (event.keyval == Clutter.Key.Escape)
|
||||
toggle ();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void window_selected (Meta.Window window)
|
||||
{
|
||||
window.activate (screen.get_display ().get_current_time ());
|
||||
toggle ();
|
||||
}
|
||||
|
||||
public void toggle ()
|
||||
{
|
||||
opened = !opened;
|
||||
|
||||
var opening = opened;
|
||||
|
||||
unowned List<Meta.WindowActor> windows = Meta.Compositor.get_window_actors (screen);
|
||||
var primary_monitor = screen.get_primary_monitor ();
|
||||
var monitor = screen.get_monitor_geometry (primary_monitor);
|
||||
|
||||
if (opening) {
|
||||
plugin.begin_modal ();
|
||||
|
||||
plugin.background_group.hide ();
|
||||
plugin.window_group.hide ();
|
||||
plugin.top_window_group.hide ();
|
||||
show ();
|
||||
grab_key_focus ();
|
||||
|
||||
icon_groups.x = monitor.width / 2 - icon_groups.width / 2;
|
||||
icon_groups.y = monitor.height - WorkspaceClone.BOTTOM_OFFSET + 20;
|
||||
}
|
||||
|
||||
// find active workspace clone and raise it, so there are no overlaps while transitioning
|
||||
WorkspaceClone? active_workspace = null;
|
||||
var active = screen.get_active_workspace ();
|
||||
foreach (var child in workspaces.get_children ()) {
|
||||
var workspace = child as WorkspaceClone;
|
||||
if (workspace.workspace == active) {
|
||||
active_workspace = workspace;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (active_workspace != null)
|
||||
workspaces.set_child_above_sibling (active_workspace, null);
|
||||
|
||||
update_positions (false);
|
||||
|
||||
foreach (var child in workspaces.get_children ()) {
|
||||
if (opening)
|
||||
(child as WorkspaceClone).open ();
|
||||
else
|
||||
(child as WorkspaceClone).close ();
|
||||
}
|
||||
|
||||
if (!opening) {
|
||||
Timeout.add (290, () => {
|
||||
hide ();
|
||||
|
||||
plugin.background_group.show ();
|
||||
plugin.window_group.show ();
|
||||
plugin.top_window_group.show ();
|
||||
|
||||
plugin.end_modal ();
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* three types of animation for docks:
|
||||
* - window appears to be at the bottom --> slide up
|
||||
* - window appears to be at the top --> slide down
|
||||
* - window appears to be somewhere else --> fade out
|
||||
var rect = meta_window.get_outer_rect ();
|
||||
|
||||
if (about_same (rect.y, monitor_geometry.y)) {
|
||||
|
||||
float dest = opening ? monitor_geometry.y - rect.height : rect.y;
|
||||
window.animate (AnimationMode.EASE_OUT_QUAD, HIDING_DURATION, y: dest);
|
||||
|
||||
} else if (about_same (rect.y + rect.height,
|
||||
monitor_geometry.y + monitor_geometry.height)) {
|
||||
|
||||
float dest = opening ? monitor_geometry.y + monitor_geometry.height : rect.y;
|
||||
window.animate (AnimationMode.EASE_OUT_QUAD, HIDING_DURATION, y: dest);
|
||||
|
||||
} else {
|
||||
uint dest = opening ? 0 : 255;
|
||||
window.animate (AnimationMode.LINEAR, HIDING_DURATION, opacity: dest);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
return Math.fabsf (val1 - val2) <= threshold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
220
src/Widgets/MultitaskingView/Workspace.vala
Normal file
220
src/Widgets/MultitaskingView/Workspace.vala
Normal file
@ -0,0 +1,220 @@
|
||||
using Clutter;
|
||||
|
||||
namespace Gala
|
||||
{
|
||||
class FramedBackground : BackgroundManager
|
||||
{
|
||||
public FramedBackground (Meta.Screen screen)
|
||||
{
|
||||
base (screen);
|
||||
|
||||
add_effect (new BackgroundShadowEffect (screen));
|
||||
}
|
||||
|
||||
public override void paint ()
|
||||
{
|
||||
base.paint ();
|
||||
|
||||
Cogl.set_source_color4ub (0, 0, 0, 100);
|
||||
Cogl.Path.rectangle (0, 0, width, height);
|
||||
Cogl.Path.stroke ();
|
||||
|
||||
Cogl.set_source_color4ub (255, 255, 255, 80);
|
||||
Cogl.Path.rectangle (1, 1, width - 2, height - 2);
|
||||
Cogl.Path.stroke ();
|
||||
}
|
||||
}
|
||||
|
||||
class BackgroundShadowEffect : Effect
|
||||
{
|
||||
static Meta.Screen screen;
|
||||
static Cogl.Texture? bitmap;
|
||||
|
||||
const int SHADOW_SIZE = 40;
|
||||
const int SHADOW_OFFSET = 5;
|
||||
|
||||
static int width;
|
||||
static int height;
|
||||
|
||||
public BackgroundShadowEffect (Meta.Screen _screen)
|
||||
{
|
||||
if (bitmap == null) {
|
||||
screen = _screen;
|
||||
|
||||
int screen_width, screen_height;
|
||||
screen.get_size (out screen_width, out screen_height);
|
||||
|
||||
width = screen_width + SHADOW_SIZE * 2;
|
||||
height = screen_height + SHADOW_SIZE * 2;
|
||||
|
||||
var buffer = new Granite.Drawing.BufferSurface (width, height);
|
||||
buffer.context.rectangle (SHADOW_SIZE - SHADOW_OFFSET, SHADOW_SIZE - SHADOW_OFFSET,
|
||||
screen_width + SHADOW_OFFSET * 2, screen_height + SHADOW_OFFSET * 2);
|
||||
buffer.context.set_source_rgba (0, 0, 0, 0.5);
|
||||
buffer.context.fill ();
|
||||
|
||||
buffer.exponential_blur (20);
|
||||
|
||||
var surface = new Cairo.ImageSurface (Cairo.Format.ARGB32, width, height);
|
||||
var cr = new Cairo.Context (surface);
|
||||
|
||||
cr.set_source_surface (buffer.surface, 0, 0);
|
||||
cr.paint ();
|
||||
|
||||
bitmap = new Cogl.Texture.from_data (width, height, 0, Cogl.PixelFormat.BGRA_8888_PRE,
|
||||
Cogl.PixelFormat.ANY, surface.get_stride (), surface.get_data ());
|
||||
}
|
||||
}
|
||||
|
||||
public override void paint (EffectPaintFlags flags)
|
||||
{
|
||||
Cogl.set_source_texture (bitmap);
|
||||
Cogl.rectangle (-SHADOW_SIZE, -SHADOW_SIZE, width - SHADOW_SIZE, height - SHADOW_SIZE);
|
||||
|
||||
actor.continue_paint ();
|
||||
}
|
||||
}
|
||||
|
||||
public class WorkspaceClone : Clutter.Actor
|
||||
{
|
||||
public Meta.Workspace workspace { get; construct set; }
|
||||
public BackgroundManager background { get; private set; }
|
||||
public IconGroup icon_group { get; private set; }
|
||||
|
||||
public signal void window_selected (Meta.Window window);
|
||||
public signal void selected (bool close_view);
|
||||
|
||||
const int TOP_OFFSET = 20;
|
||||
public static const int BOTTOM_OFFSET = 100;
|
||||
|
||||
public WorkspaceClone (Meta.Workspace workspace)
|
||||
{
|
||||
Object (workspace: workspace);
|
||||
|
||||
background = new FramedBackground (workspace.get_screen ());
|
||||
background.reactive = true;
|
||||
background.button_press_event.connect (() => {
|
||||
selected (true);
|
||||
return false;
|
||||
});
|
||||
|
||||
icon_group = new IconGroup ();
|
||||
icon_group.selected.connect (() => {
|
||||
selected (false);
|
||||
});
|
||||
|
||||
var screen = workspace.get_screen ();
|
||||
screen.window_left_monitor.connect ((monitor, window) => {
|
||||
if (monitor == screen.get_primary_monitor ())
|
||||
remove_window (window);
|
||||
});
|
||||
workspace.window_removed.connect (remove_window);
|
||||
|
||||
screen.window_entered_monitor.connect ((monitor, window) => {
|
||||
if (monitor == screen.get_primary_monitor ())
|
||||
add_window (window);
|
||||
});
|
||||
workspace.window_added.connect (add_window);
|
||||
|
||||
add_child (background);
|
||||
}
|
||||
|
||||
private void add_window (Meta.Window window)
|
||||
{
|
||||
}
|
||||
|
||||
private void remove_window (Meta.Window window)
|
||||
{
|
||||
var window_actor = window.get_compositor_private ();
|
||||
|
||||
foreach (var child in get_children ()) {
|
||||
if (child is Clone && (child as Clone).source == window_actor) {
|
||||
child.destroy ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void shrink_rectangle (ref Meta.Rectangle rect, int amount)
|
||||
{
|
||||
rect.x += amount;
|
||||
rect.y += amount;
|
||||
rect.width -= amount * 2;
|
||||
rect.height -= amount * 2;
|
||||
}
|
||||
|
||||
public void open ()
|
||||
{
|
||||
var screen = workspace.get_screen ();
|
||||
var display = screen.get_display ();
|
||||
|
||||
var monitor = screen.get_monitor_geometry (screen.get_primary_monitor ());
|
||||
var scale = (float)(monitor.height - TOP_OFFSET - BOTTOM_OFFSET) / monitor.height;
|
||||
var pivotY = TOP_OFFSET / (monitor.height - monitor.height * scale);
|
||||
background.set_pivot_point (0.5f, pivotY);
|
||||
background.animate (AnimationMode.EASE_OUT_QUAD, 250, scale_x: scale, scale_y: scale);
|
||||
|
||||
Meta.Rectangle area = {
|
||||
(int)Math.floorf (monitor.x + monitor.width - monitor.width * scale) / 2,
|
||||
(int)Math.floorf (monitor.y + TOP_OFFSET),
|
||||
(int)Math.floorf (monitor.width * scale),
|
||||
(int)Math.floorf (monitor.height * scale)
|
||||
};
|
||||
shrink_rectangle (ref area, 32);
|
||||
|
||||
icon_group.clear ();
|
||||
|
||||
var unsorted_windows = workspace.list_windows ();
|
||||
var used_windows = new SList<Meta.Window> ();
|
||||
foreach (var window in unsorted_windows) {
|
||||
if (window.window_type == Meta.WindowType.NORMAL) {
|
||||
used_windows.append (window);
|
||||
icon_group.add_window (window, true);
|
||||
}
|
||||
}
|
||||
icon_group.redraw ();
|
||||
|
||||
var windows = display.sort_windows_by_stacking (used_windows);
|
||||
var clones = new List<WindowThumb> ();
|
||||
|
||||
foreach (var window in windows) {
|
||||
var window_actor = window.get_compositor_private () as Meta.WindowActor;
|
||||
|
||||
var clone = new WindowThumb (window, false);
|
||||
clone.selected.connect (() => {
|
||||
window_selected (clone.window);
|
||||
});
|
||||
clone.set_position (window_actor.x, window_actor.y);
|
||||
|
||||
add_child (clone);
|
||||
clones.append (clone);
|
||||
}
|
||||
|
||||
if (clones.length () > 0)
|
||||
WindowOverview.grid_placement (area, clones, place_window);
|
||||
}
|
||||
|
||||
void place_window (Actor window, Meta.Rectangle rect)
|
||||
{
|
||||
window.animate (AnimationMode.EASE_OUT_CUBIC, 250,
|
||||
x: rect.x + 0.0f, y: rect.y + 0.0f, width: rect.width + 0.0f, height: rect.height + 0.0f);
|
||||
(window as WindowThumb).place_children (rect.width, rect.height);
|
||||
}
|
||||
|
||||
public void close ()
|
||||
{
|
||||
background.animate (AnimationMode.EASE_IN_OUT_CUBIC, 300, scale_x: 1.0f, scale_y: 1.0f);
|
||||
|
||||
foreach (var child in get_children ()) {
|
||||
if (child is WindowThumb)
|
||||
(child as WindowThumb).close (true, false);
|
||||
}
|
||||
}
|
||||
|
||||
~Workspace ()
|
||||
{
|
||||
background.destroy ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,8 @@ namespace Gala
|
||||
NATURAL
|
||||
}
|
||||
|
||||
public delegate void WindowPlacer (Actor window, Meta.Rectangle rect);
|
||||
|
||||
public class WindowOverview : Actor
|
||||
{
|
||||
Plugin plugin;
|
||||
@ -93,7 +95,7 @@ namespace Gala
|
||||
const int BOTTOM_GAP = 100;
|
||||
|
||||
//some math utilities
|
||||
int squared_distance (Gdk.Point a, Gdk.Point b)
|
||||
static int squared_distance (Gdk.Point a, Gdk.Point b)
|
||||
{
|
||||
var k1 = b.x - a.x;
|
||||
var k2 = b.y - a.y;
|
||||
@ -101,7 +103,7 @@ namespace Gala
|
||||
return k1*k1 + k2*k2;
|
||||
}
|
||||
|
||||
bool rect_is_overlapping_any (Meta.Rectangle rect, Meta.Rectangle[] rects, Meta.Rectangle border)
|
||||
static bool rect_is_overlapping_any (Meta.Rectangle rect, Meta.Rectangle[] rects, Meta.Rectangle border)
|
||||
{
|
||||
if (!border.contains_rect (rect))
|
||||
return true;
|
||||
@ -116,12 +118,12 @@ namespace Gala
|
||||
return false;
|
||||
}
|
||||
|
||||
Meta.Rectangle rect_adjusted (Meta.Rectangle rect, int dx1, int dy1, int dx2, int dy2)
|
||||
static Meta.Rectangle rect_adjusted (Meta.Rectangle rect, int dx1, int dy1, int dx2, int dy2)
|
||||
{
|
||||
return {rect.x + dx1, rect.y + dy1, rect.width + (-dx1 + dx2), rect.height + (-dy1 + dy2)};
|
||||
}
|
||||
|
||||
Gdk.Point rect_center (Meta.Rectangle rect)
|
||||
static Gdk.Point rect_center (Meta.Rectangle rect)
|
||||
{
|
||||
return {rect.x + rect.width / 2, rect.y + rect.height / 2};
|
||||
}
|
||||
@ -163,13 +165,13 @@ namespace Gala
|
||||
(int)Math.floorf (geom.height - BOTTOM_GAP)};
|
||||
|
||||
if (BehaviorSettings.get_default ().schema.get_enum ("window-overview-type") == WindowOverviewType.GRID)
|
||||
grid_placement (area, monitors[i]);
|
||||
grid_placement (area, monitors[i], place_window);
|
||||
else
|
||||
natural_placement (area, monitors[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void grid_placement (Meta.Rectangle area, List<Actor> clones)
|
||||
public static void grid_placement (Meta.Rectangle area, List<Actor> clones, WindowPlacer place)
|
||||
{
|
||||
int columns = (int)Math.ceil (Math.sqrt (clones.length ()));
|
||||
int rows = (int)Math.ceil (clones.length () / (double)columns);
|
||||
@ -277,7 +279,7 @@ namespace Gala
|
||||
if (left_over != columns && slot >= columns * (rows - 1))
|
||||
target.x += (columns - left_over) * slot_width / 2;
|
||||
|
||||
place_window (window, target);
|
||||
place (window, target);
|
||||
}
|
||||
}
|
||||
|
||||
@ -492,8 +494,10 @@ namespace Gala
|
||||
}
|
||||
|
||||
// animate a window to the given position
|
||||
void place_window (WindowThumb clone, Meta.Rectangle rect)
|
||||
void place_window (Actor actor, Meta.Rectangle rect)
|
||||
{
|
||||
var clone = actor as WindowThumb;
|
||||
|
||||
var fscale = rect.width / clone.width;
|
||||
|
||||
//animate the windows and icons to the calculated positions
|
||||
|
@ -32,7 +32,7 @@ namespace Gala
|
||||
public signal void selected (Window window);
|
||||
public signal void closed ();
|
||||
|
||||
public WindowThumb (Window _window)
|
||||
public WindowThumb (Window _window, bool add_children_to_stage = true)
|
||||
{
|
||||
window = _window;
|
||||
|
||||
@ -40,10 +40,12 @@ namespace Gala
|
||||
|
||||
var actor = window.get_compositor_private () as WindowActor;
|
||||
clone = new Clone (actor);
|
||||
clone.add_constraint (new BindConstraint (this, BindCoordinate.SIZE, 0));
|
||||
|
||||
icon = new GtkClutter.Texture ();
|
||||
icon.scale_x = 0.0f;
|
||||
icon.scale_y = 0.0f;
|
||||
icon.opacity = 0;
|
||||
icon.scale_gravity = Gravity.CENTER;
|
||||
|
||||
try {
|
||||
@ -65,9 +67,40 @@ namespace Gala
|
||||
|
||||
add_child (clone);
|
||||
|
||||
if (add_children_to_stage) {
|
||||
var stage = Compositor.get_stage_for_screen (window.get_screen ());
|
||||
stage.add_child (icon);
|
||||
stage.add_child (close_button);
|
||||
} else {
|
||||
add_child (close_button);
|
||||
add_child (icon);
|
||||
}
|
||||
}
|
||||
|
||||
public void place_children (int width, int height)
|
||||
{
|
||||
float offset_x, offset_y, offset_width;
|
||||
Utils.get_window_frame_offset (window, out offset_x, out offset_y, out offset_width, null);
|
||||
float button_offset = close_button.width * 0.25f;
|
||||
|
||||
float scale = width / (window.get_compositor_private () as WindowActor).width;
|
||||
|
||||
Granite.CloseButtonPosition pos;
|
||||
Granite.Widgets.Utils.get_default_close_button_position (out pos);
|
||||
switch (pos) {
|
||||
case Granite.CloseButtonPosition.LEFT:
|
||||
close_button.x = -offset_x * scale - button_offset;
|
||||
break;
|
||||
case Granite.CloseButtonPosition.RIGHT:
|
||||
close_button.x = width - offset_width * scale - close_button.width / 2;
|
||||
break;
|
||||
}
|
||||
close_button.y = -offset_y * scale - button_offset;
|
||||
|
||||
icon.x = Math.floorf (width / 2.0f - icon.width / 2.0f);
|
||||
icon.y = Math.floorf (height - 50.0f);
|
||||
|
||||
icon.animate (AnimationMode.EASE_OUT_CUBIC, 350, scale_x: 1.0f, scale_y: 1.0f, opacity: 255);
|
||||
}
|
||||
|
||||
bool close_button_clicked (ButtonEvent event)
|
||||
@ -155,15 +188,15 @@ namespace Gala
|
||||
return true;
|
||||
}
|
||||
|
||||
public void close (bool do_animate = true)
|
||||
public void close (bool do_animate = true, bool use_scale = true)
|
||||
{
|
||||
unowned Meta.Rectangle rect = window.get_outer_rect ();
|
||||
|
||||
//FIXME need to subtract 10 here to remove jump for most windows, but adds jump for maximized ones
|
||||
float delta = window.maximized_horizontally || window.maximized_vertically ? 0 : 10;
|
||||
float x, y, w, h;
|
||||
Utils.get_window_frame_offset (window, out x, out y, out w, out h);
|
||||
|
||||
float dest_x = rect.x - delta;
|
||||
float dest_y = rect.y - delta;
|
||||
float dest_x = rect.x + x;
|
||||
float dest_y = rect.y + y;
|
||||
|
||||
//stop all running animations
|
||||
detach_animation ();
|
||||
@ -177,7 +210,17 @@ namespace Gala
|
||||
icon.animate (AnimationMode.EASE_IN_CUBIC, 100, scale_x:0.0f, scale_y:0.0f);
|
||||
close_button.animate (AnimationMode.EASE_IN_QUAD, 200, scale_x : 0.0f, scale_y : 0.0f);
|
||||
|
||||
animate (AnimationMode.EASE_IN_OUT_CUBIC, 300, scale_x:1.0f, scale_y:1.0f, x:dest_x, y:dest_y).completed.connect (() => {
|
||||
Animation a;
|
||||
if (use_scale) {
|
||||
a = animate (AnimationMode.EASE_IN_OUT_CUBIC, 300, scale_x: 1.0f, scale_y: 1.0f,
|
||||
x: dest_x, y: dest_y);
|
||||
} else {
|
||||
var window = window.get_compositor_private () as WindowActor;
|
||||
a = animate (AnimationMode.EASE_IN_OUT_CUBIC, 300, width: window.width, height: window.height,
|
||||
x: dest_x, y: dest_y);
|
||||
}
|
||||
|
||||
a.completed.connect (() => {
|
||||
clone.source.show ();
|
||||
destroy ();
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user