iPlug2 - C++ Audio Plug-in Framework
Loading...
Searching...
No Matches
IPlugWebViewEditorDelegate.h
Go to the documentation of this file.
1 /*
2 ==============================================================================
3
4 MIT License
5
6 iPlug2 WebView Library
7 Copyright (c) 2024 Oliver Larkin
8
9 Permission is hereby granted, free of charge, to any person obtaining a copy
10 of this software and associated documentation files (the "Software"), to deal
11 in the Software without restriction, including without limitation the rights
12 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 copies of the Software, and to permit persons to whom the Software is
14 furnished to do so, subject to the following conditions:
15
16 The above copyright notice and this permission notice shall be included in all
17 copies or substantial portions of the Software.
18
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 SOFTWARE.
26
27 ==============================================================================
28*/
29
30#pragma once
31
32#include "IPlugEditorDelegate.h"
33#include "IPlugWebView.h"
34#include "wdl_base64.h"
35#include "json.hpp"
36#include <functional>
37#include <filesystem>
38
44BEGIN_IPLUG_NAMESPACE
45
49 , public IWebView
50{
51 static constexpr int kDefaultMaxJSStringLength = 8192;
52
53public:
54 WebViewEditorDelegate(int nParams);
55 virtual ~WebViewEditorDelegate();
56
57 //IEditorDelegate
58 void* OpenWindow(void* pParent) override;
59
60 void CloseWindow() override
61 {
62 CloseWebView();
63 }
64
65 void SendControlValueFromDelegate(int ctrlTag, double normalizedValue) override
66 {
67 WDL_String str;
68 str.SetFormatted(mMaxJSStringLength, "SCVFD(%i, %f)", ctrlTag, normalizedValue);
69 EvaluateJavaScript(str.Get());
70 }
71
72 void SendControlMsgFromDelegate(int ctrlTag, int msgTag, int dataSize, const void* pData) override
73 {
74 WDL_String str;
75 std::vector<char> base64;
76 base64.resize(GetBase64Length(dataSize) + 1);
77 wdl_base64encode(reinterpret_cast<const unsigned char*>(pData), base64.data(), dataSize);
78 str.SetFormatted(mMaxJSStringLength, "SCMFD(%i, %i, %i, '%s')", ctrlTag, msgTag, dataSize, base64.data());
79 EvaluateJavaScript(str.Get());
80 }
81
82 void SendParameterValueFromDelegate(int paramIdx, double value, bool normalized) override
83 {
84 WDL_String str;
85
86 if (!normalized)
87 {
88 value = GetParam(paramIdx)->ToNormalized(value);
89 }
90
91 str.SetFormatted(mMaxJSStringLength, "SPVFD(%i, %f)", paramIdx, value);
92 EvaluateJavaScript(str.Get());
93 }
94
95 void SendArbitraryMsgFromDelegate(int msgTag, int dataSize, const void* pData) override
96 {
97 WDL_String str;
98 std::vector<char> base64;
99 if (dataSize)
100 {
101 base64.resize(GetBase64Length(dataSize) + 1);
102 wdl_base64encode(reinterpret_cast<const unsigned char*>(pData), base64.data(), dataSize);
103 }
104 str.SetFormatted(mMaxJSStringLength, "SAMFD(%i, %i, '%s')", msgTag, static_cast<int>(base64.size()), base64.data());
105 EvaluateJavaScript(str.Get());
106 }
107
108 void SendMidiMsgFromDelegate(const IMidiMsg& msg) override
109 {
110 WDL_String str;
111 str.SetFormatted(mMaxJSStringLength, "SMMFD(%i, %i, %i)", msg.mStatus, msg.mData1, msg.mData2);
112 EvaluateJavaScript(str.Get());
113 }
114
115 bool OnKeyDown(const IKeyPress& key) override;
116 bool OnKeyUp(const IKeyPress& key) override;
117
118 // IWebView
119
120 void SendJSONFromDelegate(const nlohmann::json& jsonMessage)
121 {
122 SendArbitraryMsgFromDelegate(-1, static_cast<int>(jsonMessage.dump().size()), jsonMessage.dump().c_str());
123 }
124
125 void OnMessageFromWebView(const char* jsonStr) override
126 {
127 auto json = nlohmann::json::parse(jsonStr, nullptr, false);
128
129 if (json["msg"] == "SPVFUI")
130 {
131 assert(json["paramIdx"] > -1);
132 SendParameterValueFromUI(json["paramIdx"], json["value"]);
133 }
134 else if (json["msg"] == "BPCFUI")
135 {
136 assert(json["paramIdx"] > -1);
137 BeginInformHostOfParamChangeFromUI(json["paramIdx"]);
138 }
139 else if (json["msg"] == "EPCFUI")
140 {
141 assert(json["paramIdx"] > -1);
142 EndInformHostOfParamChangeFromUI(json["paramIdx"]);
143 }
144 else if (json["msg"] == "SAMFUI")
145 {
146 std::vector<unsigned char> base64;
147
148 if(json.count("data") > 0 && json["data"].is_string())
149 {
150 auto dStr = json["data"].get<std::string>();
151 int dSize = static_cast<int>(dStr.size());
152
153 // calculate the exact size of the decoded base64 data
154 int numPaddingBytes = 0;
155
156 if(dSize >= 2 && dStr[dSize-2] == '=')
157 numPaddingBytes = 2;
158 else if(dSize >= 1 && dStr[dSize-1] == '=')
159 numPaddingBytes = 1;
160
161 base64.resize((dSize * 3) / 4 - numPaddingBytes);
162 wdl_base64decode(dStr.c_str(), base64.data(), static_cast<int>(base64.size()));
163 }
164
165 SendArbitraryMsgFromUI(json["msgTag"], json["ctrlTag"], static_cast<int>(base64.size()), base64.data());
166 }
167 else if(json["msg"] == "SMMFUI")
168 {
169 IMidiMsg msg {0, json["statusByte"].get<uint8_t>(),
170 json["dataByte1"].get<uint8_t>(),
171 json["dataByte2"].get<uint8_t>()};
173 }
174 else if(json["msg"] == "SKPFUI")
175 {
176 IKeyPress keyPress = ConvertToIKeyPress(json["keyCode"].get<uint32_t>(), json["utf8"].get<std::string>().c_str(), json["S"].get<bool>(), json["C"].get<bool>(), json["A"].get<bool>());
177 json["isUp"].get<bool>() ? OnKeyUp(keyPress) : OnKeyDown(keyPress); // return value not used
178 }
179 }
180
181 void Resize(int width, int height);
182
183 void OnParentWindowResize(int width, int height) override;
184
185 void OnWebViewReady() override
186 {
187 if (mEditorInitFunc)
188 {
189 mEditorInitFunc();
190 }
191 }
192
193 void OnWebContentLoaded() override
194 {
195 nlohmann::json msg;
196
197 msg["id"] = "params";
198 std::vector<nlohmann::json> params;
199 for (int idx = 0; idx < NParams(); idx++)
200 {
201 WDL_String jsonStr;
202 IParam* pParam = GetParam(idx);
203 pParam->GetJSON(jsonStr, idx);
204 nlohmann::json paramMsg = nlohmann::json::parse(jsonStr.Get(), nullptr, true);
205 params.push_back(paramMsg);
206 }
207 msg["params"] = params;
208
209 SendJSONFromDelegate(msg);
210
211 OnUIOpen();
212 }
213
214 void SetMaxJSStringLength(int length)
215 {
216 mMaxJSStringLength = length;
217 }
218
226 void LoadIndexHtml(const char* pathOfPluginSrc, const char* bundleid)
227 {
228#if !defined OS_IOS && defined _DEBUG
229 namespace fs = std::filesystem;
230
231 fs::path mainPath(pathOfPluginSrc);
232 fs::path indexRelativePath = mainPath.parent_path() / "Resources" / "web" / "index.html";
233
234 LoadFile(indexRelativePath.string().c_str(), nullptr);
235#else
236 LoadFile("index.html", bundleid); // TODO: make this work for windows
237#endif
238 }
239
240protected:
241 int mMaxJSStringLength = kDefaultMaxJSStringLength;
242 std::function<void()> mEditorInitFunc = nullptr;
243 void* mView = nullptr;
244
245private:
246 IKeyPress ConvertToIKeyPress(uint32_t keyCode, const char* utf8, bool shift, bool ctrl, bool alt)
247 {
248 return IKeyPress(utf8, DOMKeyToVirtualKey(keyCode), shift,ctrl, alt);
249 }
250
251 static int GetBase64Length(int dataSize)
252 {
253 return static_cast<int>(4. * std::ceil((static_cast<double>(dataSize) / 3.)));
254 }
255
256#if defined OS_MAC || defined OS_IOS
257 void ResizeWebViewAndHelper(float width, float height);
258#endif
259};
260
261END_IPLUG_NAMESPACE
This pure virtual interface delegates communication in both directions between a UI editor and someth...
virtual void BeginInformHostOfParamChangeFromUI(int paramIdx)=0
Called by the UI at the beginning of a parameter change gesture, in order to notify the host (via a c...
IParam * GetParam(int paramIdx)
Get a pointer to one of the delegate's IParam objects.
virtual void SendParameterValueFromUI(int paramIdx, double normalizedValue)
SPVFUI Called by the UI during a parameter change gesture, in order to notify the host of the new val...
virtual void EndInformHostOfParamChangeFromUI(int paramIdx)=0
Called by the user interface at the end of a parameter change gesture, in order to notify the host (v...
virtual void SendMidiMsgFromUI(const IMidiMsg &msg)
SendMidiMsgFromUI (Abbreviation: SMMFUI) This method should be used when sending a MIDI message from ...
virtual void SendArbitraryMsgFromUI(int msgTag, int ctrlTag=kNoTag, int dataSize=0, const void *pData=nullptr)
SendArbitraryMsgFromUI (Abbreviation: SAMFUI)
virtual void OnUIOpen()
Override this method to do something before the UI is opened.
IPlug's parameter class.
double ToNormalized(double nonNormalizedValue) const
Convert a real value to normalized value for this parameter.
void GetJSON(WDL_String &json, int idx) const
Get a JSON description of the parameter.
IWebView is a base interface for hosting a platform web view inside an IPlug plug-in's UI.
Definition: IPlugWebView.h:44
void EvaluateJavaScript(const char *scriptStr, completionHandlerFunc func=nullptr)
Runs some JavaScript in the webview.
void LoadFile(const char *fileName, const char *bundleID="")
Load a file on disk into the web view.
An editor delegate base class that uses a platform native webview for the UI.
bool OnKeyDown(const IKeyPress &key) override
KeyDown handler, in order to get keystrokes from certain hosts/plugin formats that send key press mes...
void OnMessageFromWebView(const char *jsonStr) override
When a script in the web view posts a message, it will arrive as a UTF8 json string here.
void SendArbitraryMsgFromDelegate(int msgTag, int dataSize, const void *pData) override
SendArbitraryMsgFromDelegate (Abbreviation: SAMFD) WARNING: should not be called on the realtime audi...
void SendMidiMsgFromDelegate(const IMidiMsg &msg) override
SendMidiMsgFromDelegate (Abbreviation: SMMFD) WARNING: should not be called on the realtime audio thr...
void CloseWindow() override
If you are not using IGraphics you can if you need to free resources etc when the window closes.
bool OnKeyUp(const IKeyPress &key) override
KeyDown handler, in order to get keystrokes from certain hosts/plugin formats that send key press mes...
void SendControlValueFromDelegate(int ctrlTag, double normalizedValue) override
SendControlValueFromDelegate (Abbreviation: SCVFD) WARNING: should not be called on the realtime audi...
void LoadIndexHtml(const char *pathOfPluginSrc, const char *bundleid)
Load index.html (from plugin src dir in debug builds, and from bundle in release builds) on desktop N...
void OnWebViewReady() override
Called when the web view is ready to receive navigation instructions.
void OnParentWindowResize(int width, int height) override
Called by app wrappers when the OS window scaling buttons/resizers are used.
void * OpenWindow(void *pParent) override
If you are not using IGraphics, you can implement this method to attach to the native parent view e....
void SendControlMsgFromDelegate(int ctrlTag, int msgTag, int dataSize, const void *pData) override
SendControlMsgFromDelegate (Abbreviation: SCMFD) WARNING: should not be called on the realtime audio ...
void OnWebContentLoaded() override
Called after navigation instructions have been exectued and e.g.
void SendParameterValueFromDelegate(int paramIdx, double value, bool normalized) override
SendParameterValueFromDelegate (Abbreviation: SPVFD) WARNING: should not be called on the realtime au...
int DOMKeyToVirtualKey(uint32_t domKeyCode)
Converts a DOM virtual key code to an iPlug2 virtual key code.
Used for key press info, such as ASCII representation, virtual key (mapped to win32 codes) and modifi...
Definition: IPlugStructs.h:616
Encapsulates a MIDI message and provides helper functions.
Definition: IPlugMidi.h:31