Compare commits

...

2 commits

Author SHA1 Message Date
382b9d65d9 update bindings 2026-05-07 09:40:35 -07:00
9b975004a8 update milsko upstream 2026-05-07 09:39:56 -07:00
32 changed files with 293 additions and 49 deletions

View file

@ -41,6 +41,7 @@
"deps/milsko/src/widget/button.c",
"deps/milsko/src/widget/separator.c",
"deps/milsko/src/widget/numberentry.c",
"deps/milsko/src/widget/tab.c",
"deps/milsko/src/widget/subwindow.c",
"deps/milsko/src/widget/image.c",
"deps/milsko/src/widget/submenu.c",
@ -56,6 +57,7 @@
"deps/milsko/src/widget/combobox.c",
"deps/milsko/src/widget/checkbox.c",
"deps/milsko/src/widget/entry.c",
"deps/milsko/src/widget/calendar.c",
"deps/milsko/src/widget/window.c",
"deps/milsko/src/cursor/default.c",
"deps/milsko/src/cursor/arrow.c",
@ -97,6 +99,24 @@
"deps/milsko/src/icon/back.c",
"deps/milsko/src/icon/computer.c",
"deps/milsko/src/color.c",
"deps/milsko/external/stb_truetype.c",
"deps/milsko/external/stb_image.c",
"deps/milsko/external/stb_ds.c",
"deps/milsko/external/libz/src/inflate.c",
"deps/milsko/external/libz/src/compress.c",
"deps/milsko/external/libz/src/deflate.c",
"deps/milsko/external/libz/src/gzread.c",
"deps/milsko/external/libz/src/crc32.c",
"deps/milsko/external/libz/src/infback.c",
"deps/milsko/external/libz/src/zutil.c",
"deps/milsko/external/libz/src/gzlib.c",
"deps/milsko/external/libz/src/inftrees.c",
"deps/milsko/external/libz/src/uncompr.c",
"deps/milsko/external/libz/src/gzwrite.c",
"deps/milsko/external/libz/src/trees.c",
"deps/milsko/external/libz/src/gzclose.c",
"deps/milsko/external/libz/src/inffast.c",
"deps/milsko/external/libz/src/adler32.c",
],
"include_dirs": [
"deps/milsko/include",

2
deps/milsko vendored

@ -1 +1 @@
Subproject commit 49294dc975ea17a33726069e4d9803ba228e66ff
Subproject commit 37c3f9cbdc32618abb64209232d81dcc4bb432b8

View file

@ -1,14 +1,14 @@
// @ts-ignore
import binding from "node-gyp-build";
import addon from "node-gyp-build";
import { join } from "node:path";
import {
import type {
BaseWidgetConstructor,
MwNativeClasses,
PixmapConstructor
} from "./types";
export const BaseWidget: BaseWidgetConstructor = binding.BaseWidget;
export const NativeClasses: MwNativeClasses = binding.Classes;
export const Pixmap: PixmapConstructor = binding.Pixmap;
const binding = addon(join(__dirname, ".."));
export default binding(join(__dirname, ".."));
export const BaseWidget: BaseWidgetConstructor = binding.BaseWidget;
export const NativeClasses: MwNativeClasses = binding.NativeClasses;
export const Pixmap: PixmapConstructor = binding.Pixmap;

View file

@ -5,7 +5,7 @@ export type MwClass = object & { __brand: "MwClass" };
/**
* `BaseWidget` represents the lowest level form of widgets before the native layer.
* This class contains the fundamental native methods shared by all widgets.
* This class contains the native methods shared by all widgets.
*/
export interface BaseWidget {
area: MwRect;
@ -153,6 +153,7 @@ export interface BaseWidgetConstructor {
export interface MwNativeClasses {
Box: MwClass;
Button: MwClass;
Calendar: MwClass;
CheckBox: MwClass;
Entry: MwClass;
Frame: MwClass;
@ -164,6 +165,7 @@ export interface MwNativeClasses {
ScrollBar: MwClass;
SubMenu: MwClass;
SubWindow: MwClass;
Tab: MwClass;
Viewport: MwClass;
Window: MwClass;
ProgressBar: MwClass;
@ -194,13 +196,12 @@ export interface PixmapOptions {
}
export interface PixmapConstructor {
new (cls: BaseWidget, options?: PixmapOptions): Pixmap;
new (widget: BaseWidget, options?: PixmapOptions): Pixmap;
}
export interface WidgetOptions {
name?: string;
parent?: BaseWidget;
children?: BaseWidget[];
x?: number;
y?: number;
width?: number;

View file

@ -1,5 +1,5 @@
import { BaseWidget } from "./milsko";
import { Pixmap } from "./types";
import type { Pixmap } from "./types";
export class Widget extends BaseWidget {
public setX(value: number): this {

View file

@ -1,6 +1,6 @@
import { NativeClasses } from "../milsko";
import { Widget } from "../widget";
import { WidgetOptions } from "../types";
import type { WidgetOptions } from "../types";
export class Box extends Widget {
constructor(options: WidgetOptions) {
@ -66,4 +66,16 @@ export class Box extends Widget {
set inverted(value: boolean) {
this.setInteger("Iinverted", +value);
}
public setWaitLayout(value: number): this {
return this.setInteger("IwaitLayout", value);
}
get waitLayout(): number {
return this.getInteger("IwaitLayout");
}
set waitLayout(value: number) {
this.setInteger("IwaitLayout", value);
}
}

View file

@ -1,6 +1,6 @@
import { NativeClasses } from "../milsko";
import { Widget } from "../widget";
import { WidgetOptions, Pixmap } from "../types";
import type { WidgetOptions, Pixmap } from "../types";
export class Button extends Widget {
constructor(options: WidgetOptions) {

69
lib/widgets/calendar.ts Normal file
View file

@ -0,0 +1,69 @@
import { NativeClasses } from "../milsko";
import { Widget } from "../widget";
import type { WidgetOptions } from "../types";
export class Calendar extends Widget {
constructor(options: WidgetOptions) {
super(NativeClasses.Calendar, options);
}
public setYear(value: number): this {
return this.setInteger("Iyear", value);
}
get year(): number {
return this.getInteger("Iyear");
}
set year(value: number) {
this.setInteger("Iyear", value);
}
public setMonth(value: number): this {
return this.setInteger("Imonth", value);
}
get month(): number {
return this.getInteger("Imonth");
}
set month(value: number) {
this.setInteger("Imonth", value);
}
public setDate(value: number): this {
return this.setInteger("Idate", value);
}
get date(): number {
return this.getInteger("Idate");
}
set date(value: number) {
this.setInteger("Idate", value);
}
public setDay(value: number): this {
return this.setInteger("Iday", value);
}
get day(): number {
return this.getInteger("Iday");
}
set day(value: number) {
this.setInteger("Iday", value);
}
public setScale(value: number): this {
return this.setInteger("Iscale", value);
}
get scale(): number {
return this.getInteger("Iscale");
}
set scale(value: number) {
this.setInteger("Iscale", value);
}
}

View file

@ -1,6 +1,6 @@
import { NativeClasses } from "../milsko";
import { Widget } from "../widget";
import { WidgetOptions } from "../types";
import type { WidgetOptions } from "../types";
export class CheckBox extends Widget {
constructor(options: WidgetOptions) {

View file

@ -1,6 +1,6 @@
import { NativeClasses } from "../milsko";
import { Widget } from "../widget";
import { WidgetOptions } from "../types";
import type { WidgetOptions } from "../types";
export class ComboBox extends Widget {
constructor(options: WidgetOptions) {

View file

@ -1,6 +1,6 @@
import { NativeClasses } from "../milsko";
import { Widget } from "../widget";
import { WidgetOptions } from "../types";
import type { WidgetOptions } from "../types";
export class Entry extends Widget {
constructor(options: WidgetOptions) {

View file

@ -1,6 +1,6 @@
import { NativeClasses } from "../milsko";
import { Widget } from "../widget";
import { WidgetOptions } from "../types";
import type { WidgetOptions } from "../types";
export class Frame extends Widget {
constructor(options: WidgetOptions) {

View file

@ -1,6 +1,6 @@
import { NativeClasses } from "../milsko";
import { Widget } from "../widget";
import { WidgetOptions, Pixmap } from "../types";
import type { WidgetOptions, Pixmap } from "../types";
export class Image extends Widget {
constructor(options: WidgetOptions) {

View file

@ -1,5 +1,6 @@
export { Box } from "./box";
export { Button } from "./button";
export { Calendar } from "./calendar";
export { CheckBox } from "./checkbox";
export { Entry } from "./entry";
export { Frame } from "./frame";
@ -17,3 +18,4 @@ export { RadioBox } from "./radiobox";
export { ComboBox } from "./combobox";
export { TreeView } from "./treeview";
export { SubWindow } from "./subwindow";
export { Tab } from "./tab";

View file

@ -1,6 +1,6 @@
import { NativeClasses } from "../milsko";
import { Widget } from "../widget";
import { WidgetOptions } from "../types";
import type { WidgetOptions } from "../types";
export class Label extends Widget {
constructor(options: WidgetOptions) {

View file

@ -1,6 +1,6 @@
import { NativeClasses } from "../milsko";
import { Widget } from "../widget";
import { WidgetOptions } from "../types";
import type { WidgetOptions } from "../types";
export class ListBox extends Widget {
constructor(options: WidgetOptions) {

View file

@ -1,6 +1,6 @@
import { NativeClasses } from "../milsko";
import { Widget } from "../widget";
import { WidgetOptions } from "../types";
import type { WidgetOptions } from "../types";
export class Menu extends Widget {
constructor(options: WidgetOptions) {

View file

@ -1,6 +1,6 @@
import { NativeClasses } from "../milsko";
import { Widget } from "../widget";
import { WidgetOptions } from "../types";
import type { WidgetOptions } from "../types";
export class NumberEntry extends Widget {
constructor(options: WidgetOptions) {

View file

@ -1,6 +1,6 @@
import { NativeClasses } from "../milsko";
import { Widget } from "../widget";
import { WidgetOptions } from "../types";
import type { WidgetOptions } from "../types";
export class ProgressBar extends Widget {
constructor(options: WidgetOptions) {

View file

@ -1,6 +1,6 @@
import { NativeClasses } from "../milsko";
import { Widget } from "../widget";
import { WidgetOptions } from "../types";
import type { WidgetOptions } from "../types";
export class RadioBox extends Widget {
constructor(options: WidgetOptions) {

View file

@ -1,6 +1,6 @@
import { NativeClasses } from "../milsko";
import { Widget } from "../widget";
import { WidgetOptions } from "../types";
import type { WidgetOptions } from "../types";
export class ScrollBar extends Widget {
constructor(options: WidgetOptions) {

View file

@ -1,6 +1,6 @@
import { NativeClasses } from "../milsko";
import { Widget } from "../widget";
import { WidgetOptions } from "../types";
import type { WidgetOptions } from "../types";
export class SubMenu extends Widget {
constructor(options: WidgetOptions) {

View file

@ -1,6 +1,6 @@
import { NativeClasses } from "../milsko";
import { Widget } from "../widget";
import { WidgetOptions, Pixmap } from "../types";
import type { WidgetOptions, Pixmap } from "../types";
export class SubWindow extends Widget {
constructor(options: WidgetOptions) {

21
lib/widgets/tab.ts Normal file
View file

@ -0,0 +1,21 @@
import { NativeClasses } from "../milsko";
import { Widget } from "../widget";
import type { WidgetOptions } from "../types";
export class Tab extends Widget {
constructor(options: WidgetOptions) {
super(NativeClasses.Tab, options);
}
public setValue(value: number): this {
return this.setInteger("Ivalue", value);
}
get value(): number {
return this.getInteger("Ivalue");
}
set value(value: number) {
this.setInteger("Ivalue", value);
}
}

View file

@ -1,6 +1,6 @@
import { NativeClasses } from "../milsko";
import { Widget } from "../widget";
import { WidgetOptions } from "../types";
import type { WidgetOptions } from "../types";
export class TreeView extends Widget {
constructor(options: WidgetOptions) {

View file

@ -1,6 +1,6 @@
import { NativeClasses } from "../milsko";
import { Widget } from "../widget";
import { WidgetOptions } from "../types";
import type { WidgetOptions } from "../types";
export class Viewport extends Widget {
constructor(options: WidgetOptions) {

View file

@ -1,7 +1,7 @@
import { NativeClasses } from "../milsko";
import { Widget } from "../widget";
import { MwSizeHints } from "../structs";
import { WidgetOptions, Pixmap } from "../types";
import type { MwSizeHints } from "../structs";
import type { WidgetOptions, Pixmap } from "../types";
export class Window extends Widget {
constructor(options: WidgetOptions) {

View file

@ -1,8 +1,8 @@
{
"name": "milsko",
"version": "1.0.1",
"version": "1.2",
"description": "Lightweight and cross-platform native GUI toolkit",
"upstream": "ea20a31694",
"upstream": "v1.2",
"main": "dist/index.js",
"scripts": {
"install": "node-gyp-build",

View file

@ -218,7 +218,7 @@ function scanProperties(root) {
widgetOut.write(
"import { BaseWidget } from \"./milsko\"\n" +
"import { Pixmap } from \"./types\"\n\n" +
"import type { Pixmap } from \"./types\"\n\n" +
"export class Widget extends BaseWidget {\n"
);
@ -287,13 +287,13 @@ function scanWidgets(root) {
if (structImports.length) {
widgetFile.write(
"import { " + structImports.join(", ") +
"import type { " + structImports.join(", ") +
" } from \"../structs\"\n"
);
}
widgetFile.write(
"import { " + typeImports.join(", ") +
"import type { " + typeImports.join(", ") +
" } from \"../types\"\n"
);

View file

@ -9,6 +9,7 @@ Napi::Object ClassesInit(Napi::Env env, Napi::Object exports) {
classes.Set("Box", NAPI_MWCLASS(env, MwBoxClass));
classes.Set("Button", NAPI_MWCLASS(env, MwButtonClass));
classes.Set("Calendar", NAPI_MWCLASS(env, MwCalendarClass));
classes.Set("CheckBox", NAPI_MWCLASS(env, MwCheckBoxClass));
classes.Set("Entry", NAPI_MWCLASS(env, MwEntryClass));
classes.Set("Frame", NAPI_MWCLASS(env, MwFrameClass));
@ -20,6 +21,7 @@ Napi::Object ClassesInit(Napi::Env env, Napi::Object exports) {
classes.Set("ScrollBar", NAPI_MWCLASS(env, MwScrollBarClass));
classes.Set("SubMenu", NAPI_MWCLASS(env, MwSubMenuClass));
classes.Set("SubWindow", NAPI_MWCLASS(env, MwSubWindowClass));
classes.Set("Tab", NAPI_MWCLASS(env, MwTabClass));
classes.Set("Viewport", NAPI_MWCLASS(env, MwViewportClass));
classes.Set("Window", NAPI_MWCLASS(env, MwWindowClass));
classes.Set("ProgressBar", NAPI_MWCLASS(env, MwProgressBarClass));

View file

@ -1,2 +0,0 @@
#include <napi.h>

View file

@ -18,7 +18,7 @@ Napi::Object MwBaseWidget::Init(Napi::Env env, Napi::Object exports) {
InstanceMethod("addTickList", &MwBaseWidget::AddTickList),
InstanceMethod("forceRender", &MwBaseWidget::ForceRender),
InstanceMethod("reparent", &MwBaseWidget::Reparent),
InstanceMethod("add", &MwBaseWidget::AddChild),
InstanceMethod("addChild", &MwBaseWidget::AddChild),
InstanceMethod("destroy", &MwBaseWidget::Destroy),
InstanceMethod("getClipboard", &MwBaseWidget::GetClipboard),
@ -52,17 +52,138 @@ Napi::ObjectWrap<MwBaseWidget>(info) {
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
handle = MwCreateWidget(MwWindowClass, "window", nullptr, 0, 0, 300, 300);
if (info.Length() < 1 || !info[0].IsExternal()) {
Napi::TypeError::New(env, "Expected a MwClass as first argument")
.ThrowAsJavaScriptException();
return;
}
MwClass cls = info[0].As<Napi::External<_MwClass>>().Data();
std::string name = "widget"; // name is unused anyways
MwBaseWidget *parent = nullptr;
int xPos = 0;
int yPos = 0;
unsigned int width = 0;
unsigned int height = 0;
if (info.Length() > 1 && info[1].IsObject()) {
Napi::Object params = info[1].As<Napi::Object>();
if (params.Has("name")) {
Napi::Value nameParam = params.Get("name");
if (nameParam.IsString()) {
name = nameParam.As<Napi::String>().Utf8Value();
} else {
Napi::TypeError::New(env, "The name parameter must be a string")
.ThrowAsJavaScriptException();
return;
}
}
if (params.Has("parent")) {
Napi::Value parentParam = params.Get("parent");
if (parentParam.IsObject()) {
Napi::Object parentObj = parentParam.As<Napi::Object>();
bool isInstance = parentObj.InstanceOf(constructor.Value());
if (isInstance) {
parent = Unwrap(parentObj);
} else {
Napi::TypeError::New(env, "The parent parameter is not an instance of a widget")
.ThrowAsJavaScriptException();
return;
}
} else {
Napi::TypeError::New(env, "The parent parameter must be an instance of a widget")
.ThrowAsJavaScriptException();
return;
}
}
if (params.Has("x")) {
Napi::Value xParam = params.Get("x");
if (xParam.IsNumber()) {
xPos = xParam.As<Napi::Number>().Int32Value();
} else {
Napi::TypeError::New(env, "The x parameter must be a number")
.ThrowAsJavaScriptException();
return;
}
}
if (params.Has("y")) {
Napi::Value yParam = params.Get("y");
if (yParam.IsNumber()) {
yPos = yParam.As<Napi::Number>().Int32Value();
} else {
Napi::TypeError::New(env, "The y parameter must be a number")
.ThrowAsJavaScriptException();
return;
}
}
if (params.Has("width")) {
Napi::Value widthParam = params.Get("width");
if (widthParam.IsNumber()) {
width = widthParam.As<Napi::Number>().Uint32Value();
} else {
Napi::TypeError::New(env, "The width parameter must be a number")
.ThrowAsJavaScriptException();
return;
}
}
if (params.Has("height")) {
Napi::Value heightParam = params.Get("height");
if (heightParam.IsNumber()) {
height = heightParam.As<Napi::Number>().Uint32Value();
} else {
Napi::TypeError::New(env, "The height parameter must be a number")
.ThrowAsJavaScriptException();
return;
}
}
}
handle = MwCreateWidget(
cls,
name.c_str(),
parent ? parent->handle : nullptr,
xPos, yPos,
width, height
);
if (!handle) {
Napi::Error::New(env, "A native exception occured when creating the widget")
.ThrowAsJavaScriptException();
return;
}
if (parent) {
parent->children.push_back(this);
this->parent = parent;
}
};
MwBaseWidget::~MwBaseWidget() {
// call destructors for each children
if (children.size() > 0) {
for (const auto &child : children) {
delete child;
for (size_t i = 0; i < children.size(); i++) {
delete children[i];
}
}
if (parent) {
for (size_t i = 0; i < parent->children.size(); i++) {
if (parent->children[i] == this) {
parent->children.erase(parent->children.begin() + i);
break;
}
}
}
if (handle)
MwDestroyWidget(handle);
};
@ -249,26 +370,24 @@ Napi::Value MwBaseWidget::Reparent(const Napi::CallbackInfo &info) {
Napi::Value MwBaseWidget::AddChild(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
if (info.Length() < 1 || !info[0].IsArray()) {
Napi::TypeError::New(env, "Expected an instance of a widget class as argument")
if (info.Length() < 1) {
Napi::TypeError::New(env, "Expected at least one instance of a widget class as argument")
.ThrowAsJavaScriptException();
return env.Undefined();
}
Napi::Array widgets = info[0].As<Napi::Array>();
for (uint32_t i = 0; i < widgets.Length(); i++) {
const Napi::Value value = widgets.Get(i);
for (size_t i = 0; i < info.Length(); i++) {
const Napi::Value value = info[i];
if (!value.IsObject()) {
Napi::TypeError::New(env,
"Expected argument " + std::to_string(i) + "to be instance of a widget"
"Expected argument " + std::to_string(i) + "to be an instance of a widget"
)
.ThrowAsJavaScriptException();
return env.Null();
}
const Napi::Object wrapped = widgets.As<Napi::Object>();
const Napi::Object wrapped = value.As<Napi::Object>();
const bool isInstance = wrapped.InstanceOf(constructor.Value());
if (isInstance) {