diff --git a/examples/event-loop.js b/examples/event-loop.js index 9494853..f9202fe 100644 --- a/examples/event-loop.js +++ b/examples/event-loop.js @@ -58,9 +58,9 @@ libui.startLoop(); function logAppend(line) { const lines = log.text.split('\n'); if (lines.length > 25) { - log.text = lines.slice(1).join(os.EOL); + log.text = lines.slice(1).join("\n"); } - log.append(line + os.EOL); + log.append(line + "\n"); } function setIntervalChanged() { @@ -116,7 +116,10 @@ function makeToolbar() { let timeoutHandle = null; btnCustom.onClicked(() => { if (timeoutHandle) { + console.log('before clearTimeout', timeoutHandle); clearTimeout(timeoutHandle); + console.log('after clearTimeout', timeoutHandle); + timeoutHandle = null; return; } @@ -125,7 +128,9 @@ function makeToolbar() { timeoutHandle = setTimeout((a, b, c) => { const elapsed = Date.now() - now; logAppend(`Custom setTimeout: ${now} - elapsed ${elapsed} ms. Args: ${a} ${b} ${c}`); + timeoutHandle = null; }, 1000, 'custom', 'args', 2); + console.log('after timeoutHandle', timeoutHandle); }); toolbar.append(btnCustom, false); diff --git a/examples/example-from-napi.js b/examples/example-from-napi.js new file mode 100644 index 0000000..a34e7fc --- /dev/null +++ b/examples/example-from-napi.js @@ -0,0 +1,85 @@ +const libui = require('..'); + +const {UiWindow, UiHorizontalBox, UiMultilineEntry} = libui; + +libui.onShouldQuit(() => { + libui.stopLoop(); + global.gc(); +}); + +function createWindow() { + let win = new UiWindow('Test Window', 800, 600, false); + win.margined = true; + const logEntry = new UiMultilineEntry(); + + const entry = new UiMultilineEntry(); + entry.text = 'A test line\n'; + entry.append('A second test line\n'); + entry.onChanged(() => { + const msg = `Text changed to ${entry.text}`; + console.log(msg); + logEntry.append(msg + '\n'); + }); + + const box = new UiHorizontalBox(); + box.setPadded(true); + box.append(entry, true); + box.append(logEntry, true); + + win.setChild(box); + + win.onContentSizeChanged(() => { + const size = win.getContentSize(); + console.log(`size changed to ${size.width}x${size.height}`); + }); + let step = 0; + win.onClosing(() => { + if (win.getTitle() == 'Test Window') { + let interval = setInterval(() => { + if (step === 0) { + win.contentSize = {width: 400, height: 300}; + } + if (step === 1) { + win.margined = true; + } + if (step === 2) { + win.margined = false; + win.fullscreen = true; + } + if (step === 3) { + win.fullscreen = false; + win.borderless = true; + } + if (step === 4) { + win.borderless = false; + } + + if (step > 4) { + clearInterval(interval); + } + + step++; + + console.log({ + Margined: win.margined, + Fullscreen: win.fullscreen, + Borderless: win.borderless, + }); + }, 1000); + box.deleteAt(1); + return win.title = 'Wait some seconds please...'; + } + console.log('closing', win.title); + win.close(); + win = null; + libui.stopLoop(); + }); + + win.show(); +} + +createWindow(); +libui.startLoop(); +setInterval(() => { + global.gc(); +}, 10); diff --git a/index.js b/index.js index 12b140d..a8a4a04 100644 --- a/index.js +++ b/index.js @@ -50,44 +50,59 @@ function startLoop() { asyncHook.enable(); setTimeoutNode = global.setTimeout; - global.setTimeout = function(cb, t) { + global.setTimeout = function(cb, timeout) { + const timeoutHandle = {running: true}; + const args = Array.prototype.slice.call(arguments, 2); - return binding.lib.setTimeout(function() { - cb.apply(null, args); - }, t); - }; + binding.lib.Ui.startTimer(timeout, function() { + // console.log('timeoutHandle.running ', timeoutHandle.running); + if (timeoutHandle.running) { + cb.apply(null, args); + } + return false; + }); + return timeoutHandle; + } clearTimeoutNode = global.clearTimeout; - global.clearTimeout = function(obj) { - if (obj && obj.constructor === binding.lib.TimeoutHandle) { + + global.clearTimeout = function(timeoutHandle) { + if (timeoutHandle && typeof timeoutHandle.running === 'boolean') { + timeoutHandle.running = false; // console.log('patched clearTimeout called'); - binding.lib.clearTimeout(obj); } else { // console.log('node clearTimeout called'); // not created by us, use original // clearTimeoutNode - clearTimeoutNode(obj); + clearTimeoutNode(timeoutHandle); } }; setIntervalNode = global.setInterval; - global.setInterval = function(cb, t) { + global.setInterval = function(cb, timeout) { + const timeoutHandle = {running: true}; const args = Array.prototype.slice.call(arguments, 2); - return binding.lib.setInterval(function() { - cb.apply(null, args); - }, t); - }; + binding.lib.Ui.startTimer(timeout, function() { + if (timeoutHandle.running) { + cb.apply(null, args); + return true; + } + return false; + }); + return timeoutHandle; + } clearIntervalNode = global.clearInterval; - global.clearInterval = function(obj) { - if (obj && obj.constructor === binding.lib.TimeoutHandle) { + + global.clearInterval = function(timeoutHandle) { + if (timeoutHandle && typeof timeoutHandle.running === 'boolean') { + timeoutHandle.running = false; // console.log('patched clearInterval called'); - binding.lib.clearInterval(obj); } else { // console.log('node clearInterval called'); // not created by us, use original // clearTimeoutNode - clearIntervalNode(obj); + clearIntervalNode(timeoutHandle); } } } @@ -344,6 +359,24 @@ binding.lib.FontAttribute.prototype.getOTFeatures = function() { return this.getOTFeaturesInternal(); }; +binding.lib.UiDialogs.openFile = function(parent) { + const v = binding.lib.UiDialogs.openFileInternal(parent); + if (v) { + return v; + } else { + return null; + } +}; + +binding.lib.UiDialogs.saveFile = function(parent) { + const v = binding.lib.UiDialogs.saveFileInternal(parent); + if (v) { + return v; + } else { + return null; + } +}; + const brushType = { solid: 0, linearGradient: 1, diff --git a/package-lock.json b/package-lock.json index 75a7eaf..00207eb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "libui-node", - "version": "0.1.0", + "version": "0.3.0-rc1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index c421ee1..5fdda57 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "libui-node", - "version": "0.2.0-rc1", + "version": "0.3.0-rc1", "description": "Node.js bindings for libui", "repository": "parro-it/libui-node", "license": "MIT", diff --git a/src/Ui.cc b/src/Ui.cc index 5342ead..9563d57 100644 --- a/src/Ui.cc +++ b/src/Ui.cc @@ -10,6 +10,11 @@ static int onShouldQuit_cb(void *data) { return 0; } +static int uiTimer_cb(void *data) { + nbind::cbFunction *cb = (nbind::cbFunction *)data; + return cb->call(); +} + struct Ui { static void main() { uiMain(); @@ -40,6 +45,11 @@ struct Ui { uiFreeInitError(err); } } + + static void startTimer(int ms, nbind::cbFunction &cb) { + nbind::cbFunction *callbackJs = new nbind::cbFunction(cb); + uiTimer(ms, uiTimer_cb, callbackJs); + } }; NBIND_CLASS(Ui) { @@ -49,4 +59,5 @@ NBIND_CLASS(Ui) { method(mainStep); method(mainSteps); method(onShouldQuit); + method(startTimer); } diff --git a/src/UiWindow.cc b/src/UiWindow.cc index d6ed484..deaf91a 100644 --- a/src/UiWindow.cc +++ b/src/UiWindow.cc @@ -163,18 +163,26 @@ NBIND_CLASS(UiWindow) { struct UiDialogs { - static std::string openFile(UiWindow *parent) { + static std::string openFileInternal(UiWindow *parent) { char *char_ptr = uiOpenFile(parent->getHandle()); - std::string s(char_ptr); - uiFreeText(char_ptr); - return s; + if (char_ptr == NULL) { + return std::string(""); + } else { + std::string s(char_ptr); + uiFreeText(char_ptr); + return s; + } } - static std::string saveFile(UiWindow *parent) { + static std::string saveFileInternal(UiWindow *parent) { char *char_ptr = uiSaveFile(parent->getHandle()); - std::string s(char_ptr); - uiFreeText(char_ptr); - return s; + if (char_ptr == NULL) { + return std::string(""); + } else { + std::string s(char_ptr); + uiFreeText(char_ptr); + return s; + } } static void msgBox(UiWindow *parent, std::string title, @@ -189,8 +197,8 @@ struct UiDialogs { }; NBIND_CLASS(UiDialogs) { - method(openFile); - method(saveFile); + method(openFileInternal); + method(saveFileInternal); method(msgBox); method(msgBoxError); } diff --git a/src/arch/darwin/timer.mm b/src/arch/darwin/timer.mm deleted file mode 100644 index 43537b6..0000000 --- a/src/arch/darwin/timer.mm +++ /dev/null @@ -1,40 +0,0 @@ -#include "timer.h" -#include "nbind/api.h" -#import - -TimeoutHandle *setTimeout(nbind::cbFunction &cb, unsigned int timeout) { - nbind::cbFunction *callbackJs = new nbind::cbFunction(cb); - TimeoutHandle *timeoutHandle = new TimeoutHandle(callbackJs); - timeoutHandle->handle = - [NSTimer scheduledTimerWithTimeInterval:timeout / 1000.0 - repeats:NO - block:^(NSTimer *timer) { - CALL_JSCB(timeoutHandle); - timeoutHandle->destroy(); - }]; - return timeoutHandle; -} - -void clearTimeout(TimeoutHandle *timeoutHandle) { - NSTimer *handle = (NSTimer *)timeoutHandle->handle; - [handle invalidate]; - timeoutHandle->destroy(); -} - -TimeoutHandle *setInterval(nbind::cbFunction &cb, unsigned int timeout) { - nbind::cbFunction *callbackJs = new nbind::cbFunction(cb); - TimeoutHandle *timeoutHandle = new TimeoutHandle(callbackJs); - timeoutHandle->handle = - [NSTimer scheduledTimerWithTimeInterval:timeout / 1000.0 - repeats:YES - block:^(NSTimer *timer) { - CALL_JSCB(timeoutHandle); - }]; - return timeoutHandle; -} - -void clearInterval(TimeoutHandle *timeoutHandle) { - NSTimer *handle = (NSTimer *)timeoutHandle->handle; - [handle invalidate]; - timeoutHandle->destroy(); -} diff --git a/src/arch/unix/timer.cc b/src/arch/unix/timer.cc deleted file mode 100644 index 5b0347d..0000000 --- a/src/arch/unix/timer.cc +++ /dev/null @@ -1,39 +0,0 @@ -#include "timer.h" -#include "nbind/api.h" - -gboolean glib_timeout_cb(TimeoutHandle *timeoutHandle) { - CALL_JSCB(timeoutHandle); - timeoutHandle->destroy(); - return FALSE; -} - -gboolean glib_interval_cb(TimeoutHandle *timeoutHandle) { - CALL_JSCB(timeoutHandle); - return TRUE; -} - -TimeoutHandle *setTimeout(nbind::cbFunction &cb, unsigned int timeout) { - nbind::cbFunction *callbackJs = new nbind::cbFunction(cb); - TimeoutHandle *timeoutHandle = new TimeoutHandle(callbackJs); - timeoutHandle->handle = - g_timeout_add(timeout, (GSourceFunc)glib_timeout_cb, timeoutHandle); - return timeoutHandle; -} - -void clearTimeout(TimeoutHandle *timeoutHandle) { - g_source_remove(timeoutHandle->handle); - timeoutHandle->destroy(); -} - -TimeoutHandle *setInterval(nbind::cbFunction &cb, unsigned int timeout) { - nbind::cbFunction *callbackJs = new nbind::cbFunction(cb); - TimeoutHandle *timeoutHandle = new TimeoutHandle(callbackJs); - timeoutHandle->handle = - g_timeout_add(timeout, (GSourceFunc)glib_interval_cb, timeoutHandle); - return timeoutHandle; -} - -void clearInterval(TimeoutHandle *timeoutHandle) { - g_source_remove(timeoutHandle->handle); - timeoutHandle->destroy(); -} diff --git a/src/arch/win32/timer.cc b/src/arch/win32/timer.cc deleted file mode 100644 index af8fd90..0000000 --- a/src/arch/win32/timer.cc +++ /dev/null @@ -1,52 +0,0 @@ -#include -#include "nbind/api.h" -#include "timer.h" - -static std::map timersMap; - -void killTimer(TimeoutHandle *timeoutHandle) { - KillTimer(NULL, timeoutHandle->handle); - timersMap.erase(timeoutHandle->handle); - timeoutHandle->destroy(); -} - -void CALLBACK win_timeout_cb(HWND hwnd, UINT uMsg, UINT_PTR idEvent, - DWORD dwTime) { - TimeoutHandle *timeoutHandle = timersMap[idEvent]; - CALL_JSCB(timeoutHandle); - killTimer(timeoutHandle); -} - -void CALLBACK win_interval_cb(HWND hwnd, UINT uMsg, UINT_PTR idEvent, - DWORD dwTime) { - TimeoutHandle *timeoutHandle = timersMap[idEvent]; - CALL_JSCB(timeoutHandle); -} - -TimeoutHandle *setTimeout(nbind::cbFunction &cb, unsigned int timeout) { - nbind::cbFunction *callbackJs = new nbind::cbFunction(cb); - TimeoutHandle *timeoutHandle = new TimeoutHandle(callbackJs); - // SetTimer could work with a NULL window. - // https://stackoverflow.com/questions/7531650/can-i-use-a-settimer-api-in-a-console-c-application - timeoutHandle->handle = SetTimer(NULL, NULL, timeout, win_timeout_cb); - timersMap[timeoutHandle->handle] = timeoutHandle; - return timeoutHandle; -} - -void clearTimeout(TimeoutHandle *timeoutHandle) { - if (!timeoutHandle->destroyed) { - killTimer(timeoutHandle); - } -} - -TimeoutHandle *setInterval(nbind::cbFunction &cb, unsigned int timeout) { - nbind::cbFunction *callbackJs = new nbind::cbFunction(cb); - TimeoutHandle *timeoutHandle = new TimeoutHandle(callbackJs); - timeoutHandle->handle = SetTimer(NULL, NULL, timeout, win_interval_cb); - timersMap[timeoutHandle->handle] = timeoutHandle; - return timeoutHandle; -} - -void clearInterval(TimeoutHandle *timeoutHandle) { - killTimer(timeoutHandle); -} diff --git a/src/timer-common.cc b/src/timer-common.cc deleted file mode 100644 index 1bf5954..0000000 --- a/src/timer-common.cc +++ /dev/null @@ -1,27 +0,0 @@ -#include "nbind/api.h" -#include "timer.h" - -TimeoutHandle::TimeoutHandle(nbind::cbFunction *callbackJs) { - this->callbackJs = callbackJs; - this->destroyed = false; -} - -void TimeoutHandle::destroy() { - if (this->destroyed) { - return; - } - - delete this->callbackJs; - this->destroyed = true; -} - -#include "nbind/nbind.h" - -NBIND_GLOBAL() { - function(setTimeout); - function(clearTimeout); - function(setInterval); - function(clearInterval); -} - -NBIND_CLASS(TimeoutHandle) {}