[the-board: 5/10] [ui] Implement selection context toolbar
- From: Lucas Rocha <lucasr src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [the-board: 5/10] [ui] Implement selection context toolbar
- Date: Tue, 25 Jan 2011 22:40:10 +0000 (UTC)
commit 27114153f9a80b532c3c01bee41a697883a6cc91
Author: Lucas Rocha <lucasr gnome org>
Date: Mon Jan 24 00:49:41 2011 +0000
[ui] Implement selection context toolbar
It supports top/bottom/left/right alignment, vertical/horizontal
distribution and removal operations on selected things.
https://bugzilla.gnome.org/show_bug.cgi?id=636628
src/js/ui/mainWindow.js | 99 ++++++++++++++++-
src/js/ui/page.js | 281 +++++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 365 insertions(+), 15 deletions(-)
---
diff --git a/src/js/ui/mainWindow.js b/src/js/ui/mainWindow.js
index 9defe57..2475d52 100644
--- a/src/js/ui/mainWindow.js
+++ b/src/js/ui/mainWindow.js
@@ -70,6 +70,7 @@ MainWindow.prototype = {
this._createContext();
this._createMainToolbar();
+ this._createSelectionToolbar();
this._createSpinner();
// show loading screen as soon as possible
@@ -224,6 +225,25 @@ MainWindow.prototype = {
this._updateActiveToolbar();
},
+ _createSelectionToolbar : function() {
+ this._selectionToolbar =
+ new Toolbar.Toolbar({ title: Gettext.gettext("Selection"),
+ visible: false });
+
+ this._selectionToolbar.actor.depth = _LAYER_TOOLBOX;
+
+ this._createToolBoxAlign();
+ this._createToolBoxDistribute();
+ this._createToolBoxRemove();
+
+ this._contentBox.append(this._selectionToolbar.actor,
+ Tb.BoxPackFlags.FIXED);
+
+ this._contentBox.set_fixed_child_align(this._selectionToolbar.actor,
+ Tb.BoxAlignment.CENTER,
+ Tb.BoxAlignment.START);
+ },
+
_createSpinner : function() {
this._spinnerBox =
new Tb.Box({ orientation: Tb.BoxOrientation.VERTICAL,
@@ -254,6 +274,59 @@ MainWindow.prototype = {
this._mainToolbar.addToolBox(this._toolBoxPages);
},
+ _createToolBoxAlign : function() {
+ this._toolBoxAlign =
+ new ToolBox.ToolBox({ title: Gettext.gettext("Align") });
+
+ this._toolBoxAlign.addButton({ label: Gettext.gettext("Left"),
+ actionName: "align",
+ actionArgs: { alignment: Page.Alignment.LEFT } });
+
+ this._toolBoxAlign.addButton({ label: Gettext.gettext("Right"),
+ actionName: "align",
+ actionArgs: { alignment: Page.Alignment.RIGHT } });
+
+ this._toolBoxAlign.addButton({ label: Gettext.gettext("Top"),
+ actionName: "align",
+ actionArgs: { alignment: Page.Alignment.TOP } });
+
+ this._toolBoxAlign.addButton({ label: Gettext.gettext("Bottom"),
+ actionName: "align",
+ actionArgs: { alignment: Page.Alignment.BOTTOM } });
+
+ this._selectionToolbar.addToolBox(this._toolBoxAlign);
+ },
+
+ _createToolBoxDistribute : function() {
+ this._toolBoxDistribute =
+ new ToolBox.ToolBox({ title: Gettext.gettext("Distribute") });
+
+ let verticalArgs =
+ { label: Gettext.gettext("Vertical"),
+ actionName: "distribute",
+ actionArgs: { orientation: Page.Orientation.VERTICAL } };
+
+ this._toolBoxDistribute.addButton(verticalArgs);
+
+ let horizontalArgs =
+ { label: Gettext.gettext("Horizontal"),
+ actionName: "distribute",
+ actionArgs: { orientation: Page.Orientation.HORIZONTAL } };
+
+ this._toolBoxDistribute.addButton(horizontalArgs);
+
+ this._selectionToolbar.addToolBox(this._toolBoxDistribute);
+ },
+
+ _createToolBoxRemove : function() {
+ this._toolBoxRemove = new ToolBox.ToolBox();
+
+ this._toolBoxRemove.addButton({ label: Gettext.gettext("Remove"),
+ actionName: "remove" });
+
+ this._selectionToolbar.addToolBox(this._toolBoxRemove);
+ },
+
_createToolBoxThings : function() {
this._toolBoxThings =
new ToolBoxThings.ToolBoxThings({ mainWindow: this });
@@ -365,6 +438,9 @@ MainWindow.prototype = {
if (this._currentPage &&
this._currentPage.activeThing) {
activeToolbar = this._thingToolbar;
+ } else if (this._currentPage &&
+ this._currentPage.selectedThings.length > 1) {
+ activeToolbar = this._selectionToolbar;
} else {
activeToolbar = this._mainToolbar;
}
@@ -677,10 +753,11 @@ MainWindow.prototype = {
return;
}
- if (this._currentPage &&
- this._currentPage.activeThing) {
- this._currentPage.activeThing.doAction(actionName, actionArgs);
+ if (!this._currentPage) {
+ return;
}
+
+ this._currentPage.doAction(actionName, actionArgs);
},
_onClipboardTextReceived : function(clipboard, text, data) {
@@ -804,6 +881,10 @@ MainWindow.prototype = {
}
},
+ _onPageSelectedThingsChanged : function() {
+ this._updateActiveToolbar();
+ },
+
setCurrentPage : function(pageModel) {
if (this._currentPage &&
this._currentPage.model.id == pageModel.id) {
@@ -821,6 +902,9 @@ MainWindow.prototype = {
oldCurrentPage.disconnect(this._pageLoadedChangedId);
delete this._pageLoadedChangedId;
+
+ oldCurrentPage.disconnect(this._pageSelectedThingsChangedId);
+ delete this._pageSelectedThingsChangedId;
}
this._currentPage = new Page.Page({ context: this._context,
@@ -842,6 +926,10 @@ MainWindow.prototype = {
this._currentPage.connect("loaded-changed",
Lang.bind(this, this._onPageLoadedChanged));
+ this._pageSelectedThingsChangedId =
+ this._currentPage.connect("selected-things-changed",
+ Lang.bind(this, this._onPageSelectedThingsChanged));
+
if (oldCurrentPage) {
let onModelSaved = function() {
this._setSavingOldCurrentPage(false);
@@ -900,6 +988,11 @@ MainWindow.prototype = {
delete this._pageLoadedChangedId;
}
+ if (this._pageSelectedThingsChangedId) {
+ this._currentPage.disconnect(this._pageSelectedThingsChangedId);
+ delete this._pageSelectedThingsChangedId;
+ }
+
if (this._mainBox) {
this._mainBox.destroy();
delete this._mainBox;
diff --git a/src/js/ui/page.js b/src/js/ui/page.js
index 3b44605..68e121f 100644
--- a/src/js/ui/page.js
+++ b/src/js/ui/page.js
@@ -25,6 +25,9 @@ const _LAYER_SELECTION = 0.4;
const _ADD_THING_TIME = 0.5;
const _ADD_THING_TRANSITION = 'easeOutCubic';
+const _MOVE_THING_TIME = 0.5;
+const _MOVE_THING_TRANSITION = 'easeOutCubic';
+
const _SET_BACKGROUND_TIME = 0.3;
const _SET_BACKGROUND_TRANSITION = 'easeOutCubic';
@@ -38,6 +41,21 @@ const _NEW_THING_ROWS_INC = 10;
const _N_COLS_SLIDE_IN = 3;
const _N_ROWS_SLIDE_IN = 3;
+const _DISTRIBUTE_VERTICAL_SPACING = 10;
+const _DISTRIBUTE_HORIZONTAL_SPACING = 20;
+
+let Alignment = {
+ LEFT : 0,
+ RIGHT : 1,
+ TOP : 2,
+ BOTTOM : 3
+};
+
+let Orientation = {
+ VERTICAL : 0,
+ HORIZONTAL : 1
+};
+
function Page(args) {
this._init(args);
}
@@ -421,6 +439,236 @@ Page.prototype = {
}
},
+ _animateMoveThing : function(thing, x, y) {
+ let [deltaX, deltaY] =
+ this._clampMoveDelta(thing.area,
+ x - thing.area.x1,
+ y - thing.area.y1);
+
+ thing.actor.anchorX = deltaX;
+ thing.actor.anchorY = deltaY;
+
+ thing.setPosition(thing.area.x1 + deltaX,
+ thing.area.y1 + deltaY);
+
+ thing.emit("save");
+
+ Tweener.addTween(thing.actor,
+ { anchorX: 0,
+ anchorY: 0,
+ time: _MOVE_THING_TIME,
+ transition: _MOVE_THING_TRANSITION });
+ },
+
+ _alignSelectedThings : function(alignment) {
+ let alignmentCoord = -1;
+
+ let updateAlignmentCoord = function(thing) {
+ let area = thing.area;
+
+ switch(alignment) {
+ case Alignment.LEFT:
+ if (alignmentCoord < 0 ||
+ alignmentCoord > area.x1) {
+ alignmentCoord = area.x1;
+ }
+ break;
+
+ case Alignment.RIGHT:
+ if (alignmentCoord < 0 ||
+ alignmentCoord < area.x2) {
+ alignmentCoord = area.x2;
+ }
+ break;
+
+ case Alignment.TOP:
+ if (alignmentCoord < 0 ||
+ alignmentCoord > area.y1) {
+ alignmentCoord = area.y1;
+ }
+ break;
+
+ case Alignment.BOTTOM:
+ if (alignmentCoord < 0 ||
+ alignmentCoord < area.y2) {
+ alignmentCoord = area.y2;
+ }
+ break;
+ }
+ };
+
+ this._selectedThings.forEach(updateAlignmentCoord);
+
+ let moveThing = function(thing) {
+ let area = thing.area;
+
+ let x, y;
+
+ switch(alignment) {
+ case Alignment.LEFT:
+ x = alignmentCoord;
+ y = area.y1;
+ break;
+
+ case Alignment.RIGHT:
+ x = alignmentCoord - (area.x2 - area.x1);
+ y = area.y1;
+ break;
+
+ case Alignment.TOP:
+ x = area.x1;
+ y = alignmentCoord;
+ break;
+
+ case Alignment.BOTTOM:
+ x = area.x1;
+ y = alignmentCoord - (area.y2 - area.y1);
+ break;
+ }
+
+ this._animateMoveThing(thing, x, y);
+ };
+
+ this._selectedThings.forEach(Lang.bind(this, moveThing));
+
+ this._updateDragArea();
+ },
+
+ _distributeSelectedThings : function(orientation) {
+ let sortByCoord = function(thingA, thingB) {
+ switch(orientation) {
+ case Orientation.VERTICAL:
+ return thingA.area.x1 - thingB.area.x1;
+ break;
+
+ case Orientation.HORIZONTAL:
+ return thingA.area.y1 - thingB.area.y1;
+ break;
+ }
+
+ return 0;
+ };
+
+ this._selectedThings.sort(sortByCoord);
+
+ let pageWidth = this._mainBox.allocation.x2 -
+ this._mainBox.allocation.x1;
+
+ let pageHeight = this._mainBox.allocation.y2 -
+ this._mainBox.allocation.y1;
+
+ let centerCoord = -1;
+ let currentCoord = -1;
+ let totalSize = 0;
+
+ let updateCoords = function(thing) {
+ switch(orientation) {
+ case Orientation.VERTICAL:
+ totalSize += thing.area.y2 - thing.area.y1 +
+ _DISTRIBUTE_VERTICAL_SPACING;
+ break;
+
+ case Orientation.HORIZONTAL:
+ totalSize += thing.area.x2 - thing.area.x1 +
+ _DISTRIBUTE_HORIZONTAL_SPACING;
+ break;
+ }
+ };
+
+ this._selectedThings.forEach(updateCoords);
+
+ let moveThing = function(thing) {
+ let area = thing.area;
+
+ let x, y;
+
+ switch(orientation) {
+ case Orientation.VERTICAL:
+ if (currentCoord < 0) {
+ if (totalSize > pageHeight - area.y1) {
+ currentCoord = pageHeight - totalSize;
+ } else {
+ currentCoord = area.y1;
+ }
+ }
+
+ let width = area.x2 - area.x1;
+ let verticalCenter = area.x1 + (width / 2);
+
+ if (centerCoord < 0) {
+ centerCoord = verticalCenter;
+ }
+
+ x = area.x1 + (centerCoord - verticalCenter);
+ y = currentCoord;
+
+ currentCoord += area.y2 - area.y1 +
+ _DISTRIBUTE_VERTICAL_SPACING;
+
+ break;
+
+ case Orientation.HORIZONTAL:
+ if (currentCoord < 0) {
+ if (totalSize > pageWidth - area.x1) {
+ currentCoord = pageWidth - totalSize;
+ } else {
+ currentCoord = area.x1;
+ }
+ }
+
+ let height = area.y2 - area.y1;
+ let horizontalCenter = area.y1 + (height / 2);
+
+ if (centerCoord < 0) {
+ centerCoord = horizontalCenter;
+ }
+
+ x = currentCoord;
+ y = area.y1 + (centerCoord - horizontalCenter);
+
+ currentCoord += area.x2 - area.x1 +
+ _DISTRIBUTE_HORIZONTAL_SPACING;
+
+ break;
+ }
+
+ this._animateMoveThing(thing, x, y);
+ };
+
+ this._selectedThings.forEach(Lang.bind(this, moveThing));
+
+ this._updateDragArea();
+ },
+
+ _removeSelectedThings : function() {
+ // Store current list of selected things
+ let selectedThings = this._selectedThings;
+
+ // Clean up the list of selected things
+ // as they are not removed
+ this._selectedThings = [];
+
+ // Actually remove them from page
+ selectedThings.forEach(Lang.bind(this, this.removeThing));
+ },
+
+ _clampMoveDelta : function(area, deltaX, deltaY) {
+ let minDeltaX = -area.x1;
+ let maxDeltaX = this._mainBox.allocation.x2 -
+ this._mainBox.allocation.x1 -
+ area.x2;
+
+ let minDeltaY = -area.y1;
+ let maxDeltaY = this._mainBox.allocation.y2 -
+ this._mainBox.allocation.y1 -
+ area.y2;
+
+ deltaX = MathUtil.clamp(deltaX, minDeltaX, maxDeltaX);
+ deltaY = MathUtil.clamp(deltaY, minDeltaY, maxDeltaY);
+
+ return [deltaX, deltaY];
+ },
+
_onMainBoxClicked : function(o, event) {
if (this._cancelSelectionOnClick) {
this.unselectAllThings();
@@ -497,18 +745,8 @@ Page.prototype = {
},
_onThingDragMotion : function(thing, deltaX, deltaY) {
- let minDeltaX = -this._dragArea.x1;
- let maxDeltaX = this._mainBox.allocation.x2 -
- this._mainBox.allocation.x1 -
- this._dragArea.x2;
-
- let minDeltaY = -this._dragArea.y1;
- let maxDeltaY = this._mainBox.allocation.y2 -
- this._mainBox.allocation.y1 -
- this._dragArea.y2;
-
- deltaX = MathUtil.clamp(deltaX, minDeltaX, maxDeltaX);
- deltaY = MathUtil.clamp(deltaY, minDeltaY, maxDeltaY);
+ [deltaX, deltaY] =
+ this._clampMoveDelta(this._dragArea, deltaX, deltaY);
this._dragArea.x1 += deltaX;
this._dragArea.x2 += deltaX;
@@ -616,6 +854,11 @@ Page.prototype = {
let indexToRemove = this._things.indexOf(thing);
this._things.splice(indexToRemove, 1);
+ // Ensure the removed thing is unselected
+ // before being destroyed to avoid any dangling
+ // reference to it
+ thing.selected = false;
+
thing.disconnect(thing._Page_activateId);
thing.disconnect(thing._Page_deactivateId);
thing.disconnect(thing._Page_dragBeginId);
@@ -687,6 +930,20 @@ Page.prototype = {
this._things.forEach(unselectThing);
},
+ doAction : function(actionName, actionArgs) {
+ if (this._activeThing) {
+ this._activeThing.doAction(actionName, actionArgs);
+ } else if (this._selectedThings.length > 1) {
+ if (actionName == "align") {
+ this._alignSelectedThings(actionArgs.alignment);
+ } else if (actionName == "distribute") {
+ this._distributeSelectedThings(actionArgs.orientation);
+ } else if (actionName == "remove") {
+ this._removeSelectedThings();
+ }
+ }
+ },
+
destroy : function() {
this._destroyAllThings();
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]