diff --git a/modules/juce_gui_extra/juce_gui_extra.h b/modules/juce_gui_extra/juce_gui_extra.h index 08afbb0056..c7c7c956c2 100644 --- a/modules/juce_gui_extra/juce_gui_extra.h +++ b/modules/juce_gui_extra/juce_gui_extra.h @@ -158,3 +158,4 @@ #include "misc/juce_AnimatedAppComponent.h" #include "detail/juce_WebControlRelayEvents.h" #include "misc/juce_WebControlRelays.h" +#include "misc/juce_WebControlParameterIndexReceiver.h" diff --git a/modules/juce_gui_extra/misc/juce_WebControlParameterIndexReceiver.h b/modules/juce_gui_extra/misc/juce_WebControlParameterIndexReceiver.h new file mode 100644 index 0000000000..7e953760c4 --- /dev/null +++ b/modules/juce_gui_extra/misc/juce_WebControlParameterIndexReceiver.h @@ -0,0 +1,74 @@ +/* + ============================================================================== + + This file is part of the JUCE framework. + Copyright (c) Raw Material Software Limited + + JUCE is an open source framework subject to commercial or open source + licensing. + + By downloading, installing, or using the JUCE framework, or combining the + JUCE framework with any other source code, object code, content or any other + copyrightable work, you agree to the terms of the JUCE End User Licence + Agreement, and all incorporated terms including the JUCE Privacy Policy and + the JUCE Website Terms of Service, as applicable, which will bind you. If you + do not agree to the terms of these agreements, we will not license the JUCE + framework to you, and you must discontinue the installation or download + process and cease use of the JUCE framework. + + JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/ + JUCE Privacy Policy: https://juce.com/juce-privacy-policy + JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/ + + Or: + + You may also use this code under the terms of the AGPLv3: + https://www.gnu.org/licenses/agpl-3.0.en.html + + THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL + WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED. + + ============================================================================== +*/ + +namespace juce +{ + +#if JUCE_WEB_BROWSER || DOXYGEN + +/** This is a helper class for implementing AudioProcessorEditor::getControlParameterIndex with GUIs + using a WebBrowserComponent. + + Create an instance of this class and attach it to the WebBrowserComponent by using + WebBrowserComponent::Options::withOptionsFrom. + + In your frontend code you can use the ControlParameterIndexUpdater class, that emits + controlParameterIndexChanged events based on the mouse movement, and control parameter index + annotations attached to DOM elements. +*/ +class JUCE_API WebControlParameterIndexReceiver : public OptionsBuilder +{ +public: + /* Returns the control parameter index last reported by the WebBrowserComponent GUI to be + active. + */ + int getControlParameterIndex() const { return controlParameterIndex; } + + //============================================================================== + WebBrowserComponent::Options buildOptions (const WebBrowserComponent::Options& initialOptions) override + { + return initialOptions.withEventListener ("__juce__controlParameterIndexChanged", + [this] (auto newIndex) + { + controlParameterIndex = (int) newIndex; + }); + } + +private: + int controlParameterIndex = -1; +}; + +#endif + +} diff --git a/modules/juce_gui_extra/native/javascript/index.js b/modules/juce_gui_extra/native/javascript/index.js index 5710789436..ddeaea8df9 100644 --- a/modules/juce_gui_extra/native/javascript/index.js +++ b/modules/juce_gui_extra/native/javascript/index.js @@ -415,10 +415,78 @@ function getBackendResourceAddress(path) { return path; } +/** + * This helper class is intended to aid the implementation of + * AudioProcessorEditor::getControlParameterIndex() for editors using a WebView interface. + * + * Create an instance of this class and call its handleMouseMove() method in each mousemove event. + * + * This class can be used to continuously report the controlParameterIndexAnnotation attribute's + * value related to the DOM element that is currently under the mouse pointer. + * + * This value is defined at all times as follows + * * the annotation attribute's value for the DOM element directly under the mouse, if it has it, + * * the annotation attribute's value for the first parent element, that has it, + * * -1 otherwise. + * + * Whenever there is a change in this value, an event is emitted to the frontend with the new value. + * You can use a ControlParameterIndexReceiver object on the backend to listen to these events. + * + * @param {String} controlParameterIndexAnnotation + */ +class ControlParameterIndexUpdater { + constructor(controlParameterIndexAnnotation) { + this.controlParameterIndexAnnotation = controlParameterIndexAnnotation; + this.lastElement = null; + this.lastControlParameterIndex = null; + } + + handleMouseMove(event) { + const currentElement = document.elementFromPoint( + event.clientX, + event.clientY + ); + + if (currentElement === this.lastElement) return; + this.lastElement = currentElement; + + let controlParameterIndex = -1; + + if (currentElement !== null) + controlParameterIndex = this.#getControlParameterIndex(currentElement); + + if (controlParameterIndex === this.lastControlParameterIndex) return; + this.lastControlParameterIndex = controlParameterIndex; + + window.__JUCE__.backend.emitEvent( + "__juce__controlParameterIndexChanged", + controlParameterIndex + ); + } + + //============================================================================== + #getControlParameterIndex(element) { + const isValidNonRootElement = (e) => { + return e !== null && e !== document.documentElement; + }; + + while (isValidNonRootElement(element)) { + if (element.hasAttribute(this.controlParameterIndexAnnotation)) { + return element.getAttribute(this.controlParameterIndexAnnotation); + } + + element = element.parentElement; + } + + return -1; + } +} + export { getNativeFunction, getSliderState, getToggleState, getComboBoxState, getBackendResourceAddress, + ControlParameterIndexUpdater, };