[gnome-characters/wip/dueno/emoji: 5/5] WIP



commit 5d3d54cc48bb66fbc571035d9844427eda6bb111
Author: Daiki Ueno <dueno src gnome org>
Date:   Fri Aug 18 17:29:58 2017 +0200

    WIP

 data/mainwindow.ui  |   18 +++++
 src/categoryList.js |  199 ++++++++++++++++++++++++++++++++++++++++++++------
 src/window.js       |  108 ++++++++++++++++++++++------
 3 files changed, 279 insertions(+), 46 deletions(-)
---
diff --git a/data/mainwindow.ui b/data/mainwindow.ui
index b807448..000414f 100644
--- a/data/mainwindow.ui
+++ b/data/mainwindow.ui
@@ -29,6 +29,24 @@
          </packing>
        </child>
        <child>
+         <object class="GtkButton" id="back-button">
+            <property name="can_focus">True</property>
+            <property name="visible">False</property>
+            <style>
+              <class name="image-button"/>
+            </style>
+            <child>
+              <object class="GtkImage" id="back-button-image">
+               <property name="visible">True</property>
+               <property name="icon-name">go-previous-symbolic</property>
+              </object>
+            </child>
+         </object>
+         <packing>
+            <property name="pack-type">start</property>
+         </packing>
+       </child>
+       <child>
          <object class="GtkMenuButton" id="menu-button">
             <property name="can_focus">False</property>
             <property name="visible">True</property>
diff --git a/src/categoryList.js b/src/categoryList.js
index fbb5833..673c148 100644
--- a/src/categoryList.js
+++ b/src/categoryList.js
@@ -1,6 +1,6 @@
 // -*- Mode: js; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*-
 //
-// Copyright (C) 2014-2015  Daiki Ueno <dueno src gnome org>
+// Copyright (C) 2014-2017  Daiki Ueno <dueno src gnome org>
 //
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU General Public License
@@ -26,60 +26,129 @@ const Gettext = imports.gettext;
 const Gc = imports.gi.Gc;
 const Util = imports.util;
 
-const BaseCategoryList = [
-    {
-        name: 'recent',
-        category: Gc.Category.NONE,
-        title: N_('Recently Used'),
-        icon_name: 'document-open-recent-symbolic'
-    },
+const LetterCategoryList = [
     {
         name: 'punctuation',
         category: Gc.Category.PUNCTUATION,
         title: N_('Punctuation'),
-        icon_name: 'characters-punctuation-symbolic'
+        icon_name: 'characters-punctuation-symbolic',
+        action_name: 'subcategory'
     },
     {
         name: 'arrow',
         category: Gc.Category.ARROW,
         title: N_('Arrows'),
-        icon_name: 'characters-arrow-symbolic'
+        icon_name: 'characters-arrow-symbolic',
+        action_name: 'subcategory'
     },
     {
         name: 'bullet',
         category: Gc.Category.BULLET,
         title: N_('Bullets'),
-        icon_name: 'characters-bullet-symbolic'
+        icon_name: 'characters-bullet-symbolic',
+        action_name: 'subcategory'
     },
     {
         name: 'picture',
         category: Gc.Category.PICTURE,
         title: N_('Pictures'),
-        icon_name: 'characters-picture-symbolic'
+        icon_name: 'characters-picture-symbolic',
+        action_name: 'subcategory'
     },
     {
         name: 'currency',
         category: Gc.Category.CURRENCY,
         title: N_('Currencies'),
-        icon_name: 'characters-currency-symbolic'
+        icon_name: 'characters-currency-symbolic',
+        action_name: 'subcategory'
     },
     {
         name: 'math',
         category: Gc.Category.MATH,
         title: N_('Math'),
-        icon_name: 'characters-math-symbolic'
+        icon_name: 'characters-math-symbolic',
+        action_name: 'subcategory'
     },
     {
         name: 'letters',
         category: Gc.Category.LATIN,
         title: N_('Letters'),
-        icon_name: 'characters-latin-symbolic'
+        icon_name: 'characters-latin-symbolic',
+        action_name: 'subcategory'
+    }
+];
+
+const EmojiCategoryList = [
+    {
+        name: 'recent',
+        category: Gc.Category.NONE,
+        title: N_('Recently Used'),
+        icon_name: 'document-open-recent-symbolic',
+        action_name: 'subcategory'
+    },
+    {
+        name: 'emoji-smileys',
+        category: Gc.Category.EMOJI_SMILEYS,
+        title: N_('Smileys & People'),
+        icon_name: 'characters-emoji-smileys',
+        action_name: 'subcategory'
+    },
+    {
+        name: 'emoji-animals',
+        category: Gc.Category.EMOJI_ANIMALS,
+        title: N_('Animals & Nature'),
+        icon_name: 'characters-emoji-animals',
+        action_name: 'subcategory'
+    },
+    {
+        name: 'emoji-food',
+        category: Gc.Category.EMOJI_FOOD,
+        title: N_('Food & Drink'),
+        icon_name: 'characters-emoji-food',
+        action_name: 'subcategory'
+    },
+    {
+        name: 'emoji-activities',
+        category: Gc.Category.EMOJI_ACTIVITIES,
+        title: N_('Activities'),
+        icon_name: 'characters-emoji-activities',
+        action_name: 'subcategory'
     },
     {
-        name: 'emoticon',
-        category: Gc.Category.EMOTICON,
-        title: N_('Emoticons'),
-        icon_name: 'face-smile-symbolic'
+        name: 'emoji-travel',
+        category: Gc.Category.EMOJI_TRAVEL,
+        title: N_('Travel & Places'),
+        icon_name: 'characters-emoji-travel',
+        action_name: 'subcategory'
+    },
+    {
+        name: 'emoji-objects',
+        category: Gc.Category.EMOJI_OBJECTS,
+        title: N_('Objects'),
+        icon_name: 'characters-emoji-objects',
+        action_name: 'subcategory'
+    },
+    {
+        name: 'emoji-symbols',
+        category: Gc.Category.EMOJI_SYMBOLS,
+        title: N_('Symbols'),
+        icon_name: 'characters-emoji-symbols',
+        action_name: 'subcategory'
+    },
+    {
+        name: 'emoji-flags',
+        category: Gc.Category.EMOJI_FLAGS,
+        title: N_('Flags'),
+        icon_name: 'characters-emoji-flags',
+        action_name: 'subcategory'
+    },
+    {
+        name: 'letters',
+        category: Gc.Category.NONE,
+        title: N_('Letters & Symbols'),
+        icon_name: 'characters-latin-symbolic',
+        secondary_icon_name: 'go-next-symbolic',
+        action_name: 'category',
     }
 ];
 
@@ -106,11 +175,18 @@ const CategoryListRowWidget = new Lang.Class({
                                     halign: Gtk.Align.START });
         label.get_style_context().add_class('category-label');
         hbox.pack_start(label, true, true, 0);
+
+        if (category.secondary_icon_name) {
+            let icon = new Gio.ThemedIcon({ name: category.secondary_icon_name });
+            let image = Gtk.Image.new_from_gicon(icon, Gtk.IconSize.BUTTON);
+            image.get_style_context().add_class('category-icon');
+            hbox.pack_start(image, false, false, 2);
+        }
     }
 });
 
-var CategoryListWidget = new Lang.Class({
-    Name: 'CategoryListWidget',
+const LetterCategoryListWidget = new Lang.Class({
+    Name: 'LetterCategoryListWidget',
     Extends: Gtk.ListBox,
 
     _init: function(params) {
@@ -132,8 +208,8 @@ var CategoryListWidget = new Lang.Class({
     vfunc_row_selected: function(row) {
         if (row != null) {
             let toplevel = row.get_toplevel();
-            let category = toplevel.lookup_action('category');
-            category.activate(new GLib.Variant('s', row.category.name));
+            let action = toplevel.lookup_action('subcategory');
+            action.activate(new GLib.Variant('s', row.category.name));
         }
     },
 
@@ -248,7 +324,7 @@ var CategoryListWidget = new Lang.Class({
         if (this._categoryList != null)
             return;
 
-        this._categoryList = BaseCategoryList.slice();
+        this._categoryList = LetterCategoryList.slice();
 
         // Populate the "scripts" element of the "Letter" category
         // object, based on the current locale and the input-sources
@@ -281,3 +357,78 @@ var CategoryListWidget = new Lang.Class({
         return null;
     }
 });
+
+const EmojiCategoryListWidget = new Lang.Class({
+    Name: 'EmojiCategoryListWidget',
+    Extends: Gtk.ListBox,
+
+    _init: function(params) {
+        params = Params.fill(params, {});
+        this.parent(params);
+
+        this.get_style_context().add_class('categories');
+
+        this._categoryList = EmojiCategoryList.slice();
+
+        for (let index in this._categoryList) {
+            let category = this._categoryList[index];
+            let rowWidget = new CategoryListRowWidget({}, category);
+            rowWidget.get_style_context().add_class('category');
+            // Add a separator over the "Letters & Symbols" category
+            if (category.name == 'letters') {
+                let separator = new Gtk.Separator();
+                let separatorRowWidget = new Gtk.ListBoxRow({
+                    selectable: false
+                });
+                separatorRowWidget.add(separator);
+                this.add(separatorRowWidget);
+            }
+            this.add(rowWidget);
+        }
+    },
+
+    vfunc_row_selected: function(row) {
+        if (row != null && row.selectable) {
+            let toplevel = row.get_toplevel();
+            let action = toplevel.lookup_action(row.category.action_name);
+            action.activate(new GLib.Variant('s', row.category.name));
+        }
+    },
+
+    getCategoryList: function() {
+        return this._categoryList;
+    },
+
+    getCategory: function(name) {
+        for (let index in this._categoryList) {
+            let category = this._categoryList[index];
+            if (category.name == name)
+                return category;
+        }
+        return null;
+    }
+});
+
+var CategoryListView = new Lang.Class({
+    Name: 'CategoryListView',
+    Extends: Gtk.Stack,
+
+    _init: function(params) {
+        params = Params.fill(params, {
+            hexpand: true, vexpand: true,
+            transition_type: Gtk.StackTransitionType.SLIDE_RIGHT
+        });
+        this.parent(params);
+
+        let emojiCategoryList = new EmojiCategoryListWidget({});
+        this.add_named(emojiCategoryList, 'emojis');
+        let letterCategoryList = new LetterCategoryListWidget({});
+        this.add_named(letterCategoryList, 'letters');
+
+        this.set_visible_child_name('emojis');
+    },
+
+    getCategoryList: function() {
+        return ['emojis', 'letters'];
+    }
+});
diff --git a/src/window.js b/src/window.js
index 83acdb4..580f70c 100644
--- a/src/window.js
+++ b/src/window.js
@@ -24,6 +24,7 @@
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+const Gc = imports.gi.Gc;
 const Gio = imports.gi.Gio;
 const GLib = imports.gi.GLib;
 const GObject = imports.gi.GObject;
@@ -44,7 +45,7 @@ var MainWindow = new Lang.Class({
     Extends: Gtk.ApplicationWindow,
     Template: 'resource:///org/gnome/Characters/mainwindow.ui',
     InternalChildren: ['main-headerbar', 'search-active-button',
-                       'search-bar', 'search-entry',
+                       'search-bar', 'search-entry', 'back-button',
                        'menu-button',
                        'main-grid', 'main-hbox', 'sidebar-grid'],
     Properties: {
@@ -74,7 +75,11 @@ var MainWindow = new Lang.Class({
                           { name: 'category',
                             activate: this._category,
                             parameter_type: new GLib.VariantType('s'),
-                            state: new GLib.Variant('s', 'punctuation') },
+                            state: new GLib.Variant('s', 'emojis') },
+                          { name: 'subcategory',
+                            activate: this._subcategory,
+                            parameter_type: new GLib.VariantType('s'),
+                            state: new GLib.Variant('s', 'emoji-smileys') },
                           { name: 'character',
                             activate: this._character,
                             parameter_type: new GLib.VariantType('s') },
@@ -96,24 +101,31 @@ var MainWindow = new Lang.Class({
         this._search_entry.connect('search-changed',
                                    Lang.bind(this, this._handleSearchChanged));
 
+        this._back_button.connect('clicked',
+                                  Lang.bind(this, function() {
+                                      let action = this.lookup_action('category');
+                                      action.activate(new GLib.Variant('s', 'emojis'));
+                                  }));
+        this._back_button.bind_property('visible',
+                                        this._search_active_button, 'visible',
+                                        GObject.BindingFlags.SYNC_CREATE |
+                                        GObject.BindingFlags.INVERT_BOOLEAN);
+
         this._menu_popover = new Menu.MenuPopover({});
         this._menu_button.set_popover(this._menu_popover);
 
-        this._categoryList =
-            new CategoryList.CategoryListWidget({ vexpand: true });
+        this._categoryListView =
+            new CategoryList.CategoryListView({ vexpand: true });
         let scroll = new Gtk.ScrolledWindow({
             hscrollbar_policy: Gtk.PolicyType.NEVER,
             hexpand: false,
         });
-        scroll.add(this._categoryList);
+        scroll.add(this._categoryListView);
         this._sidebar_grid.add(scroll);
 
-        this._mainView = new MainView({ categoryList: this._categoryList });
-
-        if (this._mainView.recentCharacters.length == 0) {
-            let row = this._categoryList.get_row_at_index(1);
-            this._categoryList.select_row(row);
-        }
+        this._mainView = new MainView({
+            categoryListView: this._categoryListView
+        });
 
         this._main_hbox.pack_start(this._mainView, true, true, 0);
         this._main_grid.show_all();
@@ -123,6 +135,22 @@ var MainWindow = new Lang.Class({
         this.connect('key-press-event', Lang.bind(this, this._handleKeyPress));
     },
 
+    vfunc_map: function() {
+        this.parent();
+        this._selectFirstSubcategory();
+    },
+
+    // Select the first subcategory which contains at least one character.
+    _selectFirstSubcategory: function() {
+        let categoryList = this._categoryListView.get_visible_child();
+        let index = 0;
+        let row = categoryList.get_row_at_index(index);
+        if (row.category.name == 'recent' &&
+            this._mainView.recentCharacters.length == 0)
+            index++;
+        categoryList.select_row(categoryList.get_row_at_index(index));
+    },
+
     get search_active() {
         return this._searchActive;
     },
@@ -200,7 +228,35 @@ var MainWindow = new Lang.Class({
 
         let [name, length] = v.get_string()
 
-        let category = this._categoryList.getCategory(name);
+        this._categoryListView.set_visible_child_name(name);
+        let categoryList = this._categoryListView.get_visible_child();
+        if (categoryList == null)
+            return;
+
+        this._selectFirstSubcategory();
+        let category = categoryList.get_selected_row().category;
+
+        if (name == 'emojis') {
+            this._back_button.hide();
+        } else {
+            this._back_button.show();
+        }
+
+        Util.assertNotEqual(category, null);
+        this._mainView.setPage(category);
+        this._updateTitle(category.title);
+    },
+
+    _subcategory: function(action, v) {
+        this.search_active = false;
+
+        let [name, length] = v.get_string()
+
+        let categoryList = this._categoryListView.get_visible_child();
+        if (categoryList == null)
+            return;
+
+        let category = categoryList.getCategory(name);
 
         Util.assertNotEqual(category, null);
         this._mainView.setPage(category);
@@ -262,7 +318,7 @@ const MainView = new Lang.Class({
     },
 
     _init: function(params) {
-        let filtered = Params.filter(params, { categoryList: null });
+        let filtered = Params.filter(params, { categoryListView: null });
         params = Params.fill(params, {
             hexpand: true, vexpand: true,
             transition_type: Gtk.StackTransitionType.CROSSFADE
@@ -271,17 +327,25 @@ const MainView = new Lang.Class({
 
         this._filterFontFamily = null;
         this._characterLists = {};
-        this._categoryList = filtered.categoryList;
+        this._categoryListView = filtered.categoryListView;
 
         let characterList;
-        let categories = this._categoryList.getCategoryList();
-        for (let index in categories) {
-            let category = categories[index];
-            characterList = this._createCharacterList(
-                category.name, _('%s Character List').format(category.title));
-            // FIXME: Can't use GtkContainer.child_get_property.
-            characterList.title = category.title;
-            this.add_titled(characterList, category.name, category.title);
+        let categories = this._categoryListView.getCategoryList();
+
+        for (let i in categories) {
+            let category = categories[i];
+            let categoryList = this._categoryListView.get_child_by_name(category);
+            let subcategories = categoryList.getCategoryList();
+            for (let j in subcategories) {
+                let subcategory = subcategories[j];
+                if (subcategory.action_name != 'subcategory')
+                    continue;
+                characterList = this._createCharacterList(
+                    subcategory.name, _('%s Character List').format(subcategory.title));
+                // FIXME: Can't use GtkContainer.child_get_property.
+                characterList.title = subcategory.title;
+                this.add_titled(characterList, subcategory.name, subcategory.title);
+            }
         }
 
         characterList = this._createCharacterList(


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]