[gnome-characters/bilelmoussaoui/gtk4: 40/76] characters view: implement scrollable




commit 67eb70690e427ad68b90421f19204778cef28ec4
Author: Bilal Elmoussaoui <bil elmoussaoui gmail com>
Date:   Mon Nov 22 19:20:32 2021 +0100

    characters view: implement scrollable
    
    Merge both CharactersView & CharactersListView into one
    and make it a GtkScrollable

 data/characters_view.ui                      |  10 -
 data/meson.build                             |   1 -
 data/org.gnome.Characters.data.gresource.xml |   1 -
 data/window.ui                               |   2 +-
 src/charactersView.js                        | 360 ++++++++++++---------------
 src/sidebar.js                               |   1 -
 src/window.js                                |   5 +-
 7 files changed, 169 insertions(+), 211 deletions(-)
---
diff --git a/data/meson.build b/data/meson.build
index abd70fa..87da838 100644
--- a/data/meson.build
+++ b/data/meson.build
@@ -10,7 +10,6 @@ resource_data = files(
   'icons/hicolor/scalable/categories/characters-punctuation-symbolic.svg',
   'style.css',
   'character_dialog.ui',
-  'characters_view.ui',
   'menu.ui',
   'shortcuts.ui',
   'sidebar.ui',
diff --git a/data/org.gnome.Characters.data.gresource.xml b/data/org.gnome.Characters.data.gresource.xml
index 116d834..914073b 100644
--- a/data/org.gnome.Characters.data.gresource.xml
+++ b/data/org.gnome.Characters.data.gresource.xml
@@ -2,7 +2,6 @@
 <gresources>
   <gresource prefix="/org/gnome/Characters">
     <file preprocess="xml-stripblanks">character_dialog.ui</file>
-    <file preprocess="xml-stripblanks">characters_view.ui</file>
     <file preprocess="xml-stripblanks" alias="gtk/help-overlay.ui">shortcuts.ui</file>
     <file preprocess="xml-stripblanks">menu.ui</file>
     <file preprocess="xml-stripblanks">sidebar.ui</file>
diff --git a/data/window.ui b/data/window.ui
index 13a42f9..6613b9a 100644
--- a/data/window.ui
+++ b/data/window.ui
@@ -149,7 +149,7 @@
                   <object class="GtkStackPage">
                     <property name="name">character-list</property>
                     <property name="child">
-                      <object class="GtkScrolledWindow">
+                      <object class="GtkScrolledWindow" id="scrolledWindow">
                         <property name="hscrollbar-policy">never</property>
                         <child>
                           <object class="Gjs_CharactersView" id="charactersView" />
diff --git a/src/charactersView.js b/src/charactersView.js
index 4f5ce66..e144eac 100644
--- a/src/charactersView.js
+++ b/src/charactersView.js
@@ -182,20 +182,120 @@ const CharacterListRow = GObject.registerClass({
     }
 });
 
-const CharacterListWidget = GObject.registerClass({
+const MAX_SEARCH_RESULTS = 100;
+
+var FontFilter = GObject.registerClass({
+    Properties: {
+        'font': GObject.ParamSpec.string(
+            'font', '', '',
+            GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE,
+            'Cantarell 50'),
+    },
+    Signals: {
+        'filter-set': { param_types: [] },
+    },
+}, class FontFilter extends GObject.Object {
+    _init() {
+        super._init({});
+
+        this._fontDescription = null;
+        this._filterFontDescription = null;
+
+        Main.settings.bind('font', this, 'font', Gio.SettingsBindFlags.DEFAULT);
+    }
+
+    get font() {
+        return this._font;
+    }
+
+    set font(v) {
+        let fontDescription = Pango.FontDescription.from_string(v);
+        if (fontDescription.get_size() === 0)
+            fontDescription.set_size(CELL_SIZE * Pango.SCALE);
+
+        if (this._fontDescription &&
+            fontDescription.equal(this._fontDescription))
+            return;
+
+        this._font = v;
+        this._fontDescription = fontDescription;
+    }
+
+    get fontDescription() {
+        if (this._filterFontDescription)
+            return this._filterFontDescription;
+        return this._fontDescription;
+    }
+
+    setFilterFont(v) {
+        let fontDescription;
+        if (v === null) {
+            fontDescription = null;
+        } else {
+            fontDescription = Pango.FontDescription.from_string(v);
+            fontDescription.set_size(this._fontDescription.get_size());
+        }
+
+        if (this._filterFontDescription !== null && fontDescription === null ||
+            this._filterFontDescription === null && fontDescription !== null ||
+            this._filterFontDescription !== null && fontDescription !== null &&
+             !fontDescription.equal(this._filterFontDescription)) {
+            this._filterFontDescription = fontDescription;
+            this.emit('filter-set');
+        }
+    }
+
+    filter(widget, characters) {
+        let fontDescription = this._fontDescription;
+        if (this._filterFontDescription) {
+            let context = widget.get_pango_context();
+            let filterFont = context.load_font(this._filterFontDescription);
+            let filteredCharacters = [];
+            for (let index = 0; index < characters.length; index++) {
+                let uc = characters[index];
+                if (Gc.pango_context_font_has_glyph(context, filterFont, uc))
+                    filteredCharacters.push(uc);
+            }
+            characters = filteredCharacters;
+            fontDescription = this._filterFontDescription;
+        }
+
+        return [fontDescription, characters];
+    }
+});
+
+var CharactersView = GObject.registerClass({
     Signals: {
         'character-selected': { param_types: [GObject.TYPE_STRING] },
     },
-}, class CharacterListWidget extends Gtk.Widget {
-    _init(numRows) {
+    Properties: {
+        'vscroll-policy': GObject.ParamSpec.override('vscroll-policy', Gtk.Scrollable),
+        'vadjustment': GObject.ParamSpec.override('vadjustment', Gtk.Scrollable),
+        'hscroll-policy': GObject.ParamSpec.override('hscroll-policy', Gtk.Scrollable),
+        'hadjustment': GObject.ParamSpec.override('hadjustment', Gtk.Scrollable),
+    },
+    Implements: [Gtk.Scrollable],
+}, class CharactersView extends Gtk.Widget {
+    _init() {
         super._init({
-            hexpand: true,
-            vexpand: true,
+            vadjustment: new Gtk.Adjustment(),
+            hadjustment: new Gtk.Adjustment(),
         });
-        this.add_css_class('character-list');
-        this._cellsPerRow = CELLS_PER_ROW;
-        this._numRows = numRows;
+
+        this._selectedCharacter = null;
         this._characters = [];
+        this._spinnerTimeoutId = 0;
+        this._searchContext = null;
+        this._cancellable = new Gio.Cancellable();
+        this._cancellable.connect(() => {
+            this._stopSpinner();
+            this._searchContext = null;
+            this._characters = [];
+            this._updateCharacterList();
+        });
+
+        this._cellsPerRow = CELLS_PER_ROW;
+        this._numRows = NUM_ROWS;
         this._rows = [];
         /* this.add_events(Gdk.EventMask.BUTTON_PRESS_MASK |
                         Gdk.EventMask.BUTTON_RELEASE_MASK);
@@ -208,9 +308,31 @@ const CharacterListWidget = GObject.registerClass({
         gestureClick.connect('pressed', this.onButtonPress.bind(this));
         gestureClick.connect('released', this.onButtonRelease.bind(this));
         this.add_controller(gestureClick);
+    }
+
+    get vadjustment() {
+        return this._vadjustment;
+    }
+
+    set vadjustment(adj) {
+        adj.connect('value-changed', () => {
+            this.queue_draw();
+        });
+        this._vadjustment = adj;
+    }
+
+    get hadjustment() {
+        return this._hadjustment;
+    }
+
+    set hadjustment(adj) {
+        adj.connect('value-changed', () => {
+            this.queue_draw();
+        });
+        this._hadjustment = adj;
 
-        this._character = null;
     }
+
     /*
     vfunc_drag_begin(context) {
         let cellSize = getCellSize(this._fontDescription);
@@ -233,21 +355,24 @@ const CharacterListWidget = GObject.registerClass({
     */
 
     onButtonPress(gesture, nPress, x, y) {
+        let hadj = this.get_hadjustment();
+        let vadj = this.get_vadjustment();
+
         let cellSize = getCellSize(this._fontDescription);
-        x = Math.floor(x / cellSize);
-        y = Math.floor(y / cellSize);
+        x = Math.floor((x + hadj.get_value()) / cellSize);
+        y = Math.floor((y + vadj.get_value()) / cellSize);
+
         let index = y * this._cellsPerRow + x;
         if (index < this._characters.length)
-            this._character = this._characters[index];
+            this._selectedCharacter = this._characters[index];
         else
-            this._character = null;
+            this._selectedCharacter = null;
         return false;
     }
 
     onButtonRelease() {
-        if (this._character)
-            this.emit('character-selected', this._character);
-        log(this._character);
+        if (this._selectedCharacter)
+            this.emit('character-selected', this._selectedCharacter);
         return false;
     }
 
@@ -265,11 +390,14 @@ const CharacterListWidget = GObject.registerClass({
     }
 
     vfunc_snapshot(snapshot) {
-        // Clear the canvas.
-        let allocation = this.get_allocation();
+        let hadj = this.get_hadjustment();
+        let vadj = this.get_vadjustment();
         let rect = new Graphene.Rect({
             origin: new Graphene.Point({ x: 0, y: 0 }),
-            size: new Graphene.Size({ width: allocation.width, height: allocation.height }),
+            size: new Graphene.Size({
+                width: hadj.get_page_size(),
+                height: vadj.get_page_size(),
+            }),
         });
         let cr = snapshot.append_cairo(rect);
 
@@ -277,18 +405,13 @@ const CharacterListWidget = GObject.registerClass({
         let fg = context.get_color();
         Gdk.cairo_set_source_rgba(cr, fg);
 
-        // Use device coordinates directly, since PangoCairo doesn't
-        // work well with scaled matrix:
-        // https://bugzilla.gnome.org/show_bug.cgi?id=700592
-
-        // Redraw rows within the clipped region.
-        let [_, y1, __, y2] = cr.clipExtents();
         let cellSize = getCellSize(this._fontDescription);
-        let start = Math.max(0, Math.floor(y1 / cellSize));
-        let end = Math.min(this._rows.length, Math.ceil(y2 / cellSize));
+        let start = Math.max(0, Math.floor(vadj.get_value() / cellSize));
+        let end = Math.min(this._rows.length, Math.ceil((vadj.get_value() + vadj.get_page_size()) / 
cellSize));
+
         for (let index = start; index < end; index++) {
-            this._rows[index].draw(cr, 0, index * cellSize,
-                allocation.width, cellSize, context);
+            this._rows[index].draw(cr, 0, (index - start) * cellSize,
+                this.get_allocation().width, cellSize, context);
         }
     }
 
@@ -298,9 +421,17 @@ const CharacterListWidget = GObject.registerClass({
 
     vfunc_size_allocate(width, height, baseline) {
         super.vfunc_size_allocate(width, height, baseline);
-
         let cellSize = getCellSize(this._fontDescription);
         let cellsPerRow = Math.floor(width / cellSize);
+
+        let maxHeight = Math.floor((this._rows.length + BASELINE_OFFSET) * cellSize);
+        let maxWidth = cellsPerRow * cellSize;
+
+        let hadj = this.get_hadjustment();
+        let vadj = this.get_vadjustment();
+        vadj.configure(vadj.get_value(), 0.0, maxHeight, 0.1 * height, 0.9 * height, height);
+        hadj.configure(hadj.get_value(), 0.0, maxWidth, 0.1 * width, 0.9 * width, width);
+
         if (cellsPerRow !== this._cellsPerRow) {
             // Reflow if the number of cells per row has changed.
             this._cellsPerRow = cellsPerRow;
@@ -343,130 +474,9 @@ const CharacterListWidget = GObject.registerClass({
         this.queue_resize();
         this.queue_draw();
     }
-});
-
-const MAX_SEARCH_RESULTS = 100;
-
-var FontFilter = GObject.registerClass({
-    Properties: {
-        'font': GObject.ParamSpec.string(
-            'font', '', '',
-            GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE,
-            'Cantarell 50'),
-    },
-    Signals: {
-        'filter-set': { param_types: [] },
-    },
-}, class FontFilter extends GObject.Object {
-    _init() {
-        super._init({});
-
-        this._fontDescription = null;
-        this._filterFontDescription = null;
-
-        Main.settings.bind('font', this, 'font', Gio.SettingsBindFlags.DEFAULT);
-    }
-
-    get font() {
-        return this._font;
-    }
-
-    set font(v) {
-        let fontDescription = Pango.FontDescription.from_string(v);
-        if (fontDescription.get_size() === 0)
-            fontDescription.set_size(CELL_SIZE * Pango.SCALE);
-
-        if (this._fontDescription &&
-            fontDescription.equal(this._fontDescription))
-            return;
-
-        this._font = v;
-        this._fontDescription = fontDescription;
-    }
-
-    get fontDescription() {
-        if (this._filterFontDescription)
-            return this._filterFontDescription;
-        return this._fontDescription;
-    }
-
-    setFilterFont(v) {
-        let fontDescription;
-        if (v === null) {
-            fontDescription = null;
-        } else {
-            fontDescription = Pango.FontDescription.from_string(v);
-            fontDescription.set_size(this._fontDescription.get_size());
-        }
-
-        if (this._filterFontDescription !== null && fontDescription === null ||
-            this._filterFontDescription === null && fontDescription !== null ||
-            this._filterFontDescription !== null && fontDescription !== null &&
-             !fontDescription.equal(this._filterFontDescription)) {
-            this._filterFontDescription = fontDescription;
-            this.emit('filter-set');
-        }
-    }
-
-    filter(widget, characters) {
-        let fontDescription = this._fontDescription;
-        if (this._filterFontDescription) {
-            let context = widget.get_pango_context();
-            let filterFont = context.load_font(this._filterFontDescription);
-            let filteredCharacters = [];
-            for (let index = 0; index < characters.length; index++) {
-                let uc = characters[index];
-                if (Gc.pango_context_font_has_glyph(context, filterFont, uc))
-                    filteredCharacters.push(uc);
-            }
-            characters = filteredCharacters;
-            fontDescription = this._filterFontDescription;
-        }
-
-        return [fontDescription, characters];
-    }
-});
-
-var CharactersView = GObject.registerClass({
-    Template: 'resource:///org/gnome/Characters/characters_view.ui',
-    Signals: {
-        'character-selected': { param_types: [GObject.TYPE_STRING] },
-    },
-    Properties: {
-        'model': GObject.ParamSpec.object(
-            'model',
-            'Characters List Model', 'Characters List Model',
-            GObject.ParamFlags.READWRITE,
-            Gio.ListModel.$gtype,
-        ),
-    },
-}, class CharactersView extends Adw.Bin {
-    _init() {
-        super._init();
-
-        this._characterList = new CharacterListWidget(NUM_ROWS);
-        this._characterList.connect('character-selected', (w, c) => this.emit('character-selected', c));
-
-        this.set_child(this._characterList);
-
-        this._characters = [];
-        this._spinnerTimeoutId = 0;
-        this._searchContext = null;
-        this._cancellable = new Gio.Cancellable();
-        this._cancellable.connect(() => {
-            this._stopSpinner();
-            this._searchContext = null;
-            this._characters = [];
-            this._updateCharacterList();
-        });
-        /* TODO: use listmodels & grid view hopefully
-        scroll.connect('edge-reached', (scrolled, pos) => this._onEdgeReached(scrolled, pos));
-        scroll.connect('size-allocate', (scrolled, allocation) => this._onSizeAllocate(scrolled, 
allocation));
-        */
-    }
 
     setFontFilter(fontFilter) {
-        this._characterList.setFontDescription(fontFilter.fontDescription);
+        this.setFontDescription(fontFilter.fontDescription);
         fontFilter.connect('filter-set', () => this._updateCharacterList());
         this._fontFilter = fontFilter;
     }
@@ -496,16 +506,11 @@ var CharactersView = GObject.registerClass({
         this.setCharacters(characters);
     }
 
-    setCharacters(characters) {
-        this._characters = characters;
-        this._updateCharacterList();
-    }
-
     _updateCharacterList() {
         log('Updating characters list');
         const [fontDescription, characters] = this._fontFilter.filter(this, this._characters);
-        this._characterList.setFontDescription(fontDescription);
-        this._characterList.setCharacters(characters);
+        this.setFontDescription(fontDescription);
+        this.setCharacters(characters);
     }
 
     get initialSearchCount() {
@@ -575,40 +580,3 @@ var CharactersView = GObject.registerClass({
         this._cancellable.reset();
     }
 });
-
-var RecentCharacterListView = GObject.registerClass({
-    Signals: {
-        'character-selected': { param_types: [GObject.TYPE_STRING] },
-    },
-}, class RecentCharacterListView extends Adw.Bin {
-    _init(category) {
-        super._init({
-            hexpand: true, vexpand: false,
-        });
-
-        this._characterList = new CharacterListWidget(0);
-        this._characterList.connect('character-selected', (w, c) => this.emit('character-selected', c));
-        this.set_child(this._characterList);
-
-        this._category = category;
-        this._characters = [];
-    }
-
-    setFontFilter(fontFilter) {
-        this._characterList.setFontDescription(fontFilter.fontDescription);
-        fontFilter.connect('filter-set', () => this._updateCharacterList());
-        this._fontFilter = fontFilter;
-    }
-
-    setCharacters(characters) {
-        const result = Gc.filter_characters(this._category, characters);
-        this._characters = Util.searchResultToArray(result);
-        this._updateCharacterList();
-    }
-
-    _updateCharacterList() {
-        const [fontDescription, characters] = this._fontFilter.filter(this, this._characters);
-        this._characterList.setFontDescription(fontDescription);
-        this._characterList.setCharacters(characters);
-    }
-});
diff --git a/src/sidebar.js b/src/sidebar.js
index 8823f82..4283bd5 100644
--- a/src/sidebar.js
+++ b/src/sidebar.js
@@ -44,7 +44,6 @@ var Sidebar = GObject.registerClass({
     restorePreviousSelection() {
         if (this.lastSelectedRow)
             this._list.select_row(this.lastSelectedRow);
-
     }
 
     rowByName(name) {
diff --git a/src/window.js b/src/window.js
index a777390..cffeea3 100644
--- a/src/window.js
+++ b/src/window.js
@@ -41,7 +41,7 @@ var MainWindow = GObject.registerClass({
         'search-bar', 'search-entry', 'back-button',
         'menuPopover', 'container', 'sidebar',
         'leaflet', 'mainStack', 'windowTitle',
-        'charactersView',
+        'charactersView', 'scrolledWindow',
     ],
     Properties: {
         'search-active': GObject.ParamSpec.boolean(
@@ -70,6 +70,9 @@ var MainWindow = GObject.registerClass({
 
 
         this._sidebar.list.connect('row-selected', (sidebar, row) => {
+            const adj = this._scrolledWindow.get_vadjustment();
+            adj.set_value(0.0); // scroll back to the top
+            this._charactersView.queue_resize();
             if (row) {
                 this._sidebar.lastSelectedRow = row;
                 this.setPage(row);


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