[gnome-documents] selections: first pass at implementing a "selection" mode
- From: Cosimo Cecchi <cosimoc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-documents] selections: first pass at implementing a "selection" mode
- Date: Fri, 28 Oct 2011 19:20:50 +0000 (UTC)
commit c47df070689d586bfedfbd79b35dc6000ba3448f
Author: Cosimo Cecchi <cosimoc gnome org>
Date: Thu Oct 27 17:44:39 2011 -0400
selections: first pass at implementing a "selection" mode
The "selection" mode is entered with a button on the main toolbar or by
right clicking on any item. An OSD toolbar will appear when one or more
items are selected, offering an accessible context-menu like list of
actions (which are not yet wired).
src/Makefile-js.am | 2 +-
src/application.js | 4 +-
src/embed.js | 1 +
src/iconView.js | 14 ++-
src/lib/gd-utils.c | 47 ++++++++
src/lib/gd-utils.h | 2 +
src/listView.js | 11 ++-
src/mainToolbar.js | 100 +++++++++++++++++-
src/mainWindow.js | 49 +++++++--
src/selectionController.js | 55 ---------
src/selections.js | 261 ++++++++++++++++++++++++++++++++++++++++++++
src/sidebar.js | 15 +++-
src/view.js | 92 +++++++++-------
13 files changed, 534 insertions(+), 119 deletions(-)
---
diff --git a/src/Makefile-js.am b/src/Makefile-js.am
index 25fce67..a0828e6 100644
--- a/src/Makefile-js.am
+++ b/src/Makefile-js.am
@@ -22,7 +22,7 @@ dist_js_DATA = \
preview.js \
query.js \
searchbar.js \
- selectionController.js \
+ selections.js \
sidebar.js \
sources.js \
spinnerBox.js \
diff --git a/src/application.js b/src/application.js
index e42b53d..b5d4d6f 100644
--- a/src/application.js
+++ b/src/application.js
@@ -46,7 +46,7 @@ const MainWindow = imports.mainWindow;
const OffsetController = imports.offsetController;
const Path = imports.path;
const Query = imports.query;
-const SelectionController = imports.selectionController;
+const Selections = imports.selections;
const Sources = imports.sources;
const TrackerController = imports.trackerController;
const Tweener = imports.util.tweener;
@@ -144,7 +144,7 @@ Application.prototype = {
}
Global.sourceManager = new Sources.SourceManager();
- Global.selectionController = new SelectionController.SelectionController();
+ Global.selectionController = new Selections.SelectionController();
Global.queryBuilder = new Query.QueryBuilder();
Global.changeMonitor = new ChangeMonitor.TrackerChangeMonitor();
Global.collectionManager = new Collections.CollectionManager();
diff --git a/src/embed.js b/src/embed.js
index f737eeb..9eb4b4f 100644
--- a/src/embed.js
+++ b/src/embed.js
@@ -54,6 +54,7 @@ ViewEmbed.prototype = {
this._viewSettingsId = 0;
this.widget = new Gtk.Grid({ orientation: Gtk.Orientation.VERTICAL });
+ this.actor = new GtkClutter.Actor({ contents: this.widget });
this._toolbar = new MainToolbar.MainToolbar();
this.widget.add(this._toolbar.widget);
diff --git a/src/iconView.js b/src/iconView.js
index 8531ce8..f9d7fbc 100644
--- a/src/iconView.js
+++ b/src/iconView.js
@@ -46,16 +46,10 @@ IconView.prototype = {
this.widget.item_width = _VIEW_ITEM_WIDTH;
this.widget.column_spacing = _VIEW_COLUMN_SPACING;
this.widget.margin = _VIEW_MARGIN;
- this.widget.set_selection_mode(Gtk.SelectionMode.MULTIPLE);
this.widget.connect('item-activated',
Lang.bind(this, this._onItemActivated));
- this.widget.connect('button-press-event',
- Lang.bind(this, this._onButtonPressEvent));
-
- this.widget.show();
-
// chain up to the parent
View.View.prototype._init.call(this);
},
@@ -76,6 +70,14 @@ IconView.prototype = {
return this.widget.get_path_at_pos(position[0], position[1]);
},
+ setSelectionMode: function(mode) {
+ this.getSelectionObject().set_selection_mode(mode);
+ },
+
+ setSingleClickMode: function(mode) {
+ Gd.gtk_icon_view_set_activate_on_single_click(this.widget, mode);
+ },
+
scrollToPath: function(path) {
this.widget.scroll_to_path(path, false, 0, 0);
},
diff --git a/src/lib/gd-utils.c b/src/lib/gd-utils.c
index a0aaba1..e9eedd8 100644
--- a/src/lib/gd-utils.c
+++ b/src/lib/gd-utils.c
@@ -262,6 +262,53 @@ gd_gtk_tree_view_set_activate_on_single_click (GtkTreeView *tree_view,
}
}
+static gboolean
+icon_view_button_press_callback (GtkWidget *icon_view,
+ GdkEventButton *event,
+ gpointer data)
+{
+ GtkTreePath *path;
+
+ if (event->button == 1 && event->type == GDK_BUTTON_PRESS) {
+ path = gtk_icon_view_get_path_at_pos (GTK_ICON_VIEW (icon_view),
+ event->x, event->y);
+
+ if (path != NULL) {
+ gtk_icon_view_item_activated (GTK_ICON_VIEW (icon_view), path);
+ gtk_tree_path_free (path);
+ }
+ }
+
+ return FALSE;
+}
+
+void
+gd_gtk_icon_view_set_activate_on_single_click (GtkIconView *icon_view,
+ gboolean should_activate)
+{
+ guint button_press_id;
+
+ button_press_id = GPOINTER_TO_UINT
+ (g_object_get_data (G_OBJECT (icon_view),
+ "gd-icon-view-activate"));
+
+ if (button_press_id && !should_activate) {
+ g_signal_handler_disconnect (icon_view, button_press_id);
+ g_object_set_data (G_OBJECT (icon_view),
+ "gd-icon-view-activate",
+ NULL);
+ } else if (!button_press_id && should_activate) {
+ button_press_id =
+ g_signal_connect (icon_view,
+ "button_press_event",
+ G_CALLBACK (icon_view_button_press_callback),
+ NULL);
+ g_object_set_data (G_OBJECT (icon_view),
+ "gd-icon-view-activate",
+ GUINT_TO_POINTER (button_press_id));
+ }
+}
+
/* utility to stretch a frame to the desired size */
static void
diff --git a/src/lib/gd-utils.h b/src/lib/gd-utils.h
index ba11267..fca249d 100644
--- a/src/lib/gd-utils.h
+++ b/src/lib/gd-utils.h
@@ -57,6 +57,8 @@ gboolean gd_queue_thumbnail_job_for_file_finish (GAsyncResult *res);
void gd_gtk_tree_view_set_activate_on_single_click (GtkTreeView *tree_view,
gboolean should_activate);
+void gd_gtk_icon_view_set_activate_on_single_click (GtkIconView *icon_view,
+ gboolean should_activate);
GdkPixbuf * gd_embed_image_in_frame (GdkPixbuf *source_image,
GdkPixbuf *frame_image,
diff --git a/src/listView.js b/src/listView.js
index c1a314b..a8ed48c 100644
--- a/src/listView.js
+++ b/src/listView.js
@@ -46,9 +46,6 @@ ListView.prototype = {
this.widget.show();
- let selection = this.widget.get_selection();
- selection.set_mode(Gtk.SelectionMode.MULTIPLE);
-
// chain up to the parent
View.View.prototype._init.call(this);
},
@@ -77,6 +74,14 @@ ListView.prototype = {
this.widget.scroll_to_cell(path, null, false, 0, 0);
},
+ setSelectionMode: function(mode) {
+ this.getSelectionObject().set_mode(mode);
+ },
+
+ setSingleClickMode: function(mode) {
+ Gd.gtk_tree_view_set_activate_on_single_click(this.widget, mode);
+ },
+
createRenderers: function() {
let col = new Gtk.TreeViewColumn();
this.widget.append_column(col);
diff --git a/src/mainToolbar.js b/src/mainToolbar.js
index 2a007dd..c5a1ef9 100644
--- a/src/mainToolbar.js
+++ b/src/mainToolbar.js
@@ -41,10 +41,14 @@ MainToolbar.prototype = {
_init: function() {
this._model = null;
this._whereId = 0;
+ this._selectionChangedId = 0;
this.widget = new Gtk.Toolbar({ icon_size: Gtk.IconSize.MENU });
this.widget.get_style_context().add_class(Gtk.STYLE_CLASS_MENUBAR);
+ this._selectionModeId =
+ Global.selectionController.connect('selection-mode-changed',
+ Lang.bind(this, this._onSelectionModeChanged));
this._windowModeId =
Global.modeController.connect('window-mode-changed',
Lang.bind(this, this._onWindowModeChanged));
@@ -59,12 +63,67 @@ MainToolbar.prototype = {
this._whereId = 0;
}
+ if (this._selectionChangedId != 0) {
+ Global.selectionController.disconnect(this._selectionChangedId);
+ this._selectionChangedId = 0;
+ }
+
this.widget.foreach(Lang.bind(this, function(widget) {
widget.destroy();
}));
},
+ _updateSelectionLabel: function() {
+ let length = Global.selectionController.getSelection().length;
+
+ if (length == 0)
+ this._selectionLabel.set_markup('<i>' + _("Click on items to select them") + '</i>');
+ else
+ this._selectionLabel.set_markup('<b>' + _("%d selected").format(length) + '</b>');
+ },
+
+ _populateForSelectionMode: function() {
+ this.widget.set_style(Gtk.ToolbarStyle.TEXT);
+
+ let selectAll = new Gtk.ToolButton({ stock_id: 'gtk-select-all' });
+ selectAll.get_style_context().add_class('raised');
+ this.widget.insert(selectAll, 0);
+
+ selectAll.connect('clicked', Lang.bind(this,
+ function() {
+ Global.selectionController.selectAll();
+ }));
+
+ this._selectionLabel = new Gtk.Label();
+
+ let labelItem = new Gtk.ToolItem({ child: this._selectionLabel });
+ labelItem.set_expand(true);
+ this.widget.insert(labelItem, 1);
+
+ let cancel = new Gtk.ToolButton({ stock_id: 'gtk-cancel' });
+ cancel.get_style_context().add_class('raised');
+ this.widget.insert(cancel, 2);
+
+ cancel.connect('clicked', Lang.bind(this,
+ function() {
+ Global.selectionController.setSelectionMode(false);
+ }));
+
+ let sizeGroup = new Gtk.SizeGroup();
+ sizeGroup.add_widget(cancel);
+ sizeGroup.add_widget(selectAll);
+
+ this._selectionChangedId =
+ Global.selectionController.connect('selection-changed',
+ Lang.bind(this, this._updateSelectionLabel));
+ this._updateSelectionLabel();
+
+ this.widget.show_all();
+ },
+
_populateForOverview: function() {
+ this.widget.set_style(Gtk.ToolbarStyle.ICONS);
+
let iconView = new Gtk.ToggleButton({ child: new Gtk.Image({ icon_name: 'view-grid-symbolic',
pixel_size: 16 }) });
iconView.get_style_context().add_class('linked');
@@ -97,6 +156,22 @@ MainToolbar.prototype = {
item2.add(this._whereLabel);
this.widget.insert(item2, 1);
+ let separator = new Gtk.SeparatorToolItem({ draw: false });
+ separator.set_expand(true);
+ this.widget.insert(separator, 2);
+
+ let item3 = new Gtk.ToggleToolButton({ icon_name: 'emblem-default-symbolic' });
+ item3.get_style_context().add_class('raised');
+ this.widget.insert(item3, 3);
+
+ item3.connect('toggled', Lang.bind(this,
+ function(button) {
+ let isToggled = button.get_active();
+ Global.selectionController.setSelectionMode(isToggled);
+ }));
+ // set initial state
+ item3.set_active(Global.selectionController.getSelectionMode());
+
this._whereId =
Global.sideFilterController.connect('changed',
Lang.bind(this, this._onSideFilterChanged));
@@ -106,6 +181,8 @@ MainToolbar.prototype = {
},
_populateForPreview: function(model) {
+ this.widget.set_style(Gtk.ToolbarStyle.ICONS);
+
let back = new Gtk.ToolButton({ icon_name: 'go-previous-symbolic' });
back.get_style_context().add_class('raised');
this.widget.insert(back, 0);
@@ -186,6 +263,18 @@ MainToolbar.prototype = {
this._populateForPreview();
},
+ _onSelectionModeChanged: function(controller, mode) {
+ if (Global.modeController.getWindowMode() != WindowMode.WindowMode.OVERVIEW)
+ return;
+
+ this._clearToolbar();
+
+ if (mode)
+ this._populateForSelectionMode();
+ else
+ this._populateForOverview();
+ },
+
setModel: function(model) {
if (!model)
return;
@@ -200,7 +289,16 @@ MainToolbar.prototype = {
},
destroy: function() {
- Global.modeController.disconnect(this._windowModeId);
+ if (this._windowModeId != 0) {
+ Global.modeController.disconnect(this._windowModeId);
+ this._windowModeId = 0;
+ }
+
+ if (this._selectionModeId != 0) {
+ Global.selectionController.disconnect(this._selectionModeId);
+ this._selectionModeId = 0;
+ }
+
this.widget.destroy();
}
};
diff --git a/src/mainWindow.js b/src/mainWindow.js
index 9b53cd3..8d645fc 100644
--- a/src/mainWindow.js
+++ b/src/mainWindow.js
@@ -31,6 +31,7 @@ const Mainloop = imports.mainloop;
const Embed = imports.embed;
const Global = imports.global;
const Searchbar = imports.searchbar;
+const Selections = imports.selections;
const Sidebar = imports.sidebar;
const Utils = imports.utils;
const WindowMode = imports.windowMode;
@@ -38,6 +39,7 @@ const WindowMode = imports.windowMode;
const _ = imports.gettext.gettext;
const _CONFIGURE_ID_TIMEOUT = 100; // msecs
+const _OSD_TOOLBAR_SPACING = 60;
function MainWindow() {
this._init();
@@ -106,21 +108,46 @@ MainWindow.prototype = {
this._clutterBox.add_actor(this._searchbar.actor);
this._clutterBoxLayout.set_fill(this._searchbar.actor, true, false);
- // second child: the actual sidebar + embed, filling both axis
- // and expanding
- this._grid = new Gtk.Grid({ orientation: Gtk.Orientation.HORIZONTAL });
- this._gridActor = new GtkClutter.Actor({ contents: this._grid });
- this._clutterBox.add_actor(this._gridActor);
- this._clutterBoxLayout.set_expand(this._gridActor, true);
- this._clutterBoxLayout.set_fill(this._gridActor, true, true);
+ // second child: an horizontal layout box which will
+ // contain the sidebar and the embed
+ this._horizLayout = new Clutter.BoxLayout();
+ this._horizBox = new Clutter.Box({ layout_manager: this._horizLayout });
+ this._clutterBox.add_actor(this._horizBox);
+ this._clutterBoxLayout.set_expand(this._horizBox, true);
+ this._clutterBoxLayout.set_fill(this._horizBox, true, true);
+ // create the sidebar and pack it as a first child into the
+ // horizontal box
this._sidebar = new Sidebar.Sidebar();
- this._grid.add(this._sidebar.widget);
+ this._horizBox.add_actor(this._sidebar.actor);
+ this._horizLayout.set_fill(this._sidebar.actor, false, true);
+ // create the embed and pack it as the second child into
+ // the horizontal box
this._embed = new Embed.ViewEmbed();
- this._grid.add(this._embed.widget);
+ this._horizBox.add_actor(this._embed.actor);
+ this._horizLayout.set_expand(this._embed.actor, true);
+ this._horizLayout.set_fill(this._embed.actor, true, true);
+
+ // create the OSD toolbar for selected items, it's hidden by default
+ this._selectionToolbar = new Selections.SelectionToolbar();
+ this._selectionToolbar.actor.add_constraint(
+ new Clutter.AlignConstraint({ align_axis: Clutter.AlignAxis.X_AXIS,
+ source: this._embed.actor,
+ factor: 0.50 }));
+ let yConstraint =
+ new Clutter.BindConstraint({ source: this._embed.actor,
+ coordinate: Clutter.BindCoordinate.Y,
+ offset: this._embed.actor.height - _OSD_TOOLBAR_SPACING });
+ this._selectionToolbar.actor.add_constraint(yConstraint);
+
+ // refresh the constraint offset when the height of the embed actor changes
+ this._embed.actor.connect("notify::height", Lang.bind(this,
+ function() {
+ yConstraint.set_offset(this._embed.actor.height - _OSD_TOOLBAR_SPACING);
+ }));
- this._grid.show_all();
+ Global.stage.add_actor(this._selectionToolbar.actor);
},
_saveWindowGeometry: function() {
diff --git a/src/selections.js b/src/selections.js
new file mode 100644
index 0000000..21ef32f
--- /dev/null
+++ b/src/selections.js
@@ -0,0 +1,261 @@
+/*
+ * Copyright (c) 2011 Red Hat, Inc.
+ *
+ * Gnome Documents 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 2 of the License, or (at your
+ * option) any later version.
+ *
+ * Gnome Documents 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 Gnome Documents; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Author: Cosimo Cecchi <cosimoc redhat com>
+ *
+ */
+
+const Gtk = imports.gi.Gtk;
+const GtkClutter = imports.gi.GtkClutter;
+const _ = imports.gettext.gettext;
+
+const Global = imports.global;
+const Tweener = imports.util.tweener;
+
+const Lang = imports.lang;
+const Signals = imports.signals;
+
+function SelectionController() {
+ this._init();
+};
+
+SelectionController.prototype = {
+ _init: function() {
+ this._selection = [];
+ this._selectionMode = false;
+ },
+
+ selectAll: function() {
+ this.emit('select-all');
+ },
+
+ setSelection: function(selection) {
+ if (this._isFreezed)
+ return;
+
+ if (!selection)
+ this._selection = [];
+ else
+ this._selection = selection;
+
+ this.emit('selection-changed', this._selection);
+ },
+
+ getSelection: function() {
+ return this._selection;
+ },
+
+ freezeSelection: function(freeze) {
+ if (freeze == this._isFreezed)
+ return;
+
+ this._isFreezed = freeze;
+
+ if (!this._isFreezed)
+ this.emit('selection-check');
+ },
+
+ setSelectionMode: function(setting) {
+ if (this._selectionMode == setting)
+ return;
+
+ this._selectionMode = setting;
+ this.emit('selection-mode-changed', this._selectionMode);
+ },
+
+ getSelectionMode: function() {
+ return this._selectionMode;
+ }
+};
+Signals.addSignalMethods(SelectionController.prototype);
+
+function SelectionToolbar() {
+ this._init();
+}
+
+SelectionToolbar.prototype = {
+ _init: function() {
+ this._itemListeners = {};
+ this._insideRefresh = false;
+
+ this.widget = new Gtk.Toolbar({ show_arrow: false,
+ icon_size: Gtk.IconSize.MENU });
+ this.actor = new GtkClutter.Actor({ contents: this.widget,
+ show_on_set_parent: false,
+ opacity: 0 });
+
+ this._toolbarFavorite = new Gtk.ToggleToolButton({ icon_name: 'emblem-favorite-symbolic' });
+ this.widget.insert(this._toolbarFavorite, 0);
+ this._toolbarFavorite.connect('clicked', Lang.bind(this, this._onToolbarFavorite));
+
+ let separator = new Gtk.SeparatorToolItem();
+ separator.show();
+ this.widget.insert(separator, 1);
+
+ this._toolbarOpen = new Gtk.ToolButton({ icon_name: 'document-open-symbolic' });
+ this.widget.insert(this._toolbarOpen, 2);
+ this._toolbarOpen.connect('clicked', Lang.bind(this, this._onToolbarOpen));
+
+ this.widget.show();
+
+ Global.selectionController.connect('selection-mode-changed',
+ Lang.bind(this, this._onSelectionModeChanged));
+ Global.selectionController.connect('selection-changed',
+ Lang.bind(this, this._onSelectionChanged));
+ },
+
+ _onSelectionModeChanged: function(controller, mode) {
+ if (mode)
+ this._onSelectionChanged();
+ else
+ this._fadeOut();
+ },
+
+ _onSelectionChanged: function() {
+ if (!Global.selectionController.getSelectionMode())
+ return;
+
+ let selection = Global.selectionController.getSelection();
+ this._setItemListeners(selection);
+
+ if (selection.length > 0) {
+ this._setItemVisibility();
+ this._fadeIn();
+ } else {
+ this._fadeOut();
+ }
+ },
+
+ _setItemListeners: function(selection) {
+ for (idx in this._itemListeners) {
+ let doc = this._itemListeners[idx];
+ doc.disconnect(idx);
+ delete this._itemListeners[idx];
+ }
+
+ selection.forEach(Lang.bind(this,
+ function(urn) {
+ let doc = Global.documentManager.getItemById(urn);
+ let id = doc.connect('info-updated', Lang.bind(this, this._setItemVisibility));
+ this._itemListeners[id] = doc;
+ }));
+ },
+
+ _setItemVisibility: function() {
+ let apps = [];
+ let favCount = 0;
+ let showFavorite = true;
+
+ this._insideRefresh = true;
+
+ let selection = Global.selectionController.getSelection();
+ selection.forEach(Lang.bind(this,
+ function(urn) {
+ let doc = Global.documentManager.getItemById(urn);
+
+ if (doc.favorite)
+ favCount++;
+
+ if (apps.indexOf(doc.defaultAppName) == -1)
+ apps.push(doc.defaultAppName);
+ }));
+
+ showFavorite &= ((favCount == 0) || (favCount == selection.length));
+
+ let openLabel = null;
+ if (apps.length == 1) {
+ // Translators: this is the Open action in a context menu
+ openLabel = _("Open with %s").format(apps[0]);
+ } else {
+ // Translators: this is the Open action in a context menu
+ openLabel = _("Open");
+ }
+
+ this._toolbarOpen.set_tooltip_text(openLabel);
+ this._toolbarOpen.show();
+
+ if (showFavorite) {
+ let isFavorite = (favCount == selection.length);
+ let favoriteLabel = '';
+
+ if (isFavorite) {
+ favoriteLabel = _("Remove from favorites");
+ this._toolbarFavorite.set_active(true);
+ this._toolbarFavorite.get_style_context().add_class('favorite');
+ } else {
+ favoriteLabel = _("Add to favorites");
+ this._toolbarFavorite.set_active(false);
+ this._toolbarFavorite.get_style_context().remove_class('favorite');
+ }
+
+ this._toolbarFavorite.reset_style();
+ this._toolbarFavorite.set_tooltip_text(favoriteLabel);
+ this._toolbarFavorite.show();
+ } else {
+ this._toolbarFavorite.hide();
+ }
+
+ this._insideRefresh = false;
+ },
+
+ _onToolbarOpen: function(widget) {
+ let selection = Global.selectionController.getSelection();
+
+ selection.forEach(Lang.bind(this,
+ function(urn) {
+ let doc = Global.documentManager.getItemById(urn);
+ doc.open(widget.get_screen(), Gtk.get_current_event_time());
+ }));
+ },
+
+ _onToolbarFavorite: function(widget) {
+ if (this._insideRefresh)
+ return;
+
+ let selection = Global.selectionController.getSelection();
+
+ selection.forEach(Lang.bind(this,
+ function(urn) {
+ let doc = Global.documentManager.getItemById(urn);
+ doc.setFavorite(!doc.favorite);
+ }));
+ },
+
+ _fadeIn: function() {
+ if (this.actor.opacity != 0)
+ return;
+
+ this.actor.opacity = 0;
+ this.actor.show();
+
+ Tweener.addTween(this.actor,
+ { opacity: 255,
+ time: 0.30,
+ transition: 'easeOutQuad' });
+ },
+
+ _fadeOut: function() {
+ Tweener.addTween(this.actor,
+ { opacity: 0,
+ time: 0.30,
+ transition: 'easeOutQuad',
+ onComplete: function() {
+ this.actor.hide();
+ },
+ onCompleteScope: this });
+ }
+};
diff --git a/src/sidebar.js b/src/sidebar.js
index 2627376..103346d 100644
--- a/src/sidebar.js
+++ b/src/sidebar.js
@@ -19,8 +19,10 @@
*
*/
+const Clutter = imports.gi.Clutter;
const Gd = imports.gi.Gd;
const Gtk = imports.gi.Gtk;
+const GtkClutter = imports.gi.GtkClutter;
const Pango = imports.gi.Pango;
const _ = imports.gettext.gettext;
@@ -29,6 +31,7 @@ const Signals = imports.signals;
const Global = imports.global;
const Sources = imports.sources;
+const Tweener = imports.util.tweener;
const WindowMode = imports.windowMode;
const _SIDEBAR_WIDTH_REQUEST = 240;
@@ -404,6 +407,8 @@ Sidebar.prototype = {
this.widget = new Gtk.Notebook({ show_tabs: false });
this.widget.get_style_context().add_class(Gtk.STYLE_CLASS_SIDEBAR);
+ this.actor = new GtkClutter.Actor({ contents: this.widget });
+
this._sourceView = new Sources.SourceView();
this.widget.insert_page(this._sourceView.widget, null, _SIDEBAR_SOURCES_PAGE);
this._sourceView.connect('source-clicked',
@@ -429,6 +434,14 @@ Sidebar.prototype = {
},
_onWindowModeChanged: function(controller, mode) {
- this.widget.set_visible(mode == WindowMode.WindowMode.OVERVIEW);
+ if (mode == WindowMode.WindowMode.PREVIEW) {
+ Tweener.addTween(this.actor, { width: 0,
+ time: 0.15,
+ transition: 'easeInQuad' });
+ } else if (mode == WindowMode.WindowMode.OVERVIEW) {
+ Tweener.addTween(this.actor, { width: this.widget.get_preferred_width()[1],
+ time: 0.15,
+ transition: 'easeOutQuad' });
+ }
}
};
diff --git a/src/view.js b/src/view.js
index 0705664..021faea 100644
--- a/src/view.js
+++ b/src/view.js
@@ -110,23 +110,34 @@ View.prototype = {
_init: function() {
this._selectedURNs = null;
+ // setup the model
this._treeModel = Global.documentManager.getModel().model;
this.widget.set_model(this._treeModel);
+ // setup selections
+ this.setSingleClickMode(true);
+
+ this.setSelectionMode(Gtk.SelectionMode.SINGLE);
+ this._selectionModeId =
+ Global.selectionController.connect('selection-mode-changed',
+ Lang.bind(this, this._onSelectionModeChanged));
+ this._selectionControllerId =
+ Global.selectionController.connect('selection-check',
+ Lang.bind(this, this._updateSelection));
+ this._selectAllId =
+ Global.selectionController.connect('select-all',
+ Lang.bind(this, this._onSelectAll));
+
this.widget.connect('destroy', Lang.bind(this,
function() {
Global.selectionController.disconnect(this._selectionControllerId);
+ Global.selectionController.disconnect(this._selectionModeId);
+ Global.selectionController.disconnect(this._selectAllId);
}));
- this.widget.connect('button-release-event', Lang.bind(this, this._onButtonReleaseEvent));
this.widget.connect('button-press-event', Lang.bind(this, this._onButtonPressEvent));
this.createRenderers();
- this._selectionController = Global.selectionController;
- this._selectionControllerId =
- this._selectionController.connect('selection-check',
- Lang.bind(this, this._updateSelection));
-
// HACK: give the view some time to setup the scrolled window
// allocation, as updateSelection() might call scrollToPath().
// Is there anything better we can do here?
@@ -137,11 +148,13 @@ View.prototype = {
}));
this.connectToSelectionChanged(Lang.bind(this, this._onSelectionChanged));
+
+ this.widget.show();
},
_updateSelection: function() {
let selectionObject = this.getSelectionObject();
- let selected = this._selectionController.getSelection().slice(0);
+ let selected = Global.selectionController.getSelection().slice(0);
if (!selected.length)
return;
@@ -169,7 +182,24 @@ View.prototype = {
}));
},
+ _onSelectAll: function() {
+ this.getSelectionObject().select_all();
+ },
+
+ _onSelectionModeChanged: function(controller, selectionMode) {
+ // setup the GtkSelectionMode of the view according to whether or not
+ // the view is in "selection mode"
+ if (selectionMode) {
+ this.setSingleClickMode(false);
+ this.setSelectionMode(Gtk.SelectionMode.MULTIPLE);
+ } else {
+ this.setSingleClickMode(true);
+ this.setSelectionMode(Gtk.SelectionMode.SINGLE);
+ }
+ },
+
_onSelectionChanged: function() {
+ // update the selection on the controller when the view signals a change
let selectedURNs = Utils.getURNsFromPaths(this.getSelection(),
this._treeModel);
Global.selectionController.setSelection(selectedURNs);
@@ -177,45 +207,29 @@ View.prototype = {
_onButtonPressEvent: function(widget, event) {
let button = event.get_button()[1];
- if (button != 3)
- return false;
-
- let coords = [ event.get_coords()[1] , event.get_coords()[2] ];
- let path = this.getPathAtPos(coords);
-
- let selection = Global.selectionController.getSelection();
+ let enteredMode = false;
- if (path) {
- let urn = Utils.getURNFromPath(path, this._treeModel);
-
- if (selection.indexOf(urn) == -1)
- this.getSelectionObject().unselect_all();
-
- this.getSelectionObject().select_path(path);
+ if (!Global.selectionController.getSelectionMode()) {
+ if (button == 3) {
+ Global.selectionController.setSelectionMode(true);
+ enteredMode = true;
+ } else {
+ return false;
+ }
}
- return true;
- },
-
- _onButtonReleaseEvent: function(view, event) {
- let button = event.get_button()[1];
let coords = [ event.get_coords()[1] , event.get_coords()[2] ];
- let timestamp = event.get_time();
-
- if (button != 3)
- return false;
-
let path = this.getPathAtPos(coords);
- if (!path)
- return false;
-
- let iter = this._treeModel.get_iter(path)[1];
-
- let urn = this._treeModel.get_value(iter, Documents.ModelColumns.URN);
- let menu = new ContextMenu(Global.selectionController.getSelection());
+ if (path) {
+ let selectionObj = this.getSelectionObject();
+ let isSelected = selectionObj.path_is_selected(path);
- menu.widget.popup_for_device(null, null, null, null, null, null, button, timestamp);
+ if (isSelected && !enteredMode)
+ selectionObj.unselect_path(path);
+ else if (!isSelected)
+ selectionObj.select_path(path);
+ }
return true;
},
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]