mirror of
https://github.com/slint-ui/slint.git
synced 2025-10-01 22:31:14 +00:00
Added window js wrapper for napi (#3544)
* Added window js wrapper for napi * Remove position tests. * Update api/napi/src/types/size.rs Co-authored-by: Simon Hausmann <simon.hausmann@slint-ui.com> * Code review fixes --------- Co-authored-by: Simon Hausmann <simon.hausmann@slint-ui.com>
This commit is contained in:
parent
8ba6fac21f
commit
d43f5fe99a
9 changed files with 274 additions and 5 deletions
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
import test from 'ava'
|
import test from 'ava'
|
||||||
|
|
||||||
import { ComponentCompiler, ComponentDefinition, ComponentInstance, Property, ValueType } from '../index'
|
import { ComponentCompiler, ComponentDefinition, ComponentInstance, ValueType} from '../index'
|
||||||
|
|
||||||
test('get/set include paths', (t) => {
|
test('get/set include paths', (t) => {
|
||||||
let compiler = new ComponentCompiler;
|
let compiler = new ComponentCompiler;
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
import test from 'ava';
|
import test from 'ava';
|
||||||
|
|
||||||
import { Brush, Color, ArrayModel } from '../index'
|
import { Brush, Color, ArrayModel, Size } from '../index'
|
||||||
|
|
||||||
test('Color from fromRgb', (t) => {
|
test('Color from fromRgb', (t) => {
|
||||||
let color = Color.fromRgb(100, 110, 120);
|
let color = Color.fromRgb(100, 110, 120);
|
||||||
|
@ -76,7 +76,6 @@ test('ArrayModel setRowData', (t) => {
|
||||||
t.is(arrayModel.rowData(0), 2);
|
t.is(arrayModel.rowData(0), 2);
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
test('ArrayModel remove', (t) => {
|
test('ArrayModel remove', (t) => {
|
||||||
let arrayModel = new ArrayModel([0, 2, 1]);
|
let arrayModel = new ArrayModel([0, 2, 1]);
|
||||||
|
|
||||||
|
|
38
api/napi/__test__/window.spec.ts
Normal file
38
api/napi/__test__/window.spec.ts
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
// Copyright © SixtyFPS GmbH <info@slint.dev>
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial
|
||||||
|
|
||||||
|
import test from 'ava'
|
||||||
|
|
||||||
|
import { ComponentCompiler, Window } from '../index'
|
||||||
|
|
||||||
|
test('Window constructor', (t) => {
|
||||||
|
t.throws(() => {
|
||||||
|
new Window()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
code: "GenericFailure",
|
||||||
|
message: "Window can only be created by using a Component."
|
||||||
|
}
|
||||||
|
);
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Window show / hide', (t) => {
|
||||||
|
let compiler = new ComponentCompiler;
|
||||||
|
let definition = compiler.buildFromSource(`
|
||||||
|
|
||||||
|
export component App inherits Window {
|
||||||
|
width: 300px;
|
||||||
|
height: 300px;
|
||||||
|
}`, "");
|
||||||
|
t.not(definition, null);
|
||||||
|
|
||||||
|
let instance = definition!.create();
|
||||||
|
t.not(instance, null);
|
||||||
|
|
||||||
|
let window = instance!.window();
|
||||||
|
t.is(window.isVisible, false);
|
||||||
|
window.show();
|
||||||
|
t.is(window.isVisible, true);
|
||||||
|
window.hide();
|
||||||
|
t.is(window.isVisible, false);
|
||||||
|
})
|
|
@ -15,3 +15,6 @@ pub use diagnostic::*;
|
||||||
|
|
||||||
mod value;
|
mod value;
|
||||||
pub use value::*;
|
pub use value::*;
|
||||||
|
|
||||||
|
mod window;
|
||||||
|
pub use window::*;
|
||||||
|
|
|
@ -2,9 +2,12 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial
|
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial
|
||||||
|
|
||||||
use i_slint_compiler::langtype::Type;
|
use i_slint_compiler::langtype::Type;
|
||||||
|
use i_slint_core::window::WindowInner;
|
||||||
use napi::{Env, Error, JsFunction, JsUnknown, NapiRaw, NapiValue, Ref, Result};
|
use napi::{Env, Error, JsFunction, JsUnknown, NapiRaw, NapiValue, Ref, Result};
|
||||||
use slint_interpreter::{ComponentHandle, ComponentInstance, Value};
|
use slint_interpreter::{ComponentHandle, ComponentInstance, Value};
|
||||||
|
|
||||||
|
use crate::JsWindow;
|
||||||
|
|
||||||
use super::JsComponentDefinition;
|
use super::JsComponentDefinition;
|
||||||
|
|
||||||
#[napi(js_name = "ComponentInstance")]
|
#[napi(js_name = "ComponentInstance")]
|
||||||
|
@ -329,6 +332,11 @@ impl JsComponentInstance {
|
||||||
.map_err(|_| napi::Error::from_reason("Cannot invoke callback."))?;
|
.map_err(|_| napi::Error::from_reason("Cannot invoke callback."))?;
|
||||||
super::to_js_unknown(&env, &result)
|
super::to_js_unknown(&env, &result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn window(&self) -> Result<JsWindow> {
|
||||||
|
Ok(JsWindow { inner: WindowInner::from_pub(self.inner.window()).window_adapter() })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrapper around Ref<>, which requires manual ref-counting.
|
// Wrapper around Ref<>, which requires manual ref-counting.
|
||||||
|
|
103
api/napi/src/interpreter/window.rs
Normal file
103
api/napi/src/interpreter/window.rs
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
// Copyright © SixtyFPS GmbH <info@slint.dev>
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial
|
||||||
|
|
||||||
|
use crate::types::{JsPoint, JsSize};
|
||||||
|
use i_slint_core::window::WindowAdapterRc;
|
||||||
|
use slint_interpreter::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize};
|
||||||
|
|
||||||
|
#[napi(js_name = "Window")]
|
||||||
|
pub struct JsWindow {
|
||||||
|
pub(crate) inner: WindowAdapterRc,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<WindowAdapterRc> for JsWindow {
|
||||||
|
fn from(instance: WindowAdapterRc) -> Self {
|
||||||
|
Self { inner: instance }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
impl JsWindow {
|
||||||
|
#[napi(constructor)]
|
||||||
|
pub fn new() -> napi::Result<Self> {
|
||||||
|
Err(napi::Error::from_reason(
|
||||||
|
"Window can only be created by using a Component.".to_string(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn show(&self) -> napi::Result<()> {
|
||||||
|
self.inner
|
||||||
|
.window()
|
||||||
|
.show()
|
||||||
|
.map_err(|_| napi::Error::from_reason("Cannot show window.".to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
pub fn hide(&self) -> napi::Result<()> {
|
||||||
|
self.inner
|
||||||
|
.window()
|
||||||
|
.hide()
|
||||||
|
.map_err(|_| napi::Error::from_reason("Cannot hide window.".to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(getter)]
|
||||||
|
pub fn is_visible(&self) -> bool {
|
||||||
|
self.inner.window().is_visible()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(getter)]
|
||||||
|
pub fn get_logical_position(&self) -> JsPoint {
|
||||||
|
let pos = self.inner.window().position().to_logical(self.inner.window().scale_factor());
|
||||||
|
JsPoint { x: pos.x as f64, y: pos.y as f64 }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(setter)]
|
||||||
|
pub fn set_logical_position(&self, position: JsPoint) {
|
||||||
|
self.inner
|
||||||
|
.window()
|
||||||
|
.set_position(LogicalPosition { x: position.x as f32, y: position.y as f32 });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(getter)]
|
||||||
|
pub fn get_physical_position(&self) -> JsPoint {
|
||||||
|
let pos = self.inner.window().position();
|
||||||
|
JsPoint { x: pos.x as f64, y: pos.y as f64 }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(setter)]
|
||||||
|
pub fn set_physical_position(&self, position: JsPoint) {
|
||||||
|
self.inner.window().set_position(PhysicalPosition {
|
||||||
|
x: position.x.floor() as i32,
|
||||||
|
y: position.y.floor() as i32,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(getter)]
|
||||||
|
pub fn get_logical_size(&self) -> JsSize {
|
||||||
|
let size = self.inner.window().size().to_logical(self.inner.window().scale_factor());
|
||||||
|
JsSize { width: size.width as f64, height: size.height as f64 }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(setter)]
|
||||||
|
pub fn set_logical_size(&self, size: JsSize) {
|
||||||
|
self.inner.window().set_size(LogicalSize::from_physical(
|
||||||
|
PhysicalSize { width: size.width.floor() as u32, height: size.height.floor() as u32 },
|
||||||
|
self.inner.window().scale_factor(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(getter)]
|
||||||
|
pub fn get_physical_size(&self) -> JsSize {
|
||||||
|
let size = self.inner.window().size();
|
||||||
|
JsSize { width: size.width as f64, height: size.height as f64 }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi(setter)]
|
||||||
|
pub fn set_physical_size(&self, size: JsSize) {
|
||||||
|
self.inner.window().set_size(PhysicalSize {
|
||||||
|
width: size.width.floor() as u32,
|
||||||
|
height: size.height.floor() as u32,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,3 +9,9 @@ pub use image_data::*;
|
||||||
|
|
||||||
mod model;
|
mod model;
|
||||||
pub use model::*;
|
pub use model::*;
|
||||||
|
|
||||||
|
mod point;
|
||||||
|
pub use point::*;
|
||||||
|
|
||||||
|
mod size;
|
||||||
|
pub use size::*;
|
||||||
|
|
52
api/napi/src/types/point.rs
Normal file
52
api/napi/src/types/point.rs
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
// Copyright © SixtyFPS GmbH <info@slint.dev>
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial
|
||||||
|
|
||||||
|
use napi::{
|
||||||
|
bindgen_prelude::{FromNapiValue, Object},
|
||||||
|
JsUnknown,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[napi(js_name = Point)]
|
||||||
|
pub struct JsPoint {
|
||||||
|
pub x: f64,
|
||||||
|
pub y: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
impl JsPoint {
|
||||||
|
#[napi(constructor)]
|
||||||
|
pub fn new(x: f64, y: f64) -> Self {
|
||||||
|
Self { x, y }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromNapiValue for JsPoint {
|
||||||
|
unsafe fn from_napi_value(
|
||||||
|
env: napi::sys::napi_env,
|
||||||
|
napi_val: napi::sys::napi_value,
|
||||||
|
) -> napi::Result<Self> {
|
||||||
|
let obj = unsafe { Object::from_napi_value(env, napi_val)? };
|
||||||
|
let x: f64 = obj
|
||||||
|
.get::<_, JsUnknown>("x")
|
||||||
|
.ok()
|
||||||
|
.flatten()
|
||||||
|
.and_then(|p| p.coerce_to_number().ok())
|
||||||
|
.and_then(|f64_num| f64_num.try_into().ok())
|
||||||
|
.ok_or_else(
|
||||||
|
|| napi::Error::from_reason(
|
||||||
|
format!("Cannot convert object to Point, because the provided object does not have an f64 x property")
|
||||||
|
))?;
|
||||||
|
let y: f64 = obj
|
||||||
|
.get::<_, JsUnknown>("y")
|
||||||
|
.ok()
|
||||||
|
.flatten()
|
||||||
|
.and_then(|p| p.coerce_to_number().ok())
|
||||||
|
.and_then(|f64_num| f64_num.try_into().ok())
|
||||||
|
.ok_or_else(
|
||||||
|
|| napi::Error::from_reason(
|
||||||
|
format!("Cannot convert object to Point, because the provided object does not have an f64 y property")
|
||||||
|
))?;
|
||||||
|
|
||||||
|
Ok(JsPoint { x, y })
|
||||||
|
}
|
||||||
|
}
|
60
api/napi/src/types/size.rs
Normal file
60
api/napi/src/types/size.rs
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
// Copyright © SixtyFPS GmbH <info@slint.dev>
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial
|
||||||
|
|
||||||
|
use napi::{
|
||||||
|
bindgen_prelude::{FromNapiValue, Object},
|
||||||
|
JsUnknown, Result,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[napi(js_name = Size)]
|
||||||
|
pub struct JsSize {
|
||||||
|
pub width: f64,
|
||||||
|
pub height: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[napi]
|
||||||
|
impl JsSize {
|
||||||
|
#[napi(constructor)]
|
||||||
|
pub fn new(width: f64, height: f64) -> Result<Self> {
|
||||||
|
if width < 0. {
|
||||||
|
return Err(napi::Error::from_reason("width cannot be negative".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if height < 0. {
|
||||||
|
return Err(napi::Error::from_reason("height cannot be negative".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self { width, height })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromNapiValue for JsSize {
|
||||||
|
unsafe fn from_napi_value(
|
||||||
|
env: napi::sys::napi_env,
|
||||||
|
napi_val: napi::sys::napi_value,
|
||||||
|
) -> napi::Result<Self> {
|
||||||
|
let obj = unsafe { Object::from_napi_value(env, napi_val)? };
|
||||||
|
let width: f64 = obj
|
||||||
|
.get::<_, JsUnknown>("width")
|
||||||
|
.ok()
|
||||||
|
.flatten()
|
||||||
|
.and_then(|p| p.coerce_to_number().ok())
|
||||||
|
.and_then(|f64_num| f64_num.try_into().ok())
|
||||||
|
.ok_or_else(
|
||||||
|
|| napi::Error::from_reason(
|
||||||
|
format!("Cannot convert object to Size, because the provided object does not have an f64 width property")
|
||||||
|
))?;
|
||||||
|
let height: f64 = obj
|
||||||
|
.get::<_, JsUnknown>("height")
|
||||||
|
.ok()
|
||||||
|
.flatten()
|
||||||
|
.and_then(|p| p.coerce_to_number().ok())
|
||||||
|
.and_then(|f64_num| f64_num.try_into().ok())
|
||||||
|
.ok_or_else(
|
||||||
|
|| napi::Error::from_reason(
|
||||||
|
format!("Cannot convert object to Size, because the provided object does not have an f64 height property")
|
||||||
|
))?;
|
||||||
|
|
||||||
|
Ok(JsSize { width, height })
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue