213 lines
6.4 KiB
C++
213 lines
6.4 KiB
C++
/*
|
|
==============================================================================
|
|
|
|
PluginEditor.cpp
|
|
Created: 4 Nov 2025 6:20:46pm
|
|
Author: mickl
|
|
|
|
==============================================================================
|
|
*/
|
|
|
|
#include "PluginEditor.h"
|
|
#include "DemoUtilities.h"
|
|
|
|
|
|
|
|
static ZipFile* getZipFile()
|
|
{
|
|
static auto stream = createAssetInputStream("webviewplugin-gui_1.0.0.zip", AssertAssetExists::no);
|
|
|
|
if (stream == nullptr)
|
|
return nullptr;
|
|
|
|
static ZipFile f{ stream.get(), false };
|
|
return &f;
|
|
}
|
|
|
|
static const char* getMimeForExtension(const String& extension)
|
|
{
|
|
static const std::unordered_map<String, const char*> mimeMap =
|
|
{
|
|
{ { "htm" }, "text/html" },
|
|
{ { "html" }, "text/html" },
|
|
{ { "txt" }, "text/plain" },
|
|
{ { "jpg" }, "image/jpeg" },
|
|
{ { "jpeg" }, "image/jpeg" },
|
|
{ { "svg" }, "image/svg+xml" },
|
|
{ { "ico" }, "image/vnd.microsoft.icon" },
|
|
{ { "json" }, "application/json" },
|
|
{ { "png" }, "image/png" },
|
|
{ { "css" }, "text/css" },
|
|
{ { "map" }, "application/json" },
|
|
{ { "js" }, "text/javascript" },
|
|
{ { "woff2" }, "font/woff2" }
|
|
};
|
|
|
|
if (const auto it = mimeMap.find(extension.toLowerCase()); it != mimeMap.end())
|
|
return it->second;
|
|
|
|
jassertfalse;
|
|
return "";
|
|
}
|
|
|
|
static String getExtension(String filename)
|
|
{
|
|
return filename.fromLastOccurrenceOf(".", false, false);
|
|
}
|
|
|
|
static auto streamToVector(InputStream& stream)
|
|
{
|
|
std::vector<std::byte> result((size_t)stream.getTotalLength());
|
|
stream.setPosition(0);
|
|
[[maybe_unused]] const auto bytesRead = stream.read(result.data(), result.size());
|
|
jassert(bytesRead == (ssize_t)result.size());
|
|
return result;
|
|
}
|
|
|
|
std::optional<WebBrowserComponent::Resource> WebViewPluginAudioProcessorEditor::getResource(const String& url)
|
|
{
|
|
const auto urlToRetrive = url == "/" ? String{ "index.html" }
|
|
: url.fromFirstOccurrenceOf("/", false, false);
|
|
|
|
if (auto* archive = getZipFile())
|
|
{
|
|
if (auto* entry = archive->getEntry(urlToRetrive))
|
|
{
|
|
auto stream = rawToUniquePtr(archive->createStreamForEntry(*entry));
|
|
auto v = streamToVector(*stream);
|
|
auto mime = getMimeForExtension(getExtension(entry->filename).toLowerCase());
|
|
return WebBrowserComponent::Resource{ std::move(v),
|
|
std::move(mime) };
|
|
}
|
|
}
|
|
|
|
if (urlToRetrive == "index.html")
|
|
{
|
|
auto fallbackIndexHtml = createAssetInputStream("webviewplugin-gui-fallback.html");
|
|
return WebBrowserComponent::Resource{ streamToVector(*fallbackIndexHtml),
|
|
String { "text/html" } };
|
|
}
|
|
|
|
if (urlToRetrive == "data.txt")
|
|
{
|
|
WebBrowserComponent::Resource resource;
|
|
static constexpr char testData[] = "testdata";
|
|
MemoryInputStream stream{ testData, numElementsInArray(testData) - 1, false };
|
|
return WebBrowserComponent::Resource{ streamToVector(stream), String { "text/html" } };
|
|
}
|
|
|
|
if (urlToRetrive == "midNoteData.json")
|
|
{
|
|
|
|
juce::Array<var> notes;
|
|
int voice_num = 0;
|
|
for (auto& voice : processorRef.shifter.voices) {
|
|
if (voice.onoff_) {
|
|
auto obj = new DynamicObject();
|
|
obj->setProperty("voice", voice_num);
|
|
obj->setProperty("midi", voice.GetMidiNote());
|
|
notes.add(var(obj));
|
|
}
|
|
voice_num++;
|
|
}
|
|
|
|
DynamicObject::Ptr d(new DynamicObject());
|
|
d->setProperty("notes", notes);
|
|
|
|
const auto s = JSON::toString(d.get());
|
|
MemoryInputStream stream{ s.getCharPointer(), s.getNumBytesAsUTF8(), false };
|
|
return WebBrowserComponent::Resource{ streamToVector(stream), String { "application/json" } };
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
#if JUCE_ANDROID
|
|
// The localhost is available on this address to the emulator
|
|
const String localDevServerAddress = "http://10.0.2.2:3000/";
|
|
#else
|
|
const String localDevServerAddress = "http://localhost:3000/";
|
|
#endif
|
|
|
|
bool SinglePageBrowser::pageAboutToLoad(const String& newURL)
|
|
{
|
|
return newURL == localDevServerAddress || newURL == getResourceProviderRoot();
|
|
}
|
|
|
|
|
|
WebViewPluginAudioProcessorEditor::WebViewPluginAudioProcessorEditor(WebViewPluginAudioProcessor& p)
|
|
: AudioProcessorEditor(&p), processorRef(p)
|
|
{
|
|
auto options = WebBrowserComponent::Options{}
|
|
.withBackend(WebBrowserComponent::Options::Backend::webview2)
|
|
.withWinWebView2Options(WebBrowserComponent::Options::WinWebView2{}
|
|
.withUserDataFolder(File::getSpecialLocation(File::SpecialLocationType::tempDirectory)))
|
|
.withNativeIntegrationEnabled()
|
|
.withOptionsFrom(controlParameterIndexReceiver)
|
|
.withResourceProvider([this](const auto& url)
|
|
{
|
|
return getResource(url);
|
|
},
|
|
URL{ localDevServerAddress }.getOrigin());
|
|
|
|
|
|
for (auto& sliderId : p.parameters.sliderIds) {
|
|
slider_relays.push_back(new WebSliderRelay{ sliderId });
|
|
slider_attatchments.push_back(new
|
|
WebSliderParameterAttachment(
|
|
*processorRef.state.getParameter(sliderId),
|
|
*slider_relays.back(),
|
|
processorRef.state.undoManager));
|
|
options = options.withOptionsFrom(*slider_relays.back());
|
|
}
|
|
|
|
for (auto& toggleId : p.parameters.toggleIds) {
|
|
toggle_relays.push_back(new WebToggleButtonRelay{ toggleId });
|
|
toggle_attatchments.push_back(new
|
|
WebToggleButtonParameterAttachment(
|
|
*processorRef.state.getParameter(toggleId),
|
|
*toggle_relays.back(),
|
|
processorRef.state.undoManager));
|
|
options = options.withOptionsFrom(*toggle_relays.back());
|
|
}
|
|
|
|
webComponent = new SinglePageBrowser(options);
|
|
addAndMakeVisible(*webComponent);
|
|
|
|
webComponent->goToURL(localDevServerAddress);
|
|
|
|
//webComponent.goToURL (WebBrowserComponent::getResourceProviderRoot());
|
|
setSize(800, 390);
|
|
|
|
|
|
startTimerHz(60);
|
|
/*for (int i = 0; i < processorRef.parameters.sliderIds.size(); ++i) {
|
|
slider_attatchments.push_back(new
|
|
WebSliderParameterAttachment(
|
|
*processorRef.state.getParameter(processorRef.parameters.sliderIds[i]),
|
|
*slider_relays.back(),
|
|
processorRef.state.undoManager));
|
|
}
|
|
|
|
for (int i = 0; i < processorRef.parameters.toggleIds.size(); ++i) {
|
|
toggle_attatchments.push_back(new
|
|
WebToggleButtonParameterAttachment(
|
|
*processorRef.state.getParameter(processorRef.parameters.toggleIds[i]),
|
|
*toggle_relays.back(),
|
|
processorRef.state.undoManager));
|
|
}*/
|
|
|
|
}
|
|
|
|
void WebViewPluginAudioProcessorEditor::paint(Graphics& g)
|
|
{
|
|
// (Our component is opaque, so we must completely fill the background with a solid colour)
|
|
g.fillAll(getLookAndFeel().findColour(ResizableWindow::backgroundColourId));
|
|
}
|
|
|
|
void WebViewPluginAudioProcessorEditor::resized()
|
|
{
|
|
if (webComponent == nullptr) return;
|
|
webComponent->setBounds(getLocalBounds());
|
|
}
|