[gnome-shell/overlay-design02] Replace main AppDisplay with AppWell
- From: Colin Walters <walters src gnome org>
- To: svn-commits-list gnome org
- Subject: [gnome-shell/overlay-design02] Replace main AppDisplay with AppWell
- Date: Fri, 26 Jun 2009 00:36:36 +0000 (UTC)
commit e57b7ec3359ce39e4fa360c744883fb37a069a49
Author: Colin Walters <walters verbum org>
Date: Thu Jun 25 17:45:06 2009 -0400
Replace main AppDisplay with AppWell
This is a start towards implementing the 02 overlay design. The
default applications has moved into GConf. We keep around an AppDisplay
instance for handling the right side behavior.
js/misc/appInfo.js | 68 +++++++++---------
js/ui/appDisplay.js | 197 +++++++++++++++++++++++++++++++++++++++++++++++++++
js/ui/overlay.js | 58 ++--------------
3 files changed, 235 insertions(+), 88 deletions(-)
---
diff --git a/js/misc/appInfo.js b/js/misc/appInfo.js
index 32f9bd0..61b36bd 100644
--- a/js/misc/appInfo.js
+++ b/js/misc/appInfo.js
@@ -7,32 +7,6 @@ const Shell = imports.gi.Shell;
const Main = imports.ui.main;
-// TODO - move this into GConf once we're not a plugin anymore
-// but have taken over metacity
-// This list is taken from GNOME Online popular applications
-// http://online.gnome.org/applications
-// but with nautilus removed (since it should already be running)
-// and evince, totem, and gnome-file-roller removed (since they're
-// usually started by opening documents, not by opening the app
-// directly)
-const DEFAULT_APPLICATIONS = [
- 'mozilla-firefox.desktop',
- 'gnome-terminal.desktop',
- 'evolution.desktop',
- 'gedit.desktop',
- 'mozilla-thunderbird.desktop',
- 'rhythmbox.desktop',
- 'epiphany.desktop',
- 'xchat.desktop',
- 'openoffice.org-1.9-writer.desktop',
- 'emacs.desktop',
- 'gnome-system-monitor.desktop',
- 'openoffice.org-1.9-calc.desktop',
- 'eclipse.desktop',
- 'openoffice.org-1.9-impress.desktop',
- 'vncviewer.desktop'
-];
-
function AppInfo(appId) {
this._init(appId);
}
@@ -41,8 +15,9 @@ AppInfo.prototype = {
_init : function(appId) {
this.appId = appId;
this._gAppInfo = Gio.DesktopAppInfo.new(appId);
- if (!this._gAppInfo)
+ if (!this._gAppInfo) {
throw new Error('Unknown appId ' + appId);
+ }
this.id = this._gAppInfo.get_id();
this.name = this._gAppInfo.get_name();
@@ -120,16 +95,39 @@ function getMostUsedApps(count) {
}
}
+ let favs = getFavorites();
// Fill the list with default applications it's not full yet
- for (let i = 0; i < DEFAULT_APPLICATIONS.length && matches.length <= count; i++) {
- let appId = DEFAULT_APPLICATIONS[i];
- if (alreadyAdded[appId])
- continue;
-
- let appInfo = getAppInfo(appId);
- if (appInfo)
- matches.push(appInfo);
+ for (let i = 0; i < favs.length && favs.length <= count; i++) {
+ matches.push(favs[i]);
}
return matches;
}
+
+function _idListToInfos(ids) {
+ let infos = [];
+ for (let i = 0; i < ids.length; i++) {
+ let display = getAppInfo(ids[i]);
+ if (display == null)
+ continue;
+ infos.push(display);
+ }
+ return infos;
+}
+
+function getFavorites() {
+ let system = Shell.AppSystem.get_default();
+
+ return _idListToInfos(system.get_favorites());
+}
+
+function getRunning() {
+ let monitor = Shell.AppMonitor.get_default();
+ let basename = function (n) {
+ let i = n.lastIndexOf('/');
+ if (i < 0)
+ return n;
+ return n.substring(i+1);
+ }
+ return _idListToInfos(monitor.get_running_app_ids().map(basename));
+}
diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js
index 1f2a6b7..3729b14 100644
--- a/js/ui/appDisplay.js
+++ b/js/ui/appDisplay.js
@@ -5,16 +5,21 @@ const Clutter = imports.gi.Clutter;
const Pango = imports.gi.Pango;
const Gio = imports.gi.Gio;
const Gtk = imports.gi.Gtk;
+const Tidy = imports.gi.Tidy;
const Shell = imports.gi.Shell;
const Lang = imports.lang;
const Signals = imports.signals;
+const Mainloop = imports.mainloop;
const AppInfo = imports.misc.appInfo;
+const DND = imports.ui.dnd;
const GenericDisplay = imports.ui.genericDisplay;
const ENTERED_MENU_COLOR = new Clutter.Color();
ENTERED_MENU_COLOR.from_pixel(0x00ff0022);
+const APP_ICON_SIZE = 48;
+
const MENU_ICON_SIZE = 24;
const MENU_SPACING = 15;
@@ -147,6 +152,7 @@ MenuItem.prototype = {
}
Signals.addSignalMethods(MenuItem.prototype);
+
/* This class represents a display containing a collection of application items.
* The applications are sorted based on their popularity by default, and based on
* their name if some search filter is applied.
@@ -428,3 +434,194 @@ AppDisplay.prototype = {
};
Signals.addSignalMethods(AppDisplay.prototype);
+
+function WellDisplayItem(appInfo, isFavorite) {
+ this._init(appInfo, isFavorite);
+}
+
+WellDisplayItem.prototype = {
+ _init : function(appInfo, isFavorite) {
+ this.appInfo = appInfo;
+
+ this.isFavorite = isFavorite;
+
+ this.actor = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
+ width: APP_ICON_SIZE,
+ reactive: true });
+ this.actor._delegate = this;
+ this.actor.connect('button-release-event', Lang.bind(this, function (b, e) {
+ this.launch();
+ this.emit('activated');
+ }));
+
+ let draggable = DND.makeDraggable(this.actor);
+
+ this._icon = appInfo.getIcon(APP_ICON_SIZE);
+
+ this.actor.append(this._icon, Big.BoxPackFlags.NONE);
+
+ let count = Shell.AppMonitor.get_default().get_window_count(appInfo.appId);
+
+ this._name = new Clutter.Text({ color: GenericDisplay.ITEM_DISPLAY_NAME_COLOR,
+ font_name: "Sans 12px",
+ ellipsize: Pango.EllipsizeMode.END,
+ text: appInfo.name });
+ if (count > 0) {
+ let runningBox = new Big.Box({ /* border_color: GenericDisplay.ITEM_DISPLAY_NAME_COLOR,
+ border: 1,
+ padding: 1 */ });
+ runningBox.append(this._name, Big.BoxPackFlags.EXPAND);
+ this.actor.append(runningBox, Big.BoxPackFlags.NONE);
+ } else {
+ this.actor.append(this._name, Big.BoxPackFlags.NONE);
+ }
+ },
+
+ // Opens an application represented by this display item.
+ launch : function() {
+ this.appInfo.launch();
+ },
+
+ // Draggable interface - FIXME deduplicate with GenericDisplay
+ getDragActor: function(stageX, stageY) {
+ this.dragActor = new Clutter.Clone({ source: this._icon });
+ [this.dragActor.width, this.dragActor.height] = this._icon.get_transformed_size();
+
+ // If the user dragged from the icon itself, then position
+ // the dragActor over the original icon. Otherwise center it
+ // around the pointer
+ let [iconX, iconY] = this._icon.get_transformed_position();
+ let [iconWidth, iconHeight] = this._icon.get_transformed_size();
+ if (stageX > iconX && stageX <= iconX + iconWidth &&
+ stageY > iconY && stageY <= iconY + iconHeight)
+ this.dragActor.set_position(iconX, iconY);
+ else
+ this.dragActor.set_position(stageX - this.dragActor.width / 2, stageY - this.dragActor.height / 2);
+ return this.dragActor;
+ },
+
+ // Returns the original icon that is being used as a source for the cloned texture
+ // that represents the item as it is being dragged.
+ getDragActorSource: function() {
+ return this._icon;
+ }
+};
+
+Signals.addSignalMethods(WellDisplayItem.prototype);
+
+function WellArea(width, isFavorite) {
+ this._init(width, isFavorite);
+}
+
+WellArea.prototype = {
+ _init : function(width, isFavorite) {
+ this.isFavorite = isFavorite;
+
+ this.actor = new Tidy.Grid({ width: width });
+ this.actor._delegate = this;
+ },
+
+ redisplay: function (infos) {
+ let children;
+
+ children = this.actor.get_children();
+ children.forEach(Lang.bind(this, function (v) {
+ this.actor.remove_actor(v);
+ v.destroy();
+ }));
+
+ for (let i = 0; i < infos.length; i++) {
+ let display = new WellDisplayItem(infos[i], this.isFavorite);
+ display.connect('activated', Lang.bind(this, function (display) {
+ this.emit('activated', display);
+ }));
+ this.actor.add_actor(display.actor);
+ };
+ },
+
+ // Draggable target interface
+ acceptDrop : function(source, actor, x, y, time) {
+ let global = Shell.Global.get();
+
+ if (!(source instanceof WellDisplayItem)) {
+ return false;
+ }
+
+ let appSystem = Shell.AppSystem.get_default();
+ let id = source.appInfo.appId;
+ if (source.isFavorite && (!this.isFavorite)) {
+ Mainloop.idle_add(function () {
+ appSystem.remove_favorite(id);
+ });
+ } else if ((!source.isFavorite) && this.isFavorite) {
+ Mainloop.idle_add(function () {
+ appSystem.add_favorite(id);
+ });
+ }
+
+ return true;
+ }
+}
+
+Signals.addSignalMethods(WellArea.prototype);
+
+function AppWell(width) {
+ this._init(width);
+}
+
+AppWell.prototype = {
+ _init : function(width) {
+ this._menus = [];
+ this._menuDisplays = [];
+
+ this.actor = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
+ width: width });
+
+ this._appSystem = Shell.AppSystem.get_default();
+ this._appMonitor = Shell.AppMonitor.get_default();
+
+ this._appSystem.connect('changed', Lang.bind(this, function(appSys) {
+ this._redisplay();
+ }));
+ this._appMonitor.connect('changed', Lang.bind(this, function(monitor) {
+ this._redisplay();
+ }));
+
+ this._favoritesArea = new WellArea(width, true);
+ this._favoritesArea.connect('activated', Lang.bind(this, function (a, display) {
+ this.emit('activated');
+ }));
+ this.actor.append(this._favoritesArea.actor, Big.BoxPackFlags.NONE);
+
+ this._runningBox = new Big.Box({ border_color: GenericDisplay.ITEM_DISPLAY_NAME_COLOR,
+ border: 1,
+ corner_radius: 3,
+ padding: GenericDisplay.PREVIEW_BOX_PADDING });
+ this._runningArea = new WellArea(width, false);
+ this._runningArea.connect('activated', Lang.bind(this, function (a, display) {
+ this.emit('activated');
+ }));
+ this._runningBox.append(this._runningArea.actor, Big.BoxPackFlags.EXPAND);
+ this.actor.append(this._runningBox, Big.BoxPackFlags.NONE);
+
+ this._redisplay();
+ },
+
+ _redisplay: function() {
+ let arrayToObject = function(a) {
+ let o = {};
+ for (let i = 0; i < a.length; i++)
+ o[a[i]] = 1;
+ return o;
+ };
+ let favorites = AppInfo.getFavorites();
+ let favoriteIds = arrayToObject(favorites.map(function (e) { return e.appId; }));
+ let running = AppInfo.getRunning().filter(function (e) {
+ return !(e.appId in favoriteIds);
+ });
+ this._favoritesArea.redisplay(favorites);
+ this._runningArea.redisplay(running);
+ }
+};
+
+Signals.addSignalMethods(AppWell.prototype);
diff --git a/js/ui/overlay.js b/js/ui/overlay.js
index fdfda5a..03078ff 100644
--- a/js/ui/overlay.js
+++ b/js/ui/overlay.js
@@ -283,7 +283,6 @@ Dash.prototype = {
this._searchEntry.entry.connect('activate', function (se) {
// only one of the displays will have an item selected, so it's ok to
// call activateSelected() on all of them
- me._appDisplay.activateSelected();
me._docDisplay.activateSelected();
me._resultsAppsSection.display.activateSelected();
me._resultsDocsSection.display.activateSelected();
@@ -329,11 +328,9 @@ Dash.prototype = {
this._appsContent = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL });
this._appsSection.append(this._appsContent, Big.BoxPackFlags.EXPAND);
- this._appDisplay = new AppDisplay.AppDisplay(this._displayWidth, this._itemDisplayHeight / 2, DASH_COLUMNS, DASH_SECTION_PADDING);
- let sideArea = this._appDisplay.getSideArea();
- sideArea.hide();
- this._appsContent.append(sideArea, Big.BoxPackFlags.NONE);
- this._appsContent.append(this._appDisplay.actor, Big.BoxPackFlags.EXPAND);
+ this._appWell = new AppDisplay.AppWell(this._displayWidth);
+ this._appWell.actor.show();
+ this._appsContent.append(this._appWell.actor, Big.BoxPackFlags.EXPAND);
let moreAppsBox = new Big.Box({x_align: Big.BoxAlignment.END});
this._moreAppsLink = new Link.Link({ color: DASH_TEXT_COLOR,
@@ -444,13 +441,12 @@ Dash.prototype = {
let itemDetailsAvailableWidth = this._detailsWidth - DASH_SECTION_PADDING * 2 - DASH_BORDER_WIDTH * 2;
let itemDetailsAvailableHeight = detailsHeight - DASH_SECTION_PADDING * 2 - DASH_BORDER_WIDTH * 2;
- this._appDisplay.setAvailableDimensionsForItemDetails(itemDetailsAvailableWidth, itemDetailsAvailableHeight);
this._docDisplay.setAvailableDimensionsForItemDetails(itemDetailsAvailableWidth, itemDetailsAvailableHeight);
this._resultsAppsSection.display.setAvailableDimensionsForItemDetails(itemDetailsAvailableWidth, itemDetailsAvailableHeight);
this._resultsDocsSection.display.setAvailableDimensionsForItemDetails(itemDetailsAvailableWidth, itemDetailsAvailableHeight);
/* Proxy the activated signals */
- this._appDisplay.connect('activated', function(appDisplay) {
+ this._appWell.connect('activated', function(well) {
me.emit('activated');
});
this._docDisplay.connect('activated', function(docDisplay) {
@@ -462,27 +458,7 @@ Dash.prototype = {
this._resultsDocsSection.display.connect('activated', function(resultsDocsDisplay) {
me.emit('activated');
});
- this._appDisplay.connect('selected', function(appDisplay) {
- // We allow clicking on any item to select it, so if an
- // item in the app display is selected, we need to make sure that
- // no item in the doc display has the selection.
- me._docDisplay.unsetSelected();
- me._resultsDocsSection.display.unsetSelected();
- me._resultsAppsSection.display.unsetSelected();
- if (me._firstSelectAfterOverlayShow) {
- me._firstSelectAfterOverlayShow = false;
- } else if (!me._detailsShowing()) {
- me._detailsPane.show();
- me.emit('panes-displayed');
- }
- me._detailsContent.remove_all();
- me._detailsContent.append(me._appDisplay.selectedItemDetails, Big.BoxPackFlags.NONE);
- });
this._docDisplay.connect('selected', function(docDisplay) {
- // We allow clicking on any item to select it, so if an
- // item in the doc display is selected, we need to make sure that
- // no item in the app display has the selection.
- me._appDisplay.unsetSelected();
me._resultsDocsSection.display.unsetSelected();
me._resultsAppsSection.display.unsetSelected();
if (!me._detailsShowing()) {
@@ -493,7 +469,6 @@ Dash.prototype = {
me._detailsContent.append(me._docDisplay.selectedItemDetails, Big.BoxPackFlags.NONE);
});
this._resultsDocsSection.display.connect('selected', function(resultsDocDisplay) {
- me._appDisplay.unsetSelected();
me._docDisplay.unsetSelected();
me._resultsAppsSection.display.unsetSelected();
if (!me._detailsShowing()) {
@@ -504,7 +479,6 @@ Dash.prototype = {
me._detailsContent.append(me._resultsDocsSection.display.selectedItemDetails, Big.BoxPackFlags.NONE);
});
this._resultsAppsSection.display.connect('selected', function(resultsAppDisplay) {
- me._appDisplay.unsetSelected();
me._docDisplay.unsetSelected();
me._resultsDocsSection.display.unsetSelected();
if (!me._detailsShowing()) {
@@ -514,12 +488,6 @@ Dash.prototype = {
me._detailsContent.remove_all();
me._detailsContent.append(me._resultsAppsSection.display.selectedItemDetails, Big.BoxPackFlags.NONE);
});
- this._appDisplay.connect('redisplayed', function(appDisplay) {
- me._ensureItemSelected();
- });
- this._docDisplay.connect('redisplayed', function(docDisplay) {
- me._ensureItemSelected();
- });
this._moreAppsLink.connect('clicked',
function(o, event) {
@@ -543,7 +511,6 @@ Dash.prototype = {
show: function() {
let global = Shell.Global.get();
- this._appDisplay.show();
this._appsContent.show();
this._docDisplay.show();
global.stage.set_key_focus(this._searchEntry.entry);
@@ -567,22 +534,6 @@ Dash.prototype = {
this._unsetSearchMode();
},
- // Ensures that one of the displays has the selection if neither owns it after the
- // latest redisplay. This can be applicable if the display that earlier had the
- // selection no longer has any items, or if their is a single section being shown
- // in the expanded view and it went from having no matching items to having some.
- // We first try to place the selection in the applications section, because it is
- // displayed above the documents section.
- _ensureItemSelected: function() {
- if (!this._appDisplay.hasSelected() && !this._docDisplay.hasSelected()) {
- if (this._appDisplay.hasItems()) {
- this._appDisplay.selectFirstItem();
- } else if (this._docDisplay.hasItems()) {
- this._docDisplay.selectFirstItem();
- }
- }
- },
-
// Sets the 'More' mode for browsing applications.
_setMoreAppsMode: function() {
if (this._moreAppsMode)
@@ -811,6 +762,7 @@ Overlay.prototype = {
// the item on any workspace.
handleDragOver : function(source, actor, x, y, time) {
if (source instanceof GenericDisplay.GenericDisplayItem) {
+ log("unsetting more mode");
this._dash.unsetMoreMode();
return true;
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]