Removed MultitaskingView folder, use get_frame_rect when available, use TiledWindow/Container for WindowOverview

This commit is contained in:
Tom Beckmann 2014-06-29 12:01:09 +02:00
parent c399e882d8
commit 74f471d029
10 changed files with 160 additions and 425 deletions

View File

@ -48,16 +48,15 @@ gala_VALASOURCES = \
Background/BackgroundManager.vala \
Background/SlideShow.vala \
Background/SystemBackground.vala \
Widgets/IconGroup.vala \
Widgets/IconGroupContainer.vala \
Widgets/MonitorClone.vala \
Widgets/MultitaskingView.vala \
Widgets/TiledWindow.vala \
Widgets/TiledWindowContainer.vala \
Widgets/WindowOverview.vala \
Widgets/WindowSwitcher.vala \
Widgets/WindowThumb.vala \
Widgets/MultitaskingView/IconGroup.vala \
Widgets/MultitaskingView/IconGroupContainer.vala \
Widgets/MultitaskingView/MonitorClone.vala \
Widgets/MultitaskingView/MultitaskingView.vala \
Widgets/MultitaskingView/TiledWindow.vala \
Widgets/MultitaskingView/TiledWindowContainer.vala \
Widgets/MultitaskingView/WorkspaceClone.vala \
Widgets/WorkspaceClone.vala \
$(NULL)
nodist_gala_SOURCES = \

View File

@ -69,6 +69,8 @@ namespace Gala
}
}
public bool overview_mode { get; construct; }
DragDropAction drag_action;
Clone? clone = null;
@ -81,9 +83,9 @@ namespace Gala
Actor active_shape;
GtkClutter.Texture window_icon;
public TiledWindow (Meta.Window window)
public TiledWindow (Meta.Window window, bool overview_mode = false)
{
Object (window: window);
Object (window: window, overview_mode: overview_mode);
}
construct
@ -92,23 +94,23 @@ namespace Gala
window.unmanaged.connect (unmanaged);
drag_action = new DragDropAction (DragDropActionType.SOURCE, "multitaskingview-window");
drag_action.drag_begin.connect (drag_begin);
drag_action.destination_crossed.connect (drag_destination_crossed);
drag_action.drag_end.connect (drag_end);
drag_action.drag_canceled.connect (drag_canceled);
drag_action.actor_clicked.connect ((button) => {
switch (button) {
case 1:
selected ();
break;
case 2:
close_window ();
break;
}
});
if (overview_mode) {
var click_action = new ClickAction ();
click_action.clicked.connect (() => {
actor_clicked (click_action.get_button ());
});
add_action (drag_action);
add_action (click_action);
} else {
drag_action = new DragDropAction (DragDropActionType.SOURCE, "multitaskingview-window");
drag_action.drag_begin.connect (drag_begin);
drag_action.destination_crossed.connect (drag_destination_crossed);
drag_action.drag_end.connect (drag_end);
drag_action.drag_canceled.connect (drag_canceled);
drag_action.actor_clicked.connect (actor_clicked);
add_action (drag_action);
}
close_button = Utils.create_close_button ();
close_button.opacity = 0;
@ -180,6 +182,9 @@ namespace Gala
return;
}
if (overview_mode)
actor.hide ();
clone = new Clone (actor.get_texture ());
add_child (clone);
@ -189,7 +194,11 @@ namespace Gala
transition_to_original_state (false);
#if HAS_MUTTER312
var outer_rect = window.get_frame_rect ();
#else
var outer_rect = window.get_outer_rect ();
#endif
add_effect_with_name ("shadow", new ShadowEffect (outer_rect.width, outer_rect.height, 40, 5));
#if HAS_MUTTER312
window.size_changed.connect (update_shadow_size);
@ -197,6 +206,9 @@ namespace Gala
actor.size_changed.connect (update_shadow_size);
#endif
if (should_fade ())
opacity = 0;
// if we were waiting the view was most probably already opened when our window
// finally got available. So we fade-in and make sure we took the took place.
// If the slot is not available however, the view was probably closed while this
@ -210,6 +222,16 @@ namespace Gala
}
}
/**
* If we are in overview mode, we may display windows from workspaces other than
* the current one. To ease their appearance we have to fade them in.
*/
bool should_fade ()
{
return overview_mode
&& window.get_workspace () != window.get_screen ().get_active_workspace ();
}
/**
* Sets a timeout of 500ms after which, if no new resize action reset it,
* the shadow will be resized and a request_reposition() will be emitted to
@ -221,7 +243,11 @@ namespace Gala
Source.remove (shadow_update_timeout);
shadow_update_timeout = Timeout.add (500, () => {
#if HAS_MUTTER312
var rect = window.get_frame_rect ();
#else
var rect = window.get_outer_rect ();
#endif
var effect = get_effect ("shadow") as ShadowEffect;
effect.update_size (rect.width, rect.height);
@ -241,13 +267,24 @@ namespace Gala
*/
public void transition_to_original_state (bool animate)
{
#if HAS_MUTTER312
var outer_rect = window.get_frame_rect ();
#else
var outer_rect = window.get_outer_rect ();
#endif
float offset_x = 0, offset_y = 0;
var parent = get_parent ();
if (parent != null)
parent.get_transformed_position (out offset_x, out offset_y);
if (parent != null) {
// in overview_mode the parent has just been added to the stage, so the
// transforme position is not set yet. However, the set position is correct
// for overview anyway, so we can just use that.
if (overview_mode)
parent.get_position (out offset_x, out offset_y);
else
parent.get_transformed_position (out offset_x, out offset_y);
}
set_easing_mode (AnimationMode.EASE_IN_OUT_CUBIC);
set_easing_duration (animate ? 300 : 0);
@ -256,6 +293,9 @@ namespace Gala
set_size (outer_rect.width, outer_rect.height);
window_icon.opacity = 0;
if (should_fade ())
opacity = 0;
}
/**
@ -272,6 +312,17 @@ namespace Gala
set_position (rect.x, rect.y);
window_icon.opacity = 255;
// for overview mode, windows may be faded out initially. Make sure
// to fade those in.
if (overview_mode) {
save_easing_state ();
set_easing_mode (AnimationMode.EASE_OUT_QUAD);
set_easing_duration (300);
opacity = 255;
restore_easing_state ();
}
}
/**
@ -305,7 +356,11 @@ namespace Gala
#else
var input_rect = window.get_input_rect ();
#endif
#if HAS_MUTTER312
var outer_rect = window.get_frame_rect ();
#else
var outer_rect = window.get_outer_rect ();
#endif
var scale_factor = (float)width / outer_rect.width;
var shadow_effect = get_effect ("shadow") as ShadowEffect;
@ -397,6 +452,17 @@ namespace Gala
destroy ();
}
void actor_clicked (uint32 button) {
switch (button) {
case 1:
selected ();
break;
case 2:
close_window ();
break;
}
}
/**
* A drag action has been initiated on us, we reparent ourselves to the stage so
* we can move freely, scale ourselves to a smaller scale and request that the

View File

@ -32,6 +32,8 @@ namespace Gala
public int padding_right { get; set; default = 12; }
public int padding_bottom { get; set; default = 12; }
public bool overview_mode { get; construct; }
bool opened;
/**
@ -40,8 +42,9 @@ namespace Gala
*/
TiledWindow? current_window;
public TiledWindowContainer ()
public TiledWindowContainer (bool overview_mode = false)
{
Object (overview_mode: overview_mode);
}
construct
@ -70,7 +73,7 @@ namespace Gala
var windows_ordered = display.sort_windows_by_stacking (windows);
var new_window = new TiledWindow (window);
var new_window = new TiledWindow (window, overview_mode);
new_window.selected.connect (window_selected_cb);
new_window.destroy.connect (window_destroyed);
@ -180,13 +183,25 @@ namespace Gala
var windows = new List<InternalUtils.TilableWindow?> ();
foreach (var child in get_children ()) {
unowned TiledWindow window = (TiledWindow) child;
#if HAS_MUTTER312
windows.prepend ({ window.window.get_frame_rect (), window });
#else
windows.prepend ({ window.window.get_outer_rect (), window });
#endif
}
windows.reverse ();
if (windows.length () < 1)
return;
// make sure the windows are always in the same order so the algorithm
// doesn't give us different slots based on stacking order, which can lead
// to windows flying around weirdly
windows.sort ((a, b) => {
var seq_a = ((TiledWindow) a.id).window.get_stable_sequence ();
var seq_b = ((TiledWindow) b.id).window.get_stable_sequence ();
return (int) (seq_b - seq_a);
});
Meta.Rectangle area = {
padding_left,
padding_top,

View File

@ -40,17 +40,16 @@ namespace Gala
bool ready;
//the workspaces which we expose right now
// the workspaces which we expose right now
List<Workspace> workspaces;
static const int PADDING = 50;
public WindowOverview (WindowManager _wm)
{
wm = _wm;
screen = wm.get_screen ();
screen.workspace_switched.connect (() => close (false));
screen.restacked.connect (restack_windows);
visible = false;
ready = true;
@ -59,11 +58,7 @@ namespace Gala
public override bool key_press_event (Clutter.KeyEvent event)
{
//FIXME need to figure out the actual keycombo, for now leave it by
// default and others will close it by selecting a window!
if (event.keyval == Clutter.Key.w ||
event.keyval == Clutter.Key.a ||
event.keyval == Clutter.Key.Escape) {
if (event.keyval == Clutter.Key.Escape) {
close (true);
return true;
@ -85,86 +80,6 @@ namespace Gala
return true;
}
void calculate_places (List<Actor> windows)
{
var clones = windows.copy ();
clones.sort ((a, b) => {
return (int)(a as WindowThumb).window.get_stable_sequence () -
(int)(b as WindowThumb).window.get_stable_sequence ();
});
// sort windows by monitor
List<InternalUtils.TilableWindow?>[] monitors = {};
monitors.resize (screen.get_n_monitors ());
foreach (var clone in clones) {
// we had some crashes here so there's a reasonable suspicion
// that get_monitor() could be larger than get_n_monitors()
var thumb = clone as WindowThumb;
var index = thumb.window.get_monitor ();
if (index >= screen.get_n_monitors ()) {
critical ("Window '%s' has a monitor assigned that does not actually exists",
(clone as WindowThumb).window.get_title ());
index = screen.get_n_monitors () - 1;
}
monitors[index].append ({ thumb.window.get_outer_rect (), thumb });
}
for (var i = 0; i < screen.get_n_monitors (); i++) {
if (monitors[i].length () == 0)
continue;
// get the area used by the expo algorithms together
var geom = screen.get_monitor_geometry (i);
Meta.Rectangle area = {(int)Math.floorf (geom.x + BORDER),
(int)Math.floorf (geom.y + TOP_GAP),
(int)Math.floorf (geom.width - BORDER * 2),
(int)Math.floorf (geom.height - BOTTOM_GAP)};
/*TODO if (BehaviorSettings.get_default ().schema.get_enum ("window-overview-type") == WindowOverviewType.GRID)
grid_placement (area, monitors[i], place_window);
else
natural_placement (area, monitors[i]);*/
var result = InternalUtils.calculate_grid_placement (area, monitors[i]);
foreach (var window in result) {
place_window ((WindowThumb)window.id, window.rect);
}
}
}
// animate a window to the given position
void place_window (WindowThumb clone, Meta.Rectangle rect)
{
var fscale = rect.width / clone.width;
//animate the windows and icons to the calculated positions
clone.icon.x = rect.x + Math.floorf (clone.width * fscale / 2.0f - clone.icon.width / 2.0f);
clone.icon.y = rect.y + Math.floorf (clone.height * fscale - 50.0f);
clone.icon.get_parent ().set_child_above_sibling (clone.icon, null);
float offset_x, offset_y, offset_width;
Utils.get_window_frame_offset (clone.window, out offset_x, out offset_y, out offset_width, null);
float button_offset = clone.close_button.width * 0.25f;
Granite.CloseButtonPosition pos;
Granite.Widgets.Utils.get_default_close_button_position (out pos);
switch (pos) {
case Granite.CloseButtonPosition.LEFT:
clone.close_button.x = rect.x - offset_x * fscale - button_offset;
break;
case Granite.CloseButtonPosition.RIGHT:
clone.close_button.x = rect.x + rect.width - offset_width * fscale - clone.close_button.width / 2;
break;
}
clone.close_button.y = rect.y - offset_y * fscale - button_offset;
clone.animate (Clutter.AnimationMode.EASE_OUT_CUBIC, 250, scale_x:fscale, scale_y:fscale, x:rect.x+0.0f, y:rect.y+0.0f)
.completed.connect (() => ready = true );
clone.icon.opacity = 0;
clone.icon.animate (Clutter.AnimationMode.EASE_OUT_CUBIC, 350, scale_x:1.0f, scale_y:1.0f, opacity:255);
}
public void open (bool animate = true, bool all_windows = false)
{
if (!ready)
@ -225,27 +140,41 @@ namespace Gala
grab_key_focus ();
wm.block_keybindings_in_modal = false;
wm.begin_modal ();
visible = true;
for (var i = 0; i < screen.get_n_monitors (); i++) {
var geometry = screen.get_monitor_geometry (i);
var container = new TiledWindowContainer (true);
container.set_position (geometry.x, geometry.y);
container.set_size (geometry.width, geometry.height);
container.window_selected.connect (thumb_selected);
add_child (container);
}
foreach (var window in windows) {
var actor = window.get_compositor_private () as WindowActor;
if (actor == null)
return;
actor.hide ();
var clone = new WindowThumb (window);
clone.x = actor.x;
clone.y = actor.y;
clone.selected.connect (thumb_selected);
clone.closed.connect (thumb_closed);
add_child (clone);
((TiledWindowContainer) get_child_at_index (window.get_monitor ())).add_window (window);
}
calculate_places (get_children ());
foreach (var child in get_children ())
((TiledWindowContainer) child).open ();
ready = true;
}
void restack_windows (Screen screen)
{
foreach (var child in get_children ())
((TiledWindowContainer) child).restack_windows (screen);
}
void window_left_monitor (int num, Window window)
@ -258,7 +187,7 @@ namespace Gala
if (window.get_workspace () == workspace ||
(window.is_on_all_workspaces () && window.get_screen () == workspace.get_screen ())) {
#endif
remove_window (window);
((TiledWindowContainer) get_child_at_index (num)).remove_window (window);
return;
}
}
@ -266,58 +195,19 @@ namespace Gala
void add_window (Window window)
{
if (!visible || window.get_workspace () != screen.get_active_workspace ()
if (!visible
|| (window.window_type != WindowType.NORMAL && window.window_type != WindowType.DIALOG))
return;
var actor = window.get_compositor_private () as WindowActor;
if (actor == null) {
//the window possibly hasn't reached the compositor yet
Idle.add (() => {
if (window.get_compositor_private () != null &&
window.get_workspace () == screen.get_active_workspace ())
add_window (window);
return false;
});
return;
}
actor.hide ();
var clone = new WindowThumb (window);
clone.x = actor.x;
clone.y = actor.y;
clone.selected.connect (thumb_selected);
clone.closed.connect (thumb_closed);
add_child (clone);
calculate_places (get_children ());
// make sure the window belongs to one of our workspaces
foreach (var workspace in workspaces)
if (window.get_workspace () == workspace)
((TiledWindowContainer) get_child_at_index (window.get_monitor ())).add_window (window);
}
void remove_window (Window window)
{
WindowThumb thumb = null;
foreach (var child in get_children ()) {
if ((child as WindowThumb).window == window)
thumb = child as WindowThumb;
}
if (thumb != null) {
thumb_closed (thumb);
}
}
void thumb_closed (WindowThumb thumb)
{
thumb.destroy ();
var children = get_children ();
if (children.length () > 0)
calculate_places (children);
else
close (false);
((TiledWindowContainer) get_child_at_index (window.get_monitor ())).remove_window (window);
}
void thumb_selected (Window window)
@ -349,34 +239,33 @@ namespace Gala
ready = false;
wm.end_modal ();
wm.block_keybindings_in_modal = true;
wm.update_input_area ();
foreach (var child in get_children ()) {
var exposed = child as WindowThumb;
exposed.close (animate);
exposed.selected.disconnect (thumb_selected);
((TiledWindowContainer) child).close ();
}
if (animate) {
Clutter.Threads.Timeout.add (300, () => {
visible = false;
ready = true;
foreach (var window in screen.get_active_workspace ().list_windows ()) {
if (window.showing_on_its_workspace ())
(window.get_compositor_private () as Actor).show ();
}
cleanup ();
return false;
});
} else {
ready = true;
visible = false;
} else
cleanup ();
}
foreach (var window in screen.get_active_workspace ().list_windows ())
if (window.showing_on_its_workspace ())
(window.get_compositor_private () as Actor).show ();
}
void cleanup ()
{
ready = true;
visible = false;
foreach (var window in screen.get_active_workspace ().list_windows ())
if (window.showing_on_its_workspace ())
(window.get_compositor_private () as Actor).show ();
destroy_all_children ();
}
}
}

View File

@ -1,234 +0,0 @@
//
// Copyright (C) 2012 Tom Beckmann
//
// 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 WindowThumb : Actor
{
const int WAIT_FOR_CONFIRMATION_DIALOG = 100;
public signal void selected (Window window);
public signal void closed ();
public weak Window window;
Clone clone;
public GtkClutter.Texture icon;
public GtkClutter.Texture close_button;
public WindowThumb (Window _window, bool add_children_to_stage = true)
{
window = _window;
reactive = true;
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 {
icon.set_from_pixbuf (Utils.get_icon_for_window (window, 64));
} catch (Error e) { warning (e.message); }
close_button = new GtkClutter.Texture ();
close_button.reactive = true;
close_button.visible = false;
close_button.scale_x = 0.0f;
close_button.scale_y = 0.0f;
close_button.scale_gravity = Gravity.CENTER;
close_button.button_release_event.connect (close_button_clicked);
close_button.leave_event.connect ((e) => leave_event (e));
try {
close_button.set_from_pixbuf (Granite.Widgets.Utils.get_close_pixbuf ());
} catch (Error e) { warning (e.message); }
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)
{
if (event.button != 1)
return false;
close_window ();
return true;
}
public void close_window ()
{
get_parent ().set_child_below_sibling (this, null);
// make sure we dont see a window closing animation in the background
clone.source.opacity = 0;
window.delete (window.get_screen ().get_display ().get_current_time ());
// see if the window is still alive after the animation ended. If it is, it's pretty certain that it
// popped up some kind of confirmation dialog, so we focus it
Clutter.Threads.Timeout.add (AnimationSettings.get_default ().close_duration + WAIT_FOR_CONFIRMATION_DIALOG, () => {
if (clone != null && clone.source != null && !(clone.source as Meta.WindowActor).is_destroyed ()) {
clone.source.opacity = 255;
selected (window);
}
return false;
});
}
public override void destroy ()
{
clone.destroy ();
close_button.destroy ();
icon.destroy ();
base.destroy ();
}
public override bool enter_event (CrossingEvent event)
{
//if we're still animating don't show the close button
if (get_animation () != null)
return false;
close_button.visible = true;
close_button.animate (AnimationMode.EASE_OUT_ELASTIC, 400, scale_x : 1.0f, scale_y : 1.0f);
return true;
}
public override bool motion_event (MotionEvent event)
{
if (get_animation () != null)
return false;
close_button.visible = true;
close_button.animate (AnimationMode.EASE_OUT_ELASTIC, 400, scale_x : 1.0f, scale_y : 1.0f);
return true;
}
public override bool leave_event (CrossingEvent event)
{
if (event.related == close_button)
return false;
close_button.animate (AnimationMode.EASE_IN_QUAD, 200, scale_x : 0.0f, scale_y : 0.0f)
.completed.connect (() => close_button.visible = false );
return true;
}
public override bool button_release_event (ButtonEvent event)
{
switch (event.button) {
case 1:
get_parent ().set_child_above_sibling (this, null);
selected (window);
break;
case 2:
close_window ();
break;
}
return true;
}
public void close (bool do_animate = true, bool use_scale = true)
{
unowned Meta.Rectangle rect = window.get_outer_rect ();
float x, y, w, h;
Utils.get_window_frame_offset (window, out x, out y, out w, out h);
float dest_x = rect.x + x;
float dest_y = rect.y + y;
//stop all running animations
detach_animation ();
icon.detach_animation ();
close_button.detach_animation ();
bool dont_show = window.minimized || window.get_workspace () != window.get_screen ().get_active_workspace ();
do_animate = do_animate && !dont_show;
if (do_animate) {
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);
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 ();
});
} else {
if (!dont_show)
clone.source.show ();
destroy ();
}
}
}
}