Skip to content

Instantly share code, notes, and snippets.

@nikki93
Created June 21, 2021 17:28
Show Gist options
  • Save nikki93/6e41ab5bc177209944aea99dc1041c95 to your computer and use it in GitHub Desktop.
Save nikki93/6e41ab5bc177209944aea99dc1041c95 to your computer and use it in GitHub Desktop.
#include "core.hh"
// Elements
JS_DEFINE(void, JS_uiElemOpenStart, (const char *tag),
{ IncrementalDOM.elementOpenStart(UTF8ToString(tag)); })
JS_DEFINE(void, JS_uiElemOpenStartKeyInt, (const char *tag, int key),
{ IncrementalDOM.elementOpenStart(UTF8ToString(tag), key); })
JS_DEFINE(void, JS_uiElemOpenStartKeyString, (const char *tag, const char *key),
{ IncrementalDOM.elementOpenStart(UTF8ToString(tag), UTF8ToString(key)); })
JS_DEFINE(void, JS_uiElemOpenEnd, (), { IncrementalDOM.elementOpenEnd(); })
JS_DEFINE(
void, JS_uiElemClose, (const char *tag), { IncrementalDOM.elementClose(UTF8ToString(tag)); })
JS_DEFINE(int, JS_uiGetElemToken, (), {
const target = IncrementalDOM.currentElement();
let token = target.__UIToken;
if (!token) {
token = UI.nextToken++;
target.__UIToken = token;
}
return token;
})
// Attributes
JS_DEFINE(void, JS_uiAttrInt, (const char *name, int value),
{ IncrementalDOM.attr(UTF8ToString(name), value); })
JS_DEFINE(void, JS_uiAttrFloat, (const char *name, float value),
{ IncrementalDOM.attr(UTF8ToString(name), value); })
JS_DEFINE(void, JS_uiAttrDouble, (const char *name, double value),
{ IncrementalDOM.attr(UTF8ToString(name), value); })
JS_DEFINE(void, JS_uiAttrString, (const char *name, const char *value),
{ IncrementalDOM.attr(UTF8ToString(name), UTF8ToString(value)); })
JS_DEFINE(
void, JS_uiAttrEmpty, (const char *name), { IncrementalDOM.attr(UTF8ToString(name), ""); })
JS_DEFINE(void, JS_uiAttrClass, (const char *value),
{ IncrementalDOM.attr("class", UTF8ToString(value)); })
// Text
JS_DEFINE(void, JS_uiText, (const char *value), { IncrementalDOM.text(UTF8ToString(value)); })
// Events
JS_DEFINE(int, JS_uiGetEventCount, (const char *type), {
const typeStr = UTF8ToString(type);
const target = IncrementalDOM.currentElement();
if (!target.__UIIsEventListenerSetup) {
UI.setupEventListener(target, typeStr);
target.__UIIsEventListenerSetup = true;
}
const counts = window.UI.eventCounts.get(target);
if (typeof(counts) == "undefined") {
return 0;
}
const count = counts[typeStr];
if (typeof(count) == "undefined") {
return 0;
}
delete counts[typeStr];
return count;
})
JS_DEFINE(void, JS_uiClearEventCounts, (), { window.UI.eventCounts = new WeakMap(); })
JS_DEFINE(bool, JS_uiIsKeyboardCaptured, (), { return !!window.UI.isKeyboardCaptured; })
// Values
JS_DEFINE(char *, JS_uiGetValue, (), {
let value = IncrementalDOM.currentElement().value;
if (!value) {
value = "";
}
return allocate(intArrayFromString(value), ALLOC_NORMAL);
});
JS_DEFINE(void, JS_uiSetValue, (const char *value),
{ IncrementalDOM.currentElement().value = UTF8ToString(value); });
// Patch
JS_EXPORT void JS_uiCallPatchFunc() {
if (uiPatchState.wrapper) {
uiPatchState.wrapper();
uiPatchState = {};
}
}
JS_DEFINE(void, JS_uiPatch, (const char *id), {
const el = document.getElementById(UTF8ToString(id));
if (el) {
IncrementalDOM.patch(el, Module._JS_uiCallPatchFunc.bind(Module));
}
})
#pragma once
#include "core.hh"
//
// JS interface
//
// Elements
extern "C" void JS_uiElemOpenStart(const char *tag);
extern "C" void JS_uiElemOpenStartKeyInt(const char *tag, int key);
extern "C" void JS_uiElemOpenStartKeyString(const char *tag, const char *key);
extern "C" void JS_uiElemOpenEnd();
extern "C" void JS_uiElemClose(const char *tag);
extern "C" int JS_uiGetElemToken();
// Attributes
extern "C" void JS_uiAttrInt(const char *name, int value);
extern "C" void JS_uiAttrFloat(const char *name, float value);
extern "C" void JS_uiAttrDouble(const char *name, double value);
extern "C" void JS_uiAttrString(const char *name, const char *value);
extern "C" void JS_uiAttrEmpty(const char *name);
extern "C" void JS_uiAttrClass(const char *value);
// Text
extern "C" void JS_uiText(const char *value);
// Events
extern "C" int JS_uiGetEventCount(const char *type);
extern "C" void JS_uiClearEventCounts();
extern "C" bool JS_uiIsKeyboardCaptured();
// Value
extern "C" char *JS_uiGetValue();
extern "C" void JS_uiSetValue(const char *value);
// Patch
extern "C" void JS_uiPatch(const char *id);
//
// Elements
//
struct UIElem {
const char *tag = "div";
bool ended = false;
~UIElem() {
end();
JS_uiElemClose(tag);
}
UIElem &operator()(const char *attrName, int value) {
JS_uiAttrInt(attrName, value);
return *this;
}
UIElem &operator()(const char *attrName, float value) {
JS_uiAttrFloat(attrName, value);
return *this;
}
UIElem &operator()(const char *attrName, double value) {
JS_uiAttrDouble(attrName, value);
return *this;
}
UIElem &operator()(const char *attrName, const char *value) {
JS_uiAttrString(attrName, value);
return *this;
}
UIElem &operator()(const char *attrName, char *value) {
return operator()(attrName, (const char *)value);
}
UIElem &operator()(const char *attrName, bool value) {
if (value) {
JS_uiAttrEmpty(attrName);
}
return *this;
}
UIElem &operator()(const char *klass) {
JS_uiAttrClass(klass);
return *this;
}
UIElem &operator()(char *klass) {
JS_uiAttrClass(klass);
return *this;
}
UIElem &operator()(const char *eventName, auto &&f) {
end();
auto count = JS_uiGetEventCount(eventName);
for (auto i = 0; i < count; ++i) {
callEventHandler(f);
}
return *this;
}
UIElem &operator()(const char *eventName, rl::KeyboardKey key, auto &&f) {
if (!JS_uiIsKeyboardCaptured() && rl::IsKeyReleased(key)) {
callEventHandler(f);
}
return operator()(eventName, std::forward<decltype(f)>(f));
}
UIElem &operator()(const char *eventName, rl::KeyboardKey mod, rl::KeyboardKey key, auto &&f) {
if (!JS_uiIsKeyboardCaptured()
&& ((rl::IsKeyDown(mod) && rl::IsKeyReleased(key))
|| (rl::IsKeyReleased(mod) && rl::IsKeyDown(key)))) {
callEventHandler(f);
}
return operator()(eventName, std::forward<decltype(f)>(f));
}
UIElem &operator()(auto &&f) {
end();
f();
return *this;
}
void end() {
if (!ended) {
JS_uiElemOpenEnd();
ended = true;
}
}
template<typename F>
void callEventHandler(F &&f) {
if constexpr (std::is_invocable_v<F, const char *>) {
auto value = JS_uiGetValue();
if constexpr (std::is_convertible_v<std::invoke_result_t<F, const char *>, const char *>) {
JS_uiSetValue(f(value));
} else {
f(value);
}
std::free(value);
} else {
f();
}
}
};
inline UIElem ui(const char *tag) {
JS_uiElemOpenStart(tag);
return { .tag = tag };
}
inline UIElem ui(const char *tag, int key) {
JS_uiElemOpenStartKeyInt(tag, key);
return { .tag = tag };
}
inline UIElem ui(const char *tag, const char *key) {
JS_uiElemOpenStartKeyString(tag, key);
return { .tag = tag };
}
//
// Text
//
inline void uiText(const char *format, auto &&...args) {
JS_uiText(bprint<1024>(format, std::forward<decltype(args)>(args)...));
}
inline void uiText(const char *msg) {
JS_uiText(msg);
}
//
// Patch
//
inline struct UIPatchState {
void (*wrapper)() = nullptr;
void *func = nullptr;
} uiPatchState;
template<typename F>
inline void uiPatch(const char *id, F &&f) {
#ifdef __EMSCRIPTEN__
uiPatchState.func = (void *)&f;
uiPatchState.wrapper = []() {
if (uiPatchState.func) {
auto &f = *static_cast<F *>(uiPatchState.func);
f();
}
};
JS_uiPatch(id);
uiPatchState = {};
#else
f();
#endif
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment