<commit_before>// Copyright 2015 Endless Mobile, Inc.

/* exported Arrangement, place_card, get_spare_pixels_for_card_index */

const Gdk = imports.gi.Gdk;
const GObject = imports.gi.GObject;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;

const ContentObjectModel = imports.search.contentObjectModel;
const Module = imports.app.interfaces.module;

/**
 * Interface: Arrangement
 * Arrangement and order of cards in a container
 *
 * An arrangement controls how a group of cards are presented in the UI.
 * Examples of arrangements: a list, a grid, etc.
 *
 * Slots:
 *   card-type - controls how the card models are converted into cards
 *   order
 *   filter
 */
const Arrangement = new Lang.Interface({
    Name: 'Arrangement',
    GTypeName: 'EknArrangement',
    Requires: [ Gtk.Container, Module.Module ],

    Properties: {
        /**
         * Property: all-visible
         * Whether all children are visible or some were cut off
         *
         * Flags:
         *   read-only
         */
        'all-visible': GObject.ParamSpec.boolean('all-visible', 'All visible',
            'All children visible',
            GObject.ParamFlags.READABLE,
            true),
        /**
         * Property: fade-cards
         * Whether to fade in cards or just show them
         *
         * In some circumstances, arrangements should fade in their cards.
         * For example, in a <SearchModule>, lazily loaded batches of cards
         * beyond the first batch should fade in instead of appearing abruptly.
         *
         * Set this to *true* to make newly added cards fade in, instead of
         * appearing abruptly. The animation's appearance is controlled by the
         * *invisible* and *fade-in* CSS classes on the <Card> widget.
         *
         * You can opt out of this if the arrangement should never fade in
         * its cards. In that case, override <Arrangement.fade_card>.
         *
         * Default:
         *   false
         */
        'fade-cards': GObject.ParamSpec.boolean('fade-cards', 'Fade cards',
            'Whether new cards should fade in gradually',
            GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT,
            false),
    },

    Slots: {
        'card': {
            multi: true,
        },
        'order': {},
        'filter': {},
    },

    Signals: {
        /**
         * Signal: card-clicked
         * Emitted when one of the arrangement's cards is clicked
         *
         * Parameters:
         *   model - the <ContentObjectModel> of the card that was clicked
         */
        'card-clicked': {
            param_types: [ ContentObjectModel.ContentObjectModel ],
        },
    },

    _interface_init: function () {
        this._cards_by_id = new Map();
        this._models = [];
        this._order_module = this.create_submodule('order');
        this._filter_module = this.create_submodule('filter');
    },


    /**
     * Method: get_count
     * Count the card models in the arrangement
     *
     * This is a method for technical reasons, but it should be treated like a
     * read-only property.
     *
     * Returns:
     *   Number of card models in the arrangement
     */
    get_count: function () {
        return this._models.length;
    },

    /**
     * Method: get_card_count
     * Count the cards being shown in the arrangement
     *
     * Contrast to <get_count()>.
     * This returns the number of cards currently displaying in the arrangement.
     * There may be card models added to the arrangement that are not shown as
     * cards because they are filtered out.
     *
     * This is a method for technical reasons, but it should be treated like a
     * read-only property.
     *
     * Returns:
     *   Number of cards in the arrangement
     */
    get_card_count: function () {
        return this._cards_by_id.size;
    },

    _create_card: function (model) {
        let card_props = { model: model };
        if (this._highlight_string)
            card_props.highlight_string = this._highlight_string;
        let card = this.create_submodule('card', card_props);
        this._cards_by_id.set(model.ekn_id, card);

        // It's either this or have Card require Gtk.Button, but that would be
        // very bad for DocumentCards.
        if (GObject.signal_lookup('clicked', card.constructor.$gtype) !== 0) {
            card.connect('clicked', card => {
                this.emit('card-clicked', card.model);
            });
        }

        return card;
    },

    /**
     * Method: set_models
     * Display card models in the arrangement
     *
     * Note that adding a card directly with *Gtk.Container.add()* or one of its
     * more specialized relatives will not add a model to the arrangement.
     *
     * Parameters:
     *   models - an array of <ContentObjectModel>s
     */
    set_models: function (models) {
        // FIXME: Stopgap measure until all content groups use selections
        models = models.slice();
        if (this._filter_module)
            models = models.filter(this._filter_module.include.bind(this._filter_module));
        if (this._order_module)
            models.sort(this._order_module.compare.bind(this._order_module));

        // unpack all cards
        let cards = [...this._cards_by_id.values()];
        cards.forEach((card) => {
            this.unpack_card(card);
        });

        this._models = models;

        // only maintain the number of models we care about
        if (this.get_max_cards() > -1)
            this._models = this._models.slice(0, this.get_max_cards());

        let ekn_id_set = new Set();

        // Add cards for models that we don't have yet.
        this._models.forEach((model) => {
            ekn_id_set.add(model.ekn_id);
            if (!this._cards_by_id.get(model.ekn_id))
                this._cards_by_id.set(model.ekn_id, this._create_card(model));

            let card = this._cards_by_id.get(model.ekn_id);
            this.pack_card(card);
            if (this.fade_cards)
                this.fade_card_in(card);
            else
                card.show_all();
        });

        // Delete cards for models we don't need anymore
        [...this._cards_by_id.keys()].filter((key) => !ekn_id_set.has(key))
                                     .forEach((key) => this._cards_by_id.delete(key));
    },

    /**
     * Method: remove_model
     * Remove a card model from the arrangement
     *
     * Note that removing a card directly with *Gtk.Container.remove()* will
     * not remove the model from the arrangement.
     *
     * Parameters:
     *   model - a <ContentObjectModel>
     */
    remove_model: function (model) {
        let match = this._models.filter((iter_m) => model.ekn_id === iter_m.ekn_id);
        if (match.length > 0)
            this._models.splice(this._models.indexOf(match[0]), 1);
        let card = this._cards_by_id.get(model.ekn_id);
        if (card) {<commit_msg>Only fade in cards that are newly created<commit_after>// Copyright 2015 Endless Mobile, Inc.

/* exported Arrangement, place_card, get_spare_pixels_for_card_index */

const Gdk = imports.gi.Gdk;
const GObject = imports.gi.GObject;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;

const ContentObjectModel = imports.search.contentObjectModel;
const Module = imports.app.interfaces.module;

/**
 * Interface: Arrangement
 * Arrangement and order of cards in a container
 *
 * An arrangement controls how a group of cards are presented in the UI.
 * Examples of arrangements: a list, a grid, etc.
 *
 * Slots:
 *   card-type - controls how the card models are converted into cards
 *   order
 *   filter
 */
const Arrangement = new Lang.Interface({
    Name: 'Arrangement',
    GTypeName: 'EknArrangement',
    Requires: [ Gtk.Container, Module.Module ],

    Properties: {
        /**
         * Property: all-visible
         * Whether all children are visible or some were cut off
         *
         * Flags:
         *   read-only
         */
        'all-visible': GObject.ParamSpec.boolean('all-visible', 'All visible',
            'All children visible',
            GObject.ParamFlags.READABLE,
            true),
        /**
         * Property: fade-cards
         * Whether to fade in cards or just show them
         *
         * In some circumstances, arrangements should fade in their cards.
         * For example, in a <SearchModule>, lazily loaded batches of cards
         * beyond the first batch should fade in instead of appearing abruptly.
         *
         * Set this to *true* to make newly added cards fade in, instead of
         * appearing abruptly. The animation's appearance is controlled by the
         * *invisible* and *fade-in* CSS classes on the <Card> widget.
         *
         * You can opt out of this if the arrangement should never fade in
         * its cards. In that case, override <Arrangement.fade_card>.
         *
         * Default:
         *   false
         */
        'fade-cards': GObject.ParamSpec.boolean('fade-cards', 'Fade cards',
            'Whether new cards should fade in gradually',
            GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT,
            false),
    },

    Slots: {
        'card': {
            multi: true,
        },
        'order': {},
        'filter': {},
    },

    Signals: {
        /**
         * Signal: card-clicked
         * Emitted when one of the arrangement's cards is clicked
         *
         * Parameters:
         *   model - the <ContentObjectModel> of the card that was clicked
         */
        'card-clicked': {
            param_types: [ ContentObjectModel.ContentObjectModel ],
        },
    },

    _interface_init: function () {
        this._cards_by_id = new Map();
        this._models = [];
        this._order_module = this.create_submodule('order');
        this._filter_module = this.create_submodule('filter');
    },


    /**
     * Method: get_count
     * Count the card models in the arrangement
     *
     * This is a method for technical reasons, but it should be treated like a
     * read-only property.
     *
     * Returns:
     *   Number of card models in the arrangement
     */
    get_count: function () {
        return this._models.length;
    },

    /**
     * Method: get_card_count
     * Count the cards being shown in the arrangement
     *
     * Contrast to <get_count()>.
     * This returns the number of cards currently displaying in the arrangement.
     * There may be card models added to the arrangement that are not shown as
     * cards because they are filtered out.
     *
     * This is a method for technical reasons, but it should be treated like a
     * read-only property.
     *
     * Returns:
     *   Number of cards in the arrangement
     */
    get_card_count: function () {
        return this._cards_by_id.size;
    },

    _create_card: function (model) {
        let card_props = { model: model };
        if (this._highlight_string)
            card_props.highlight_string = this._highlight_string;
        let card = this.create_submodule('card', card_props);
        this._cards_by_id.set(model.ekn_id, card);

        // It's either this or have Card require Gtk.Button, but that would be
        // very bad for DocumentCards.
        if (GObject.signal_lookup('clicked', card.constructor.$gtype) !== 0) {
            card.connect('clicked', card => {
                this.emit('card-clicked', card.model);
            });
        }

        return card;
    },

    /**
     * Method: set_models
     * Display card models in the arrangement
     *
     * Note that adding a card directly with *Gtk.Container.add()* or one of its
     * more specialized relatives will not add a model to the arrangement.
     *
     * Parameters:
     *   models - an array of <ContentObjectModel>s
     */
    set_models: function (models) {
        // FIXME: Stopgap measure until all content groups use selections
        models = models.slice();
        if (this._filter_module)
            models = models.filter(this._filter_module.include.bind(this._filter_module));
        if (this._order_module)
            models.sort(this._order_module.compare.bind(this._order_module));

        // unpack all cards
        let cards = [...this._cards_by_id.values()];
        cards.forEach((card) => {
            this.unpack_card(card);
        });

        this._models = models;

        // only maintain the number of models we care about
        if (this.get_max_cards() > -1)
            this._models = this._models.slice(0, this.get_max_cards());

        let ekn_id_set = new Set();

        // Add cards for models that we don't have yet.
        this._models.forEach((model) => {
            ekn_id_set.add(model.ekn_id);
            let newly_created = false;
            if (!this._cards_by_id.get(model.ekn_id)) {
                this._cards_by_id.set(model.ekn_id, this._create_card(model));
                newly_created = true;
            }

            let card = this._cards_by_id.get(model.ekn_id);
            this.pack_card(card);
            // Fading in a card that already exists on the arrangement looks weird
            // and is a performance hit.
            if (this.fade_cards && newly_created)
                this.fade_card_in(card);
            else
                card.show_all();
        });

        // Delete cards for models we don't need anymore
        [...this._cards_by_id.keys()].filter((key) => !ekn_id_set.has(key))
                                     .forEach((key) => this._cards_by_id.delete(key));
    },

    /**
     * Method: remove_model
     * Remove a card model from the arrangement
     *
     * Note that removing a card directly with *Gtk.Container.remove()* will
     * not remove the model from the arrangement.
     *
     * Parameters:
     *   model - a <ContentObjectModel>
     */
    remove_model: function (model) {
        let match = this._models.filter((iter_m) => model.ekn_id === iter_m.ekn_id);
        if (match.length > 0)
            this._models.splice(this._models.indexOf(match[0]), 1);
        let card = this._cards_by_id.get(model.ekn_id);
        if (card) {
{'fraction_non_alphanumeric': 0.10122397298818232, 'fraction_numerical': 0.0014068655036578502, 'mean_word_length': 2.016549968173138, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 33, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3929884', 'n_tokens_mistral': 4039, 'n_tokens_neox': 3936, 'n_words': 1184}