[gnome-maps/wip/completion: 10/10] Replace GtkEntryCompletion with SearchPopup
- From: Jonas Danielsson <jonasdn src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-maps/wip/completion: 10/10] Replace GtkEntryCompletion with SearchPopup
- Date: Wed, 26 Nov 2014 10:19:56 +0000 (UTC)
commit 55ace1ed4109607c2560bd415d8429165fd63372
Author: Jonas Danielsson <jonas threetimestwo org>
Date: Mon Nov 24 03:49:21 2014 -0500
Replace GtkEntryCompletion with SearchPopup
https://bugzilla.gnome.org/show_bug.cgi?id=739036
src/placeEntry.js | 59 +++++++++++++-------
src/placeStore.js | 17 ------
src/search-popup.ui | 60 +++++++++++++++------
src/searchPopup.js | 146 +++++++++++++++++++++++++++++++++++++++++++++++++--
4 files changed, 222 insertions(+), 60 deletions(-)
---
diff --git a/src/placeEntry.js b/src/placeEntry.js
index 409bc17..806188d 100644
--- a/src/placeEntry.js
+++ b/src/placeEntry.js
@@ -21,6 +21,7 @@
* Mattias Bengtsson <mattias jc bengtsson gmail com>
*/
+const GLib = imports.gi.GLib;
const GObject = imports.gi.GObject;
const Geocode = imports.gi.GeocodeGlib;
const Gtk = imports.gi.Gtk;
@@ -94,17 +95,31 @@ const PlaceEntry = new Lang.Class({
let parseOnFocusOut = props.parseOnFocusOut;
delete props.parseOnFocusOut;
- props.completion = this._createCompletion();
this.parent(props);
- this._popover = this._createPopover(numVisible, maxChars);
+ this._filter = new Gtk.TreeModelFilter({
+ child_model: Application.placeStore
+ });
+ this._filter.set_visible_func(this._completionVisibleFunc.bind(this),
+ null,
+ null);
+ this._popover = this._createPopover(numVisible, maxChars);
this.connect('activate', this._onActivate.bind(this));
- this.connect('search-changed', (function() {
- this.popover.hide();
-
- if (this.text.length === 0)
+ this.connect('changed', (function() {
+ if (this.text.length === 0) {
+ this._popover.hide();
this.place = null;
+ return;
+ }
+
+ /* Filter model based on input text */
+ this._filter.refilter();
+
+ if (this._filter.iter_n_children(null) > 0)
+ this._popover.showCompletion(this._filter, this.text);
+ else
+ this._popover.hide();
}).bind(this));
if (parseOnFocusOut) {
@@ -115,21 +130,6 @@ const PlaceEntry = new Lang.Class({
}
},
- _createCompletion: function() {
- let { completion } = Utils.getUIObject('place-entry',
- ['completion']);
-
- completion.set_model(Application.placeStore);
- completion.set_match_func(PlaceStore.completionMatchFunc);
-
- completion.connect('match-selected', (function(c, model, iter) {
- this.place = model.get_value(iter, PlaceStore.Columns.PLACE);
- return true;
- }).bind(this));
-
- return completion;
- },
-
_createPopover: function(numVisible, maxChars) {
let popover = new SearchPopup.SearchPopup({ num_visible: numVisible,
relative_to: this,
@@ -149,6 +149,23 @@ const PlaceEntry = new Lang.Class({
return popover;
},
+ _completionVisibleFunc: function(model, iter) {
+ let name = model.get_value(iter, PlaceStore.Columns.NAME);
+
+ if (name === null)
+ return false;
+
+ name = GLib.utf8_normalize (name, -1, GLib.NormalizeMode.ALL);
+ if (name === null)
+ return false;
+
+ if (!GLib.ascii_strncasecmp(name, this.text, this.text.length))
+ return true;
+ else
+ return false;
+
+ },
+
_validateCoordinates: function(lat, lon) {
return lat <= 90 && lat >= -90 && lon <= 180 && lon >= -180;
},
diff --git a/src/placeStore.js b/src/placeStore.js
index 3723714..1209366 100644
--- a/src/placeStore.js
+++ b/src/placeStore.js
@@ -46,23 +46,6 @@ const Columns = {
ADDED: 4
};
-function completionMatchFunc(completion, key, iter) {
- let model = completion.get_model();
- let name = model.get_value(iter, Columns.NAME);
-
- if (name === null)
- return false;
-
- name = GLib.utf8_normalize (name, -1, GLib.NormalizeMode.ALL);
- if (name === null)
- return false;
-
- if (!GLib.ascii_strncasecmp(name, key, key.length))
- return true;
- else
- return false;
-}
-
const PlaceStore = new Lang.Class({
Name: 'PlaceStore',
Extends: Gtk.ListStore,
diff --git a/src/search-popup.ui b/src/search-popup.ui
index a768c89..aa923ad 100644
--- a/src/search-popup.ui
+++ b/src/search-popup.ui
@@ -5,41 +5,67 @@
<property name="visible">False</property>
<property name="no_show_all">True</property>
<property name="hexpand">False</property>
+ <property name="modal">False</property>
<style>
<class name="maps-popover"/>
</style>
<child>
- <object class="GtkStack" id="stack">
+ <object class="GtkGrid" id="mainGrid">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="transition-type">crossfade</property>
- <style>
- <class name="maps-stack"/>
- </style>
+ <property name="can_focus">True</property>
+ <property name="orientation">vertical</property>
<child>
- <object class="GtkScrolledWindow" id="scrolledWindow">
+ <object class="GtkRevealer" id="hintRevealer">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="hscrollbar_policy">never</property>
- <property name="shadow_type">in</property>
<child>
- <object class="GtkListBox" id="list">
+ <object class="GtkLabel" id="hintLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="expand">True</property>
- <property name="activate_on_single_click">True</property>
+ <property name="label" translatable="yes">Press enter to search</property>
+ <property name="margin_bottom">10</property>
+ <property name="margin_top">5</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
</object>
</child>
</object>
</child>
<child>
- <object class="GtkSpinner" id="spinner">
+ <object class="GtkStack" id="stack">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="halign">center</property>
- <property name="valign">center</property>
- <property name="width_request">16</property>
- <property name="height_request">16</property>
+ <property name="transition-type">crossfade</property>
+ <style>
+ <class name="maps-stack"/>
+ </style>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledWindow">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkListBox" id="list">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="expand">True</property>
+ <property name="activate_on_single_click">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSpinner" id="spinner">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+ <property name="width_request">16</property>
+ <property name="height_request">16</property>
+ </object>
+ </child>
</object>
</child>
</object>
diff --git a/src/searchPopup.js b/src/searchPopup.js
index a48553d..5468e9c 100644
--- a/src/searchPopup.js
+++ b/src/searchPopup.js
@@ -18,13 +18,25 @@
* Author: Jonas Danielsson <jonas threetimestwo org>
*/
+const Gdk = imports.gi.Gdk;
+const GLib = imports.gi.GLib;
const GObject = imports.gi.GObject;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
+const PlaceFormatter = imports.placeFormatter;
const PlaceListRow = imports.placeListRow;
+const PlaceStore = imports.placeStore;
const _PLACE_ICON_SIZE = 20;
+const _ROW_HEIGHT = 50;
+
+const Mode = {
+ IDLE: 0,
+ ACTIVATED: 1,
+ COMPLETION: 2,
+ RESULT: 3
+};
const SearchPopup = new Lang.Class({
Name: 'SearchPopup',
@@ -33,7 +45,8 @@ const SearchPopup = new Lang.Class({
'selected' : { param_types: [ GObject.TYPE_OBJECT ] }
},
Template: 'resource:///org/gnome/maps/search-popup.ui',
- InternalChildren: [ 'scrolledWindow',
+ InternalChildren: [ 'hintRevealer',
+ 'scrolledWindow',
'stack',
'spinner',
'list' ],
@@ -47,11 +60,38 @@ const SearchPopup = new Lang.Class({
this.parent(props);
- this._list.connect('row-activated', (function(list, row) {
- if (row)
- this.emit('selected', row.place);
+ this._entry = this.relative_to;
+
+ this._list.connect('row-activated', (function(list, row) {
+ if (row)
+ this.emit('selected', row.place);
+ }).bind(this));
+
+ // Make sure we clear all selected rows when the search string change
+ this._entry.connect('changed', (function() {
+ this._list.unselect_all();
}).bind(this));
+ // Do not show 'press enter to search' when we have
+ // selected rows in completion mode.
+ this._list.connect('selected-rows-changed', (function(list, row) {
+ if (this._mode !== Mode.COMPLETION)
+ return;
+
+ if (this._list.get_selected_rows().length > 0)
+ this._hintRevealer.reveal_child = false;
+ else
+ this._hintRevealer.reveal_child = true;
+ }).bind(this));
+
+ // We need to propagate events to the listbox so that we can
+ // keep typing while selecting a place. But we do not want to
+ // propagate the 'enter' key press if there is a selection.
+ this._entry.connect('key-press-event',
+ this._propagateKeys.bind(this));
+ this._entry.connect('button-press-event',
+ this._list.unselect_all.bind(this._list));
+
this._list.set_header_func(function(row, before) {
let header = new Gtk.Separator();
if (before)
@@ -66,6 +106,8 @@ const SearchPopup = new Lang.Class({
},
showSpinner: function() {
+ this._hintRevealer.reveal_child = false;
+
this._spinner.start();
this._stack.set_visible_child(this._spinner);
@@ -74,6 +116,11 @@ const SearchPopup = new Lang.Class({
},
showResult: function() {
+ if (this._mode !== Mode.RESULT)
+ this._mode = Mode.RESULT;
+
+ this._hintRevealer.reveal_child = false;
+
if (this._spinner.active)
this._spinner.stop();
@@ -81,8 +128,21 @@ const SearchPopup = new Lang.Class({
if (!this.get_visible())
this.show();
+ },
+
+ showCompletion: function(filter, searchString) {
+ if (this._mode === Mode.ACTIVATED) {
+ this._mode = Mode.IDLE;
+ return;
+ }
+ this._populateFromFilter(filter, searchString);
+ this._mode = Mode.COMPLETION;
+ this._hintRevealer.reveal_child = true;
+
+ this._stack.set_visible_child(this._scrolledWindow);
- this.grab_focus();
+ if (!this.get_visible())
+ this.show();
},
vfunc_hide: function() {
@@ -90,6 +150,9 @@ const SearchPopup = new Lang.Class({
this._spinner.stop();
this.parent();
+
+ if (this.get_mapped())
+ this.unmap();
},
updateResult: function(places, searchString) {
@@ -107,5 +170,78 @@ const SearchPopup = new Lang.Class({
can_focus: true });
this._list.add(row);
}).bind(this));
+ },
+
+ _propagateKeys: function(entry, event) {
+ let row = this._list.get_selected_row();
+ if (!row)
+ row = this._list.get_row_at_index(0);
+ if (!row)
+ return false;
+
+ let length = this._list.get_children().length;
+ let keyval = event.get_keyval()[1];
+
+ if (keyval === Gdk.KEY_Escape) {
+ this.hide();
+ return false;
+ }
+
+ // If we get an 'enter' keypress and we have a selected
+ // row, we do not want to propagate the event.
+ if ((this.visible && row.is_selected()) &&
+ keyval === Gdk.KEY_Return ||
+ keyval === Gdk.KEY_KP_ENTER ||
+ keyval === Gdk.KEY_ISO_Enter) {
+ row.activate();
+ this._mode = Mode.ACTIVATED;
+
+ return true;
+ } else if (keyval === Gdk.KEY_KP_Up || keyval === Gdk.KEY_Up) {
+ this.show();
+
+ if (!row.is_selected()) {
+ let pRow = this._list.get_row_at_index(length - 1);
+ this._list.select_row(pRow);
+ return false;
+ }
+
+ if (row.get_index() > 0) {
+ let pRow = this._list.get_row_at_index(row.get_index() - 1);
+ this._list.select_row(pRow);
+ } else
+ this._list.unselect_all();
+ } else if (keyval === Gdk.KEY_KP_Down || keyval === Gdk.KEY_Down) {
+ this.show();
+
+ if (!row.is_selected()) {
+ this._list.select_row(row);
+ return false;
+ }
+ if (row.get_index() != (length - 1)) {
+ let nRow = this._list.get_row_at_index(row.get_index() + 1);
+ this._list.select_row(nRow);
+ }
+ else
+ this._list.unselect_all();
+ }
+ return false;
+ },
+
+ _populateFromFilter: function(filter, searchString) {
+ this._list.forall(function(row) {
+ row.destroy();
+ });
+
+ filter.foreach((function(model, path, iter) {
+ let place = model.get_value(iter, PlaceStore.Columns.PLACE);
+ let type = model.get_value(iter, PlaceStore.Columns.TYPE);
+ let row = new PlaceListRow.PlaceListRow({ place: place,
+ searchString: searchString,
+ type: type,
+ maxChars: this._maxChars,
+ can_focus: true });
+ this._list.add(row);
+ }).bind(this));
}
});
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]