initial commit

This commit is contained in:
PwLDev 2026-04-28 13:11:03 -07:00
commit 18255ce4cd
21 changed files with 1244 additions and 0 deletions

0
src/classes.cpp Normal file
View file

4
src/classes.hpp Normal file
View file

@ -0,0 +1,4 @@
#pragma once
#include <napi.h>
Napi::Object ClassesInit(Napi::Env env, Napi::Object exports);

13
src/milsko.cpp Normal file
View file

@ -0,0 +1,13 @@
#include "widget.hpp"
#include <Mw/Milsko.h>
#include <napi.h>
Napi::Object Init(Napi::Env env, Napi::Object exports) {
MwLibraryInit();
MwBaseWidget::Init(env, exports);
return exports;
}
NODE_API_MODULE(milsko, Init);

368
src/widget.cpp Normal file
View file

@ -0,0 +1,368 @@
#include "widget.hpp"
Napi::FunctionReference MwBaseWidget::constructor;
Napi::Object MwBaseWidget::Init(Napi::Env env, Napi::Object exports) {
Napi::HandleScope scope(env);
Napi::Function func = DefineClass(env, "MwBaseWidget",
{
InstanceMethod("setArea", &SetArea),
InstanceMethod("move", &Move),
InstanceMethod("resize", &Resize),
InstanceMethod("step", &Step),
InstanceMethod("loop", &Loop),
InstanceMethod("show", &Show),
InstanceMethod("hideCursor", &HideCursor),
InstanceMethod("grabPointer", &GrabPointer),
InstanceMethod("getClipboard", &GetClipboard),
InstanceMethod("addTickList", &AddTickList),
InstanceMethod("forceRender", &ForceRender),
InstanceMethod("reparent", &Reparent),
InstanceMethod("add", &Add),
InstanceMethod("remove", &Remove),
InstanceAccessor("area", &GetArea, nullptr),
InstanceAccessor("isPending", &IsPending, nullptr),
InstanceAccessor("parent", &GetParent, nullptr),
InstanceAccessor("children", &GetChildren, nullptr),
InstanceAccessor("name", &GetName, nullptr),
InstanceAccessor("cursorCoord", &GetCursorCoord, nullptr),
InstanceAccessor("screenSize", &GetScreenSize, nullptr),
InstanceAccessor("coordinateType", &GetCoordinateType, nullptr),
});
constructor = Napi::Persistent(func);
constructor.SuppressDestruct();
exports.Set("MwBaseWidget", func);
return exports;
};
MwBaseWidget::MwBaseWidget(const Napi::CallbackInfo &info):
Napi::ObjectWrap<MwBaseWidget>(info) {
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
handle = MwCreateWidget(MwWindowClass, "window", nullptr, 0, 0, 300, 300);
};
MwBaseWidget::~MwBaseWidget() {
// call destructors for each children
if (children.size() > 0) {
for (const auto &child : children) {
delete child;
}
}
if (handle)
MwDestroyWidget(handle);
};
Napi::Value MwBaseWidget::GetArea(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
Napi::Object area = Napi::Object::New(env);
area.Set("x", Napi::Number::New(env, MwGetInteger(handle, MwNx)));
area.Set("y", Napi::Number::New(env, MwGetInteger(handle, MwNy)));
area.Set("width", Napi::Number::New(env, MwGetInteger(handle, MwNwidth)));
area.Set("height", Napi::Number::New(env, MwGetInteger(handle, MwNheight)));
return area;
}
Napi::Value MwBaseWidget::SetArea(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
if (info.Length() > 0 && info[0].IsObject()) {
const Napi::Object rect = info[0].ToObject();
MwVaApply(handle,
MwNx, rect.Has("x") ? rect.Get("x").As<Napi::Number>().Uint32Value() : 0,
MwNy, rect.Has("y") ? rect.Get("y").As<Napi::Number>().Uint32Value() : 0,
MwNwidth, rect.Has("width") ? rect.Get("width").As<Napi::Number>().Uint32Value() : 0,
MwNheight, rect.Has("height") ? rect.Get("height").As<Napi::Number>().Uint32Value() : 0,
nullptr
);
} else {
Napi::TypeError::New(env, "Expected an object type of MwRect as argument")
.ThrowAsJavaScriptException();
}
return info.This();
}
Napi::Value MwBaseWidget::Move(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
if (info.Length() > 1 && info[0].IsNumber() && info[1].IsNumber()) {
int x = info[0].As<Napi::Number>().Uint32Value();
int y = info[1].As<Napi::Number>().Uint32Value();
MwVaApply(handle,
MwNx, x,
MwNy, y,
nullptr
);
} else {
Napi::TypeError::New(env, "Expected two number arguments (x, y)")
.ThrowAsJavaScriptException();
}
return Napi::Value();
}
Napi::Value MwBaseWidget::Resize(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
if (info.Length() > 1 && info[0].IsNumber() && info[1].IsNumber()) {
int width = info[0].As<Napi::Number>().Uint32Value();
int height = info[1].As<Napi::Number>().Uint32Value();
MwVaApply(handle,
MwNwidth, width,
MwNheight, height,
nullptr
);
} else {
Napi::TypeError::New(env, "Expected two number arguments (width, height)")
.ThrowAsJavaScriptException();
}
return Napi::Value();
}
Napi::Value MwBaseWidget::Step(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
int step = MwStep(handle);
return Napi::Boolean::New(env, step == 0);
}
Napi::Value MwBaseWidget::IsPending(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
bool pending = MwPending(handle);
return Napi::Boolean::New(env, pending);
}
Napi::Value MwBaseWidget::Loop(const Napi::CallbackInfo &info) {
MwLoop(handle);
return Napi::Value();
}
Napi::Value MwBaseWidget::Show(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
bool toggle = true;
if (info.Length() > 0 && info[0].IsBoolean()) {
toggle = info[0].As<Napi::Boolean>().Value();
}
MwShow(handle, static_cast<int>(toggle));
return Napi::Value();
}
Napi::Value MwBaseWidget::HideCursor(const Napi::CallbackInfo &info) {
MwHideCursor(handle);
return Napi::Value();
}
Napi::Value MwBaseWidget::GrabPointer(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
bool toggle = true;
if (info.Length() > 0 && info[0].IsBoolean()) {
toggle = info[0].As<Napi::Boolean>().Value();
}
MwGrabPointer(handle, static_cast<int>(toggle));
return Napi::Value();
}
Napi::Value MwBaseWidget::AddTickList(const Napi::CallbackInfo &info) {
MwAddTickList(handle);
return Napi::Value();
}
Napi::Value MwBaseWidget::ForceRender(const Napi::CallbackInfo &info) {
MwForceRender(handle);
return Napi::Value();
}
Napi::Value MwBaseWidget::Reparent(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
if (info.Length() < 1 || !info[0].IsObject() || !info[0].IsNull()) {
Napi::TypeError::New(env, "Expected an instance of a widget class as argument")
.ThrowAsJavaScriptException();
return env.Null();
}
MwBaseWidget *newParent = nullptr;
if (info[0].IsObject()) {
Napi::Object wrapped = info[0].As<Napi::Object>();
const bool isInstance = wrapped.InstanceOf(constructor.Value());
if (!isInstance) {
Napi::TypeError::New(env, "Provided argument is not an instance of a widget")
.ThrowAsJavaScriptException();
return env.Null();
}
newParent = MwBaseWidget::Unwrap(wrapped);
}
if (parent) {
// if the current parent has this widget as a child, disown :(
for (size_t i = 0; i < parent->children.size(); i++) {
if (parent->children[i] == this) {
parent->children.erase(parent->children.begin() + i);
break;
}
}
}
MwReparent(
handle,
newParent ? newParent->handle : nullptr
);
parent = newParent;
newParent->children.push_back(this);
return Napi::Value();
}
Napi::Value MwBaseWidget::Add(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
if (info.Length() < 1 || !info[0].IsObject()) {
Napi::TypeError::New(env, "Expected an instance of a widget class as argument")
.ThrowAsJavaScriptException();
return env.Undefined();
}
Napi::Object wrapped = info[0].As<Napi::Object>();
const bool isInstance = wrapped.InstanceOf(constructor.Value());
if (isInstance) {
MwBaseWidget *child = MwBaseWidget::Unwrap(wrapped);
if (child == this) {
Napi::Error::New(env, "Cannot add widget as child of itself")
.ThrowAsJavaScriptException();
return env.Null();
}
if (child->parent) {
// if the current parent has this widget as a child, disown :(
for (int i = 0; i < child->parent->children.size(); i++) {
if (child->parent->children[i] == this) {
child->parent->children.erase(child->parent->children.begin() + i);
break;
}
}
}
MwReparent(
child->handle,
handle
);
child->parent = this;
children.push_back(child);
} else {
Napi::TypeError::New(env, "Provided argument is not an instance of a widget")
.ThrowAsJavaScriptException();
return env.Null();
}
return Napi::Value();
}
Napi::Value MwBaseWidget::Remove(const Napi::CallbackInfo &info) {
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;
}
}
}
MwReparent(handle, nullptr);
return Napi::Value();
}
Napi::Value MwBaseWidget::GetParent(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
if (!parent)
return env.Null();
return parent->Value();
}
Napi::Value MwBaseWidget::GetChildren(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
Napi::Array result = Napi::Array::New(env, children.size());
for (size_t i = 0; i < children.size(); i++) {
result.Set(i, children[i]->Value());
}
return result;
}
Napi::Value MwBaseWidget::GetName(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
const std::string name = MwGetName(handle);
return Napi::String::New(env, name);
}
Napi::Value MwBaseWidget::GetCursorCoord(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
Napi::Object coord = Napi::Object::New(env);
MwPoint point;
MwGetCursorCoord(handle, &point);
coord.Set("x", Napi::Number::New(env, point.x));
coord.Set("y", Napi::Number::New(env, point.y));
return coord;
}
Napi::Value MwBaseWidget::GetScreenSize(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
Napi::Object size = Napi::Object::New(env);
MwRect rect;
MwGetScreenSize(handle, &rect);
size.Set("x", Napi::Number::New(env, rect.x));
size.Set("y", Napi::Number::New(env, rect.y));
size.Set("width", Napi::Number::New(env, rect.width));
size.Set("height", Napi::Number::New(env, rect.height));
return size;
}
Napi::Value MwBaseWidget::GetCoordinateType(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
int coordType = MwGetCoordinateType(handle);
return Napi::Number::New(env, coordType);
}
Napi::Value MwBaseWidget::GetClipboard(const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
if (info.Length() > 0 && info[0].IsNumber()) {
int type = info[0].As<Napi::Number>().Uint32Value();
MwGetClipboard(handle, type);
} else {
Napi::TypeError::New(env, "Expected a clipboard type number argument.")
.ThrowAsJavaScriptException();
}
return env.Undefined();
}

42
src/widget.hpp Normal file
View file

@ -0,0 +1,42 @@
#pragma once
#include <Mw/Milsko.h>
#include <napi.h>
#include <vector>
class MwBaseWidget : public Napi::ObjectWrap<MwBaseWidget> {
public:
static Napi::Object Init(Napi::Env env, Napi::Object exports);
MwBaseWidget(const Napi::CallbackInfo &info);
~MwBaseWidget();
protected:
static Napi::FunctionReference constructor;
MwWidget handle;
MwBaseWidget *parent;
std::vector<MwBaseWidget *> children;
Napi::Value SetArea(const Napi::CallbackInfo &info);
Napi::Value Move(const Napi::CallbackInfo &info);
Napi::Value Resize(const Napi::CallbackInfo &info);
Napi::Value Step(const Napi::CallbackInfo &info);
Napi::Value Loop(const Napi::CallbackInfo &info);
Napi::Value Show(const Napi::CallbackInfo &info);
Napi::Value HideCursor(const Napi::CallbackInfo &info);
Napi::Value GrabPointer(const Napi::CallbackInfo &info);
Napi::Value AddTickList(const Napi::CallbackInfo &info);
Napi::Value ForceRender(const Napi::CallbackInfo &info);
Napi::Value Reparent(const Napi::CallbackInfo &info);
Napi::Value Add(const Napi::CallbackInfo &info);
Napi::Value Remove(const Napi::CallbackInfo &info);
Napi::Value GetClipboard(const Napi::CallbackInfo &info);
Napi::Value GetArea(const Napi::CallbackInfo &info);
Napi::Value IsPending(const Napi::CallbackInfo &info);
Napi::Value GetParent(const Napi::CallbackInfo &info);
Napi::Value GetChildren(const Napi::CallbackInfo &info);
Napi::Value GetName(const Napi::CallbackInfo &info);
Napi::Value GetCursorCoord(const Napi::CallbackInfo &info);
Napi::Value GetScreenSize(const Napi::CallbackInfo &info);
Napi::Value GetCoordinateType(const Napi::CallbackInfo &info);
};