mirror of
https://github.com/aharabada/glitchy-engine-beef.git
synced 2026-09-05 21:01:52 +00:00
Start of custom title, call beef delegates from JS
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using Ultralight.CAPI;
|
||||
using GlitchyEngine;
|
||||
|
||||
namespace GlitchyEditor.Ultralight;
|
||||
|
||||
delegate void JSCallback(JSContextRef context, JSObjectRef thisObject, Span<JSValueRef> arguments, JSValueRef* exception = null);
|
||||
|
||||
static class UltralightHelper
|
||||
{
|
||||
private static JSValueRef NativeFunctionCallback(JSContextRef ctx, JSObjectRef fn, JSObjectRef thisObject, uint32 argumentCount, JSValueRef* arguments, JSValueRef* exception)
|
||||
{
|
||||
JSCallback callback = (JSCallback)Internal.UnsafeCastToObject(JSObjectGetPrivate(fn));
|
||||
|
||||
if (callback == null)
|
||||
{
|
||||
Log.EngineLogger.Error("The native callback of the function is null.");
|
||||
return JSValueMakeNull(ctx);
|
||||
}
|
||||
|
||||
callback(ctx, thisObject, Span<JSValueRef>(arguments, argumentCount));
|
||||
|
||||
return JSValueMakeNull(ctx);
|
||||
}
|
||||
|
||||
private static void NativeFunctionFinalize(JSObjectRef object)
|
||||
{
|
||||
JSCallback callback = (JSCallback)Internal.UnsafeCastToObject(JSObjectGetPrivate(object));
|
||||
delete:(StdAllocator()) callback;
|
||||
}
|
||||
|
||||
private static JSClassRef NativeFunctionClass()
|
||||
{
|
||||
static JSClassRef instance = null;
|
||||
if (instance == null)
|
||||
{
|
||||
JSClassDefinition def = .();
|
||||
def.className = "NativeFunction";
|
||||
def.attributes = (.)JSClassAttribute.kJSClassAttributeNone;
|
||||
def.callAsFunction = => NativeFunctionCallback;
|
||||
def.finalize = => NativeFunctionFinalize;
|
||||
instance = JSClassCreate(&def);
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static JSObjectRef CreateJsFunctionFromDelegate(JSContextRef context, JSCallback ownCallback)
|
||||
{
|
||||
return JSObjectMake(context, NativeFunctionClass(), Internal.UnsafeCastToPtr(ownCallback));
|
||||
}
|
||||
}
|
||||
@@ -1,86 +1,78 @@
|
||||
using Ultralight.CAPI;
|
||||
using System;
|
||||
using GlitchyEngine;
|
||||
|
||||
namespace GlitchyEditor.Ultralight;
|
||||
|
||||
class UltralightMainWindow : UltralightWindow
|
||||
{
|
||||
public this() : base("Glitchy Engine", "file:///dist/index.html") // "file:///react-ui-prototype/dist/index.html"
|
||||
//public this() : base("Entity Hierarchy", "file:///another-react-test/index.html")
|
||||
public this() : base("Glitchy Engine", "file:///dist/index.html")
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
///
|
||||
/// This callback is bound to a JavaScript function on the page.
|
||||
///
|
||||
static JSValueRef GetMessage(JSContextRef ctx, JSObjectRef fn, JSObjectRef thisObject, uint32 argumentCount, JSValueRef* arguments, JSValueRef* exception) {
|
||||
///
|
||||
/// Create a JavaScript String from a C-string, initialize it with our
|
||||
/// welcome message.
|
||||
///
|
||||
JSStringRef str = JSStringCreateWithUTF8CString("Hello from Beef!");
|
||||
private bool _hoveringNonClientArea;
|
||||
|
||||
void HandleHoverNonClientArea(JSContextRef context, JSObjectRef thisObject, Span<JSValueRef> arguments, JSValueRef* exception = null)
|
||||
{
|
||||
Log.EngineLogger.Info("HandleHoverNonClientArea");
|
||||
|
||||
if (arguments.Length != 1)
|
||||
{
|
||||
Log.EngineLogger.Error("EngineGlue.setHoverNonClientArea: called with wrong number of arguments.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!JSValueIsBoolean(context, arguments[0]))
|
||||
{
|
||||
Log.EngineLogger.Error($"EngineGlue.setHoverNonClientArea: expected boolean, but received {JSValueGetType(context, arguments[0])} instead.");
|
||||
return;
|
||||
}
|
||||
|
||||
_hoveringNonClientArea = JSValueToBoolean(context, arguments[0]);
|
||||
|
||||
Log.ClientLogger.Info($"_hoveringNonClientArea: {_hoveringNonClientArea}");
|
||||
}
|
||||
|
||||
private void RegisterBeefFunction(JSContextRef context, JSObjectRef object, StringView functionName, JSCallback callback)
|
||||
{
|
||||
JSObjectRef func = UltralightHelper.CreateJsFunctionFromDelegate(context, callback);
|
||||
|
||||
///
|
||||
/// Create a garbage-collected JSValue using the String we just created.
|
||||
///
|
||||
/// **Note**:
|
||||
/// Both JSValueRef and JSObjectRef types are garbage-collected types. (And actually,
|
||||
/// JSObjectRef is just a typedef of JSValueRef, they share definitions).
|
||||
///
|
||||
/// The garbage collector in JavaScriptCore periodically scans the entire stack to check if
|
||||
/// there are any active JSValueRefs, and marks those with no references for destruction.
|
||||
///
|
||||
/// If you happen to store a JSValueRef/JSObjectRef in heap memory or in memory unreachable
|
||||
/// by the stack-based garbage-collector, you should explicitly call JSValueProtect() and
|
||||
/// JSValueUnprotect() on the reference to ensure it is kept alive.
|
||||
///
|
||||
JSValueRef value = JSValueMakeString(ctx, str);
|
||||
JSStringRef name = JSStringCreateWithUTF8CString(functionName.ToScopeCStr!());
|
||||
|
||||
JSObjectRef exception;
|
||||
JSObjectSetProperty(context, object, name, func, 0, &exception);
|
||||
|
||||
JSStringRelease(name);
|
||||
}
|
||||
|
||||
|
||||
private void RegisterEngineGlueFunctions()
|
||||
{
|
||||
JSContextRef context = ulViewLockJSContext(_view);
|
||||
defer ulViewUnlockJSContext(_view);
|
||||
|
||||
///
|
||||
/// Release the string we created earlier (we only Release what we Create).
|
||||
///
|
||||
JSStringRelease(str);
|
||||
JSObjectRef globalObject = JSContextGetGlobalObject(context);
|
||||
|
||||
return value;
|
||||
JSValueRef exception = null;
|
||||
JSStringRef scriptGlueName = JSStringCreateWithUTF8CString("EngineGlue");
|
||||
JSValueRef scriptGlue = JSObjectGetProperty(context, globalObject, scriptGlueName, &exception);
|
||||
JSStringRelease(scriptGlueName);
|
||||
|
||||
if (JSValueGetType(context, scriptGlue) == .kJSTypeUndefined)
|
||||
{
|
||||
Log.EngineLogger.Error("Failed to get EngineGlue object from JS context.");
|
||||
return;
|
||||
}
|
||||
|
||||
StdAllocator stdAlloc = StdAllocator();
|
||||
|
||||
RegisterBeefFunction(context, scriptGlue, "setHoverNonClientArea", new:stdAlloc => HandleHoverNonClientArea);
|
||||
}
|
||||
|
||||
protected override void OnDOMReady(C_View* caller, uint64 frame_id, bool is_main_frame, C_String* url)
|
||||
{
|
||||
///
|
||||
/// Acquire the page's JavaScript execution context.
|
||||
///
|
||||
/// This locks the JavaScript context so we can modify it safely on this thread, we need to
|
||||
/// unlock it when we're done via ulViewUnlockJSContext().
|
||||
///
|
||||
JSContextRef ctx = ulViewLockJSContext(_view);
|
||||
RegisterEngineGlueFunctions();
|
||||
|
||||
///
|
||||
/// Create a JavaScript String containing the name of our callback.
|
||||
///
|
||||
JSStringRef name = JSStringCreateWithUTF8CString("OnCreateEntityClick");
|
||||
|
||||
///
|
||||
/// Create a garbage-collected JavaScript function that is bound to our native C callback
|
||||
/// 'GetMessage()'.
|
||||
///
|
||||
JSObjectRef func = JSObjectMakeFunctionWithCallback(ctx, name, => GetMessage);
|
||||
|
||||
///
|
||||
/// Store our function in the page's global JavaScript object so that it is accessible from the
|
||||
/// page as 'GetMessage()'.
|
||||
///
|
||||
/// The global JavaScript object is also known as 'window' in JS.
|
||||
///
|
||||
JSObjectSetProperty(ctx, JSContextGetGlobalObject(ctx), name, func, 0, null);
|
||||
|
||||
///
|
||||
/// Release the JavaScript String we created earlier.
|
||||
///
|
||||
JSStringRelease(name);
|
||||
|
||||
///
|
||||
/// Unlock the JS context so other threads can modify JavaScript state.
|
||||
///
|
||||
ulViewUnlockJSContext(_view);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,9 +60,10 @@ abstract class UltralightWindow
|
||||
private void InitCallbacks()
|
||||
{
|
||||
void* userData = Internal.UnsafeCastToPtr(this);
|
||||
|
||||
ulViewSetDOMReadyCallback(_view, => OnDOMReady, userData);
|
||||
|
||||
ulViewSetFailLoadingCallback(_view, => OnFailedLoading, userData);
|
||||
ulViewSetWindowObjectReadyCallback(_view, => OnWindowObjectReady, userData);
|
||||
ulViewSetDOMReadyCallback(_view, => OnDOMReady, userData);
|
||||
|
||||
ulViewSetAddConsoleMessageCallback(_view, => OnAddConsoleMessage, userData);
|
||||
|
||||
@@ -235,12 +236,15 @@ abstract class UltralightWindow
|
||||
ulDestroyMouseEvent(evt);
|
||||
|
||||
_cursorPosition = .(e.PositionX, e.PositionY);
|
||||
Log.EngineLogger.Warning($"{_cursorPosition.X} {_cursorPosition.Y}");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool MousePressed(MouseButtonEvent e, bool press)
|
||||
{
|
||||
Log.ClientLogger.Warning($"{e.MouseButton} {press}");
|
||||
|
||||
ULMouseButton button = .kMouseButton_None;
|
||||
|
||||
switch (e.MouseButton)
|
||||
@@ -261,6 +265,14 @@ abstract class UltralightWindow
|
||||
#endregion Events
|
||||
|
||||
#region Ultralight Callbacks
|
||||
|
||||
private static void OnWindowObjectReady(void* user_data, ULView caller, uint64 frame_id, bool is_main_frame, ULString url)
|
||||
{
|
||||
UltralightWindow window = (UltralightWindow)Internal.UnsafeCastToObject(user_data);
|
||||
window.OnWindowObjectReady(caller, frame_id, is_main_frame, url);
|
||||
}
|
||||
|
||||
protected virtual void OnWindowObjectReady(ULView caller, uint64 frame_id, bool is_main_frame, ULString url) { }
|
||||
|
||||
private static void OnDOMReady(void* user_data, ULView caller, uint64 frame_id, bool is_main_frame, ULString url)
|
||||
{
|
||||
@@ -269,7 +281,7 @@ abstract class UltralightWindow
|
||||
}
|
||||
|
||||
protected virtual void OnDOMReady(ULView caller, uint64 frame_id, bool is_main_frame, ULString url) { }
|
||||
|
||||
|
||||
private static void OnFailedLoading(void* user_data, C_View* caller, uint64 frame_id, bool is_main_frame, C_String* url, C_String* description, C_String* error_domain, int32 error_code)
|
||||
{
|
||||
UltralightWindow window = (UltralightWindow)Internal.UnsafeCastToObject(user_data);
|
||||
|
||||
Reference in New Issue
Block a user