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
JavaScript
"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;