[gnome-documents] preview: add a first implementation of find as you type
- From: Cosimo Cecchi <cosimoc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-documents] preview: add a first implementation of find as you type
- Date: Thu, 19 Jul 2012 03:30:18 +0000 (UTC)
commit 7d613c5018c4a104bb81a0d1cda35935fdd415e5
Author: Cosimo Cecchi <cosimoc gnome org>
Date: Wed Jul 18 17:59:58 2012 -0400
preview: add a first implementation of find as you type
- Split a PreviewToolbar subclass from MainToolbar, and have embed
replace the toolbar with it in preview mode
- Add a PreviewSearchbar subclass of Searchbar that triggers an
EvJobFind in the EvView
- Hook up event handling to MainWindow
src/embed.js | 27 ++++-
src/lib/gd-utils.c | 10 ++
src/lib/gd-utils.h | 5 +
src/mainToolbar.js | 277 +++++++++++++++++++++-------------------------------
src/mainWindow.js | 19 ++--
src/preview.js | 166 +++++++++++++++++++++++++++++++
6 files changed, 324 insertions(+), 180 deletions(-)
---
diff --git a/src/embed.js b/src/embed.js
index 6460d65..30393c5 100644
--- a/src/embed.js
+++ b/src/embed.js
@@ -65,11 +65,6 @@ const Embed = new Lang.Class({
this._overlayLayout.add(this._contentsActor,
Clutter.BinAlignment.FILL, Clutter.BinAlignment.FILL);
- // pack the toolbar
- this._toolbar = new MainToolbar.OverviewToolbar(this._overlayLayout);
- this._contentsActor.add_actor(this._toolbar.actor);
- this._contentsLayout.set_fill(this._toolbar.actor, true, false);
-
// pack the main GtkNotebook and a spinnerbox in a BinLayout, so that
// we can easily bring them front/back
this._viewLayout = new Clutter.BinLayout();
@@ -229,6 +224,15 @@ const Embed = new Lang.Class({
if (this._preview)
this._preview.setModel(null);
+ if (this._toolbar)
+ this._toolbar.actor.destroy();
+
+ // pack the toolbar
+ this._toolbar = new MainToolbar.OverviewToolbar(this._overlayLayout);
+ this._contentsLayout.pack_start = true;
+ this._contentsActor.add_actor(this._toolbar.actor);
+ this._contentsLayout.set_fill(this._toolbar.actor, true, false);
+
this._spinnerBox.moveOut();
this._errorBox.moveOut();
@@ -236,11 +240,24 @@ const Embed = new Lang.Class({
},
_prepareForPreview: function() {
+ if (this._toolbar)
+ this._toolbar.actor.destroy();
+
+ // pack the toolbar
+ this._toolbar = new Preview.PreviewToolbar(this._preview);
+ this._contentsLayout.pack_start = true;
+ this._contentsActor.add_actor(this._toolbar.actor);
+ this._contentsLayout.set_fill(this._toolbar.actor, true, false);
+
this._notebook.set_current_page(this._previewPage);
},
_setError: function(primary, secondary) {
this._errorBox.update(primary, secondary);
this._errorBox.moveIn();
+ },
+
+ getMainToolbar: function(event) {
+ return this._toolbar;
}
});
diff --git a/src/lib/gd-utils.c b/src/lib/gd-utils.c
index 164de3d..e3b2eff 100644
--- a/src/lib/gd-utils.c
+++ b/src/lib/gd-utils.c
@@ -649,3 +649,13 @@ gd_create_variant_from_pixbuf (GdkPixbuf *pixbuf)
g_object_ref (pixbuf)));
return g_variant_ref_sink (variant);
}
+
+void
+gd_ev_view_find_changed (EvView *view,
+ EvJobFind *job,
+ gint page)
+{
+ ev_view_find_changed (view,
+ ev_job_find_get_results (job),
+ page);
+}
diff --git a/src/lib/gd-utils.h b/src/lib/gd-utils.h
index d82e800..601cbe6 100644
--- a/src/lib/gd-utils.h
+++ b/src/lib/gd-utils.h
@@ -23,6 +23,7 @@
#define __GD_UTILS_H__
#include <gtk/gtk.h>
+#include <evince-view.h>
void gd_queue_thumbnail_job_for_file_async (GFile *file,
GAsyncReadyCallback callback,
@@ -57,5 +58,9 @@ void gd_entry_focus_hack (GtkWidget *entry,
GVariant *gd_create_variant_from_pixbuf (GdkPixbuf *pixbuf);
+void gd_ev_view_find_changed (EvView *view,
+ EvJobFind *job,
+ gint page);
+
#endif /* __GD_UTILS_H__ */
diff --git a/src/mainToolbar.js b/src/mainToolbar.js
index 4c68740..6ce23f8 100644
--- a/src/mainToolbar.js
+++ b/src/mainToolbar.js
@@ -47,10 +47,6 @@ const MainToolbar = new Lang.Class({
_init: function() {
this._model = null;
- this._collBackButton = null;
- this._collectionId = 0;
- this._selectionChangedId = 0;
-
this.widget = new Gd.MainToolbar({ icon_size: Gtk.IconSize.MENU });
this.widget.get_style_context().add_class(Gtk.STYLE_CLASS_MENUBAR);
this.widget.show();
@@ -64,6 +60,35 @@ const MainToolbar = new Lang.Class({
false, true, false,
Clutter.BoxAlignment.CENTER, Clutter.BoxAlignment.START);
+ this.createSearchbar();
+ },
+
+ createSearchbar: function() {
+ log('Error: MainToolbar subclasses must implement createSearchbar');
+ },
+
+ handleEvent: function(event) {
+ let res = this._searchbar.handleEvent(event);
+ return res;
+ },
+
+ toggleSearch: function() {
+ this._searchbar.toggle();
+ }
+});
+
+const OverviewToolbar = new Lang.Class({
+ Name: 'OverviewToolbar',
+ Extends: MainToolbar,
+
+ _init: function(overlayLayout) {
+ this._overlayLayout = overlayLayout;
+ this._collBackButton = null;
+ this._collectionId = 0;
+ this._selectionChangedId = 0;
+
+ this.parent();
+
// setup listeners to mode changes that affect the toolbar layout
this._searchStringId =
Global.searchController.connect('search-string-changed',
@@ -80,20 +105,12 @@ const MainToolbar = new Lang.Class({
this._selectionModeId =
Global.selectionController.connect('selection-mode-changed',
Lang.bind(this, this._resetToolbarMode));
- this._windowModeId =
- Global.modeController.connect('window-mode-changed',
- Lang.bind(this, this._resetToolbarMode));
this._resetToolbarMode();
this.widget.connect('destroy', Lang.bind(this,
function() {
this._clearStateData();
- if (this._windowModeId != 0) {
- Global.modeController.disconnect(this._windowModeId);
- this._windowModeId = 0;
- }
-
if (this._selectionModeId != 0) {
Global.selectionController.disconnect(this._selectionModeId);
this._selectionModeId = 0;
@@ -121,92 +138,54 @@ const MainToolbar = new Lang.Class({
}));
},
- _clearStateData: function() {
- this._model = null;
- this._collBackButton = null;
-
- if (this._collectionId != 0) {
- Global.collectionManager.disconnect(this._collectionId);
- this._collectionId = 0;
- }
-
- if (this._selectionChangedId != 0) {
- Global.selectionController.disconnect(this._selectionChangedId);
- this._selectionChangedId = 0;
- }
- },
-
- _clearToolbar: function() {
- this._clearStateData();
-
- this.widget.get_style_context().remove_class('documents-selection-mode');
- this.widget.reset_style();
- this.widget.clear();
- },
-
_setToolbarTitle: function() {
- let windowMode = Global.modeController.getWindowMode();
let selectionMode = Global.selectionController.getSelectionMode();
let activeCollection = Global.collectionManager.getActiveItem();
let primary = null;
let detail = null;
- if (windowMode == WindowMode.WindowMode.OVERVIEW) {
- if (!selectionMode) {
- if (activeCollection) {
- primary = activeCollection.name;
- } else {
- let string = Global.searchController.getString();
-
- if (string == '') {
- let searchType = Global.searchTypeManager.getActiveItem();
- let searchSource = Global.sourceManager.getActiveItem();
-
- if (searchType.id != 'all')
- primary = searchType.name;
- else
- primary = _("New and Recent");
-
- if (searchSource.id != 'all')
- detail = searchSource.name;
- } else {
- let searchMatch = Global.searchMatchManager.getActiveItem();
-
- primary = _("Results for \"%s\"").format(string);
- if (searchMatch.id == 'title')
- detail = _("filtered by title");
- else if (searchMatch.id == 'author')
- detail = _("filtered by author");
- }
- }
+ if (!selectionMode) {
+ if (activeCollection) {
+ primary = activeCollection.name;
} else {
- let length = Global.selectionController.getSelection().length;
-
- if (length == 0)
- detail = _("Click on items to select them");
- else
- detail = Gettext.ngettext("%d selected",
- "%d selected",
- length).format(length);
-
- if (activeCollection) {
- primary = activeCollection.name;
- } else if (length != 0) {
- primary = detail;
- detail = null;
- }
- }
- } else if (windowMode == WindowMode.WindowMode.PREVIEW) {
- let doc = Global.documentManager.getActiveItem();
- primary = doc.name;
+ let string = Global.searchController.getString();
+
+ if (string == '') {
+ let searchType = Global.searchTypeManager.getActiveItem();
+ let searchSource = Global.sourceManager.getActiveItem();
- if (this._model) {
- let curPage, totPages;
+ if (searchType.id != 'all')
+ primary = searchType.name;
+ else
+ primary = _("New and Recent");
- curPage = this._model.get_page();
- totPages = this._model.get_document().get_n_pages();
+ if (searchSource.id != 'all')
+ detail = searchSource.name;
+ } else {
+ let searchMatch = Global.searchMatchManager.getActiveItem();
+
+ primary = _("Results for \"%s\"").format(string);
+ if (searchMatch.id == 'title')
+ detail = _("filtered by title");
+ else if (searchMatch.id == 'author')
+ detail = _("filtered by author");
+ }
+ }
+ } else {
+ let length = Global.selectionController.getSelection().length;
- detail = _("%d of %d").format(curPage + 1, totPages);
+ if (length == 0)
+ detail = _("Click on items to select them");
+ else
+ detail = Gettext.ngettext("%d selected",
+ "%d selected",
+ length).format(length);
+
+ if (activeCollection) {
+ primary = activeCollection.name;
+ } else if (length != 0) {
+ primary = detail;
+ detail = null;
}
}
@@ -233,26 +212,23 @@ const MainToolbar = new Lang.Class({
Lang.bind(this, this._setToolbarTitle));
},
- _populateForPreview: function(model) {
- //back button, on the left of the toolbar
- let iconName =
- (this.widget.get_direction() == Gtk.TextDirection.RTL) ?
- 'go-next-symbolic' : 'go-previous-symbolic';
-
- let backButton =
- this.widget.add_button(iconName, _("Back"), true);
- backButton.connect('clicked', Lang.bind(this,
- function() {
- Global.documentManager.setActiveItem(null);
- }));
+ _onActiveCollectionChanged: function() {
+ let item = Global.collectionManager.getActiveItem();
- // menu button, on the right of the toolbar
- let menuModel = new Gio.Menu();
- menuModel.append_item(Gio.MenuItem.new(_("Open"), 'app.open-current'));
- menuModel.append_item(Gio.MenuItem.new(_("Print"), 'app.print-current'));
+ if (item && !this._collBackButton) {
+ this._collBackButton =
+ this.widget.add_button('go-previous-symbolic', _("Back"), true);
+ this._collBackButton.connect('clicked', Lang.bind(this,
+ function() {
+ Global.collectionManager.setActiveItem(null);
+ }));
+ } else if (!item && this._collBackButton) {
+ this._collBackButton.destroy();
+ this._collBackButton = null;
+ }
- let menuButton = this.widget.add_menu('emblem-system-symbolic', null, false);
- menuButton.set_menu_model(menuModel);
+ this._setToolbarTitle();
+ this._searchbar.hide();
},
_populateForOverview: function() {
@@ -270,87 +246,56 @@ const MainToolbar = new Lang.Class({
this._onActiveCollectionChanged();
},
- _onActiveCollectionChanged: function() {
- let item = Global.collectionManager.getActiveItem();
+ _clearStateData: function() {
+ this._collBackButton = null;
- if (item && !this._collBackButton) {
- this._collBackButton =
- this.widget.add_button('go-previous-symbolic', _("Back"), true);
- this._collBackButton.connect('clicked', Lang.bind(this,
- function() {
- Global.collectionManager.setActiveItem(null);
- }));
- } else if (!item && this._collBackButton) {
- this._collBackButton.destroy();
- this._collBackButton = null;
+ if (this._collectionId != 0) {
+ Global.collectionManager.disconnect(this._collectionId);
+ this._collectionId = 0;
}
- this._setToolbarTitle();
- this.searchbar.hide();
+ if (this._selectionChangedId != 0) {
+ Global.selectionController.disconnect(this._selectionChangedId);
+ this._selectionChangedId = 0;
+ }
},
- _resetToolbarMode: function() {
- this._clearToolbar();
-
- let windowMode = Global.modeController.getWindowMode();
- if (windowMode == WindowMode.WindowMode.OVERVIEW) {
- let selectionMode = Global.selectionController.getSelectionMode();
- if (selectionMode)
- this._populateForSelectionMode();
- else
- this._populateForOverview();
- } else if (windowMode == WindowMode.WindowMode.PREVIEW) {
- this._populateForPreview();
- }
+ _clearToolbar: function() {
+ this._clearStateData();
- this._setToolbarTitle();
- this.widget.show_all();
+ this.widget.get_style_context().remove_class('documents-selection-mode');
+ this.widget.reset_style();
+ this.widget.clear();
},
- setModel: function(model) {
- if (!model)
- return;
+ _resetToolbarMode: function() {
+ this._clearToolbar();
- this._model = model;
- this._model.connect('page-changed', Lang.bind(this,
- function() {
- this._setToolbarTitle();
- }));
+ let selectionMode = Global.selectionController.getSelectionMode();
+ if (selectionMode)
+ this._populateForSelectionMode();
+ else
+ this._populateForOverview();
this._setToolbarTitle();
- }
-});
-
-const OverviewToolbar = new Lang.Class({
- Name: 'OverviewToolbar',
- Extends: MainToolbar,
+ this.widget.show_all();
- _init: function(overlayLayout) {
- this.parent();
+ if (Global.searchController.getString() != '')
+ this._searchbar.show();
+ },
+ createSearchbar: function() {
// create the dropdown for the search bar, it's hidden by default
let dropdown = new Searchbar.Dropdown();
- overlayLayout.add(dropdown.actor,
+ this._overlayLayout.add(dropdown.actor,
Clutter.BinAlignment.CENTER, Clutter.BinAlignment.FIXED);
dropdown.actor.add_constraint(
new Clutter.BindConstraint({ source: this.toolbarActor,
coordinate: Clutter.BindCoordinate.Y }));
- this.searchbar = new Searchbar.OverviewSearchbar(dropdown);
+ this._searchbar = new Searchbar.OverviewSearchbar(dropdown);
this.layout.pack_start = true;
- this.layout.pack(this.searchbar.actor, false, true, false,
+ this.layout.pack(this._searchbar.actor, false, true, false,
Clutter.BoxAlignment.CENTER, Clutter.BoxAlignment.START);
- },
-
- _resetToolbarMode: function() {
- this.parent();
-
- let mode = Global.modeController.getWindowMode();
-
- if (mode == WindowMode.WindowMode.PREVIEW)
- this.searchbar.hide();
- else if (mode == WindowMode.WindowMode.OVERVIEW &&
- Global.searchController.getString() != '')
- this.searchbar.show();
}
});
diff --git a/src/mainWindow.js b/src/mainWindow.js
index 7125a0b..1c8a7b6 100644
--- a/src/mainWindow.js
+++ b/src/mainWindow.js
@@ -140,6 +140,16 @@ const MainWindow = new Lang.Class({
},
_onKeyPressEvent: function(widget, event) {
+ let toolbar = this._embed.getMainToolbar();
+
+ if (Utils.isSearchEvent(event)) {
+ toolbar.toggleSearch();
+ return true;
+ }
+
+ if (toolbar.handleEvent(event))
+ return true;
+
if (Global.modeController.getWindowMode() == WindowMode.WindowMode.PREVIEW)
return this._handleKeyPreview(event);
else
@@ -156,7 +166,6 @@ const MainWindow = new Lang.Class({
((state & Gdk.ModifierType.MOD1_MASK) != 0 &&
(direction == Gtk.TextDirection.LTR && keyval == Gdk.KEY_Left) ||
(direction == Gtk.TextDirection.RTL && keyval == Gdk.KEY_Right)) ||
- keyval == Gdk.KEY_BackSpace ||
keyval == Gdk.KEY_Back) {
Global.documentManager.setActiveItem(null);
return true;
@@ -168,14 +177,6 @@ const MainWindow = new Lang.Class({
_handleKeyOverview: function(event) {
let keyval = event.get_keyval()[1];
- if (Utils.isSearchEvent(event)) {
- this._embed._toolbar.searchbar.toggle();
- return true;
- }
-
- if (this._embed._toolbar.searchbar.deliverEvent(event))
- return true;
-
if (Global.selectionController.getSelectionMode() &&
keyval == Gdk.KEY_Escape) {
Global.selectionController.setSelectionMode(false);
diff --git a/src/preview.js b/src/preview.js
index 1fc397d..124f5b1 100644
--- a/src/preview.js
+++ b/src/preview.js
@@ -23,8 +23,10 @@ const Clutter = imports.gi.Clutter;
const EvView = imports.gi.EvinceView;
const Gd = imports.gi.Gd;
const Gdk = imports.gi.Gdk;
+const Gio = imports.gi.Gio;
const Gtk = imports.gi.Gtk;
const GtkClutter = imports.gi.GtkClutter;
+const _ = imports.gettext.gettext;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
@@ -32,6 +34,7 @@ const Mainloop = imports.mainloop;
const Global = imports.global;
const Tweener = imports.util.tweener;
const MainToolbar = imports.mainToolbar;
+const Searchbar = imports.searchbar;
const View = imports.view;
const _FULLSCREEN_TOOLBAR_TIMEOUT = 2; // seconds
@@ -312,3 +315,166 @@ const PreviewFullscreenToolbar = new Lang.Class({
transition: 'easeOutQuad' });
}
});
+
+const PreviewToolbar = new Lang.Class({
+ Name: 'PreviewToolbar',
+ Extends: MainToolbar.MainToolbar,
+
+ _init: function(previewView) {
+ this._previewView = previewView;
+
+ this.parent();
+
+ // back button, on the left of the toolbar
+ let iconName =
+ (this.widget.get_direction() == Gtk.TextDirection.RTL) ?
+ 'go-next-symbolic' : 'go-previous-symbolic';
+ let backButton =
+ this.widget.add_button(iconName, _("Back"), true);
+ backButton.connect('clicked', Lang.bind(this,
+ function() {
+ Global.documentManager.setActiveItem(null);
+ }));
+
+ // menu button, on the right of the toolbar
+ let menuModel = new Gio.Menu();
+ menuModel.append_item(Gio.MenuItem.new(_("Open"), 'app.open-current'));
+ menuModel.append_item(Gio.MenuItem.new(_("Print"), 'app.print-current'));
+
+ let menuButton = this.widget.add_menu('emblem-system-symbolic', null, false);
+ menuButton.set_menu_model(menuModel);
+
+ this._setToolbarTitle();
+ this.widget.show_all();
+ },
+
+ createSearchbar: function() {
+ this._searchbar = new PreviewSearchbar(this._previewView);
+ this.layout.pack_start = false;
+ this.layout.pack(this._searchbar.actor, false, true, false,
+ Clutter.BoxAlignment.CENTER, Clutter.BoxAlignment.START);
+ },
+
+ _setToolbarTitle: function() {
+ let doc = Global.documentManager.getActiveItem();
+ let primary = doc.name;
+ let detail = null;
+
+ if (this._model) {
+ let curPage, totPages;
+
+ curPage = this._model.get_page();
+ totPages = this._model.get_document().get_n_pages();
+
+ detail = _("%d of %d").format(curPage + 1, totPages);
+ }
+
+ if (detail)
+ detail = '(' + detail + ')';
+
+ this.widget.set_labels(primary, detail);
+ },
+
+ setModel: function(model) {
+ if (!model)
+ return;
+
+ this._model = model;
+ this._model.connect('page-changed', Lang.bind(this,
+ function() {
+ this._setToolbarTitle();
+ }));
+
+ this._setToolbarTitle();
+ }
+});
+
+const PreviewSearchbar = new Lang.Class({
+ Name: 'PreviewSearchbar',
+ Extends: Searchbar.Searchbar,
+
+ _init: function(previewView) {
+ this.parent();
+
+ this._previewView = previewView;
+ },
+
+ createSearchWidgets: function() {
+ this._searchContainer = new Gtk.Box({ orientation: Gtk.Orientation.HORIZONTAL,
+ spacing: 6 });
+
+ this._searchEntry = new Gtk.SearchEntry({ hexpand: true });
+ this._searchEntry.connect('activate', Lang.bind(this, this._searchNext));
+ this._searchContainer.add(this._searchEntry);
+
+ let controlsBox = new Gtk.Box({ orientation: Gtk.Orientation.HORIZONTAL });
+ controlsBox.get_style_context().add_class('linked');
+ controlsBox.get_style_context().add_class('raised');
+ this._searchContainer.add(controlsBox);
+
+ let prev = new Gtk.Button();
+ prev.connect('clicked', Lang.bind(this, this._searchPrev));
+ prev.set_image(new Gtk.Image({ icon_name: 'go-up-symbolic',
+ icon_size: Gtk.IconSize.MENU,
+ margin: 2 }));
+ prev.set_tooltip_text(_("Find Previous"));
+ controlsBox.add(prev);
+
+ let next = new Gtk.Button();
+ next.connect('clicked', Lang.bind(this, this._searchNext));
+ next.set_image(new Gtk.Image({ icon_name: 'go-down-symbolic',
+ icon_size: Gtk.IconSize.MENU,
+ margin: 2 }));
+ next.set_tooltip_text(_("Find Next"));
+ controlsBox.add(next);
+ },
+
+ entryChanged: function() {
+ this._previewView.view.find_search_changed();
+ this._startSearch();
+ },
+
+ show: function() {
+ this.parent();
+
+ this._previewView.view.find_set_highlight_search(true);
+ this._startSearch();
+ },
+
+ hide: function() {
+ this.parent();
+
+ this._previewView.view.find_set_highlight_search(false);
+ },
+
+ _startSearch: function() {
+ let model = this._previewView.getModel();
+ if (!model)
+ return;
+
+ let str = this._searchEntry.get_text();
+ if (!str)
+ return;
+
+ let evDoc = model.get_document();
+ let job = EvView.JobFind.new(evDoc, model.get_page(), evDoc.get_n_pages(),
+ str, false);
+ job.connect('updated', Lang.bind(this, this._onSearchJobUpdated));
+
+ job.scheduler_push_job(EvView.JobPriority.PRIORITY_NONE);
+ },
+
+ _searchPrev: function() {
+ this._previewView.view.find_previous();
+ },
+
+ _searchNext: function() {
+ this._previewView.view.find_next();
+ },
+
+ _onSearchJobUpdated: function(job, page) {
+ // FIXME: ev_job_find_get_results() returns a GList **
+ // and thus is not introspectable
+ Gd.ev_view_find_changed(this._previewView.view, job, page);
+ }
+});
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]