UNPKG

slint-ui

Version:

Slint is a declarative GUI toolkit to build native user interfaces for desktop and embedded applications.

295 lines (294 loc) 9.27 kB
"use strict"; // Copyright © SixtyFPS GmbH <info@slint.dev> // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0 var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.ArrayModel = exports.Model = void 0; const napi = __importStar(require("../binding.cjs")); class ModelIterator { row; model; constructor(model) { this.model = model; this.row = 0; } next() { if (this.row < this.model.rowCount()) { const row = this.row; this.row++; return { done: false, value: this.model.rowData(row), }; } return { done: true, value: undefined, }; } } /** * Model<T> is the interface for feeding dynamic data into * `.slint` views. * * A model is organized like a table with rows of data. The * fields of the data type T behave like columns. * * @template T the type of the model's items. * * ### Example * As an example let's see the implementation of {@link ArrayModel} * * ```js * export class ArrayModel<T> extends Model<T> { * private a: Array<T> * * constructor(arr: Array<T>) { * super(); * this.a = arr; * } * * rowCount() { * return this.a.length; * } * * rowData(row: number) { * return this.a[row]; * } * * setRowData(row: number, data: T) { * this.a[row] = data; * this.notifyRowDataChanged(row); * } * * push(...values: T[]) { * let size = this.a.length; * Array.prototype.push.apply(this.a, values); * this.notifyRowAdded(size, arguments.length); * } * * remove(index: number, size: number) { * let r = this.a.splice(index, size); * this.notifyRowRemoved(index, size); * } * * get length(): number { * return this.a.length; * } * * values(): IterableIterator<T> { * return this.a.values(); * } * * entries(): IterableIterator<[number, T]> { * return this.a.entries() * } *} * ``` */ class Model { /** * @hidden */ modelNotify; /** * @hidden */ constructor(modelNotify) { this.modelNotify = modelNotify ?? napi.jsModelNotifyNew(); } /** * Implementations of this function must store the provided data parameter * in the model at the specified row. * @param _row index in range 0..(rowCount() - 1). * @param _data new data item to store on the given row index */ setRowData(_row, _data) { console.log("setRowData called on a model which does not re-implement this method. This happens when trying to modify a read-only model"); } [Symbol.iterator]() { return new ModelIterator(this); } /** * Notifies the view that the data of the current row is changed. * @param row index of the changed row. */ notifyRowDataChanged(row) { napi.jsModelNotifyRowDataChanged(this.modelNotify, row); } /** * Notifies the view that multiple rows are added to the model. * @param row index of the first added row. * @param count the number of added items. */ notifyRowAdded(row, count) { napi.jsModelNotifyRowAdded(this.modelNotify, row, count); } /** * Notifies the view that multiple rows are removed to the model. * @param row index of the first removed row. * @param count the number of removed items. */ notifyRowRemoved(row, count) { napi.jsModelNotifyRowRemoved(this.modelNotify, row, count); } /** * Notifies the view that the complete data must be reload. */ notifyReset() { napi.jsModelNotifyReset(this.modelNotify); } } exports.Model = Model; /** * ArrayModel wraps a JavaScript array for use in `.slint` views. The underlying * array can be modified with the [[ArrayModel.push]], [[ArrayModel.remove]], and * [[ArrayModel.splice]] methods. */ class ArrayModel extends Model { /** * @hidden */ #array; /** * Creates a new ArrayModel. * * @param arr */ constructor(arr) { super(); this.#array = arr; } /** * Returns the number of entries in the array model. */ get length() { return this.#array.length; } /** * Returns the number of entries in the array model. */ rowCount() { return this.#array.length; } /** * Returns the data at the specified row. * @param row index in range 0..(rowCount() - 1). * @returns undefined if row is out of range otherwise the data. */ rowData(row) { return this.#array[row]; } /** * Stores the given data on the given row index and notifies run-time about the changed row. * @param row index in range 0..(rowCount() - 1). * @param data new data item to store on the given row index */ setRowData(row, data) { this.#array[row] = data; this.notifyRowDataChanged(row); } /** * Pushes new values to the array that's backing the model and notifies * the run-time about the added rows. * @param values list of values that will be pushed to the array. */ push(...values) { const size = this.#array.length; Array.prototype.push.apply(this.#array, values); this.notifyRowAdded(size, arguments.length); } /** * Removes the last element from the array and returns it. * * @returns The removed element or undefined if the array is empty. */ pop() { const last = this.#array.pop(); if (last !== undefined) { this.notifyRowRemoved(this.#array.length, 1); } return last; } /** * Removes the specified number of element from the array that's backing * the model, starting at the specified index. * @param index index of first row to remove. * @param size number of rows to remove. */ remove(index, size) { const r = this.#array.splice(index, size); this.notifyRowRemoved(index, size); } /** * Removes elements from the array that's backing the model and, if * necessary, inserts new elements in their place, following the semantics * of `Array.prototype.splice`. The run-time is notified about the removed * and added rows. * @param start zero-based index at which to start changing the array; negative values count back from the end and out-of-range values are clamped. * @param deleteCount number of elements to remove starting at `start`; if omitted, all elements from `start` to the end are removed. * @param items elements to insert at `start`. * @returns an array containing the removed elements. */ splice(start, deleteCount, ...items) { const len = this.#array.length; // Normalize `start` the way `Array.prototype.splice` does, so the // change notifications point at the actual mutation index. const actualStart = start < 0 ? Math.max(len + start, 0) : Math.min(start, len); const removed = deleteCount === undefined ? this.#array.splice(actualStart) : this.#array.splice(actualStart, deleteCount, ...items); if (removed.length > 0) { this.notifyRowRemoved(actualStart, removed.length); } if (items.length > 0) { this.notifyRowAdded(actualStart, items.length); } return removed; } /** * Returns an iterable of values in the array. */ values() { return this.#array.values(); } /** * Returns an iterable of key, value pairs for every entry in the array. */ entries() { return this.#array.entries(); } } exports.ArrayModel = ArrayModel;