mirror of
https://github.com/slint-ui/slint.git
synced 2025-10-02 06:41: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 { ComponentCompiler, ComponentDefinition, ComponentInstance, Property, ValueType } from '../index'
|
||||
import { ComponentCompiler, ComponentDefinition, ComponentInstance, ValueType} from '../index'
|
||||
|
||||
test('get/set include paths', (t) => {
|
||||
let compiler = new ComponentCompiler;
|
||||
|
@ -236,4 +236,4 @@ test('non-existent properties and callbacks', (t) => {
|
|||
});
|
||||
t.is(callback_err!.code, 'GenericFailure');
|
||||
t.is(callback_err!.message, 'Callback non-existent-callback not found in the component');
|
||||
})
|
||||
})
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
import test from 'ava';
|
||||
|
||||
import { Brush, Color, ArrayModel } from '../index'
|
||||
import { Brush, Color, ArrayModel, Size } from '../index'
|
||||
|
||||
test('Color from fromRgb', (t) => {
|
||||
let color = Color.fromRgb(100, 110, 120);
|
||||
|
@ -76,7 +76,6 @@ test('ArrayModel setRowData', (t) => {
|
|||
t.is(arrayModel.rowData(0), 2);
|
||||
})
|
||||
|
||||
|
||||
test('ArrayModel remove', (t) => {
|
||||
let arrayModel = new ArrayModel([0, 2, 1]);
|
||||
|
||||
|
@ -87,4 +86,4 @@ test('ArrayModel remove', (t) => {
|
|||
arrayModel.remove(0, 2);
|
||||
t.is(arrayModel.rowCount(), 1);
|
||||
t.is(arrayModel.rowData(0), 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;
|
||||
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
|
||||
|
||||
use i_slint_compiler::langtype::Type;
|
||||
use i_slint_core::window::WindowInner;
|
||||
use napi::{Env, Error, JsFunction, JsUnknown, NapiRaw, NapiValue, Ref, Result};
|
||||
use slint_interpreter::{ComponentHandle, ComponentInstance, Value};
|
||||
|
||||
use crate::JsWindow;
|
||||
|
||||
use super::JsComponentDefinition;
|
||||
|
||||
#[napi(js_name = "ComponentInstance")]
|
||||
|
@ -329,6 +332,11 @@ impl JsComponentInstance {
|
|||
.map_err(|_| napi::Error::from_reason("Cannot invoke callback."))?;
|
||||
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.
|
||||
|
|
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;
|
||||
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