15#include "IGraphicsWeb.h"
18BEGIN_IGRAPHICS_NAMESPACE
20void GetScreenDimensions(
int& width,
int& height)
22 width = val::global(
"window")[
"innerWidth"].as<
int>();
23 height = val::global(
"window")[
"innerHeight"].as<
int>();
27END_IGRAPHICS_NAMESPACE
30using namespace igraphics;
31using namespace emscripten;
34double gPrevMouseDownTime = 0.;
35bool gFirstClick =
false;
37#pragma mark - Private Classes and Structs
41class IGraphicsWeb::Font :
public PlatformFont
44 Font(
const char* fontName,
const char* fontStyle)
45 : PlatformFont(true), mDescriptor{fontName, fontStyle}
48 FontDescriptor GetDescriptor()
override {
return &mDescriptor; }
51 std::pair<WDL_String, WDL_String> mDescriptor;
57 FileFont(
const char* fontName,
const char* fontStyle,
const char* fontPath)
58 : Font(fontName, fontStyle), mPath(fontPath)
63 IFontDataPtr GetFontData()
override;
69IFontDataPtr IGraphicsWeb::FileFont::GetFontData()
71 IFontDataPtr fontData(
new IFontData());
72 FILE* fp = fopen(mPath.Get(),
"rb");
79 fontData = std::make_unique<IFontData>((
int) ftell(fp));
81 if (!fontData->GetSize())
85 size_t readSize = fread(fontData->Get(), 1, fontData->GetSize(), fp);
88 if (readSize && readSize == fontData->GetSize())
89 fontData->SetFaceIdx(0);
97 MemoryFont(
const char* fontName,
const char* fontStyle,
const void* pData,
int dataSize)
98 : Font(fontName, fontStyle)
101 mData.Set((
const uint8_t*)pData, dataSize);
104 IFontDataPtr GetFontData()
override
106 return IFontDataPtr(
new IFontData(mData.Get(), mData.GetSize(), 0));
110 WDL_TypedBuf<uint8_t> mData;
113#pragma mark - Utilities and Callbacks
115static EM_BOOL key_callback(
int eventType,
const EmscriptenKeyboardEvent* pEvent,
void* pUserData)
123 if ((VK >= kVK_0 && VK <= kVK_Z) || VK == kVK_NONE)
124 keyUTF8.Set(pEvent->key);
130 static_cast<bool>(pEvent->shiftKey),
131 static_cast<bool>(pEvent->ctrlKey || pEvent->metaKey),
132 static_cast<bool>(pEvent->altKey)};
136 case EMSCRIPTEN_EVENT_KEYDOWN:
138 return pGraphicsWeb->OnKeyDown(pGraphicsWeb->mPrevX, pGraphicsWeb->mPrevY, keyPress);
140 case EMSCRIPTEN_EVENT_KEYUP:
142 return pGraphicsWeb->OnKeyUp(pGraphicsWeb->mPrevX, pGraphicsWeb->mPrevY, keyPress);
151static EM_BOOL outside_mouse_callback(
int eventType,
const EmscriptenMouseEvent* pEvent,
void* pUserData)
156 val rect = GetCanvas().call<val>(
"getBoundingClientRect");
157 info.x = (pEvent->targetX - rect[
"left"].as<
double>()) / pGraphics->GetDrawScale();
158 info.y = (pEvent->targetY - rect[
"top"].as<
double>()) / pGraphics->GetDrawScale();
159 info.dX = pEvent->movementX;
160 info.dY = pEvent->movementY;
161 info.ms = {(pEvent->buttons & 1) != 0, (pEvent->buttons & 2) != 0,
static_cast<bool>(pEvent->shiftKey),
static_cast<bool>(pEvent->ctrlKey),
static_cast<bool>(pEvent->altKey)};
162 std::vector<IMouseInfo> list {info};
166 case EMSCRIPTEN_EVENT_MOUSEUP:
169 list[0].ms.L = pEvent->button == 0;
170 list[0].ms.R = pEvent->button == 2;
171 pGraphics->OnMouseUp(list);
172 emscripten_set_mousemove_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, pGraphics, 1,
nullptr);
173 emscripten_set_mouseup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, pGraphics, 1,
nullptr);
176 case EMSCRIPTEN_EVENT_MOUSEMOVE:
178 if(pEvent->buttons != 0 && !pGraphics->IsInPlatformTextEntry())
179 pGraphics->OnMouseDrag(list);
186 pGraphics->mPrevX = info.x;
187 pGraphics->mPrevY = info.y;
192static EM_BOOL mouse_callback(
int eventType,
const EmscriptenMouseEvent* pEvent,
void* pUserData)
197 info.x = pEvent->targetX / pGraphics->GetDrawScale();
198 info.y = pEvent->targetY / pGraphics->GetDrawScale();
199 info.dX = pEvent->movementX;
200 info.dY = pEvent->movementY;
201 info.ms = {(pEvent->buttons & 1) != 0,
202 (pEvent->buttons & 2) != 0,
203 static_cast<bool>(pEvent->shiftKey),
204 static_cast<bool>(pEvent->ctrlKey),
205 static_cast<bool>(pEvent->altKey)};
207 std::vector<IMouseInfo> list {info};
210 case EMSCRIPTEN_EVENT_MOUSEDOWN:
212 const double timestamp = GetTimestamp();
213 const double timeDiff = timestamp - gPrevMouseDownTime;
215 if (gFirstClick && timeDiff < 0.3)
218 pGraphics->OnMouseDblClick(info.x, info.y, info.ms);
223 pGraphics->OnMouseDown(list);
226 gPrevMouseDownTime = timestamp;
230 case EMSCRIPTEN_EVENT_MOUSEUP:
233 list[0].ms.L = pEvent->button == 0;
234 list[0].ms.R = pEvent->button == 2;
235 pGraphics->OnMouseUp(list);
238 case EMSCRIPTEN_EVENT_MOUSEMOVE:
242 if(pEvent->buttons == 0)
243 pGraphics->OnMouseOver(info.x, info.y, info.ms);
246 if(!pGraphics->IsInPlatformTextEntry())
247 pGraphics->OnMouseDrag(list);
251 case EMSCRIPTEN_EVENT_MOUSEENTER:
252 pGraphics->OnSetCursor();
253 pGraphics->OnMouseOver(info.x, info.y, info.ms);
254 emscripten_set_mousemove_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, pGraphics, 1,
nullptr);
256 case EMSCRIPTEN_EVENT_MOUSELEAVE:
257 if(pEvent->buttons != 0)
259 emscripten_set_mousemove_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, pGraphics, 1, outside_mouse_callback);
260 emscripten_set_mouseup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, pGraphics, 1, outside_mouse_callback);
262 pGraphics->OnMouseOut();
break;
267 pGraphics->mPrevX = info.x;
268 pGraphics->mPrevY = info.y;
273static EM_BOOL wheel_callback(
int eventType,
const EmscriptenWheelEvent* pEvent,
void* pUserData)
277 IMouseMod modifiers(
false,
false, pEvent->mouse.shiftKey, pEvent->mouse.ctrlKey, pEvent->mouse.altKey);
279 double x = pEvent->mouse.targetX;
280 double y = pEvent->mouse.targetY;
286 case EMSCRIPTEN_EVENT_WHEEL: pGraphics->
OnMouseWheel(x, y, modifiers, pEvent->deltaY);
294EM_BOOL touch_callback(
int eventType,
const EmscriptenTouchEvent* pEvent,
void* pUserData)
299 std::vector<IMouseInfo> points;
301 static EmscriptenTouchPoint previousTouches[32];
303 for (
auto i = 0; i < pEvent->numTouches; i++)
306 info.x = pEvent->touches[i].targetX / drawScale;
307 info.y = pEvent->touches[i].targetY / drawScale;
308 info.dX = info.x - (previousTouches[i].targetX / drawScale);
309 info.dY = info.y - (previousTouches[i].targetY / drawScale);
312 static_cast<bool>(pEvent->shiftKey),
313 static_cast<bool>(pEvent->ctrlKey),
314 static_cast<bool>(pEvent->altKey),
315 static_cast<ITouchID
>(pEvent->touches[i].identifier)
318 if(pEvent->touches[i].isChanged)
319 points.push_back(info);
322 memcpy(previousTouches, pEvent->touches,
sizeof(previousTouches));
326 case EMSCRIPTEN_EVENT_TOUCHSTART:
329 case EMSCRIPTEN_EVENT_TOUCHEND:
332 case EMSCRIPTEN_EVENT_TOUCHMOVE:
335 case EMSCRIPTEN_EVENT_TOUCHCANCEL:
343static EM_BOOL complete_text_entry(
int eventType,
const EmscriptenFocusEvent* focusEvent,
void* pUserData)
347 val input = val::global(
"document").call<val>(
"getElementById", std::string(
"textEntry"));
348 std::string str = input[
"value"].as<std::string>();
349 val::global(
"document")[
"body"].call<
void>(
"removeChild", input);
350 pGraphics->SetControlValueAfterTextEdit(str.c_str());
355static EM_BOOL text_entry_keydown(
int eventType,
const EmscriptenKeyboardEvent* pEvent,
void* pUserData)
360 static_cast<bool>(pEvent->shiftKey),
361 static_cast<bool>(pEvent->ctrlKey),
362 static_cast<bool>(pEvent->altKey)};
364 if (keyPress.VK == kVK_RETURN || keyPress.VK == kVK_TAB)
365 return complete_text_entry(0,
nullptr, pUserData);
370static EM_BOOL uievent_callback(
int eventType,
const EmscriptenUiEvent* pEvent,
void* pUserData)
374 if (eventType == EMSCRIPTEN_EVENT_RESIZE)
376 pGraphics->GetDelegate()->OnParentWindowResize(pEvent->windowInnerWidth, pEvent->windowInnerHeight);
384IColorPickerHandlerFunc gColorPickerHandlerFunc =
nullptr;
386static void color_picker_callback(val e)
388 if(gColorPickerHandlerFunc)
390 std::string colorStrHex = e[
"target"][
"value"].as<std::string>();
392 if (colorStrHex[0] ==
'#')
393 colorStrHex = colorStrHex.erase(0, 1);
397 sscanf(colorStrHex.c_str(),
"%02x%02x%02x", &result.R, &result.G, &result.B);
399 gColorPickerHandlerFunc(result);
403static void file_dialog_callback(val e)
408EMSCRIPTEN_BINDINGS(events) {
409 function(
"color_picker_callback", color_picker_callback);
410 function(
"file_dialog_callback", file_dialog_callback);
415IGraphicsWeb::IGraphicsWeb(
IGEditorDelegate& dlg,
int w,
int h,
int fps,
float scale)
416: IGRAPHICS_DRAW_CLASS(dlg, w, h, fps, scale)
418 val keys = val::global(
"Object").call<val>(
"keys", GetPreloadedImages());
420 DBGMSG(
"Preloaded %i images\n", keys[
"length"].as<int>());
422 emscripten_set_mousedown_callback(
"#canvas",
this, 1, mouse_callback);
423 emscripten_set_mouseup_callback(
"#canvas",
this, 1, mouse_callback);
424 emscripten_set_mousemove_callback(
"#canvas",
this, 1, mouse_callback);
425 emscripten_set_mouseenter_callback(
"#canvas",
this, 1, mouse_callback);
426 emscripten_set_mouseleave_callback(
"#canvas",
this, 1, mouse_callback);
427 emscripten_set_wheel_callback(
"#canvas",
this, 1, wheel_callback);
428 emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW,
this, 1, key_callback);
429 emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW,
this, 1, key_callback);
430 emscripten_set_touchstart_callback(
"#canvas",
this, 1, touch_callback);
431 emscripten_set_touchend_callback(
"#canvas",
this, 1, touch_callback);
432 emscripten_set_touchmove_callback(
"#canvas",
this, 1, touch_callback);
433 emscripten_set_touchcancel_callback(
"#canvas",
this, 1, touch_callback);
434 emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW,
this, 1, uievent_callback);
437IGraphicsWeb::~IGraphicsWeb()
441void* IGraphicsWeb::OpenWindow(
void* pHandle)
444 EmscriptenWebGLContextAttributes attr;
445 emscripten_webgl_init_context_attributes(&attr);
450 EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_create_context(
"#canvas", &attr);
451 emscripten_webgl_make_context_current(ctx);
454 OnViewInitialized(
nullptr );
456 SetScreenScale(std::ceil(std::max(emscripten_get_device_pixel_ratio(), 1.)));
458 GetDelegate()->LayoutUI(
this);
459 GetDelegate()->OnUIOpen();
464void IGraphicsWeb::HideMouseCursor(
bool hide,
bool lock)
466 if (mCursorHidden == hide)
471#ifdef IGRAPHICS_WEB_POINTERLOCK
473 emscripten_request_pointerlock(
"#canvas", EM_FALSE);
476 val::global(
"document")[
"body"][
"style"].set(
"cursor",
"none");
478 mCursorHidden =
true;
483#ifdef IGRAPHICS_WEB_POINTERLOCK
485 emscripten_exit_pointerlock();
490 mCursorHidden =
false;
495ECursor IGraphicsWeb::SetMouseCursor(ECursor cursorType)
497 std::string cursor(
"pointer");
501 case ECursor::ARROW: cursor =
"default";
break;
502 case ECursor::IBEAM: cursor =
"text";
break;
503 case ECursor::WAIT: cursor =
"wait";
break;
504 case ECursor::CROSS: cursor =
"crosshair";
break;
505 case ECursor::UPARROW: cursor =
"n-resize";
break;
506 case ECursor::SIZENWSE: cursor =
"nwse-resize";
break;
507 case ECursor::SIZENESW: cursor =
"nesw-resize";
break;
508 case ECursor::SIZEWE: cursor =
"ew-resize";
break;
509 case ECursor::SIZENS: cursor =
"ns-resize";
break;
510 case ECursor::SIZEALL: cursor =
"move";
break;
511 case ECursor::INO: cursor =
"not-allowed";
break;
512 case ECursor::HAND: cursor =
"pointer";
break;
513 case ECursor::APPSTARTING: cursor =
"progress";
break;
514 case ECursor::HELP: cursor =
"help";
break;
517 val::global(
"document")[
"body"][
"style"].set(
"cursor", cursor);
521void IGraphicsWeb::GetMouseLocation(
float& x,
float&y)
const
528void IGraphicsWeb::OnMainLoopTimer()
531 int screenScale = (int) std::ceil(std::max(emscripten_get_device_pixel_ratio(), 1.));
534 if (!gGraphics || !gGraphics->AssetsLoaded())
537 if (screenScale != gGraphics->GetScreenScale())
539 gGraphics->SetScreenScale(screenScale);
542 if (gGraphics->IsDirty(rects))
544 gGraphics->SetAllControlsClean();
545 gGraphics->Draw(rects);
549EMsgBoxResult IGraphicsWeb::ShowMessageBox(
const char* str,
const char* , EMsgBoxType type, IMsgBoxCompletionHandlerFunc completionHandler)
551 ReleaseMouseCapture();
553 EMsgBoxResult result = kNoResult;
559 val::global(
"window").call<val>(
"alert", std::string(str));
560 result = EMsgBoxResult::kOK;
566 result =
static_cast<EMsgBoxResult
>(val::global(
"window").call<val>(
"confirm", std::string(str)).as<int>());
571 return result = kNoResult;
574 if(completionHandler)
575 completionHandler(result);
580void IGraphicsWeb::PromptForFile(WDL_String& filename, WDL_String& path, EFileAction action,
const char* ext, IFileDialogCompletionHandlerFunc completionHandler)
592void IGraphicsWeb::PromptForDirectory(WDL_String& path, IFileDialogCompletionHandlerFunc completionHandler)
605bool IGraphicsWeb::PromptForColor(
IColor& color,
const char* str, IColorPickerHandlerFunc func)
607 ReleaseMouseCapture();
609 gColorPickerHandlerFunc = func;
611 val inputEl = val::global(
"document").call<val>(
"createElement", std::string(
"input"));
612 inputEl.call<
void>(
"setAttribute", std::string(
"type"), std::string(
"color"));
614 colorStr.SetFormatted(64,
"#%02x%02x%02x", color.R, color.G, color.B);
615 inputEl.call<
void>(
"setAttribute", std::string(
"value"), std::string(colorStr.Get()));
616 inputEl.call<
void>(
"click");
617 inputEl.call<
void>(
"addEventListener", std::string(
"input"), val::module_property(
"color_picker_callback"),
false);
618 inputEl.call<
void>(
"addEventListener", std::string(
"onChange"), val::module_property(
"color_picker_callback"),
false);
623void IGraphicsWeb::CreatePlatformTextEntry(
int paramIdx,
const IText& text,
const IRECT& bounds,
int length,
const char* str)
625 val input = val::global(
"document").call<val>(
"createElement", std::string(
"input"));
626 val rect = GetCanvas().call<val>(
"getBoundingClientRect");
628 auto setDim = [&input](
const char *dimName,
double pixels)
631 dimstr.SetFormatted(32,
"%fpx", pixels);
632 input[
"style"].set(dimName, std::string(dimstr.Get()));
635 auto setColor = [&input](
const char *colorName,
IColor color)
638 str.SetFormatted(64,
"rgba(%d, %d, %d, %d)", color.R, color.G, color.B, color.A);
639 input[
"style"].set(colorName, std::string(str.Get()));
642 input.set(
"id", std::string(
"textEntry"));
643 input[
"style"].set(
"position", val(
"fixed"));
644 setDim(
"left", rect[
"left"].as<double>() + bounds.L);
645 setDim(
"top", rect[
"top"].as<double>() + bounds.T);
646 setDim(
"width", bounds.
W());
647 setDim(
"height", bounds.
H());
649 setColor(
"color", text.mTextEntryFGColor);
650 setColor(
"background-color", text.mTextEntryBGColor);
651 if (paramIdx > kNoParameter)
653 const IParam* pParam = GetDelegate()->GetParam(paramIdx);
655 switch (pParam->
Type())
657 case IParam::kTypeEnum:
658 case IParam::kTypeInt:
659 case IParam::kTypeBool:
660 input.set(
"type", val(
"number"));
662 case IParam::kTypeDouble:
663 input.set(
"type", val(
"number"));
671 input.set(
"type", val(
"text"));
674 val::global(
"document")[
"body"].call<
void>(
"appendChild", input);
675 input.call<
void>(
"focus");
676 emscripten_set_focusout_callback(
"textEntry",
this, 1, complete_text_entry);
677 emscripten_set_keydown_callback(
"textEntry",
this, 1, text_entry_keydown);
685bool IGraphicsWeb::OpenURL(
const char* url,
const char* msgWindowTitle,
const char* confirmMsg,
const char* errMsgOnFailure)
687 val::global(
"window").call<val>(
"open", std::string(url), std::string(
"_blank"));
692void IGraphicsWeb::DrawResize()
694 val canvas = GetCanvas();
696 canvas[
"style"].set(
"width", val(Width() * GetDrawScale()));
697 canvas[
"style"].set(
"height", val(Height() * GetDrawScale()));
699 canvas.set(
"width", Width() * GetBackingPixelScale());
700 canvas.set(
"height", Height() * GetBackingPixelScale());
702 IGRAPHICS_DRAW_CLASS::DrawResize();
705PlatformFontPtr IGraphicsWeb::LoadPlatformFont(
const char* fontID,
const char* fileNameOrResID)
708 const EResourceLocation fontLocation =
LocateResource(fileNameOrResID,
"ttf", fullPath, GetBundleID(),
nullptr,
nullptr);
710 if (fontLocation == kNotFound)
713 return PlatformFontPtr(
new FileFont(fontID,
"", fullPath.Get()));
716PlatformFontPtr IGraphicsWeb::LoadPlatformFont(
const char* fontID,
const char* fontName, ETextStyle style)
718 const char* styles[] = {
"normal",
"bold",
"italic" };
720 return PlatformFontPtr(
new Font(fontName, styles[
static_cast<int>(style)]));
723PlatformFontPtr IGraphicsWeb::LoadPlatformFont(
const char* fontID,
void* pData,
int dataSize)
725 return PlatformFontPtr(
new MemoryFont(fontID,
"", pData, dataSize));
728#if defined IGRAPHICS_CANVAS
729#include "IGraphicsCanvas.cpp"
730#elif defined IGRAPHICS_NANOVG
731#include "IGraphicsNanoVG.cpp"
733#ifdef IGRAPHICS_FREETYPE
734#define FONS_USE_FREETYPE
EResourceLocation LocateResource(const char *fileNameOrResID, const char *type, WDL_String &result, const char *bundleID, void *pHInstance, const char *sharedResourcesSubPath)
Find the absolute path of a resource based on it's file name (e.g.
An editor delegate base class that uses IGraphics for the UI.
The lowest level base class of an IGraphics context.
virtual ECursor SetMouseCursor(ECursor cursorType=ECursor::ARROW)
Sets the mouse cursor to one of ECursor (implementations should return the result of the base impleme...
void OnMouseDrag(const std::vector< IMouseInfo > &points)
Called when the platform class sends drag events.
void OnMouseUp(const std::vector< IMouseInfo > &points)
Called when the platform class sends mouse up events.
void OnTouchCancelled(const std::vector< IMouseInfo > &points)
Called when the platform class sends touch cancel events.
void OnMouseDown(const std::vector< IMouseInfo > &points)
Called when the platform class sends mouse down events.
bool OnMouseWheel(float x, float y, const IMouseMod &mod, float delta)
float GetDrawScale() const
Gets the graphics context scaling factor.
IGraphics platform class for the web.
EParamType Type() const
Get the parameter's type.
Used to manage a list of rectangular areas and optimize them for drawing to the screen.
Used to group mouse coordinates with mouse modifier information.
int DOMKeyToVirtualKey(uint32_t domKeyCode)
Converts a DOM virtual key code to an iPlug2 virtual key code.
Used to manage color data, independent of draw class/platform.
Used for key press info, such as ASCII representation, virtual key (mapped to win32 codes) and modifi...
Used to manage mouse modifiers i.e.
Used to manage a rectangular area, independent of draw class/platform.
IText is used to manage font and text/text entry style for a piece of text on the UI,...