import * as radioGroupApi from "deft:core:radiogroup";
import * as textInputApi from "deft:core:textinput";
import * as containerApi from "deft:core:container";
import * as textEditApi from "deft:core:textedit";
import * as richTextApi from "deft:core:richtext";
import * as elementApi from "deft:core:element";
import * as buttonApi from "deft:core:button";
import * as selectApi from "deft:core:select";
import * as imageApi from "deft:core:image";
import * as radioApi from "deft:core:radio";
import * as labelApi from "deft:core:label";
import * as bodyApi from "deft:core:body";
import * as checkboxApi from "deft:core:checkbox";
import * as windowApi from "deft:core:window"
import * as popupApi from "deft:core:popup";
import * as pageApi from "deft:core:page";
import {EventBinder} from "deft:core";
export class Window {
#eventBinder;
#windowHandle;
#body;
constructor(attrs = null) {
attrs = attrs || {};
attrs.preferredRenderers = [].concat(attrs.preferredRenderers || [])
this.#windowHandle = windowApi.create(attrs);
this.#eventBinder = new EventBinder(this.#windowHandle, windowApi.bindJsEventListener, windowApi.unbindJsEventListener, this);
windowApi.setJsContext(this.#windowHandle, this);
this.#body = new BodyWidget(() => windowApi.getBodyJs(this.#windowHandle));
}
static fromHandle(windowHandle) {
return windowApi.getJsContext(windowHandle);
}
static supportMultipleWindows() {
return windowApi.supportMultipleWindows();
}
get handle() {
return this.#windowHandle
}
get body() {
return this.#body;
}
get innerSize() {
const [width, height] = windowApi.getInnerSize(this.handle);
return {width, height}
}
createPage(content, x, y) {
x = x ?? Number.NaN;
y = y ?? Number.NaN;
const page = windowApi.createPageJs(this.#windowHandle, content.handle, x, y);
return new Page(page);
}
popup(content, target) {
const rect = {
x: target.x,
y: target.y,
width: target.width || 0,
height: target.height || 0,
}
const handle = windowApi.popupJs(this.handle, content.handle, rect);
return new Popup(handle);
}
popupMenu(menu, x, y) {
windowApi.popupMenu(this.#windowHandle, menu.handle, x, y);
}
showAlert(message, options = {}) {
options = options || {};
this.showConfirm(message, {
...options,
hideCancel: true,
}).finally(() => {
options?.callback && options.callback();
});
}
showConfirm(message, options = {}) {
options = options || {};
function createBtn(label) {
const btnLabel = new LabelWidget();
btnLabel.text = label;
const btn = new ButtonWidget();
btn.style = {
minWidth: '4em',
flexDirection: 'row',
justifyContent: 'center',
}
btn.addChild(btnLabel);
return btn;
}
return new Promise((resolve) => {
if (!(message instanceof Widget)) {
const label = new LabelWidget();
label.text = message;
label.style = {
padding: '2em',
}
message = label;
}
const footer = new ContainerWidget();
footer.style = {
flexDirection: 'row',
justifyContent: 'center',
padding: '10px',
gap: '2em',
}
const btn = createBtn(options?.confirmBtnText ?? "OK");
footer.addChild(btn);
let cancelBtn;
if (!options.hideCancel) {
cancelBtn = createBtn(options?.cancelBtnText ?? "Cancel");
footer.addChild(cancelBtn);
}
const wrapper = new ContainerWidget();
wrapper.style = {
minWidth: 200,
alignItems: 'center',
}
wrapper.addChild(message);
wrapper.addChild(footer);
const dialog = this.showDialog(wrapper, options?.title);
btn.bindClick(() => {
resolve(true);
dialog.close();
});
cancelBtn?.bindClick(() => {
resolve(false);
dialog.close();
});
})
}
showDialog(content, title) {
if (Window.supportMultipleWindows()) {
const window = new Window({
resizable: false,
preferredRenderers: "SoftBuffer",
minimizable: false,
closable: false,
});
window.title = title ?? this.title ?? "";
window.body.addChild(content);
window.setModal(this);
window.bindResize((e) => {
const parentPos = this.outerPosition;
const parentSize = this.innerSize;
const x = parentPos.x + (parentSize.width - e.detail.width) / 2.0;
const y = parentPos.y + (parentSize.height - e.detail.height) / 2.0;
window.outerPosition = {x, y};
});
return {
close() {
window.close();
}
}
} else {
const wrapper = new DialogWidget();
if (title) {
const titleEl = new DialogTitleWidget();
const titleLabelEl = new LabelWidget();
titleLabelEl.text = title ?? window.title ?? "";
titleEl.addChild(titleLabelEl);
wrapper.addChild(titleEl);
}
wrapper.addChild(content);
const page = this.createPage(wrapper, NaN, NaN);
return {
close() {
page.close();
}
}
}
}
set title(title) {
windowApi.setTitle(this.#windowHandle, title);
}
get title() {
return windowApi.getTitle(this.#windowHandle);
}
get monitorSize() {
const [width, height] = windowApi.getMonitorSize(this.#windowHandle);
return {
width,
height
}
}
resize(size) {
windowApi.resize(this.#windowHandle, size);
}
drag() {
windowApi.drag(this.#windowHandle);
}
focus() {
windowApi.focus(this.#windowHandle);
}
get minimized() {
return windowApi.isMinimized(this.#windowHandle);
}
set minimized(minimized) {
windowApi.setMinimized(this.#windowHandle, minimized);
}
get maximized() {
return windowApi.isMaximized(this.#windowHandle);
}
set maximized(maximized) {
windowApi.setMaximized(this.#windowHandle, maximized);
}
setModal(owner) {
windowApi.setModal(this.#windowHandle, owner.#windowHandle)
}
set outerPosition(value) {
windowApi.setOuterPosition(this.handle, value.x, value.y);
}
get outerPosition() {
const [x, y] = windowApi.getOuterPosition(this.handle);
return {x, y}
}
close() {
windowApi.close(this.#windowHandle);
}
set visible(visible) {
windowApi.setVisible(this.#windowHandle, visible);
}
get visible() {
return windowApi.isVisible(this.#windowHandle);
}
requestFullscreen() {
windowApi.requestFullscreen(this.#windowHandle);
}
exitFullscreen() {
windowApi.exitFullscreen(this.#windowHandle);
}
get fullscreen() {
return windowApi.isFullscreen(this.#windowHandle);
}
bindResize(callback) {
this.#eventBinder.bindEvent("resize", callback);
}
bindClose(callback) {
this.bindEvent("close", callback);
}
bindFocus(callback) {
this.bindEvent("focus", callback);
}
bindBlur(callback) {
this.bindEvent("blur", callback);
}
bindEvent(type, callback) {
this.#eventBinder.bindEvent(type, callback);
}
addEventListener(type, callback) {
this.#eventBinder.addEventListener(type, callback);
}
removeEventListener(type, callback) {
this.#eventBinder.removeEventListener(type, callback);
}
}
export class Popup {
handle
constructor(handle) {
this.handle = handle;
}
close() {
popupApi.close(this.handle);
}
}
export class Page {
handle
constructor(handle) {
this.handle = handle;
}
close() {
pageApi.close(this.handle);
}
}
export class Widget {
_parent
handle
#style
#hoverStyle
#eventBinder;
constructor(el, context) {
const myContext = this;
if (typeof el === "function") {
this.handle = el();
elementApi.setJsContext(this.handle, myContext);
} else {
elementApi.setJsContext(el, myContext);
this.handle = el;
}
if (!this.handle) {
throw new Error("Failed to create element:" + el)
}
this.#eventBinder = new EventBinder(this.handle, elementApi.addJsEventListener, elementApi.removeJsEventListener, this);
}
static fromHandle(elementHandle) {
if (elementHandle) {
return null;
}
return null;
}
createEventBinder(target, addEventListenerApi, removeEventListenerApi) {
if (!removeEventListenerApi) {
removeEventListenerApi = (_t, listenerId) => {
elementApi.removeJsEventListener(this.handle, listenerId);
}
}
return new EventBinder(target, addEventListenerApi, removeEventListenerApi, this);
}
get eid() {
return elementApi.getEid(this.handle)
}
set class(clazz) {
elementApi.setClass(this.handle, clazz);
}
get class() {
return elementApi.getClass(this.handle);
}
set className(clazz) {
elementApi.setClass(this.handle, clazz);
}
get className() {
return elementApi.getClass(this.handle);
}
get parent() {
const eh = elementApi.getParentWeak(this.handle);
return Widget.fromHandle(eh);
}
set focusable(focusable) {
elementApi.setFocusable(this.handle, focusable);
}
get focusable() {
return elementApi.isFocusable(this.handle);
}
get rootWidget() {
let p = this.getParent();
if (p == null) {
return this;
} else {
return p.getRootElement();
}
}
focus() {
elementApi.focus(this.handle);
}
set tooltip(text) {
elementApi.setTooltip(this.handle, text);
}
get tooltip() {
return elementApi.getTooltip(this.handle);
}
get window() {
const windowHandle = elementApi.getWindow(this.handle);
return Window.fromHandle(windowHandle);
}
set style(style) {
this.#style = style;
elementApi.setStyle(this.handle, style);
}
get style() {
return elementApi.getStyle(this.handle);
}
set hoverStyle(style) {
this.#hoverStyle = style;
elementApi.setHoverStyle(this.handle, style);
}
get hoverStyle() {
return this.#hoverStyle;
}
set scrollTop(value) {
elementApi.setScrollTop(this.handle, value);
}
get scrollTop() {
return elementApi.getScrollTop(this.handle);
}
set scrollLeft(value) {
elementApi.setScrollLeft(this.handle, value);
}
get scrollLeft() {
return elementApi.getScrollLeft(this.handle);
}
set draggable(value) {
elementApi.setDraggable(this.handle, value);
}
get draggable() {
return elementApi.getDraggable(this.handle);
}
set cursor(value) {
elementApi.setCursor(this.handle, value);
}
get size() {
return elementApi.getSize(this.handle);
}
get contentSize() {
return elementApi.getRealContentSize(this.handle);
}
getBoundingClientRect() {
return elementApi.getBoundingClientRect(this.handle);
}
get scrollHeight() {
return elementApi.getScrollHeight(this.handle);
}
get scrollWidth() {
return elementApi.scrollWidth(this.handle);
}
setAttribute(key, value) {
elementApi.setAttribute(this.handle, key, value);
}
getAttribute(key) {
return elementApi.getAttribute(this.handle, key);
}
removeAttribute(key) {
elementApi.removeAttribute(this.handle, key);
}
bindBoundsChange(callback) {
this.bindEvent("boundschange", callback);
}
bindFocus(callback) {
this.bindEvent("focus", callback);
}
bindBlur(callback) {
this.bindEvent("blur", callback);
}
bindClick(callback) {
this.#eventBinder.bindEvent("click", callback);
}
bindContextMenu(callback) {
this.#eventBinder.bindEvent("contextmenu", callback);
}
bindMouseDown(callback) {
this.#eventBinder.bindEvent("mousedown", callback);
}
bindMouseUp(callback) {
this.#eventBinder.bindEvent("mouseup", callback);
}
bindMouseMove(callback) {
this.#eventBinder.bindEvent("mousemove", callback);
}
bindMouseEnter(callback) {
this.#eventBinder.bindEvent("mouseenter", callback);
}
bindMouseLeave(callback) {
this.#eventBinder.bindEvent("mouseleave", callback);
}
bindKeyDown(callback) {
this.#eventBinder.bindEvent("keydown", callback);
}
bindKeyUp(callback) {
this.#eventBinder.bindEvent("keyup", callback);
}
bindSizeChanged(callback) {
this.#eventBinder.bindEvent("sizechange", callback);
}
bindScroll(callback) {
this.#eventBinder.bindEvent("scroll", callback);
}
bindMouseWheel(callback) {
this.#eventBinder.bindEvent("mousewheel", callback);
}
bindDragStart(callback) {
this.#eventBinder.bindEvent("dragstart", callback);
}
bindDragOver(callback) {
this.#eventBinder.bindEvent("dragover", callback);
}
bindDrop(callback) {
this.#eventBinder.bindEvent("drop", callback);
}
bindTouchStart(callback) {
this.#eventBinder.bindEvent("touchstart", callback);
}
bindTouchMove(callback) {
this.#eventBinder.bindEvent("touchmove", callback);
}
bindTouchEnd(callback) {
this.#eventBinder.bindEvent("touchend", callback);
}
bindTouchCancel(callback) {
this.#eventBinder.bindEvent("touchcancel", callback);
}
bindDroppedFile(callback) {
this.#eventBinder.bindEvent("droppedfile", callback);
}
bindHoveredFile(callback) {
this.#eventBinder.bindEvent("hoveredfile", callback);
}
bindEvent(type, callback) {
this.#eventBinder.bindEvent(type, callback);
}
set autoFocus(value) {
elementApi.setAutoFocus(this.handle, value);
}
get autoFocus() {
return elementApi.getAutoFocus(this.handle);
}
toString() {
return this.handle + "@" + this.constructor.name
}
}
export class LabelWidget extends Widget {
constructor() {
super(labelApi.create);
}
set text(text) {
labelApi.setText(this.handle, text);
}
}
export class CheckboxWidget extends Widget {
constructor() {
super(checkboxApi.create);
}
set label(text) {
checkboxApi.setLabel(this.handle, text);
}
get label() {
return checkboxApi.getLabel(this.handle);
}
set checked(value) {
checkboxApi.setChecked(this.handle, value);
}
get checked() {
return checkboxApi.isChecked(this.handle);
}
get disabled() {
return elementApi.isDisabled(this.handle);
}
set disabled(value) {
elementApi.setDisabled(this.handle, value);
}
bindChange(callback) {
this.bindEvent("change", callback);
}
}
export class RadioWidget extends Widget {
constructor() {
super(radioApi.create);
}
set label(text) {
radioApi.setLabel(this.handle, text);
}
get label() {
return radioApi.getLabel(this.handle);
}
set checked(value) {
radioApi.setChecked(this.handle, value);
}
get checked() {
return radioApi.isChecked(this.handle);
}
get disabled() {
return elementApi.isDisabled(this.handle);
}
set disabled(value) {
elementApi.setDisabled(this.handle, value);
}
bindChange(callback) {
this.bindEvent("change", callback);
}
}
export class SelectWidget extends Widget {
constructor() {
super(selectApi.create);
}
set value(value) {
selectApi.setValue(this.handle, value + "");
}
get value() {
return selectApi.getValue(this.handle);
}
set options(options) {
selectApi.setOptions(this.handle, options);
}
get options() {
return selectApi.getOptions(this.handle);
}
set placeholder(placeholder) {
selectApi.setPlaceholder(this.handle, placeholder);
}
get placeholder() {
return selectApi.getPlaceholder(this.handle);
}
get disabled() {
return elementApi.isDisabled(this.handle);
}
set disabled(value) {
elementApi.setDisabled(this.handle, value);
}
bindChange(callback) {
this.bindEvent("change", callback);
}
}
export class RichTextWidget extends Widget {
constructor() {
super(richTextApi.create);
}
addLine(units) {
richTextApi.addLine(this.handle, units);
}
insertLine(index, units) {
richTextApi.insertLine(this.handle, index, units);
}
deleteLine(index) {
richTextApi.deleteLine(this.handle, index);
}
updateLine(index, units) {
richTextApi.updateLine(this.handle, index, units);
}
clear() {
richTextApi.clear(this.handle);
}
measureLine(units) {
return richTextApi.measureLine(this.handle, units);
}
get selectionText() {
return richTextApi.getSelectionText(this.handle);
}
}
export class ImageWidget extends Widget {
constructor() {
super(imageApi.create);
}
set src(src) {
imageApi.setSrc(this.handle, src);
}
}
export class TextInputWidget extends Widget {
constructor() {
super(textInputApi.create);
}
set text(text) {
textInputApi.setText(this.handle, text);
}
set placeholder(placeholder) {
textInputApi.setPlaceholder(this.handle, placeholder);
}
get placeholder() {
return textInputApi.getPlaceholder(this.handle);
}
set type(type) {
textInputApi.setType(this.handle, type);
}
get type() {
return textInputApi.getType(this.handle);
}
get text() {
return textInputApi.getText(this.handle);
}
get disabled() {
return elementApi.isDisabled(this.handle);
}
set disabled(value) {
elementApi.setDisabled(this.handle, value);
}
bindTextChange(callback) {
this.bindEvent("textchange", callback);
}
bindCaretChange(callback) {
this.bindEvent("caretchange", callback);
}
}
export class TextEditWidget extends Widget {
constructor() {
super(textEditApi.create);
}
set text(text) {
textEditApi.setText(this.handle, text);
}
set placeholder(placeholder) {
textEditApi.setPlaceholder(this.handle, placeholder);
}
get placeholder() {
return textEditApi.getPlaceholder(this.handle);
}
get maxHistory() {
return textEditApi.getMaxHistory(this.handle);
}
set maxHistory(maxHistory) {
textEditApi.setMaxHistory(this.handle, maxHistory);
}
setSelectionByCharOffset(start, end) {
textEditApi.setSelectionByCharOffset(this.handle, start, end)
}
setCaretByCharOffset(charOffset) {
textEditApi.setCaretByCharOffset(this.handle, charOffset);
}
get text() {
return textEditApi.getText(this.handle);
}
get disabled() {
return elementApi.isDisabled(this.handle);
}
set disabled(value) {
elementApi.setDisabled(this.handle, value);
}
bindTextChange(callback) {
this.bindEvent("textchange", callback);
}
bindCaretChange(callback) {
this.bindEvent("caretchange", callback);
}
}
export class ContainerBasedWidget extends Widget {
#children = [];
addChild(child, index= -1) {
if (child._parent === this) {
const oldIndex = this.#children.indexOf(child);
if (oldIndex === index) {
return;
}
index -= oldIndex < index ? 1 : 0;
this.removeChild(child);
this.addChild(child, index);
return;
}
if (child._parent) {
child._parent.removeChild(child);
}
child._parent = this;
if (typeof index === "number" && index >= 0 && index < this.#children.length) {
elementApi.addChildJs(this.handle, child.handle, index);
this.#children.splice(index, 0, child);
} else {
elementApi.addChildJs(this.handle, child.handle, -1);
this.#children.push(child);
}
}
addChildBefore(newNode, referenceNode) {
const index = this.#children.indexOf(referenceNode);
this.addChild(newNode, index);
}
addChildAfter(newNode, referenceNode) {
const index = this.#children.indexOf(referenceNode);
if (index >= 0) {
this.addChild(newNode, index + 1);
} else {
this.addChild(newNode);
}
}
removeChild(child) {
const index = this.#children.indexOf(child);
if (index >= 0) {
child._parent = null;
elementApi.removeChild(this.handle, index);
this.#children.splice(index, 1);
} else {
console.log("remove child failed")
}
}
get children() {
return this.#children.slice();
}
}
export class ButtonWidget extends ContainerBasedWidget {
constructor() {
super(buttonApi.create);
}
get disabled() {
return elementApi.isDisabled(this.handle);
}
set disabled(value) {
elementApi.setDisabled(this.handle, value);
}
}
export class ContainerWidget extends ContainerBasedWidget {
constructor() {
super(containerApi.create);
}
}
export class DialogWidget extends ContainerBasedWidget {
constructor() {
super(() => containerApi.newWithTag("Dialog"));
}
}
export class DialogTitleWidget extends ContainerBasedWidget {
constructor() {
super(() => containerApi.newWithTag("dialog-title"));
}
}
export class BodyWidget extends ContainerBasedWidget {
constructor(creator) {
super(creator || bodyApi.create);
}
}
export class RadioGroupWidget extends ContainerBasedWidget {
constructor() {
super(radioGroupApi.create);
}
}
globalThis.Window = Window;