Skip to content

Commit

Permalink
Try ci env 2
Browse files Browse the repository at this point in the history
  • Loading branch information
raub committed Oct 29, 2023
1 parent e1be8a6 commit 21b7b8a
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 130 deletions.
9 changes: 6 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,13 @@ jobs:
- name: Build Current Binary
run: npm run build

- name: Set Env
run: |
echo "$GITHUB_WORKSPACE/ci"
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$GITHUB_WORKSPACE/ci
- name: Run Unit Tests - Linux
if: matrix.os == 'ubuntu-20.04'
uses: coactions/setup-xvfb@v1
with:
run: |
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$GITHUB_WORKSPACE/ci
npm run test-ci
run: npm run test-ci
141 changes: 24 additions & 117 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,27 +23,27 @@ for additional details on QML features and syntax.
Node.js versions. Addon binaries are precompiled and **there is no compilation**
step during the `npm i` command.


```
const { View } = require('qml-raub');
View.init(HWND, CTX);
View.init(process.cwd(), HWND, CTX);
const ui = new View({ width, height, file: 'gui.qml' });
```

The first thing to do is to initialize the QML renderer. Pass the native
window handles to the static `init` method. A **shared**
[GL context](https://www.khronos.org/opengl/wiki/OpenGL_Context)
is created based on these handles. QML necessarily has a dedicated GL context
because of the renderer-specific requirements. The produced QML-containing
textures are available to the main application as if its own resources.
The QML engine must be initialized first. Then, new View instances can be created.
See [TypeScript defenitions](/index.d.ts) for more details.

If using the [glfw-raub](https://github.com/node-3d/glfw-raub) module for platform
window management, an example obtaining of HWND and CTX would be as follows:
QML views can process input events, as per Qt documentation. We can
propagate mouse and keyboard events to the View, and it will react as any
normal QML scene. Also there is a loop-back to propagate back any unused
events. This means a lot for screen-space UI's: we still want the underlying
app to receive mouse and keyboard events as well.

```
// window - is returned by glfw.createWindow(...) call
const HWND = glfw.platformWindow(window);
const CTX = glfw.platformContext(window);
```
Using loop-back implies a change from `source -> app` event
flow to `source -> ui -> app`. If mouse click hits a QML button, we don't
want it to also hit an object behind the button. And if some QML input is
focused, we should be able to type any text without hitting random
combinations of app's hotkeys.


### class View
Expand All @@ -58,139 +58,46 @@ For example a screen-sized rectangle with this texture would look as if it is
the app's UI, which it already almost is. Also some in-scene quads, e.g. a PC
display in the distant corner of 3d room, can be textured this way.

What is really important, is the dynamic nature of this texture. We can
propagate mouse and keyboard events to the View, and it will react as any
normal QML scene. Also there is a loop-back to propagate back any unused
events. This means a lot for screen-space UI's: we still want the underlying
app to receive mouse and keyboard events as well.

Therefore, using loop-back implies a change from `source -> app` event
flow to `source -> ui -> app`. If mouse click hits a QML button, we don't
want it to also hit an object behind the button. And if some QML input is
focused, we should be able to type any text without hitting random
combinations of app's hotkeys.

```
const view = new View({ width: 800, height: 600, file: 'ui.qml' });
const ui = new View({ width, height, file: 'gui.qml' });
```


Constructor: `View(?opts)`. Param opts (all optional):
* `number ?width 512` - QML view scene width.
* `number ?height 512` - QML view scene height.
* `boolean ?silent false` - ignore QML errors.
* `string ?file ''` - a QML file to be loaded.
* `string ?source ''` - a QML source text to be used instead of a file.

If both `file` and `source` are passed, the `file` is used. If none of them passed
through the opts, the method `load()` can be used later.


Properties:
* `get/set number width|w` - view width.
* `get/set number height|h` - view height.
* `get/set [width, height] wh` - view width and height.
* `get/set {width, height} size` - view width and height.
* `get number textureId` - current GL texture id.


Methods:
* load(opts) - load a new QML scene. The old one will be discarded, if any. A new
texture will be created upon `'load'` event. Param opts:
* `string ?file` - a QML file to be loaded.
* `string ?source` - a QML source text to be used instead of a file.

If both `file` and `source` are passed, the `file` is used. If none of them passed,
an error will be thrown.

* `mousedown(Event e)` - send mousedown event to the QML scene.
* `mouseup(Event e)` - send mouseup event to the QML scene.
* `mousemove(Event e)` - send mousemove event to the QML scene.
* `keydown(Event e)` - send keydown event to the QML scene.
* `keyup(Event e)` - send keyup event to the QML scene.

See [TypeScript defenitions](/index.d.ts) for more details.

Events:
* `'destroy'` - emitted when the scene is destroyed.
* `'load'` - emitted when the scene is fully loaded.
* `'reset', textureId` - emitted when the framebuffer has been re-created.
* `'reset', textureId` - emitted when a new texture is generated.
* <ANY_EVENTS> - being an [EventEmitter](https://nodejs.org/api/events.html),
View can emit any events it is told to. Also from QML side, a special global
views can emit anything. On QML side, a special global
function `eventEmit(type, data)` is present. Using this function any event can
be generated from QML side.

---

### class Property

Access QML data. Both read and write to a QML object is possible. The object should
have it's `objectName` set, and have a property under a given key. This class can
be used to read and write QML properties from JS.
Helper to access QML data. Automates reading and writing QML objects. A QML object should
have `objectName` and the target property. The value must be serializable.

```
const { View, Property } = require('qml-raub');
...
const view = new View({ width: 800, height: 600, file: 'ui.qml' });
const x1 = new Property({ view, name: 'obj1', key: 'x1' });
x1.value = 10;
```

> Note: The value is transmitted as JSON, so it can't be too special.
Constructor:

* `Property({ view, name, key, ?value })`
* `View view` - a view, where the property resides.
* `string name` - the name of an object within QML scene.
* `string key` - property key.
* `any ?value undefined` - initial value to be set.

If `opts.value` is present, it will be sent to QML side as soon as possible.
In case the view is not yet loaded, the value will be sent upon `'load'` event.

Properties:

* `get string opts` - holds `view, name, key` (but it's a different object) for later use.
* `get/set any value` - current property value.

> NOTE: can't fetch a property while the View is still loading.
Only try to get the value after the View has emitted the `'load'` event.

See [TypeScript defenitions](/index.d.ts) for more details.

---

### class Method

Call QML method. The QML object should have it's `objectName` set,
and have a method (function) under a given key. This class can
be used to store the credentials of a QML method for multiple calls.
Helper to call a QML method. A QML object should have `objectName`
and the target method.

```
const { View, Method } = require('qml-raub');
...
const view = new View({ width: 800, height: 600, file: 'ui.qml' });
const f1 = new Method({ view, name: 'obj1', key: 'f1' });
const y = f1(a, b, c);
```

Instances of this class are actually functions on their own. Up to 10 arguments
can be used for the call. Functions may immediately return a value.

> NOTE: can't fetch a return value while the View is still loading.
Such a call will always return `null`, however the function will still
be called right after the View emits the `'load'` event.


Constructor:

* `Method({ view, name, key })`
* `View view` - a view, where the property resides.
* `string name` - the name of an object within QML scene.
* `string key` - property key.


Properties:

* `get string opts` - holds `view, name, key` (but it's a different object) for later use.
8 changes: 1 addition & 7 deletions examples/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,7 @@ const release = () => document.makeCurrent();
View.init(process.cwd(), document.platformWindow, document.platformContext);
release();

document.show();

const ui = new View({
width: document.w,
height: document.h,
file: `${__dirname}/qml/gui.qml`,
});
const ui = new View({ width: document.w, height: document.h, file: 'qml/gui.qml' });

document.on('mousedown', ui.mousedown.bind(ui));
document.on('mouseup', ui.mouseup.bind(ui));
Expand Down
56 changes: 53 additions & 3 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,16 @@ declare module "qml-raub" {
/** Send "keyup" event into the QML scene. */
keyup(e: TKeyEvent): void;

/** Load a new QML scene from file or source text. */
/**
* Load a new QML scene from file or source text.
*
* @param opts either `{ file: string }` or `{ source: string }`.
*
* @description
*
* Can load QML from `file` (by path), or directly from `source` string.
* The old scene will be discarded, if any. A new texture will be created.
*/
load(opts: TOptsLoad): void;

/** Unload the current QML scene. */
Expand All @@ -114,7 +123,36 @@ declare module "qml-raub" {
/** Get property value from an object in QML scene. */
get(name: string, key: string): unknown;

/** Initialize the QML engine. */
/**
* Initialize the QML engine.
*
* @param cwd base directory for QML file resolution. Usually, `process.cwd()`.
* @param wnd platform window handle (e.g. HWND on Windows).
* @param ctx the OpenGL context to which the QML render texture will be made available.
*
* @see [OpenGL context](https://www.khronos.org/opengl/wiki/OpenGL_Context).
*
* @description
* QML has a dedicated OpenGL context because of the renderer-specific requirements.
* The QML render textures are available to the main application's OpenGL context as well.
*
* With [glfw-raub](https://github.com/node-3d/glfw-raub), `hwnd` and `ctx` can
* be obtained with helpers:
*
* ```
* // window - is returned by glfw.createWindow(...) call
* const hwnd = glfw.platformWindow(window);
* const ctx = glfw.platformContext(window);
* ```
*
* or
*
* ```
* // window - is an instance of glfw.Window
* const hwnd = window.platformWindow;
* const ctx = window.platformContext;
* ```
*/
static init(cwd: string, wnd: number, ctx: number): void;

/** Register a QML "library" directory. */
Expand Down Expand Up @@ -159,7 +197,13 @@ declare module "qml-raub" {
value?: T;
}>;

export class Property<T> {
/**
* QML Property interoperation helper.
*
* Automates reading and writing QML objects. A QML object should
* have `objectName` and the target property. The value must be serializable.
*/
export class Property<T = any> {
constructor(opts: TOptsProperty<T>);

get value(): T | null;
Expand All @@ -179,5 +223,11 @@ declare module "qml-raub" {
new(opts: TOptsMethod): (...args: unknown[]) => unknown;
}

/**
* QML Method interoperation helper.
*
* Automates reading and writing QML objects. A QML object should
* have `objectName` and the target property. The value must be serializable.
*/
export const Method: TNewableMethod;
}

0 comments on commit 21b7b8a

Please sign in to comment.