%PDF- %PDF-
Mini Shell

Mini Shell

Direktori : /usr/share/org.gnome.Characters/
Upload File :
Create Path :
Current File : //usr/share/org.gnome.Characters/org.gnome.Characters.src.gresource

GVariant�(
			Ե�����L���Xp��v��KFv��L��{W�y�v�8(KP�8(L<(@(��$0@(LH(L(Q�8\L(v`()y����)yL4y8y�l~8yvPy0��(�
0�v8�V�H9V�
vh�J�ж�UJ�
vX�����	v���/characterDialog.js�/* exported CharacterDialog */
// -*- Mode: js; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*-
//
// Copyright (C) 2014-2015  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
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program 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 this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

const { Adw, Gc, Gdk, Gio, GObject, Gtk, Pango } = imports.gi;

const Util = imports.util;

var CharacterDialog = GObject.registerClass({
    Signals: {
        'character-copied': { param_types: [GObject.TYPE_STRING] },
    },
    Template: 'resource:///org/gnome/Characters/character_dialog.ui',
    InternalChildren: [
        'characterStack', 'navigationView',
        'characterLabel', 'missingLabel',
        'detailRow', 'detailLabel',
        'seeAlsoRow', 'relatedPage', 'relatedListbox',
        'toastOverlay',
    ],
}, class CharacterDialog extends Adw.Window {
    _init(character, fontDescription) {
        super._init();
        this._cancellable = new Gio.Cancellable();
        this._fontDescription = fontDescription;

        const actions = new Gio.SimpleActionGroup();
        Util.initActions(actions, [
            { name: 'copy', activate: this._copyCharacter.bind(this) },
        ]);
        this.insert_action_group('character', actions);

        this._relatedPage.connect('hidden', () => {
            this._populateRelatedPage();
        });

        this._setCharacter(character);
    }

    _finishSearch(result) {
        this._related = result;
        this._seeAlsoRow.visible = result.len > 0;

        if (!this._relatedPage.get_mapped())
            this._populateRelatedPage();
    }

    _populateRelatedPage() {
        this._relatedListbox.remove_all();

        for (let index = 0; index < this._related.len; index++) {
            let uc = Gc.search_result_get(this._related, index);
            let name = Gc.character_name(uc);
            if (name === null)
                continue;

            let row = new Adw.ActionRow();
            row.set_title(Util.capitalize(name));
            row.add_prefix(new Gtk.Label({
                label: uc,
                valign: Gtk.Align.CENTER,
                halign: Gtk.Align.CENTER,
                width_request: 45,
            }));
            row.connect('activated', () => {
                this._setCharacter(uc);
                this._navigationView.pop();
            });
            row.set_activatable(true);
            this._relatedListbox.append(row);
        }
    }

    _setCharacter(uc) {
        this._character = uc;

        let name = Gc.character_name(this._character);
        if (name !== null)
            name = Util.capitalize(name);

        if (Gc.character_is_composite(uc)) {
            this._detailRow.hide();

            if (name === null)
                name = _('Unknown character name');
        } else {
            let codePoint = Util.toCodePoint(this._character);
            let codePointHex = codePoint.toString(16).toUpperCase();

            this._detailLabel.label = 'U+%04s'.format(codePointHex);
            this._detailRow.show();

            if (name === null)
                name = 'U+%04s'.format(codePointHex);
        }

        this.title = name ? name : '';
        this._characterLabel.label = this._character;

        let pangoContext = this._characterLabel.get_pango_context();
        pangoContext.set_font_description(this._fontDescription);

        var pangoLayout = Pango.Layout.new(pangoContext);
        pangoLayout.set_text(this._character, -1);
        if (pangoLayout.get_unknown_glyphs_count() === 0) {
            this._characterStack.visible_child_name = 'character';
        } else {
            var fontFamily = this._fontDescription.get_family();
            // TRANSLATORS: the first variable is a character, the second is a font
            this._missingLabel.label = _('%s is not included in %s').format(name, fontFamily);
            this._characterStack.visible_child_name = 'missing';
        }

        this._cancellable.cancel();
        this._cancellable.reset();
        let criteria = Gc.SearchCriteria.new_related(this._character);
        let context = new Gc.SearchContext({ criteria });
        context.search(
            -1,
            this._cancellable,
            (ctx, res) => {
                try {
                    let result = ctx.search_finish(res);
                    this._finishSearch(result);
                } catch (e) {
                    log(`Failed to search related: ${e.message}`);
                }
            });

        this._seeAlsoRow.visible = false;
    }

    _copyCharacter() {
        let display = Gdk.Display.get_default();
        let clipboard = display.get_clipboard();

        clipboard.set(this._character);
        this.emit('character-copied', this._character);

        const toast = new Adw.Toast({
            title: _('Character copied to clipboard'),
            timeout: 2,
        });
        this._toastOverlay.add_toast(toast);
    }
});
(uuay)js/
	main.jsp/* exported main settings */
// -*- Mode: js; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*-
//
// Copyright (c) 2013 Giovanni Campagna <scampa.giovanni@gmail.com>
//
// Redistribution and use in source and binary forms, with or without
//  modification, are permitted provided that the following conditions are met:
//   * Redistributions of source code must retain the above copyright
//     notice, this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright
//     notice, this list of conditions and the following disclaimer in the
//     documentation and/or other materials provided with the distribution.
//   * Neither the name of the GNOME Foundation nor the
//     names of its contributors may be used to endorse or promote products
//     derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

pkg.initGettext();
pkg.initFormat();
pkg.require({
    'Gdk': '4.0',
    'Gio': '2.0',
    'GLib': '2.0',
    'GObject': '2.0',
    'Gtk': '4.0',
    'Adw': '1',
    'GnomeDesktop': '4.0',
});

const { GLib, Gio, GObject, Adw } = imports.gi;

const { CharactersView } = imports.charactersView;
const { Sidebar } = imports.sidebar;
const { MainWindow } = imports.window;

const Util = imports.util;
const SearchProvider = imports.searchProvider;
var settings = null;

var MyApplication = GObject.registerClass({
}, class MyApplication extends Adw.Application {
    _init() {
        super._init({
            application_id: pkg.name,
            flags: Gio.ApplicationFlags.FLAGS_NONE,
            resource_base_path: '/org/gnome/Characters',
        });
        GLib.set_application_name(_('Characters'));

        this._searchProvider = new SearchProvider.SearchProvider(this);
    }

    get window() {
        return this._appwindow;
    }

    _onQuit() {
        this.quit();
    }

    vfunc_startup() {
        super.vfunc_startup();

        this.get_style_manager().set_color_scheme(Adw.ColorScheme.PREFER_LIGHT);

        Util.initActions(this, [
            { name: 'quit', activate: this._onQuit },
        ]);
        this.set_accels_for_action('app.quit', ['<Primary>q']);
        this.set_accels_for_action('win.find', ['<Primary>f']);
        this.set_accels_for_action('win.show-help-overlay', ['<Primary>question']);

        settings = Util.getSettings('org.gnome.Characters',
            '/org/gnome/Characters/');

        if (this.get_flags() & Gio.ApplicationFlags.IS_SERVICE)
            this.set_inactivity_timeout(10000);

        log('Characters Application started');
    }

    vfunc_dbus_register(connection, path) {
        const searchProviderPath = `${path}/SearchProvider`;
        super.vfunc_dbus_register(connection, searchProviderPath);
        this._searchProvider.export(connection, searchProviderPath);
        return true;
    }

    vfunc_activate() {
        if (!this._appwindow)
            this._appwindow = new MainWindow(this);

        if (pkg.name.endsWith('Devel'))
            this._appwindow.add_css_class('devel');

        this._appwindow.present();
        log('Characters Application activated');
    }

    vfunc_shutdown() {
        log('Characters Application exiting');

        super.vfunc_shutdown();
    }
});

function main(argv) {
    GObject.type_ensure(CharactersView.$gtype);
    GObject.type_ensure(Sidebar.$gtype);

    return new MyApplication().run(argv);
}
(uuay)org/gnome/charactersView.js�P/* exported CharactersView */
// -*- Mode: js; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*-
//
// Copyright (C) 2014-2015  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
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program 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 this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

const { Gc, Gdk, Gio, GLib, GnomeDesktop, GObject, Gtk, Pango, Graphene } = imports.gi;

Gio._promisify(Gc.SearchContext.prototype, 'search', 'search_finish');

const Util = imports.util;

const BASELINE_OFFSET = 0.85;
const CELLS_PER_ROW = 5;
const NUM_ROWS = 5;
const NUM_COLUMNS = 3;
const CELL_SIZE = 50;

function getCellSize(fontDescription) {
    if (fontDescription === null || fontDescription.get_size() === 0)
        return CELL_SIZE;
    return fontDescription.get_size() * 2 / Pango.SCALE;
}

const CharacterListRow = GObject.registerClass({
}, class CharacterListRow extends GObject.Object {
    _init(characters, fontDescription, overlayFontDescription) {
        super._init({});

        this._baseGlyphRect = null;
        this._characters = characters;
        this._fontDescription = fontDescription;
        this._overlayFontDescription = overlayFontDescription;
    }

    snapshot(snapshot, x, y, pangoContext, styleContext) {
        let layout = Pango.Layout.new(pangoContext);
        layout.set_font_description(this._fontDescription);

        // Draw characters.  Do centering and attach to the baseline.
        let cellSize = getCellSize(this._fontDescription);
        for (let i in this._characters) {
            let character = this._characters[i];
            let cellRect = new Gdk.Rectangle({
                x: x + cellSize * i,
                y,
                width: cellSize,
                height: cellSize,
            });

            layout.set_text(character, -1);
            snapshot.save();
            if (Gc.character_is_invisible(character)) {
                this._drawBoundingBox(snapshot, layout, styleContext, cellRect);
                this._drawCharacterName(snapshot, pangoContext, styleContext, cellRect, character);
            } else if (layout.get_unknown_glyphs_count() === 0) {
                let layoutBaseline = layout.get_baseline();
                let logicalRect = layout.get_extents()[0];
                snapshot.translate(new Graphene.Point({
                    x: x + cellSize * i - logicalRect.x / Pango.SCALE + (cellSize - logicalRect.width / Pango.SCALE) / 2,
                    y: y + BASELINE_OFFSET * cellSize - layoutBaseline / Pango.SCALE,
                }));

                let textColor = styleContext.get_color();
                snapshot.append_layout(layout, textColor);

            } else {
                this._drawBoundingBox(snapshot, layout, styleContext, cellRect);
                this._drawCharacterName(snapshot, pangoContext, styleContext, cellRect, character);
            }
            snapshot.restore();
        }
    }

    _computeBoundingBox(layout, cellRect) {
        let shapeRect;
        let layoutBaseline;
        if (layout.get_unknown_glyphs_count() === 0) {
            let inkRect = layout.get_extents()[1];
            layoutBaseline = layout.get_baseline();
            shapeRect = inkRect;
        } else {
            // If the character cannot be rendered with the current
            // font settings, show a rectangle calculated from the
            // base glyphs ('AA').
            if (this._baseGlyphRect === null) {
                layout.set_text('AA', -1);
                let baseInkRect = layout.get_extents()[1];
                this._baseGlyphLayoutBaseline = layout.get_baseline();
                this._baseGlyphRect = baseInkRect;
            }
            layoutBaseline = this._baseGlyphLayoutBaseline;
            shapeRect = new Pango.Rectangle({
                x: this._baseGlyphRect.x,
                y: this._baseGlyphRect.y,
                width: this._baseGlyphRect.width,
                height: this._baseGlyphRect.height,
            });
        }

        shapeRect.x = cellRect.x - shapeRect.x / Pango.SCALE + (cellRect.width - shapeRect.width / Pango.SCALE) / 2;
        shapeRect.y = cellRect.y + BASELINE_OFFSET * cellRect.height - layoutBaseline / Pango.SCALE;
        shapeRect.width /= Pango.SCALE;
        shapeRect.height /= Pango.SCALE;
        return shapeRect;
    }

    _drawBoundingBox(snapshot, pangoLayout, styleContext, cellRect) {
        snapshot.save();

        let shapeRect = this._computeBoundingBox(pangoLayout, cellRect);
        let borderWidth = 1;

        let boxBgColor = styleContext.get_color();
        boxBgColor.alpha = 0.05;

        snapshot.append_color(boxBgColor,
            new Graphene.Rect({
                origin: new Graphene.Point({
                    x: shapeRect.x - borderWidth * 2,
                    y: shapeRect.y - borderWidth * 2,
                }),
                size: new Graphene.Size({
                    width: shapeRect.width + borderWidth * 2,
                    height: shapeRect.height + borderWidth * 2,
                }),
            }),
        );
        snapshot.restore();
    }

    _drawCharacterName(snapshot, pangoContext, styleContext, cellRect, uc) {
        snapshot.save();

        let layout = Pango.Layout.new(pangoContext);
        layout.set_width(cellRect.width * Pango.SCALE * 0.8);
        layout.set_height(cellRect.height * Pango.SCALE * 0.8);
        layout.set_wrap(Pango.WrapMode.WORD);
        layout.set_ellipsize(Pango.EllipsizeMode.END);
        layout.set_alignment(Pango.Alignment.CENTER);
        layout.set_font_description(this._overlayFontDescription);
        let name = Gc.character_name(uc);
        let text = name === null ? _('Unassigned') : Util.capitalize(name);
        layout.set_text(text, -1);
        let logicalRect = layout.get_extents()[0];
        snapshot.translate(new Graphene.Point({
            x: cellRect.x - logicalRect.x / Pango.SCALE + (cellRect.width - logicalRect.width / Pango.SCALE) / 2,
            y: cellRect.y - logicalRect.y / Pango.SCALE + (cellRect.height - logicalRect.height / Pango.SCALE) / 2,
        }));

        let textColor = styleContext.get_color();
        snapshot.append_layout(layout, textColor);
        snapshot.restore();
    }
});

var CharactersView = GObject.registerClass({
    Signals: {
        'character-selected': { param_types: [GObject.TYPE_STRING] },
    },
    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),
        'loading': GObject.ParamSpec.boolean(
            'loading',
            'Loading', 'Whether the category is still loading',
            GObject.ParamFlags.READWRITE,
            false,
        ),
        'baseline': GObject.ParamSpec.boolean(
            'baseline',
            'Baseline', 'Whether to draw a baseline or not',
            GObject.ParamFlags.READWRITE,
            true,
        ),
    },
    Implements: [Gtk.Scrollable],
}, class CharactersView extends Gtk.Widget {
    _init() {
        super._init({
            vadjustment: new Gtk.Adjustment(),
            hadjustment: new Gtk.Adjustment(),
            overflow: Gtk.Overflow.HIDDEN,
        });

        this._scripts = [];
        this._scriptsLoaded = false;

        let context = this.get_pango_context();
        this._fontDescription = context.get_font_description();
        this._fontDescription.set_size(CELL_SIZE * Pango.SCALE);

        this._selectedCharacter = null;
        this._characters = [];
        this._searchContext = null;
        this._cancellable = new Gio.Cancellable();
        this._cancellable.connect(() => {
            this._searchContext = null;
            this._characters = [];
        });

        this._cellsPerRow = CELLS_PER_ROW;
        this._numRows = NUM_ROWS;
        this._rows = [];
        /*
        this.drag_source_set(Gdk.ModifierType.BUTTON1_MASK,
                             null,
                             Gdk.DragAction.COPY);
        this.drag_source_add_text_targets();
        */
        const gestureClick = new Gtk.GestureClick();
        gestureClick.connect('pressed', this.onButtonPress.bind(this));
        gestureClick.connect('released', this.onButtonRelease.bind(this));
        this.add_controller(gestureClick);
    }

    get fontDescription() {
        return this._fontDescription;
    }

    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;
    }

    /*
    vfunc_drag_begin(context) {
        let cellSize = getCellSize(this._fontDescription);
        this._dragSurface = new Cairo.ImageSurface(Cairo.Format.ARGB32,
                                                   cellSize,
                                                   cellSize);
        let cr = new Cairo.Context(this._dragSurface);
        cr.setSourceRGBA(1.0, 1.0, 1.0, 1.0);
        cr.paint();
        cr.setSourceRGBA(0.0, 0.0, 0.0, 1.0);
        let row = this._createCharacterListRow([this._character]);
        row.draw(cr, 0, 0, cellSize, cellSize, this.get_style_context());
        Gtk.drag_set_icon_surface(context, this._dragSurface, 0, 0);
    }

    vfunc_drag_data_get(context, data, info, time) {
        if (this._character !== null)
            data.set_text(this._character, -1);
    }
    */

    onButtonPress(_gesture, _nPress, x, y) {
        let hadj = this.get_hadjustment();
        let vadj = this.get_vadjustment();

        let cellSize = getCellSize(this._fontDescription);
        x = Math.min(this._cellsPerRow - 1, Math.floor((x + hadj.get_value() - this._offsetX) / cellSize));
        y = Math.floor((y + vadj.get_value()) / cellSize);

        let index = y * this._cellsPerRow + Math.max(0, x);
        if (index < this._characters.length)
            this._selectedCharacter = this._characters[index];
        else
            this._selectedCharacter = null;
    }

    onButtonRelease() {
        if (this._selectedCharacter)
            this.emit('character-selected', this._selectedCharacter);
    }

    vfunc_measure(orientation, _forSize) {
        if (orientation === Gtk.Orientation.HORIZONTAL) {
            let cellSize = getCellSize(this._fontDescription);
            let minWidth = NUM_COLUMNS * cellSize;
            let natWidth = Math.max(this._cellsPerRow, NUM_COLUMNS) * cellSize;
            return [minWidth, natWidth, -1, -1];
        } else {
            let height = Math.max(this._rows.length, this._numRows) *
                getCellSize(this._fontDescription);
            return [height, height, -1, -1];
        }
    }

    vfunc_snapshot(snapshot) {
        let vadj = this.get_vadjustment();
        let styleContext = this.get_style_context();
        let pangoContext =  this.get_pango_context();

        let cellSize = getCellSize(this._fontDescription);
        let scrollPos = Math.floor(vadj.get_value());

        let start = Math.max(0, Math.floor(scrollPos / cellSize));
        let end = Math.min(this._rows.length, Math.ceil((scrollPos + vadj.get_page_size()) / cellSize));
        let offsetY = scrollPos % cellSize;

        snapshot.translate(new Graphene.Point({ x: 0, y: -offsetY }));

        let borderColor = styleContext.lookup_color('borders')[1];
        let allocatedWidth = this.get_allocation().width;
        this._offsetX = (allocatedWidth - cellSize * this._cellsPerRow) / 2;

        for (let index = start; index < end; index++) {
            let y = (index - start) * cellSize;

            // Draw baseline.
            if (this.baseline) {
                snapshot.append_color(borderColor, new Graphene.Rect({
                    origin: new Graphene.Point({ x: 0, y: y + BASELINE_OFFSET * cellSize }),
                    size: new Graphene.Size({ width: allocatedWidth, height: 1.0 }),
                }));
            }

            this._rows[index].snapshot(snapshot, this._offsetX, y, pangoContext, styleContext);
        }
    }

    vfunc_size_allocate(width, height, baseline) {
        super.vfunc_size_allocate(width, height, baseline);
        let cellSize = getCellSize(this._fontDescription);
        let cellsPerRow = Math.floor(width / cellSize);

        if (cellsPerRow !== this._cellsPerRow) {
            // Reflow if the number of cells per row has changed.
            this._cellsPerRow = cellsPerRow;
            this._reflow();
        }

        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, Math.min(height, maxHeight));
        hadj.configure(hadj.get_value(), 0.0, maxWidth, 0.1 * width, 0.9 * width, Math.min(width, maxWidth));
    }

    _createCharacterListRow(characters) {
        var context = this.get_pango_context();
        var overlayFontDescription = context.get_font_description();
        overlayFontDescription.set_size(overlayFontDescription.get_size() * 0.8);

        let row = new CharacterListRow(characters, this._fontDescription, overlayFontDescription);
        return row;
    }

    setFontDescription(fontDescription) {
        this._fontDescription = fontDescription;
    }

    _reflow() {
        this._rows = [];

        let start = 0, stop = 1;
        for (; stop <= this._characters.length; stop++) {
            if (stop % this._cellsPerRow === 0) {
                let rowCharacters = this._characters.slice(start, stop);
                let row = this._createCharacterListRow(rowCharacters);
                this._rows.push(row);
                start = stop;
            }
        }
        if (start !== stop - 1) {
            let rowCharacters = this._characters.slice(start, stop);
            let row = this._createCharacterListRow(rowCharacters);
            this._rows.push(row);
        }
    }

    setCharacters(characters) {
        this._characters = characters;
        this._reflow();
        this.queue_resize();
    }

    _addSearchResult(result) {
        const characters = Util.searchResultToArray(result);
        this.setCharacters(this._characters.concat(characters));
    }

    async _searchWithContext(context) {
        try {
            let result = await context.search(Number.MAX_SAFE_INTEGER, this._cancellable);
            this._addSearchResult(result);
        } catch (e) {
            log(`Failed to search: ${e.message}`);
        }
    }

    async searchByCategory(category) {
        this._characters = [];
        // whether to draw a baseline or not
        this.baseline = category <= Gc.Category.LETTER_LATIN;

        if (category === Gc.Category.LETTER_LATIN) {
            if (!this._scriptsLoaded)
                await this.populateScripts(); // we run the search once the scripts are loaded

            await this._searchByScripts();

            return;
        }

        let criteria = Gc.SearchCriteria.new_category(category);
        this._searchContext = new Gc.SearchContext({ criteria });
        await this._searchWithContext(this._searchContext);
    }

    async searchByKeywords(keywords) {
        const criteria = Gc.SearchCriteria.new_keywords(keywords);
        this._searchContext = new Gc.SearchContext({
            criteria,
            flags: Gc.SearchFlag.WORD,
        });
        await this._searchWithContext(this._searchContext);
        return this._characters.length;
    }

    async _searchByScripts() {
        var criteria = Gc.SearchCriteria.new_scripts(this.scripts);
        this._searchContext = new Gc.SearchContext({ criteria });
        await this._searchWithContext(this._searchContext);
    }

    cancelSearch() {
        this._cancellable.cancel();
        this._cancellable.reset();
    }

    // / Specific to GC_CATEGORY_LETTER_LATIN
    get scripts() {
        return this._scripts;
    }

    // / Populate the "scripts" based on the current locale
    // / and the input-sources settings.
    async populateScripts() {
        this.loading = true;
        let settings =
            Util.getSettings('org.gnome.desktop.input-sources',
                '/org/gnome/desktop/input-sources/');
        if (settings) {
            let sources = settings.get_value('sources').deep_unpack();
            let hasIBus = sources.some((current, _index, _array) => {
                return current[0] === 'ibus';
            });
            if (hasIBus)
                await this._ensureIBusLanguageList(sources);
            else
                this._finishBuildScriptList(sources);
        }
    }

    async _ensureIBusLanguageList(sources) {
        if (this._ibusLanguageList !== null)
            return;

        this._ibusLanguageList = {};

        // Don't assume IBus is always available.
        let ibus;
        try {
            ibus = imports.gi.IBus;
        } catch (e) {
            this._finishBuildScriptList(sources);
            return;
        }
        Gio._promisify(ibus.Bus.prototype, 'list_engines_async', 'list_engines_async_finish');

        ibus.init();
        let bus = new ibus.Bus();
        if (bus.is_connected()) {
            let engines = await bus.list_engines_async(-1, null);
            try {
                for (let j in engines) {
                    let engine = engines[j];
                    let language = engine.get_language();
                    if (language !== null)
                        this._ibusLanguageList[engine.get_name()] = language;
                }
            } catch (e) {
                log(`Failed to list engines: ${e.message}`);
            }

        }
        this._finishBuildScriptList(sources);
    }

    _finishBuildScriptList(sources) {
        let xkbInfo = new GnomeDesktop.XkbInfo();
        let languages = [];
        for (let i in sources) {
            let [type, id] = sources[i];
            switch (type) {
            case 'xkb':
                // FIXME: Remove this check once gnome-desktop gets the
                // support for that.
                if (xkbInfo.get_languages_for_layout) {
                    languages = languages.concat(
                        xkbInfo.get_languages_for_layout(id));
                }
                break;
            case 'ibus':
                if (id in this._ibusLanguageList)
                    languages.push(this._ibusLanguageList[id]);
                break;
            }
        }

        // Add current locale language to languages.
        languages.push(Gc.get_current_language());

        let allScripts = [];
        for (let i in languages) {
            let language = GnomeDesktop.normalize_locale(languages[i]);
            if (language === null)
                continue;
            let scripts = Gc.get_scripts_for_language(languages[i]);
            for (let j in scripts) {
                let script = scripts[j];
                // Exclude Latin and Han, since Latin is always added
                // at the top and Han contains too many characters.
                if ([GLib.UnicodeScript.LATIN, GLib.UnicodeScript.HAN].indexOf(script) >= 0)
                    continue;
                if (allScripts.indexOf(script) >= 0)
                    continue;
                allScripts.push(script);
            }
        }

        allScripts.unshift(GLib.UnicodeScript.LATIN);

        this._scripts = allScripts;
        this._scriptsLoaded = true;
        this.loading = false;
        this._searchByScripts();
    }
});
(uuay)Characters/searchProvider.js�/* exported SearchProvider */
// -*- Mode: js; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*-
//
// Copyright (c) 2013 Giovanni Campagna <scampa.giovanni@gmail.com>
// Copyright (C) 2015  Daiki Ueno <dueno@src.gnome.org>
//
// Gnome Weather 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 Weather 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 Weather; if not, write to the Free Software Foundation,
// Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

const { Gc, Gio, GLib, GObject } = imports.gi;

const ByteArray = imports.byteArray;
const Util = imports.util;

const MAX_SEARCH_RESULTS = 20;

const SearchProviderInterface = ByteArray.toString(Gio.resources_lookup_data('/org/gnome/shell/ShellSearchProvider2.xml', 0).toArray());

var SearchProvider = GObject.registerClass({
    Name: 'CharactersSearchProvider',
}, class SearchProvider extends GObject.Object {
    _init(application) {
        this._app = application;

        this._impl = Gio.DBusExportedObject.wrapJSObject(SearchProviderInterface, this);
        this._cancellable = new Gio.Cancellable();
    }

    export(connection, path) {
        return this._impl.export(connection, path);
    }

    unexport(connection) {
        return this._impl.unexport_from_connection(connection);
    }

    _runQuery(keywords, invocation) {
        this._app.hold();
        this._cancellable.cancel();
        this._cancellable.reset();

        let upper = keywords.map(x => x.toUpperCase());
        let criteria = Gc.SearchCriteria.new_keywords(upper);
        let context = new Gc.SearchContext({ criteria, flags: Gc.SearchFlag.WORD });

        context.search(
            MAX_SEARCH_RESULTS,
            this._cancellable,
            (_source, res, _userData) => {
                let characters = [];
                try {
                    let result = context.search_finish(res);
                    characters = Util.searchResultToArray(result);
                } catch (e) {
                    log(`Failed to search by keywords: ${e.message}`);
                }
                invocation.return_value(new GLib.Variant('(as)', [characters]));
                this._app.release();
            });
    }

    GetInitialResultSetAsync(params, invocation) {
        this._runQuery(params[0], invocation);
    }

    GetSubsearchResultSetAsync(params, invocation) {
        this._runQuery(params[1], invocation);
    }

    GetResultMetas(identifiers) {
        this._app.hold();
        let ret = [];

        for (let i = 0; i < identifiers.length; i++) {
            let character = identifiers[i];
            let name = Gc.character_name(character);
            if (name === null)
                name = _('Unknown character name');
            else
                name = Util.capitalize(name);

            let summary = '';

            if (!Gc.character_is_composite(character)) {
                let codePoint = Util.toCodePoint(character);
                let codePointHex = codePoint.toString(16).toUpperCase();
                summary = _('U+%s').format(codePointHex);
            }

            let iconData = Util.characterToIconData(character);
            if (!iconData)
                continue;

            ret.push({
                name: new GLib.Variant('s', name),
                id: new GLib.Variant('s', identifiers[i]),
                description: new GLib.Variant('s', summary),
                clipboardText: new GLib.Variant('s', character),
                'icon-data': iconData,
            });
        }
        this._app.release();
        return ret;
    }

    ActivateResult(_id, _terms, _timestamp) {
        log('activating result');
        const notification = Gio.Notification.new(_('Character copied'));
        notification.set_body(_('Character was copied successfully'));
        this._app.send_notification(null, notification);
    }

    LaunchSearch(terms, timestamp) {
        this._app.activate();
        const window = this._app.window;
        window.setSearchKeywords(terms);
        window.present_with_time(timestamp);
    }
});
(uuay)util.js/* exported capitalize getSettings initActions searchResultToArray toCodePoint characterToIconData */
// -*- Mode: js; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*-
//
// Copyright (c) 2013 Giovanni Campagna <scampa.giovanni@gmail.com>
//
// Redistribution and use in source and binary forms, with or without
//  modification, are permitted provided that the following conditions are met:
//   * Redistributions of source code must retain the above copyright
//     notice, this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright
//     notice, this list of conditions and the following disclaimer in the
//     documentation and/or other materials provided with the distribution.
//   * Neither the name of the GNOME Foundation nor the
//     names of its contributors may be used to endorse or promote products
//     derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (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, Gio, Gdk, GLib, Graphene, Gsk, Gtk, PangoCairo, Pango } = imports.gi;

const System = imports.system;

function initActions(actionMap, simpleActionEntries, context) {
    simpleActionEntries.forEach(({ name, parameterType, state, activate }) =>  {
        let action = new Gio.SimpleAction({
            name,
            parameter_type: parameterType || null,
            state: state || null,
        });

        context = context || actionMap;
        if (activate)
            action.connect('activate', activate.bind(context));
        actionMap.add_action(action);
    });
}


function getSettings(schemaId, path) {
    const GioSSS = Gio.SettingsSchemaSource;
    let schemaSource;

    if (!pkg.moduledir.startsWith('resource://')) {
        // Running from the source tree
        schemaSource = GioSSS.new_from_directory(pkg.pkgdatadir, GioSSS.get_default(), false);
    } else {
        schemaSource = GioSSS.get_default();
    }

    let schemaObj = schemaSource.lookup(schemaId, true);
    if (!schemaObj) {
        log(`Missing GSettings schema ${schemaId}`);
        System.exit(1);
    }

    if (path === undefined)
        return new Gio.Settings({ settings_schema: schemaObj });
    else
        return Gio.Settings.new_full(schemaObj, null, path);

}

function capitalizeWord(w) {
    if (w.length > 0)
        return w[0].toUpperCase() + w.slice(1).toLowerCase();
    return w;
}

function capitalize(s) {
    return s.split(/\s+/).map(w => {
        let acronyms = ['CJK'];
        if (acronyms.indexOf(w) > -1)
            return w;
        let prefixes = ['IDEOGRAPH-', 'SELECTOR-'];
        for (let index in prefixes) {
            let prefix = prefixes[index];
            if (w.startsWith(prefix))
                return capitalizeWord(prefix) + w.slice(prefix.length);
        }
        return capitalizeWord(w);
    }).join(' ');
}

function toCodePoint(s) {
    let codePoint = s.charCodeAt(0);
    if (codePoint >= 0xD800 && codePoint <= 0xDBFF) {
        let high = codePoint;
        let low = s.charCodeAt(1);
        codePoint = 0x10000 + (high - 0xD800) * 0x400 + (low - 0xDC00);
    }

    return codePoint;
}

function searchResultToArray(result) {
    let characters = [];
    for (let index = 0; index < result.len; index++) {
        const c = Gc.search_result_get(result, index);

        if (c.trim().length)
            characters.push(c);
    }

    return characters;
}

function characterToIconData(character) {
    let size = 48.0;

    if (!character || !character.trim().length)
        return null;

    const fontMap = PangoCairo.FontMap.get_default();
    const context = fontMap.create_context();
    const layout = Pango.Layout.new(context);
    layout.set_text(character, -1);
    let white = new Gdk.RGBA({ red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0 });

    let [textWidth, textHeight] = layout.get_pixel_size();
    let textSize = Math.max(textWidth, textHeight);

    const snapshot = Gtk.Snapshot.new();

    let originX = (textSize - textWidth) / 2.0;
    let originY = (textSize - textHeight) / 2.0;
    let origin = new Graphene.Point({ x: originX, y: originY });

    let ratio = size / textSize;
    snapshot.scale(ratio, ratio);

    snapshot.save();
    snapshot.translate(origin);
    snapshot.append_layout(layout, white);
    snapshot.restore();

    const node = snapshot.to_node();
    // The snapshot may contain no nodes if there's nothing to render, like in
    // case of a layout that only contains invisible chars:
    //   https://gitlab.gnome.org/GNOME/gtk/-/issues/5747
    if (!node)
        return null;

    let renderer = Gsk.GLRenderer.new();
    try {
        renderer.realize(null);
    } catch (e) {
        renderer = new Gsk.CairoRenderer();
        renderer.realize(null);
    }

    let rect = new Graphene.Rect({
        origin: new Graphene.Point({ x: 0.0, y: 0.0 }),
        size: new Graphene.Size({ width: size, height: size }),
    });
    const texture = renderer.render_texture(node, rect);
    renderer.unrealize();

    const textureDownloader = new Gdk.TextureDownloader(texture);
    textureDownloader.set_format(Gdk.MemoryFormat.R8G8B8A8);
    const [bytes, stride] = textureDownloader.download_bytes();

    return GLib.Variant.new_tuple([
        new GLib.Variant('i', texture.get_width()),
        new GLib.Variant('i', texture.get_height()),
        new GLib.Variant('i', stride),
        new GLib.Variant('b', /* has alpha */ true),
        new GLib.Variant('i', /* bits per sample */ 8),
        new GLib.Variant('i', /* channels */ 4),
        GLib.Variant.new_from_bytes(
            GLib.VariantType.new('ay'), bytes, true),
    ]);
}
(uuay)sidebarRow.js�/* exported SidebarRow */
// -*- Mode: js; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*-
//
// 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
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program 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 this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

const { Gc, GObject, Gtk } = imports.gi;

var SidebarRow = GObject.registerClass({
    Properties: {
        'title': GObject.ParamSpec.string(
            'title',
            'Category title', 'Category title',
            GObject.ParamFlags.READWRITE,
            '',
        ),
        'category': GObject.ParamSpec.enum(
            'category',
            'Category', 'Category',
            GObject.ParamFlags.READWRITE,
            Gc.Category.$gtype,
            Gc.Category.NONE,
        ),
        'icon-name': GObject.ParamSpec.string(
            'icon-name',
            'Category Icon Name', 'Category Icon Name',
            GObject.ParamFlags.READWRITE,
            '',
        ),
    },
}, class SidebarRow extends Gtk.ListBoxRow {
    _init() {
        super._init({
            accessible_role: Gtk.AccessibleRole.ROW,
        });
        let hbox = new Gtk.Box({
            orientation: Gtk.Orientation.HORIZONTAL,
            margin_top: 12,
            margin_bottom: 12,
            margin_start: 6,
            margin_end: 6,
            spacing: 12,
        });

        let image = new Gtk.Image();
        this.bind_property('icon-name', image, 'icon-name',
            GObject.BindingFlags.DEFAULT | GObject.BindingFlags.SYNC_CREATE,
        );
        image.set_icon_size(Gtk.IconSize.LARGE_TOOLBAR);
        hbox.append(image);

        let label = new Gtk.Label({ halign: Gtk.Align.START });
        this.bind_property('title', label, 'label',
            GObject.BindingFlags.DEFAULT | GObject.BindingFlags.SYNC_CREATE,
        );
        // Because bind_property doesn't work with transform functions
        // TODO: is this really needed?
        this.connect('notify::title', row => {
            row.tooltip_text = _('%s Sidebar Row').format(row.title);
        });
        hbox.append(label);

        this.set_child(hbox);
    }

    get title() {
        return this._title || '';
    }

    set title(title) {
        this._title = title;
    }

    get category() {
        return this._category || Gc.Category.NONE;
    }

    set category(value) {
        this._category = value;
    }
});

(uuay)sidebar.js�/* exported Sidebar */
// -*- Mode: js; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*-
//
// 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
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program 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 this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.


const { Adw, GObject } = imports.gi;
const { SidebarRow } = imports.sidebarRow;

var Sidebar = GObject.registerClass({
    Template: 'resource:///org/gnome/Characters/sidebar.ui',
    InternalChildren: [
        'list',
        'recentRow', 'emojiSmileysRow', 'emojiPeopleRow', 'emojiAnimalsRow',
        'emojiFoodRow', 'emojiActivitiesRow', 'emojiTravelRow',
        'emojiObjectsRow', 'emojiSymbolsRow', 'emojiFlagsRow',
        'lettersPunctuationRow', 'lettersArrowsRow',
        'lettersBulletsRow', 'lettersPicturesRow',
        'lettersCurrencyRow', 'lettersMathRow', 'lettersLatinRow',
    ],
}, class Sidebar extends Adw.Bin {
    _init() {
        GObject.type_ensure(SidebarRow.$gtype);
        super._init({});

        this.lastSelectedRow = null;
    }

    /**
     * Restore the latest selected item
     */
    restoreSelection() {
        if (this.lastSelectedRow)
            this._list.select_row(this.lastSelectedRow);
    }

    rowByName(name) {
        switch (name) {
        case 'smileys':
            return this._emojiSmileysRow;
        case 'people':
            return this._emojiPeopleRow;
        case 'animals':
            return this._emojiAnimalsRow;
        case 'food':
            return this._emojiFoodRow;
        case 'activities':
            return this._emojiActivitesRow;
        case 'travel':
            return this._emojiTravelRow;
        case 'objects':
            return this._emojiObjectsRow;
        case 'symbols':
            return this._emojiSymbolsRow;
        case 'flags':
            return this._emojiFlagsRow;
        case 'punctuation':
            return this._lettersPunctuationRow;
        case 'arrows':
            return this._lettersArrowsRow;
        case 'bullets':
            return this._lettersBulletsRow;
        case 'pictures':
            return this._lettersPicturesRow;
        case 'currency':
            return this._lettersCurrencyRow;
        case 'math':
            return this._lettersMathRow;
        case 'latin':
            return this._lettersLatinRow;
        default:
            return this._recentRow;
        }
    }

    selectRowByName(name) {
        let row = this.rowByName(name);
        this._list.select_row(row);
    }

    unselectAll() {
        this._list.unselect_all();
    }

    get list() {
        return this._list;
    }
});
(uuay)window.js�&/* exported MainWindow */
// -*- Mode: js; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*-
//
// Copyright (c) 2013 Giovanni Campagna <scampa.giovanni@gmail.com>
//
// Redistribution and use in source and binary forms, with or without
//  modification, are permitted provided that the following conditions are met:
//   * Redistributions of source code must retain the above copyright
//     notice, this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright
//     notice, this list of conditions and the following disclaimer in the
//     documentation and/or other materials provided with the distribution.
//   * Neither the name of the GNOME Foundation nor the
//     names of its contributors may be used to endorse or promote products
//     derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (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 { Adw, Gio, GLib, GObject, Gtk } = imports.gi;

const { CharacterDialog } = imports.characterDialog;
const Main = imports.main;
const Util = imports.util;

var MainWindow = GObject.registerClass({
    Template: 'resource:///org/gnome/Characters/window.ui',
    InternalChildren: [
        'searchButton', 'search-bar', 'searchEntry',
        'container', 'sidebar', 'loadingSpinner',
        'splitView', 'mainStack', 'contentChild',
        'charactersView', 'scrolledWindow', 'primaryMenuButton',
    ],
    Properties: {
        'max-recent-characters': GObject.ParamSpec.uint(
            'max-recent-characters', '', '',
            GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE,
            0, GLib.MAXUINT32, 100),
    },
}, class MainWindow extends Adw.ApplicationWindow {
    _init(application) {
        super._init({ application, title: GLib.get_application_name() });

        this._searchKeywords = [];
        this._characterLists = {};
        this._recentCharacterLists = {};

        this._charactersView.connect('character-selected', (widget, uc) => {
            this._handleCharacterSelected(widget, uc);
        });

        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);
                this._contentChild.title = row.title;
                this._splitView.show_content = true;
            }
        });

        // FIXME: Can't use GSettings.bind with 'as' from Gjs
        let recentCharacters = Main.settings.get_value('recent-characters');
        this.recentCharacters = recentCharacters.deepUnpack();
        this._maxRecentCharacters = 100;
        Main.settings.bind('max-recent-characters', this,
            'max-recent-characters',
            Gio.SettingsBindFlags.DEFAULT);

        Util.initActions(this, [
            { name: 'about', activate: this._about },
            {
                name: 'find',
                activate: () => {
                    this._searchButton.active = !this._searchButton.active;
                },
            },
        ]);

        this._searchButton.bind_property('active',
            this._search_bar,
            'search-mode-enabled',
            GObject.BindingFlags.SYNC_CREATE | GObject.BindingFlags.BIDIRECTIONAL,
        );

        this._searchButton.connect('toggled', btn => {
            if (btn.active)
                this._searchEntry.grab_focus();
            else
                this._searchEntry.set_text('');

            this.searchActive = btn.active;
        });

        this._searchEntry.connect('search-changed', entry => this._handleSearchChanged(entry));
        this._searchEntry.set_key_capture_widget(this);
        this._searchEntry.connect('search-started', () => {
            this.searchActive = true;
            this._searchButton.set_active(true);
        });
        this._searchEntry.connect('stop-search', () => {
            this.searchActive = false;
            this._searchButton.set_active(false);
        });

        this._contentChild.connect('hiding', () => {
            this._sidebar.unselectAll();
        });

        this._charactersView.connect('notify::loading', view => {
            if (view.loading) {
                this._loadingSpinner.start();
                this._mainStack.visible_child_name = 'loading';
            } else {
                this._loadingSpinner.stop();
                this._mainStack.visible_child_name = 'character-list';
                this._mainStack.queue_draw();
            }
        });
    }

    vfunc_map() {
        super.vfunc_map();
        // Select the first subcategory which contains at least one character.
        if (this.recentCharacters.length !== 0)
            this._sidebar.selectRowByName('recent');
        else
            this._sidebar.selectRowByName('smileys');
    }

    set searchActive(v) {
        if (v) {
            this._sidebar.unselectAll();
            this._contentChild.title = _('Search Result');
        } else {
            this._sidebar.restoreSelection();
        }
    }

    _handleSearchChanged(entry) {
        const text = entry.get_text().replace(/^\s+|\s+$/g, '');
        let keywords = text === '' ? [] : text.split(/\s+/);
        keywords = keywords.map(x => x.toUpperCase());
        if (keywords !== this._searchKeywords) {
            this._charactersView.cancelSearch();
            this._searchKeywords = keywords;
            if (this._searchKeywords.length > 0)
                this.searchByKeywords(this._searchKeywords);
        }
        return true;
    }

    _about() {
        const aboutWindow = new Adw.AboutWindow({
            application_name: _('Characters'),
            application_icon: pkg.name,
            developer_name: _('The GNOME Project'),
            designers: [
                'Allan Day <allanpday@gmail.com>',
                'Jakub Steiner <jimmac@gmail.com>',
            ],
            developers: [
                'Daiki Ueno <dueno@src.gnome.org>',
                'Giovanni Campagna <scampa.giovanni@gmail.com>',
            ],
            // TRANSLATORS: put your names here, one name per line.
            translator_credits: _('translator-credits'),
            copyright: 'Copyright 2014-2018 Daiki Ueno',
            license_type: Gtk.License.GPL_2_0,
            version: pkg.version,
            website: 'https://apps.gnome.org/Characters/',
            issue_url: 'https://gitlab.gnome.org/GNOME/gnome-characters/-/issues/new',
            transient_for: this,
        });

        aboutWindow.present();
    }

    get maxRecentCharacters() {
        return this._maxRecentCharacters;
    }

    set maxRecentCharacters(v) {
        this._maxRecentCharacters = v;
        if (this.recentCharacters.length > this._maxRecentCharacters) {
            this.recentCharacters = this.recentCharacters.slice(
                0, this._maxRecentCharacters);
        }
    }

    searchByKeywords(keywords) {
        this._charactersView.searchByKeywords(keywords)
            .then(totalResults => {
                if (totalResults === 0)
                    this._mainStack.visible_child_name = 'no-results';
                else
                    this._mainStack.visible_child_name = 'character-list';
            });
    }

    setSearchKeywords(terms) {
        this._searchEntry.set_text(terms.join(' '));
        this._searchButton.set_active(true);
    }

    setPage(pageRow) {
        if (pageRow.name === 'recent') {
            // always draw a baseline for recent view
            this._charactersView.baseline = true;
            if (this.recentCharacters.length === 0) {
                this._mainStack.visible_child_name = 'empty-recent';
            } else {
                this._charactersView.setCharacters(this.recentCharacters);
                this._mainStack.visible_child_name = 'character-list';
            }
        } else {
            this._charactersView.searchByCategory(pageRow.category);

            this._mainStack.visible_child_name = 'character-list';
            // this._charactersView.model = pageRow.model;
        }
    }

    addToRecent(uc) {
        if (this.recentCharacters.indexOf(uc) < 0) {
            this.recentCharacters.unshift(uc);
            if (this.recentCharacters.length > this._maxRecentCharacters) {
                this.recentCharacters = this.recentCharacters.slice(
                    0, this._maxRecentCharacters);
            }
            Main.settings.set_value(
                'recent-characters',
                GLib.Variant.new_strv(this.recentCharacters));
        }
    }

    _handleCharacterSelected(widget, uc) {
        const dialog = new CharacterDialog(uc, this._charactersView.fontDescription);
        dialog.set_modal(true);
        dialog.set_transient_for(this.get_root());
        dialog.connect('character-copied', (_widget, char) => {
            this.addToRecent(char);
        });
        dialog.show();
    }
});
(uuay)

Zerion Mini Shell 1.0