Document the MultitaskingView, fix IconGroup.clear, make sure we don't draw the IconGroup highlight when it's not even visible

This commit is contained in:
Tom Beckmann 2014-06-24 15:05:09 +02:00
parent 54bf615d27
commit 96748eeded
7 changed files with 329 additions and 6 deletions

View File

@ -20,11 +20,20 @@ using Meta;
namespace Gala
{
/**
* Private class which is basically just a container for the actual
* icon and takes care of blending the same icon in different sizes
* over each other and various animations related to the icons
*/
class WindowIcon : Actor
{
public Window window { get; construct; }
int _icon_size;
/**
* The icon size of the WindowIcon. Once set the new icon will be
* faded over the old one and the actor animates to the new size.
*/
public int icon_size {
get {
return _icon_size;
@ -42,6 +51,11 @@ namespace Gala
}
bool _temporary;
/**
* Mark the WindowIcon as temporary. Only effect of this is that a pulse
* animation will be played on the actor. Used while DnDing window thumbs
* over the group.
*/
public bool temporary {
get {
return _temporary;
@ -99,6 +113,13 @@ namespace Gala
set_easing_duration (800);
}
/**
* Shortcut to set both position and size of the icon
*
* @param x The x coordinate to which to animate to
* @param y The y coordinate to which to animate to
* @param size The size to which to animate to and display the icon in
*/
public void place (float x, float y, int size)
{
if (initial) {
@ -115,6 +136,9 @@ namespace Gala
}
}
/**
* Fades out the old icon and fades in the new icon
*/
void fade_new_icon ()
{
var new_icon = new Utils.WindowIcon (window, icon_size);
@ -152,6 +176,11 @@ namespace Gala
}
}
/**
* Container for WindowIcons which takes care of the scaling and positioning.
* It also decides whether to draw the container shape, a plus sign or an ellipsis.
* Lastly it also includes the drawing code for the active highlight.
*/
public class IconGroup : Actor
{
public static const int SIZE = 64;
@ -159,10 +188,17 @@ namespace Gala
static const int PLUS_SIZE = 8;
static const int PLUS_WIDTH = 24;
/**
* The group has been clicked. The MultitaskingView should consider activating
* its workspace.
*/
public signal void selected ();
uint8 _backdrop_opacity = 0;
public uint8 backdrop_opacity {
/**
* The opacity of the backdrop/highlight. Set by the active property setter.
*/
protected uint8 backdrop_opacity {
get {
return _backdrop_opacity;
}
@ -171,7 +207,11 @@ namespace Gala
queue_redraw ();
}
}
bool _active = false;
/**
* Fades in/out the backdrop/highlight
*/
public bool active {
get {
return _active;
@ -194,6 +234,7 @@ namespace Gala
add_transition ("backdrop-opacity", transition);
}
}
public Workspace workspace { get; construct; }
Actor close_button;
@ -204,8 +245,6 @@ namespace Gala
{
Object (workspace: workspace);
clear ();
width = SIZE;
height = SIZE;
reactive = true;
@ -275,8 +314,16 @@ namespace Gala
return false;
}
/**
* Override the paint handler to draw our backdrop if necessary
*/
public override void paint ()
{
if (backdrop_opacity < 1) {
base.paint ();
return;
}
var width = 100;
var x = (SIZE - width) / 2;
var y = -10;
@ -301,11 +348,23 @@ namespace Gala
base.paint ();
}
/**
* Remove all currently added WindowIcons
*/
public void clear ()
{
destroy_all_children ();
icon_container.destroy_all_children ();
}
/**
* Creates a WindowIcon for the given window and adds it to the group
*
* @param window The MetaWindow for which to create the WindowIcon
* @param no_redraw If you add multiple windows at once you may want to consider
* settings this to true and when done calling redraw() manually
* @param temporary Mark the WindowIcon as temporary. Used for windows dragged over
* the group.
*/
public void add_window (Window window, bool no_redraw = false, bool temporary = false)
{
var new_window = new WindowIcon (window);
@ -322,6 +381,11 @@ namespace Gala
redraw ();
}
/**
* Remove the WindowIcon for a MetaWindow from the group
*
* @param animate Whether to fade the icon out before removing it
*/
public void remove_window (Window window, bool animate = true)
{
foreach (var child in icon_container.get_children ()) {
@ -349,11 +413,19 @@ namespace Gala
}
}
/**
* Trigger a redraw
*/
public void redraw ()
{
content.invalidate ();
}
/**
* Close handler. We close the workspace by deleting all the windows on it.
* That way the workspace won't be deleted if windows decide to ignore the
* delete signal
*/
void close ()
{
var time = workspace.get_screen ().get_display ().get_current_time ();

View File

@ -20,7 +20,11 @@ using Meta;
namespace Gala
{
// TODO: allow DnD to insert new workspaces
/**
* This class contains the icon groups at the bottom and will take
* care of displaying actors for inserting windows between the groups
* once implemented
*/
public class IconGroupContainer : Actor
{
public static const int SPACING = 48;

View File

@ -20,6 +20,13 @@ using Meta;
namespace Gala
{
/**
* More or less utility class to contain a TiledWindowContainer for each
* non-primary monitor. It's the pendant to the WorkspaceClone which is
* only placed on the primary monitor. It also draws a wallpaper behind itself
* as the WindowGroup is hidden while the view is active. Only used when
* workspaces-only-on-primary is set to true.
*/
public class MonitorClone : Actor
{
public signal void window_selected (Window window);
@ -72,6 +79,9 @@ namespace Gala
screen.restacked.disconnect (window_container.restack_windows);
}
/**
* Make sure the MonitorClone is at the location of the monitor on the stage
*/
public void update_allocation ()
{
var monitor_geometry = screen.get_monitor_geometry (monitor);
@ -81,12 +91,18 @@ namespace Gala
window_container.set_size (monitor_geometry.width, monitor_geometry.height);
}
/**
* Animate the windows from their old location to a tiled layout
*/
public void open ()
{
window_container.open ();
// background.opacity = 0; TODO consider this option
}
/**
* Animate the windows back to their old location
*/
public void close ()
{
window_container.close ();

View File

@ -20,6 +20,11 @@ using Meta;
namespace Gala
{
/**
* The central class for the MultitaskingView which takes care of
* preparing the wm, opening the components and holds containers for
* the icon groups, the WorkspaceClones and the MonitorClones.
*/
public class MultitaskingView : Actor
{
const int HIDING_DURATION = 300;
@ -105,6 +110,10 @@ namespace Gala
});
}
/**
* Places the primary container for the WorkspaceClones and the
* MonitorClones at the right positions
*/
void update_monitors ()
{
foreach (var monitor_clone in window_containers_monitors)
@ -132,12 +141,19 @@ namespace Gala
set_size (primary_geometry.width, primary_geometry.height);
}
/**
* We generally assume that when the key-focus-out signal is emitted
* a different component was opened, so we close in that case.
*/
public override void key_focus_out ()
{
if (opened && !contains (get_stage ().key_focus))
toggle ();
}
/**
* Scroll through workspaces
*/
public override bool scroll_event (ScrollEvent scroll_event)
{
if (scroll_event.direction != ScrollDirection.SMOOTH)
@ -183,6 +199,13 @@ namespace Gala
return false;
}
/**
* Places the WorkspaceClones, moves the view so that the active one is shown
* and does the same for the IconGroups.
*
* @param animate Whether to animate the movement or have all elements take their
* positions immediately.
*/
void update_positions (bool animate)
{
var active_index = screen.get_active_workspace ().index ();
@ -276,6 +299,14 @@ namespace Gala
update_positions (opened);
}
/**
* Activates the workspace of a WorkspaceClone
*
* @param close_view Whether to close the view as well. Will only be considered
* if the workspace is also the currently active workspace.
* Otherwise it will only be made active, but the view won't be
* closed.
*/
void activate_workspace (WorkspaceClone clone, bool close_view)
{
close_view = close_view && screen.get_active_workspace () == clone.workspace;
@ -286,6 +317,10 @@ namespace Gala
toggle ();
}
/**
* Collect key events, mainly for redirecting them to the TiledWindowContainers to
* select the active window.
*/
public override bool key_press_event (Clutter.KeyEvent event)
{
switch (event.keyval) {
@ -314,11 +349,22 @@ namespace Gala
return false;
}
/**
* Inform the current TiledWindowContainer that we want to move the focus in
* a specific direction.
*
* @param direction The direction in which to move the focus to
*/
void select_window (MotionDirection direction)
{
get_active_workspace_clone ().window_container.select_next_window (direction);
}
/**
* Finds the active WorkspaceClone
*
* @return The active WorkspaceClone
*/
WorkspaceClone get_active_workspace_clone ()
{
foreach (var child in workspaces.get_children ()) {
@ -344,6 +390,11 @@ namespace Gala
}
}
/**
* Toggles the view open or closed. Takes care of all the wm related tasks, like
* starting the modal mode and hiding the WindowGroup. Finally tells all components
* to animate to their positions.
*/
public void toggle ()
{
opened = !opened;

View File

@ -20,18 +20,39 @@ using Meta;
namespace Gala
{
/**
* A container for a clone of the texture of a MetaWindow, a WindowIcon,
* a close button and a shadow. Used together with the TiledWindowContainer.
*/
public class TiledWindow : Actor
{
const int WINDOW_ICON_SIZE = 64;
const int ACTIVE_SHAPE_SIZE = 12;
/**
* The window was selected. The MultitaskingView should consider activating
* the window and closing the view.
*/
public signal void selected ();
/**
* The window was moved or resized and a relayout of the tiling layout may
* be sensible right now.
*/
public signal void request_reposition ();
public Meta.Window window { get; construct; }
/**
* The currently assigned slot of the window in the tiling layout. May be null.
*/
public Meta.Rectangle? slot { get; private set; default = null; }
bool _active = false;
/**
* When active fades a white border around the window in. Used for the visually
* indicating the TiledWindowContainer's current_window.
*/
public bool active {
get {
return _active;
@ -136,6 +157,16 @@ namespace Gala
#endif
}
/**
* Waits for the texture of a new WindowActor to be available
* and makes a close of it. If it was already was assigned a slot
* at this point it will animate to it. Otherwise it will just place
* itself at the location of the original window. Also adds the shadow
* effect and makes sure the shadow is updated on size changes.
*
* @param was_waiting Internal argument used to indicate that we had to
* wait before the window's texture became available.
*/
void load_clone (bool was_waiting = false)
{
var actor = window.get_compositor_private () as WindowActor;
@ -179,6 +210,11 @@ namespace Gala
}
}
/**
* 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
* make the TiledWindowContainer calculate a new layout to honor the new size.
*/
void update_shadow_size ()
{
if (shadow_update_timeout != 0)
@ -198,6 +234,11 @@ namespace Gala
});
}
/**
* Place the window at the location of the original MetaWindow
*
* @param animate Animate the transformation of the placement
*/
public void transition_to_original_state (bool animate)
{
var outer_rect = window.get_outer_rect ();
@ -217,6 +258,9 @@ namespace Gala
window_icon.opacity = 0;
}
/**
* Animate the window to the given slot
*/
public void take_slot (Meta.Rectangle rect)
{
slot = rect;
@ -230,6 +274,11 @@ namespace Gala
window_icon.opacity = 255;
}
/**
* Except for the texture clone and the highlight all children are placed
* according to their given allocations. The first two are placed in a way
* that compensates for invisible borders of the texture.
*/
public override void allocate (ActorBox box, AllocationFlags flags)
{
base.allocate (box, flags);
@ -270,6 +319,10 @@ namespace Gala
clone.allocate (alloc, flags);
}
/**
* Place the widgets, that is the close button and the WindowIcon of the window,
* at their positions inside the actor for a given width and height.
*/
public void place_widgets (int dest_width, int dest_height)
{
Granite.CloseButtonPosition pos;
@ -299,6 +352,12 @@ namespace Gala
window_icon.restore_easing_state ();
}
/**
* Send the window the delete signal and listen for new windows to be added
* to the window's workspace, in which case we check if the new window is a
* dialog of the window we were going to delete. If that's the case, we request
* to select our window.
*/
void close_window ()
{
check_confirm_dialog_cb = window.get_workspace ().window_added.connect (check_confirm_dialog);
@ -319,6 +378,9 @@ namespace Gala
}
}
/**
* The window unmanaged by the compositor, so we need to destroy ourselves too.
*/
void unmanaged ()
{
if (drag_action.dragging)
@ -335,6 +397,11 @@ namespace Gala
destroy ();
}
/**
* 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
* position we just freed is immediately filled by the TiledWindowContainer.
*/
Actor drag_begin (float click_x, float click_y)
{
float abs_x, abs_y;
@ -363,6 +430,11 @@ namespace Gala
return this;
}
/**
* When we cross an IconGroup, we animate to an even smaller size and slightly
* less opacity and add ourselves as temporary window to the group. When left,
* we reverse those steps.
*/
void drag_destination_crossed (Actor destination, bool hovered)
{
var icon_group = destination as IconGroup;
@ -395,6 +467,11 @@ namespace Gala
}
}
/**
* Depending on the destination we have different ways to find the correct destination.
* After we found one we destroy ourselves so the dragged clone immediately disappears,
* otherwise we cancel the drag and animate back to our old place.
*/
void drag_end (Actor destination)
{
Meta.Workspace workspace = null;
@ -429,6 +506,9 @@ namespace Gala
drag_canceled ();
}
/**
* Animate back to our previous position with a bouncing animation.
*/
void drag_canceled ()
{
get_parent ().remove_child (this);

View File

@ -20,6 +20,9 @@ using Meta;
namespace Gala
{
/**
* Container which controls the layout of a set of TiledWindows.
*/
public class TiledWindowContainer : Actor
{
public signal void window_selected (Window window);
@ -30,6 +33,11 @@ namespace Gala
public int padding_bottom { get; set; default = 12; }
bool opened;
/**
* The window that is currently selected via keyboard shortcuts. It is not
* necessarily the same as the active window.
*/
TiledWindow? current_window;
public TiledWindowContainer ()
@ -42,6 +50,11 @@ namespace Gala
current_window = null;
}
/**
* Create a TiledWindow for a MetaWindow and add it to the group
*
* @param window The window for which to create the TiledWindow for
*/
public void add_window (Window window)
{
unowned Meta.Display display = window.get_display ();
@ -89,6 +102,9 @@ namespace Gala
reflow ();
}
/**
* Find and remove the TiledWindow for a MetaWindow
*/
public void remove_window (Window window)
{
foreach (var child in get_children ()) {
@ -121,6 +137,10 @@ namespace Gala
});
}
/**
* Sort the windows z-order by their actual stacking to make intersections
* during animations correct.
*/
public void restack_windows (Screen screen)
{
unowned Meta.Display display = screen.get_display ();
@ -148,6 +168,10 @@ namespace Gala
}
}
/**
* Recalculate the tiling positions of the windows and animate them to
* the resulting spots.
*/
public void reflow ()
{
if (!opened)
@ -179,6 +203,12 @@ namespace Gala
}
}
/**
* Look for the next window in a direction and make this window the
* new current_window. Used for keyboard navigation.
*
* @param direction The MetaMotionDirection in which to search for windows for.
*/
public void select_next_window (MotionDirection direction)
{
if (get_n_children () < 1)
@ -264,12 +294,18 @@ namespace Gala
current_window = closest;
}
/**
* Emit the selected signal for the current_window.
*/
public void activate_selected_window ()
{
if (current_window != null)
current_window.selected ();
}
/**
* When opened the TiledWindows are animated to a tiled layout
*/
public void open (Window? selected_window = null)
{
if (opened)
@ -299,6 +335,10 @@ namespace Gala
reflow ();
}
/**
* Calls the transition_to_original_state() function on each child
* to make them take their original locations again.
*/
public void close ()
{
if (!opened)

View File

@ -20,6 +20,9 @@ using Meta;
namespace Gala
{
/**
* Utility class which adds a border and a shadow to a Background
*/
class FramedBackground : Background
{
public FramedBackground (Screen screen)
@ -50,13 +53,43 @@ namespace Gala
}
}
public class WorkspaceClone : Clutter.Actor
/**
* This is the container which manages a clone of the background which will
* be scaled and animated inwards, a TiledWindowContainer for the windows on
* this workspace and also holds the instance for this workspace's IconGroup.
* The latter is not added to the WorkspaceClone itself though but to a container
* of the MultitaskingView.
*/
public class WorkspaceClone : Actor
{
/**
* The offset of the scaled background to the bottom of the monitor bounds
*/
public const int BOTTOM_OFFSET = 100;
/**
* The offset of the scaled background to the top of the monitor bounds
*/
const int TOP_OFFSET = 20;
/**
* The amount of time a window has to be over the WorkspaceClone while in drag
* before we activate the workspace.
*/
const int HOVER_ACTIVATE_DELAY = 400;
/**
* A window has been selected, the MultitaskingView should consider activating
* and closing the view.
*/
public signal void window_selected (Window window);
/**
* The background has been selected. Switch to that workspace.
*
* @param close_view If the MultitaskingView should also consider closing itself
* after switching.
*/
public signal void selected (bool close_view);
public Workspace workspace { get; construct; }
@ -64,6 +97,10 @@ namespace Gala
public TiledWindowContainer window_container { get; private set; }
bool _active = false;
/**
* If this WorkspaceClone is currently the active one. Also sets the active
* state on its IconGroup.
*/
public bool active {
get {
return _active;
@ -163,6 +200,10 @@ namespace Gala
background.destroy ();
}
/**
* Add a window to the TiledWindowContainer and the IconGroup if it really
* belongs to this workspace and this monitor.
*/
void add_window (Window window)
{
if (window.window_type != WindowType.NORMAL
@ -178,6 +219,9 @@ namespace Gala
icon_group.add_window (window);
}
/**
* Remove a window from the TiledWindowContainer and the IconGroup
*/
void remove_window (Window window)
{
window_container.remove_window (window);
@ -195,6 +239,12 @@ namespace Gala
remove_window (window);
}
/**
* Utility function to shrink a MetaRectangle on all sides for the given amount.
* Negative amounts will scale it instead.
*
* @param amount The amount in px to shrink.
*/
static inline void shrink_rectangle (ref Meta.Rectangle rect, int amount)
{
rect.x += amount;
@ -203,6 +253,12 @@ namespace Gala
rect.height -= amount * 2;
}
/**
* Animates the background to its scale, causes a redraw on the IconGroup and
* makes sure the TiledWindowContainer animates its windows to their tiled layout.
* Also sets the current_window of the TiledWindowContainer to the active window
* if it belongs to this workspace.
*/
public void open ()
{
if (opened)
@ -242,6 +298,10 @@ namespace Gala
window_container.open (screen.get_active_workspace () == workspace ? display.get_focus_window () : null);
}
/**
* Close the view again by animating the background back to its scale and
* the windows back to their old locations.
*/
public void close ()
{
if (!opened)