Rewrite Dash, remove hardcoded width/height from GenericDisplay
This patch is a near-total rewrite of the Dash. First, the dash
code moves into a separate file, dash.js.
Inside dash.js, the components are more broken up into separate
classes; in particular there's now a Pane class and a MoreLink
class. Instead of each section of the dash, when activated,
attempting to close all N-1 other sections, instead there
is the concept of a single "active pane", and when e.g. activating
the More link for documents, if we know there's an active pane
which happens to be the apps, close it.
Many redundant containers were removed from the dash, and all
manual width, height and x/y offsets are entirely gone. We move
the visual apperance closer to the design by using the view-more.svg,
etc.
To complete the removal of height/width calculations from the dash,
we also had to do the same for GenericDisplay. Also clean up
the positioning inside overlay.js so calculation of children's
positioning is inside a single function that flows from screen.width
and screen.height, so in the future we can stop passing the width
into the Dash constructor and call this once and work on screen
resizing.
2009-08-01 02:12:01 +00:00
|
|
|
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
|
|
|
|
2011-01-20 15:23:43 +00:00
|
|
|
const Clutter = imports.gi.Clutter;
|
Rewrite Dash, remove hardcoded width/height from GenericDisplay
This patch is a near-total rewrite of the Dash. First, the dash
code moves into a separate file, dash.js.
Inside dash.js, the components are more broken up into separate
classes; in particular there's now a Pane class and a MoreLink
class. Instead of each section of the dash, when activated,
attempting to close all N-1 other sections, instead there
is the concept of a single "active pane", and when e.g. activating
the More link for documents, if we know there's an active pane
which happens to be the apps, close it.
Many redundant containers were removed from the dash, and all
manual width, height and x/y offsets are entirely gone. We move
the visual apperance closer to the design by using the view-more.svg,
etc.
To complete the removal of height/width calculations from the dash,
we also had to do the same for GenericDisplay. Also clean up
the positioning inside overlay.js so calculation of children's
positioning is inside a single function that flows from screen.width
and screen.height, so in the future we can stop passing the width
into the Dash constructor and call this once and work on screen
resizing.
2009-08-01 02:12:01 +00:00
|
|
|
const Signals = imports.signals;
|
|
|
|
const Lang = imports.lang;
|
2010-11-08 01:51:02 +00:00
|
|
|
const Meta = imports.gi.Meta;
|
2010-07-21 23:29:02 +00:00
|
|
|
const Shell = imports.gi.Shell;
|
2009-10-22 17:33:09 +00:00
|
|
|
const St = imports.gi.St;
|
Rewrite Dash, remove hardcoded width/height from GenericDisplay
This patch is a near-total rewrite of the Dash. First, the dash
code moves into a separate file, dash.js.
Inside dash.js, the components are more broken up into separate
classes; in particular there's now a Pane class and a MoreLink
class. Instead of each section of the dash, when activated,
attempting to close all N-1 other sections, instead there
is the concept of a single "active pane", and when e.g. activating
the More link for documents, if we know there's an active pane
which happens to be the apps, close it.
Many redundant containers were removed from the dash, and all
manual width, height and x/y offsets are entirely gone. We move
the visual apperance closer to the design by using the view-more.svg,
etc.
To complete the removal of height/width calculations from the dash,
we also had to do the same for GenericDisplay. Also clean up
the positioning inside overlay.js so calculation of children's
positioning is inside a single function that flows from screen.width
and screen.height, so in the future we can stop passing the width
into the Dash constructor and call this once and work on screen
resizing.
2009-08-01 02:12:01 +00:00
|
|
|
|
|
|
|
const AppDisplay = imports.ui.appDisplay;
|
2010-07-21 23:29:02 +00:00
|
|
|
const AppFavorites = imports.ui.appFavorites;
|
2010-02-07 17:59:30 +00:00
|
|
|
const DND = imports.ui.dnd;
|
2010-11-08 01:51:02 +00:00
|
|
|
const IconGrid = imports.ui.iconGrid;
|
Rewrite Dash, remove hardcoded width/height from GenericDisplay
This patch is a near-total rewrite of the Dash. First, the dash
code moves into a separate file, dash.js.
Inside dash.js, the components are more broken up into separate
classes; in particular there's now a Pane class and a MoreLink
class. Instead of each section of the dash, when activated,
attempting to close all N-1 other sections, instead there
is the concept of a single "active pane", and when e.g. activating
the More link for documents, if we know there's an active pane
which happens to be the apps, close it.
Many redundant containers were removed from the dash, and all
manual width, height and x/y offsets are entirely gone. We move
the visual apperance closer to the design by using the view-more.svg,
etc.
To complete the removal of height/width calculations from the dash,
we also had to do the same for GenericDisplay. Also clean up
the positioning inside overlay.js so calculation of children's
positioning is inside a single function that flows from screen.width
and screen.height, so in the future we can stop passing the width
into the Dash constructor and call this once and work on screen
resizing.
2009-08-01 02:12:01 +00:00
|
|
|
const Main = imports.ui.main;
|
2011-01-21 11:06:13 +00:00
|
|
|
const Tweener = imports.ui.tweener;
|
2010-07-21 23:29:02 +00:00
|
|
|
const Workspace = imports.ui.workspace;
|
2009-11-29 22:45:30 +00:00
|
|
|
|
2011-01-21 11:06:13 +00:00
|
|
|
const DASH_ANIMATION_TIME = 0.2;
|
|
|
|
|
2011-01-20 15:23:43 +00:00
|
|
|
// A container like StBin, but taking the child's scale into account
|
|
|
|
// when requesting a size
|
|
|
|
function DashItemContainer() {
|
|
|
|
this._init();
|
|
|
|
}
|
|
|
|
|
|
|
|
DashItemContainer.prototype = {
|
|
|
|
_init: function() {
|
|
|
|
this.actor = new Shell.GenericContainer({ style_class: 'dash-item-container' });
|
|
|
|
this.actor.connect('get-preferred-width',
|
|
|
|
Lang.bind(this, this._getPreferredWidth));
|
|
|
|
this.actor.connect('get-preferred-height',
|
|
|
|
Lang.bind(this, this._getPreferredHeight));
|
|
|
|
this.actor.connect('allocate',
|
|
|
|
Lang.bind(this, this._allocate));
|
|
|
|
this.actor._delegate = this;
|
|
|
|
|
|
|
|
this.child = null;
|
2011-01-21 11:06:13 +00:00
|
|
|
this._childScale = 1;
|
|
|
|
this._childOpacity = 255;
|
2011-01-20 15:23:43 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
_allocate: function(actor, box, flags) {
|
|
|
|
if (this.child == null)
|
|
|
|
return;
|
|
|
|
|
|
|
|
let availWidth = box.x2 - box.x1;
|
|
|
|
let availHeight = box.y2 - box.y1;
|
|
|
|
let [minChildWidth, minChildHeight, natChildWidth, natChildHeight] =
|
|
|
|
this.child.get_preferred_size();
|
|
|
|
let [childScaleX, childScaleY] = this.child.get_scale();
|
|
|
|
|
|
|
|
let childWidth = Math.min(natChildWidth * childScaleX, availWidth);
|
|
|
|
let childHeight = Math.min(natChildHeight * childScaleY, availHeight);
|
|
|
|
|
|
|
|
let childBox = new Clutter.ActorBox();
|
|
|
|
childBox.x1 = (availWidth - childWidth) / 2;
|
|
|
|
childBox.y1 = (availHeight - childHeight) / 2;
|
|
|
|
childBox.x2 = childBox.x1 + childWidth;
|
|
|
|
childBox.y2 = childBox.y1 + childHeight;
|
|
|
|
|
|
|
|
this.child.allocate(childBox, flags);
|
|
|
|
},
|
|
|
|
|
|
|
|
_getPreferredHeight: function(actor, forWidth, alloc) {
|
|
|
|
alloc.min_size = 0;
|
|
|
|
alloc.natural_size = 0;
|
|
|
|
|
|
|
|
if (this.child == null)
|
|
|
|
return;
|
|
|
|
|
|
|
|
let [minHeight, natHeight] = this.child.get_preferred_height(forWidth);
|
|
|
|
alloc.min_size += minHeight * this.child.scale_y;
|
|
|
|
alloc.natural_size += natHeight * this.child.scale_y;
|
|
|
|
},
|
|
|
|
|
|
|
|
_getPreferredWidth: function(actor, forHeight, alloc) {
|
|
|
|
alloc.min_size = 0;
|
|
|
|
alloc.natural_size = 0;
|
|
|
|
|
|
|
|
if (this.child == null)
|
|
|
|
return;
|
|
|
|
|
|
|
|
let [minWidth, natWidth] = this.child.get_preferred_width(forHeight);
|
|
|
|
alloc.min_size = minWidth * this.child.scale_y;
|
|
|
|
alloc.natural_size = natWidth * this.child.scale_y;
|
|
|
|
},
|
|
|
|
|
|
|
|
setChild: function(actor) {
|
|
|
|
if (this.child == actor)
|
|
|
|
return;
|
|
|
|
|
|
|
|
this.actor.destroy_children();
|
|
|
|
|
|
|
|
this.child = actor;
|
|
|
|
this.actor.add_actor(this.child);
|
2011-01-21 11:06:13 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
animateIn: function() {
|
|
|
|
if (this.child == null)
|
|
|
|
return;
|
|
|
|
|
|
|
|
this.childScale = 0;
|
|
|
|
this.childOpacity = 0;
|
|
|
|
Tweener.addTween(this,
|
|
|
|
{ childScale: 1.0,
|
|
|
|
childOpacity: 255,
|
|
|
|
time: DASH_ANIMATION_TIME,
|
|
|
|
transition: 'easeOutQuad'
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
animateOutAndDestroy: function() {
|
|
|
|
if (this.child == null) {
|
|
|
|
this.actor.destroy();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.childScale = 1.0;
|
|
|
|
Tweener.addTween(this,
|
|
|
|
{ childScale: 0.0,
|
|
|
|
childOpacity: 0,
|
|
|
|
time: DASH_ANIMATION_TIME,
|
|
|
|
transition: 'easeOutQuad',
|
|
|
|
onComplete: Lang.bind(this, function() {
|
|
|
|
this.actor.destroy();
|
|
|
|
})
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
set childScale(scale) {
|
|
|
|
this._childScale = scale;
|
|
|
|
|
|
|
|
if (this.child == null)
|
|
|
|
return;
|
|
|
|
|
|
|
|
this.child.set_scale_with_gravity(scale, scale,
|
|
|
|
Clutter.Gravity.CENTER);
|
|
|
|
this.actor.queue_relayout();
|
|
|
|
},
|
|
|
|
|
|
|
|
get childScale() {
|
|
|
|
return this._childScale;
|
|
|
|
},
|
|
|
|
|
|
|
|
set childOpacity(opacity) {
|
|
|
|
this._childOpacity = opacity;
|
|
|
|
|
|
|
|
if (this.child == null)
|
|
|
|
return;
|
|
|
|
|
|
|
|
this.child.set_opacity(opacity);
|
|
|
|
this.actor.queue_redraw();
|
|
|
|
},
|
|
|
|
|
|
|
|
get childOpacity() {
|
|
|
|
return this._childOpacity;
|
2011-01-20 15:23:43 +00:00
|
|
|
}
|
|
|
|
};
|
2010-07-21 23:29:02 +00:00
|
|
|
|
2010-11-08 01:51:02 +00:00
|
|
|
function RemoveFavoriteIcon() {
|
|
|
|
this._init();
|
|
|
|
}
|
|
|
|
|
|
|
|
RemoveFavoriteIcon.prototype = {
|
2011-01-20 15:23:43 +00:00
|
|
|
__proto__: DashItemContainer.prototype,
|
|
|
|
|
2010-11-08 01:51:02 +00:00
|
|
|
_init: function() {
|
2011-01-20 15:23:43 +00:00
|
|
|
DashItemContainer.prototype._init.call(this);
|
|
|
|
|
|
|
|
this._iconBin = new St.Bin({ style_class: 'remove-favorite' });
|
2010-11-08 01:51:02 +00:00
|
|
|
this._iconActor = null;
|
|
|
|
this.icon = new IconGrid.BaseIcon(_("Remove"),
|
|
|
|
{ setSizeManually: true,
|
2011-01-20 14:27:13 +00:00
|
|
|
showLabel: false,
|
2010-11-08 01:51:02 +00:00
|
|
|
createIcon: Lang.bind(this, this._createIcon) });
|
2011-01-20 15:23:43 +00:00
|
|
|
this._iconBin.set_child(this.icon.actor);
|
|
|
|
this._iconBin._delegate = this;
|
|
|
|
|
|
|
|
this.setChild(this._iconBin);
|
2011-01-21 11:06:13 +00:00
|
|
|
this.hiding = false;
|
|
|
|
},
|
|
|
|
|
|
|
|
animateOutAndDestroy: function() {
|
|
|
|
DashItemContainer.prototype.animateOutAndDestroy.call(this);
|
|
|
|
this.hiding = true;
|
2010-11-08 01:51:02 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
_createIcon: function(size) {
|
|
|
|
this._iconActor = new St.Icon({ icon_name: 'user-trash',
|
|
|
|
style_class: 'remove-favorite-icon',
|
|
|
|
icon_size: size });
|
|
|
|
return this._iconActor;
|
|
|
|
},
|
|
|
|
|
|
|
|
setHover: function(hovered) {
|
2011-01-20 15:23:43 +00:00
|
|
|
this._iconBin.set_hover(hovered);
|
2010-11-08 01:51:02 +00:00
|
|
|
if (this._iconActor)
|
|
|
|
this._iconActor.set_hover(hovered);
|
|
|
|
},
|
|
|
|
|
|
|
|
// Rely on the dragged item being a favorite
|
|
|
|
handleDragOver: function(source, actor, x, y, time) {
|
|
|
|
return DND.DragMotionResult.MOVE_DROP;
|
|
|
|
},
|
|
|
|
|
|
|
|
acceptDrop: function(source, actor, x, y, time) {
|
|
|
|
let app = null;
|
|
|
|
if (source instanceof AppDisplay.AppWellIcon) {
|
|
|
|
let appSystem = Shell.AppSystem.get_default();
|
|
|
|
app = appSystem.get_app(source.getId());
|
2011-01-31 01:25:00 +00:00
|
|
|
} else if (source.metaWindow) {
|
2010-11-08 01:51:02 +00:00
|
|
|
let tracker = Shell.WindowTracker.get_default();
|
|
|
|
app = tracker.get_window_app(source.metaWindow);
|
|
|
|
}
|
|
|
|
|
|
|
|
let id = app.get_id();
|
|
|
|
|
|
|
|
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this,
|
|
|
|
function () {
|
|
|
|
AppFavorites.getAppFavorites().removeFavorite(id);
|
|
|
|
return false;
|
|
|
|
}));
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2011-01-20 15:23:43 +00:00
|
|
|
function DragPlaceholderItem() {
|
|
|
|
this._init();
|
|
|
|
}
|
|
|
|
|
|
|
|
DragPlaceholderItem.prototype = {
|
|
|
|
__proto__: DashItemContainer.prototype,
|
|
|
|
|
|
|
|
_init: function() {
|
|
|
|
DashItemContainer.prototype._init.call(this);
|
|
|
|
this.setChild(new St.Bin({ style_class: 'dash-placeholder' }));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2010-07-21 23:29:02 +00:00
|
|
|
function Dash() {
|
|
|
|
this._init();
|
|
|
|
}
|
|
|
|
|
|
|
|
Dash.prototype = {
|
|
|
|
_init : function() {
|
|
|
|
this._maxHeight = -1;
|
2011-02-12 00:17:10 +00:00
|
|
|
this.iconSize = 64;
|
2011-02-02 15:44:07 +00:00
|
|
|
this._shownInitially = false;
|
2010-11-08 01:51:02 +00:00
|
|
|
|
|
|
|
this._dragPlaceholder = null;
|
|
|
|
this._dragPlaceholderPos = -1;
|
2011-01-21 11:06:13 +00:00
|
|
|
this._animatingPlaceholdersCount = 0;
|
2010-11-08 01:51:02 +00:00
|
|
|
this._favRemoveTarget = null;
|
2010-07-21 23:29:02 +00:00
|
|
|
|
|
|
|
this._box = new St.BoxLayout({ name: 'dash',
|
|
|
|
vertical: true,
|
|
|
|
clip_to_allocation: true });
|
|
|
|
this._box._delegate = this;
|
|
|
|
|
|
|
|
this.actor = new St.Bin({ y_align: St.Align.START, child: this._box });
|
|
|
|
this.actor.connect('notify::height', Lang.bind(this,
|
|
|
|
function() {
|
|
|
|
if (this._maxHeight != this.actor.height)
|
|
|
|
this._queueRedisplay();
|
|
|
|
this._maxHeight = this.actor.height;
|
|
|
|
}));
|
|
|
|
|
|
|
|
this._workId = Main.initializeDeferredWork(this._box, Lang.bind(this, this._redisplay));
|
|
|
|
|
|
|
|
this._tracker = Shell.WindowTracker.get_default();
|
|
|
|
this._appSystem = Shell.AppSystem.get_default();
|
|
|
|
|
|
|
|
this._appSystem.connect('installed-changed', Lang.bind(this, this._queueRedisplay));
|
|
|
|
AppFavorites.getAppFavorites().connect('changed', Lang.bind(this, this._queueRedisplay));
|
|
|
|
this._tracker.connect('app-state-changed', Lang.bind(this, this._queueRedisplay));
|
2010-11-08 01:51:02 +00:00
|
|
|
|
2011-02-11 23:01:59 +00:00
|
|
|
Main.overview.connect('item-drag-begin',
|
|
|
|
Lang.bind(this, this._onDragBegin));
|
|
|
|
Main.overview.connect('item-drag-end',
|
|
|
|
Lang.bind(this, this._onDragEnd));
|
2011-03-09 15:56:08 +00:00
|
|
|
Main.overview.connect('item-drag-cancelled',
|
|
|
|
Lang.bind(this, this._onDragCancelled));
|
2011-02-11 23:01:59 +00:00
|
|
|
Main.overview.connect('window-drag-begin',
|
|
|
|
Lang.bind(this, this._onDragBegin));
|
2011-03-09 15:56:08 +00:00
|
|
|
Main.overview.connect('window-drag-cancelled',
|
|
|
|
Lang.bind(this, this._onDragCancelled));
|
2011-02-11 23:01:59 +00:00
|
|
|
Main.overview.connect('window-drag-end',
|
|
|
|
Lang.bind(this, this._onDragEnd));
|
2010-11-08 01:51:02 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
_onDragBegin: function() {
|
2011-03-09 15:56:08 +00:00
|
|
|
this._dragCancelled = false;
|
2010-11-08 01:51:02 +00:00
|
|
|
this._dragMonitor = {
|
|
|
|
dragMotion: Lang.bind(this, this._onDragMotion)
|
|
|
|
};
|
|
|
|
DND.addDragMonitor(this._dragMonitor);
|
|
|
|
},
|
|
|
|
|
2011-03-09 15:56:08 +00:00
|
|
|
_onDragCancelled: function() {
|
|
|
|
this._dragCancelled = true;
|
|
|
|
this._endDrag();
|
|
|
|
},
|
|
|
|
|
2010-11-08 01:51:02 +00:00
|
|
|
_onDragEnd: function() {
|
2011-03-09 15:56:08 +00:00
|
|
|
if (this._dragCancelled)
|
|
|
|
return;
|
|
|
|
|
|
|
|
this._endDrag();
|
|
|
|
},
|
|
|
|
|
|
|
|
_endDrag: function() {
|
2010-11-08 01:51:02 +00:00
|
|
|
this._clearDragPlaceholder();
|
|
|
|
if (this._favRemoveTarget) {
|
2011-01-21 11:06:13 +00:00
|
|
|
this._favRemoveTarget.actor.hide();
|
2010-12-16 21:13:47 +00:00
|
|
|
this._adjustIconSize();
|
2011-01-21 11:06:13 +00:00
|
|
|
this._favRemoveTarget.actor.show();
|
|
|
|
|
|
|
|
this._favRemoveTarget.animateOutAndDestroy();
|
|
|
|
this._favRemoveTarget.actor.connect('destroy', Lang.bind(this,
|
|
|
|
function() {
|
|
|
|
this._favRemoveTarget = null;
|
|
|
|
}));
|
2010-11-08 01:51:02 +00:00
|
|
|
}
|
|
|
|
DND.removeMonitor(this._dragMonitor);
|
|
|
|
},
|
|
|
|
|
|
|
|
_onDragMotion: function(dragEvent) {
|
|
|
|
let app = null;
|
|
|
|
if (dragEvent.source instanceof AppDisplay.AppWellIcon)
|
|
|
|
app = this._appSystem.get_app(dragEvent.source.getId());
|
2011-01-31 01:25:00 +00:00
|
|
|
else if (dragEvent.source.metaWindow)
|
2010-11-08 01:51:02 +00:00
|
|
|
app = this._tracker.get_window_app(dragEvent.source.metaWindow);
|
|
|
|
else
|
|
|
|
return DND.DragMotionResult.CONTINUE;
|
|
|
|
|
|
|
|
let id = app.get_id();
|
|
|
|
|
|
|
|
let favorites = AppFavorites.getAppFavorites().getFavoriteMap();
|
|
|
|
|
|
|
|
let srcIsFavorite = (id in favorites);
|
|
|
|
|
|
|
|
if (srcIsFavorite && this._favRemoveTarget == null) {
|
|
|
|
this._favRemoveTarget = new RemoveFavoriteIcon();
|
2011-02-12 00:17:10 +00:00
|
|
|
this._favRemoveTarget.icon.setIconSize(this.iconSize);
|
2010-11-08 01:51:02 +00:00
|
|
|
this._box.add(this._favRemoveTarget.actor);
|
2010-12-16 21:13:47 +00:00
|
|
|
this._adjustIconSize();
|
2011-01-21 11:06:13 +00:00
|
|
|
this._favRemoveTarget.animateIn();
|
2010-11-08 01:51:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let favRemoveHovered = false;
|
|
|
|
if (this._favRemoveTarget)
|
|
|
|
favRemoveHovered =
|
|
|
|
this._favRemoveTarget.actor.contains(dragEvent.targetActor);
|
|
|
|
|
|
|
|
if (!this._box.contains(dragEvent.targetActor) || favRemoveHovered)
|
|
|
|
this._clearDragPlaceholder();
|
|
|
|
|
|
|
|
if (this._favRemoveTarget)
|
|
|
|
this._favRemoveTarget.setHover(favRemoveHovered);
|
|
|
|
|
|
|
|
return DND.DragMotionResult.CONTINUE;
|
|
|
|
},
|
|
|
|
|
2010-07-21 23:29:02 +00:00
|
|
|
_appIdListToHash: function(apps) {
|
|
|
|
let ids = {};
|
|
|
|
for (let i = 0; i < apps.length; i++)
|
|
|
|
ids[apps[i].get_id()] = apps[i];
|
|
|
|
return ids;
|
|
|
|
},
|
|
|
|
|
|
|
|
_queueRedisplay: function () {
|
|
|
|
Main.queueDeferredWork(this._workId);
|
|
|
|
},
|
|
|
|
|
2011-01-20 15:23:43 +00:00
|
|
|
_createAppItem: function(app) {
|
2011-01-13 15:02:55 +00:00
|
|
|
let display = new AppDisplay.AppWellIcon(app,
|
2011-01-20 14:27:13 +00:00
|
|
|
{ setSizeManually: true,
|
|
|
|
showLabel: false });
|
2010-11-08 01:51:02 +00:00
|
|
|
display._draggable.connect('drag-begin',
|
|
|
|
Lang.bind(this, function() {
|
|
|
|
display.actor.opacity = 50;
|
|
|
|
}));
|
|
|
|
display._draggable.connect('drag-end',
|
|
|
|
Lang.bind(this, function() {
|
|
|
|
display.actor.opacity = 255;
|
|
|
|
}));
|
2011-02-10 00:41:51 +00:00
|
|
|
display.actor.set_tooltip_text(app.get_name());
|
|
|
|
|
2011-01-20 15:23:43 +00:00
|
|
|
let item = new DashItemContainer();
|
|
|
|
item.setChild(display.actor);
|
|
|
|
|
2011-02-12 00:17:10 +00:00
|
|
|
display.icon.setIconSize(this.iconSize);
|
2010-11-27 21:31:14 +00:00
|
|
|
|
2011-01-20 15:23:43 +00:00
|
|
|
return item;
|
2010-11-08 01:51:02 +00:00
|
|
|
},
|
|
|
|
|
2010-11-27 21:31:14 +00:00
|
|
|
_adjustIconSize: function() {
|
|
|
|
let children = this._box.get_children();
|
|
|
|
if (children.length == 0) {
|
|
|
|
this._box.add_style_pseudo_class('empty');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this._box.remove_style_pseudo_class('empty');
|
|
|
|
|
|
|
|
if (this._maxHeight == -1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
let iconChildren = children.filter(function(actor) {
|
2011-01-20 15:23:43 +00:00
|
|
|
return actor.visible &&
|
|
|
|
actor._delegate.child &&
|
|
|
|
actor._delegate.child._delegate &&
|
|
|
|
actor._delegate.child._delegate.icon;
|
2010-11-27 21:31:14 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
// Compute the amount of extra space (or missing space) we have
|
|
|
|
// per icon with the current icon size
|
|
|
|
let [minHeight, natHeight] = this.actor.get_preferred_height(-1);
|
|
|
|
let diff = (this._maxHeight - natHeight) / iconChildren.length;
|
|
|
|
|
2011-02-09 22:40:48 +00:00
|
|
|
let iconSizes = [ 16, 22, 24, 32, 48, 64 ];
|
2010-11-27 21:31:14 +00:00
|
|
|
|
|
|
|
let newIconSize = 16;
|
|
|
|
for (let i = 0; i < iconSizes.length; i++) {
|
2011-02-12 00:17:10 +00:00
|
|
|
if (iconSizes[i] < this.iconSize + diff)
|
2010-11-27 21:31:14 +00:00
|
|
|
newIconSize = iconSizes[i];
|
|
|
|
}
|
|
|
|
|
2011-02-12 00:17:10 +00:00
|
|
|
if (newIconSize == this.iconSize)
|
2010-11-27 21:31:14 +00:00
|
|
|
return;
|
|
|
|
|
2011-02-12 00:17:10 +00:00
|
|
|
let oldIconSize = this.iconSize;
|
|
|
|
this.iconSize = newIconSize;
|
2010-11-27 21:31:14 +00:00
|
|
|
|
2011-01-21 11:06:13 +00:00
|
|
|
let scale = oldIconSize / newIconSize;
|
2010-11-27 21:31:14 +00:00
|
|
|
for (let i = 0; i < iconChildren.length; i++) {
|
2011-01-20 15:23:43 +00:00
|
|
|
let icon = iconChildren[i]._delegate.child._delegate.icon;
|
2011-01-21 11:06:13 +00:00
|
|
|
|
|
|
|
// Set the new size immediately, to keep the icons' sizes
|
2011-02-12 00:17:10 +00:00
|
|
|
// in sync with this.iconSize
|
|
|
|
icon.setIconSize(this.iconSize);
|
2011-02-09 22:55:50 +00:00
|
|
|
|
|
|
|
// Don't animate the icon size change when the overview
|
|
|
|
// is not visible or when initially filling the dash
|
|
|
|
if (!Main.overview.visible || !this._shownInitially)
|
|
|
|
continue;
|
|
|
|
|
2011-01-21 11:06:13 +00:00
|
|
|
let [targetWidth, targetHeight] = icon.icon.get_size();
|
|
|
|
|
|
|
|
// Scale the icon's texture to the previous size and
|
|
|
|
// tween to the new size
|
|
|
|
icon.icon.set_size(icon.icon.width * scale,
|
|
|
|
icon.icon.height * scale);
|
|
|
|
|
|
|
|
Tweener.addTween(icon.icon,
|
|
|
|
{ width: targetWidth,
|
|
|
|
height: targetHeight,
|
|
|
|
time: DASH_ANIMATION_TIME,
|
|
|
|
transition: 'easeOutQuad'
|
|
|
|
});
|
2010-11-27 21:31:14 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2010-07-21 23:29:02 +00:00
|
|
|
_redisplay: function () {
|
|
|
|
let favorites = AppFavorites.getAppFavorites().getFavoriteMap();
|
|
|
|
|
|
|
|
/* hardcode here pending some design about how exactly desktop contexts behave */
|
|
|
|
let contextId = '';
|
|
|
|
|
|
|
|
let running = this._tracker.get_running_apps(contextId);
|
|
|
|
|
2011-01-13 12:44:40 +00:00
|
|
|
let children = this._box.get_children().filter(function(actor) {
|
2011-01-20 15:23:43 +00:00
|
|
|
return actor._delegate.child &&
|
|
|
|
actor._delegate.child._delegate &&
|
|
|
|
actor._delegate.child._delegate.app;
|
2011-01-13 12:44:40 +00:00
|
|
|
});
|
|
|
|
// Apps currently in the dash
|
|
|
|
let oldApps = children.map(function(actor) {
|
2011-01-20 15:23:43 +00:00
|
|
|
return actor._delegate.child._delegate.app;
|
2011-01-13 12:44:40 +00:00
|
|
|
});
|
|
|
|
// Apps supposed to be in the dash
|
|
|
|
let newApps = [];
|
|
|
|
|
|
|
|
for (let id in favorites)
|
|
|
|
newApps.push(favorites[id]);
|
2010-07-21 23:29:02 +00:00
|
|
|
|
|
|
|
for (let i = 0; i < running.length; i++) {
|
|
|
|
let app = running[i];
|
|
|
|
if (app.get_id() in favorites)
|
|
|
|
continue;
|
2011-01-13 12:44:40 +00:00
|
|
|
newApps.push(app);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Figure out the actual changes to the list of items; we iterate
|
|
|
|
// over both the list of items currently in the dash and the list
|
|
|
|
// of items expected there, and collect additions and removals.
|
|
|
|
// Moves are both an addition and a removal, where the order of
|
|
|
|
// the operations depends on whether we encounter the position
|
|
|
|
// where the item has been added first or the one from where it
|
|
|
|
// was removed.
|
|
|
|
// There is an assumption that only one item is moved at a given
|
|
|
|
// time; when moving several items at once, everything will still
|
|
|
|
// end up at the right position, but there might be additional
|
|
|
|
// additions/removals (e.g. it might remove all the launchers
|
|
|
|
// and add them back in the new order even if a smaller set of
|
|
|
|
// additions and removals is possible).
|
|
|
|
// If above assumptions turns out to be a problem, we might need
|
|
|
|
// to use a more sophisticated algorithm, e.g. Longest Common
|
|
|
|
// Subsequence as used by diff.
|
|
|
|
let addedItems = [];
|
|
|
|
let removedActors = [];
|
|
|
|
|
|
|
|
let newIndex = 0;
|
|
|
|
let oldIndex = 0;
|
|
|
|
while (newIndex < newApps.length || oldIndex < oldApps.length) {
|
|
|
|
// No change at oldIndex/newIndex
|
|
|
|
if (oldApps[oldIndex] == newApps[newIndex]) {
|
|
|
|
oldIndex++;
|
|
|
|
newIndex++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// App removed at oldIndex
|
|
|
|
if (oldApps[oldIndex] &&
|
|
|
|
newApps.indexOf(oldApps[oldIndex]) == -1) {
|
|
|
|
removedActors.push(children[oldIndex]);
|
|
|
|
oldIndex++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// App added at newIndex
|
|
|
|
if (newApps[newIndex] &&
|
|
|
|
oldApps.indexOf(newApps[newIndex]) == -1) {
|
|
|
|
addedItems.push({ app: newApps[newIndex],
|
2011-01-20 15:23:43 +00:00
|
|
|
item: this._createAppItem(newApps[newIndex]),
|
2011-01-13 12:44:40 +00:00
|
|
|
pos: newIndex });
|
|
|
|
newIndex++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// App moved
|
|
|
|
let insertHere = newApps[newIndex + 1] &&
|
|
|
|
newApps[newIndex + 1] == oldApps[oldIndex];
|
|
|
|
let alreadyRemoved = removedActors.reduce(function(result, actor) {
|
2011-01-20 15:23:43 +00:00
|
|
|
let removedApp = actor._delegate.child._delegate.app;
|
2011-01-13 12:44:40 +00:00
|
|
|
return result || removedApp == newApps[newIndex];
|
|
|
|
}, false);
|
|
|
|
|
|
|
|
if (insertHere || alreadyRemoved) {
|
2011-01-20 15:23:43 +00:00
|
|
|
let newItem = this._createAppItem(newApps[newIndex]);
|
2011-01-13 12:44:40 +00:00
|
|
|
addedItems.push({ app: newApps[newIndex],
|
2011-01-20 15:23:43 +00:00
|
|
|
item: newItem,
|
2011-01-13 12:44:40 +00:00
|
|
|
pos: newIndex + removedActors.length });
|
|
|
|
newIndex++;
|
|
|
|
} else {
|
|
|
|
removedActors.push(children[oldIndex]);
|
|
|
|
oldIndex++;
|
|
|
|
}
|
2010-07-21 23:29:02 +00:00
|
|
|
}
|
|
|
|
|
2011-01-13 12:44:40 +00:00
|
|
|
for (let i = 0; i < addedItems.length; i++)
|
2011-01-20 15:23:43 +00:00
|
|
|
this._box.insert_actor(addedItems[i].item.actor,
|
|
|
|
addedItems[i].pos);
|
2011-01-13 12:44:40 +00:00
|
|
|
|
2011-01-21 11:06:13 +00:00
|
|
|
// Hide removed actors to not take them into account
|
|
|
|
// when adjusting the icon size ...
|
2011-01-13 12:44:40 +00:00
|
|
|
for (let i = 0; i < removedActors.length; i++)
|
2011-01-21 11:06:13 +00:00
|
|
|
removedActors[i].hide();
|
|
|
|
|
|
|
|
// ... and do the same for the remove target if necessary
|
|
|
|
if (this._favRemoveTarget && this._favRemoveTarget.hiding)
|
|
|
|
this._favRemoveTarget.actor.hide();
|
2011-01-13 12:44:40 +00:00
|
|
|
|
2010-11-27 21:31:14 +00:00
|
|
|
this._adjustIconSize();
|
2011-01-21 11:06:13 +00:00
|
|
|
|
|
|
|
if (this._favRemoveTarget && this._favRemoveTarget.hiding)
|
|
|
|
this._favRemoveTarget.actor.show();
|
|
|
|
|
2011-02-02 15:44:07 +00:00
|
|
|
// Skip animations on first run when adding the initial set
|
|
|
|
// of items, to avoid all items zooming in at once
|
|
|
|
if (!this._shownInitially) {
|
|
|
|
this._shownInitially = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-01-21 11:06:13 +00:00
|
|
|
for (let i = 0; i < removedActors.length; i++) {
|
|
|
|
removedActors[i].show();
|
|
|
|
let item = removedActors[i]._delegate;
|
2011-02-06 16:46:12 +00:00
|
|
|
|
|
|
|
// Don't animate item removal when the overview is hidden
|
|
|
|
if (Main.overview.visible)
|
|
|
|
item.animateOutAndDestroy();
|
|
|
|
else
|
|
|
|
item.actor.destroy();
|
2011-01-21 11:06:13 +00:00
|
|
|
}
|
|
|
|
|
2011-02-06 16:46:12 +00:00
|
|
|
// Don't animate item addition when the overview is hidden
|
|
|
|
if (!Main.overview.visible)
|
|
|
|
return;
|
|
|
|
|
2011-01-21 11:06:13 +00:00
|
|
|
for (let i = 0; i < addedItems.length; i++)
|
|
|
|
addedItems[i].item.animateIn();
|
2010-07-21 23:29:02 +00:00
|
|
|
},
|
|
|
|
|
2010-11-08 01:51:02 +00:00
|
|
|
_clearDragPlaceholder: function() {
|
|
|
|
if (this._dragPlaceholder) {
|
2011-01-21 11:06:13 +00:00
|
|
|
this._dragPlaceholder.animateOutAndDestroy();
|
2010-11-08 01:51:02 +00:00
|
|
|
this._dragPlaceholder = null;
|
|
|
|
this._dragPlaceholderPos = -1;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2010-07-21 23:29:02 +00:00
|
|
|
handleDragOver : function(source, actor, x, y, time) {
|
|
|
|
let app = null;
|
|
|
|
if (source instanceof AppDisplay.AppWellIcon)
|
|
|
|
app = this._appSystem.get_app(source.getId());
|
2011-01-31 01:25:00 +00:00
|
|
|
else if (source.metaWindow)
|
2010-07-21 23:29:02 +00:00
|
|
|
app = this._tracker.get_window_app(source.metaWindow);
|
|
|
|
|
|
|
|
// Don't allow favoriting of transient apps
|
|
|
|
if (app == null || app.is_transient())
|
|
|
|
return DND.DragMotionResult.NO_DROP;
|
|
|
|
|
2011-01-06 13:19:44 +00:00
|
|
|
let favorites = AppFavorites.getAppFavorites().getFavorites();
|
|
|
|
let numFavorites = favorites.length;
|
|
|
|
|
|
|
|
let favPos = favorites.indexOf(app);
|
|
|
|
|
2011-01-21 11:06:13 +00:00
|
|
|
let children = this._box.get_children();
|
|
|
|
let numChildren = children.length;
|
2010-11-08 01:51:02 +00:00
|
|
|
let boxHeight = this._box.height;
|
|
|
|
|
|
|
|
// Keep the placeholder out of the index calculation; assuming that
|
|
|
|
// the remove target has the same size as "normal" items, we don't
|
|
|
|
// need to do the same adjustment there.
|
|
|
|
if (this._dragPlaceholder) {
|
2011-01-20 15:23:43 +00:00
|
|
|
boxHeight -= this._dragPlaceholder.actor.height;
|
2010-11-08 01:51:02 +00:00
|
|
|
numChildren--;
|
|
|
|
}
|
|
|
|
|
|
|
|
let pos = Math.round(y * numChildren / boxHeight);
|
|
|
|
|
|
|
|
if (pos != this._dragPlaceholderPos && pos <= numFavorites) {
|
2011-01-21 11:06:13 +00:00
|
|
|
if (this._animatingPlaceholdersCount > 0) {
|
|
|
|
let appChildren = children.filter(function(actor) {
|
|
|
|
return actor._delegate &&
|
|
|
|
actor._delegate.child &&
|
|
|
|
actor._delegate.child._delegate &&
|
|
|
|
actor._delegate.child._delegate.app;
|
|
|
|
});
|
|
|
|
this._dragPlaceholderPos = children.indexOf(appChildren[pos]);
|
|
|
|
} else {
|
|
|
|
this._dragPlaceholderPos = pos;
|
|
|
|
}
|
2011-01-06 13:19:44 +00:00
|
|
|
|
|
|
|
// Don't allow positioning before or after self
|
2011-01-21 11:06:13 +00:00
|
|
|
if (favPos != -1 && (pos == favPos || pos == favPos + 1)) {
|
|
|
|
if (this._dragPlaceholder) {
|
|
|
|
this._dragPlaceholder.animateOutAndDestroy();
|
|
|
|
this._animatingPlaceholdersCount++;
|
|
|
|
this._dragPlaceholder.actor.connect('destroy',
|
|
|
|
Lang.bind(this, function() {
|
|
|
|
this._animatingPlaceholdersCount--;
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
this._dragPlaceholder = null;
|
|
|
|
|
2011-01-06 13:19:44 +00:00
|
|
|
return DND.DragMotionResult.CONTINUE;
|
2011-01-21 11:06:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// If the placeholder already exists, we just move
|
|
|
|
// it, but if we are adding it, expand its size in
|
|
|
|
// an animation
|
|
|
|
let fadeIn;
|
|
|
|
if (this._dragPlaceholder) {
|
|
|
|
this._dragPlaceholder.actor.destroy();
|
|
|
|
fadeIn = false;
|
|
|
|
} else {
|
|
|
|
fadeIn = true;
|
|
|
|
}
|
2011-01-06 13:19:44 +00:00
|
|
|
|
2011-01-20 15:23:43 +00:00
|
|
|
this._dragPlaceholder = new DragPlaceholderItem();
|
|
|
|
this._box.insert_actor(this._dragPlaceholder.actor,
|
|
|
|
this._dragPlaceholderPos);
|
2011-01-21 11:06:13 +00:00
|
|
|
if (fadeIn)
|
|
|
|
this._dragPlaceholder.animateIn();
|
2010-11-08 01:51:02 +00:00
|
|
|
}
|
|
|
|
|
2011-01-06 13:19:44 +00:00
|
|
|
let srcIsFavorite = (favPos != -1);
|
2010-07-21 23:29:02 +00:00
|
|
|
|
|
|
|
if (srcIsFavorite)
|
2010-11-08 01:51:02 +00:00
|
|
|
return DND.DragMotionResult.MOVE_DROP;
|
2010-07-21 23:29:02 +00:00
|
|
|
|
|
|
|
return DND.DragMotionResult.COPY_DROP;
|
|
|
|
},
|
|
|
|
|
|
|
|
// Draggable target interface
|
|
|
|
acceptDrop : function(source, actor, x, y, time) {
|
|
|
|
let app = null;
|
|
|
|
if (source instanceof AppDisplay.AppWellIcon) {
|
|
|
|
app = this._appSystem.get_app(source.getId());
|
2011-01-31 01:25:00 +00:00
|
|
|
} else if (source.metaWindow) {
|
2010-07-21 23:29:02 +00:00
|
|
|
app = this._tracker.get_window_app(source.metaWindow);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Don't allow favoriting of transient apps
|
|
|
|
if (app == null || app.is_transient()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
let id = app.get_id();
|
|
|
|
|
|
|
|
let favorites = AppFavorites.getAppFavorites().getFavoriteMap();
|
|
|
|
|
|
|
|
let srcIsFavorite = (id in favorites);
|
|
|
|
|
2010-11-08 01:51:02 +00:00
|
|
|
let favPos = 0;
|
|
|
|
let children = this._box.get_children();
|
|
|
|
for (let i = 0; i < this._dragPlaceholderPos; i++) {
|
2011-01-21 11:06:13 +00:00
|
|
|
if (this._dragPlaceholder &&
|
|
|
|
children[i] == this._dragPlaceholder.actor)
|
|
|
|
continue;
|
|
|
|
|
2011-01-20 15:23:43 +00:00
|
|
|
let childId = children[i]._delegate.child._delegate.app.get_id();
|
2010-11-08 01:51:02 +00:00
|
|
|
if (childId == id)
|
|
|
|
continue;
|
|
|
|
if (childId in favorites)
|
|
|
|
favPos++;
|
|
|
|
}
|
|
|
|
|
|
|
|
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this,
|
|
|
|
function () {
|
|
|
|
let appFavorites = AppFavorites.getAppFavorites();
|
|
|
|
if (srcIsFavorite)
|
|
|
|
appFavorites.moveFavoriteToPos(id, favPos);
|
|
|
|
else
|
|
|
|
appFavorites.addFavoriteAtPos(id, favPos);
|
2010-07-21 23:29:02 +00:00
|
|
|
return false;
|
|
|
|
}));
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
Signals.addSignalMethods(Dash.prototype);
|