mirror of
https://github.com/slint-ui/slint.git
synced 2025-08-04 18:58:36 +00:00
Node.js: Implement Iterable<T> for Model<T>
This makes it much easier to extra data from Models, such as using them in for loops. This is the equivalent of Model::iter() in Rust.
This commit is contained in:
parent
841cb25565
commit
a211a104a1
4 changed files with 87 additions and 3 deletions
|
@ -427,6 +427,8 @@ test('ArrayModel', (t) => {
|
|||
let instance = definition!.create();
|
||||
t.not(instance, null);
|
||||
|
||||
t.deepEqual(Array.from(new ArrayModel([3, 2, 1])), [3, 2, 1]);
|
||||
|
||||
instance!.setProperty("int-model", new ArrayModel([10, 9, 8]));
|
||||
|
||||
let intArrayModel = instance!.getProperty("int-model") as ArrayModel<number>;
|
||||
|
@ -608,10 +610,12 @@ test('model from array', (t) => {
|
|||
|
||||
instance!.setProperty("int-array", [10, 9, 8]);
|
||||
let wrapped_int_model = instance!.getProperty("int-array");
|
||||
t.deepEqual(Array.from(wrapped_int_model), [10, 9, 8]);
|
||||
t.deepEqual(wrapped_int_model.rowCount(), 3);
|
||||
t.deepEqual(wrapped_int_model.rowData(0), 10);
|
||||
t.deepEqual(wrapped_int_model.rowData(1), 9);
|
||||
t.deepEqual(wrapped_int_model.rowData(2), 8);
|
||||
t.deepEqual(Array.from(wrapped_int_model), [10, 9, 8]);
|
||||
|
||||
instance!.setProperty("string-array", ["Simon", "Olivier", "Auri", "Tobias", "Florian"]);
|
||||
let wrapped_string_model = instance!.getProperty("string-array");
|
||||
|
|
|
@ -117,6 +117,31 @@ export interface ImageData {
|
|||
get height(): number;
|
||||
}
|
||||
|
||||
class ModelIterator<T> implements Iterator<T> {
|
||||
private row: number;
|
||||
private model: Model<T>;
|
||||
|
||||
constructor(model: Model<T>) {
|
||||
this.model = model;
|
||||
this.row = 0;
|
||||
}
|
||||
|
||||
public next(): IteratorResult<T> {
|
||||
if (this.row < this.model.rowCount()) {
|
||||
let 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.
|
||||
|
@ -176,7 +201,7 @@ export interface ImageData {
|
|||
*}
|
||||
* ```
|
||||
*/
|
||||
export abstract class Model<T> {
|
||||
export abstract class Model<T> implements Iterable<T> {
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
|
@ -221,6 +246,10 @@ export abstract class Model<T> {
|
|||
);
|
||||
}
|
||||
|
||||
[Symbol.iterator](): Iterator<T> {
|
||||
return new ModelIterator(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the view that the data of the current row is changed.
|
||||
* @param row index of the changed row.
|
||||
|
|
|
@ -75,7 +75,7 @@ pub fn to_js_unknown(env: &Env, value: &Value) -> Result<JsUnknown> {
|
|||
maybe_js_model
|
||||
} else {
|
||||
let model_wrapper: ReadOnlyRustModel = model.clone().into();
|
||||
Ok(model_wrapper.into_instance(*env)?.as_object(*env).into_unknown())
|
||||
model_wrapper.into_js(env)
|
||||
}
|
||||
}
|
||||
_ => env.get_undefined().map(|v| v.into_unknown()),
|
||||
|
|
|
@ -5,7 +5,7 @@ use std::rc::Rc;
|
|||
|
||||
use i_slint_compiler::langtype::Type;
|
||||
use i_slint_core::model::{Model, ModelNotify, ModelRc};
|
||||
use napi::bindgen_prelude::*;
|
||||
use napi::{bindgen_prelude::*, JsSymbol};
|
||||
use napi::{Env, JsExternal, JsFunction, JsNumber, JsObject, JsUnknown, Result, ValueType};
|
||||
|
||||
use crate::{to_js_unknown, to_value, RefCountedReference};
|
||||
|
@ -157,4 +157,55 @@ impl ReadOnlyRustModel {
|
|||
pub fn set_row_data(&self, _env: Env, _row: u32, _data: JsUnknown) {
|
||||
eprintln!("setRowData called on a model which does not re-implement this method. This happens when trying to modify a read-only model")
|
||||
}
|
||||
|
||||
pub fn into_js(self, env: &Env) -> Result<JsUnknown> {
|
||||
let model = self.0.clone();
|
||||
let iterator_env = env.clone();
|
||||
|
||||
let mut obj = self.into_instance(*env)?.as_object(*env);
|
||||
|
||||
// Implement Iterator protocol by hand until it's stable in napi-rs
|
||||
let iterator_symbol = env
|
||||
.get_global()
|
||||
.and_then(|global| global.get_named_property::<JsObject>("Symbol"))
|
||||
.and_then(|symbol_obj| symbol_obj.get::<&str, JsSymbol>("iterator"))?
|
||||
.expect("fatal: Unable to find Symbol.iterator");
|
||||
|
||||
obj.set_property(
|
||||
iterator_symbol,
|
||||
env.create_function_from_closure("rust model iterator", move |_| {
|
||||
Ok(ModelIterator { model: model.clone(), row: 0, env: iterator_env }
|
||||
.into_instance(iterator_env)?
|
||||
.as_object(iterator_env))
|
||||
})?,
|
||||
)?;
|
||||
|
||||
Ok(obj.into_unknown())
|
||||
}
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub struct ModelIterator {
|
||||
model: ModelRc<slint_interpreter::Value>,
|
||||
row: usize,
|
||||
env: Env,
|
||||
}
|
||||
|
||||
#[napi]
|
||||
impl ModelIterator {
|
||||
#[napi]
|
||||
pub fn next(&mut self) -> Result<JsUnknown> {
|
||||
let mut result = self.env.create_object()?;
|
||||
if self.row >= self.model.row_count() {
|
||||
result.set_named_property("done", true)?;
|
||||
} else {
|
||||
let row = self.row;
|
||||
self.row += 1;
|
||||
result.set_named_property(
|
||||
"value",
|
||||
self.model.row_data(row).and_then(|value| to_js_unknown(&self.env, &value).ok()),
|
||||
)?
|
||||
}
|
||||
return Ok(result.into_unknown());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue