mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
WebBrowserComponent: Windows: Add accessibility integration
This commit is contained in:
parent
6ef45eb20c
commit
7657efd227
12 changed files with 381 additions and 29 deletions
|
|
@ -28,6 +28,63 @@ namespace juce
|
|||
|
||||
AccessibilityHandler* AccessibilityHandler::currentlyFocusedHandler = nullptr;
|
||||
|
||||
class NativeChildHandler
|
||||
{
|
||||
public:
|
||||
static NativeChildHandler& getInstance()
|
||||
{
|
||||
static NativeChildHandler instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void* getNativeChild (Component& component) const
|
||||
{
|
||||
if (auto it = nativeChildForComponent.find (&component);
|
||||
it != nativeChildForComponent.end())
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Component* getComponent (void* nativeChild) const
|
||||
{
|
||||
if (auto it = componentForNativeChild.find (nativeChild);
|
||||
it != componentForNativeChild.end())
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void setNativeChild (Component& component, void* nativeChild)
|
||||
{
|
||||
clearComponent (component);
|
||||
|
||||
if (nativeChild != nullptr)
|
||||
{
|
||||
nativeChildForComponent[&component] = nativeChild;
|
||||
componentForNativeChild[nativeChild] = &component;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
NativeChildHandler() = default;
|
||||
|
||||
void clearComponent (Component& component)
|
||||
{
|
||||
if (auto* nativeChild = getNativeChild (component))
|
||||
componentForNativeChild.erase (nativeChild);
|
||||
|
||||
nativeChildForComponent.erase (&component);
|
||||
}
|
||||
|
||||
std::map<void*, Component*> componentForNativeChild;
|
||||
std::map<Component*, void*> nativeChildForComponent;
|
||||
};
|
||||
|
||||
AccessibilityHandler::AccessibilityHandler (Component& comp,
|
||||
AccessibilityRole accessibilityRole,
|
||||
AccessibilityActions accessibilityActions,
|
||||
|
|
@ -322,6 +379,21 @@ std::unique_ptr<AccessibilityHandler::AccessibilityNativeImpl> AccessibilityHand
|
|||
#endif
|
||||
}
|
||||
|
||||
void* AccessibilityHandler::getNativeChildForComponent (Component& component)
|
||||
{
|
||||
return NativeChildHandler::getInstance().getNativeChild (component);
|
||||
}
|
||||
|
||||
Component* AccessibilityHandler::getComponentForNativeChild (void* nativeChild)
|
||||
{
|
||||
return NativeChildHandler::getInstance().getComponent (nativeChild);
|
||||
}
|
||||
|
||||
void AccessibilityHandler::setNativeChildForComponent (Component& component, void* nativeChild)
|
||||
{
|
||||
NativeChildHandler::getInstance().setNativeChild (component, nativeChild);
|
||||
}
|
||||
|
||||
#if ! JUCE_NATIVE_ACCESSIBILITY_INCLUDED
|
||||
void AccessibilityHandler::notifyAccessibilityEvent (AccessibilityEvent) const {}
|
||||
void AccessibilityHandler::postAnnouncement (const String&, AnnouncementPriority) {}
|
||||
|
|
|
|||
|
|
@ -291,6 +291,26 @@ public:
|
|||
AccessibilityNativeHandle* getNativeImplementation() const;
|
||||
/** @internal */
|
||||
std::type_index getTypeIndex() const { return typeIndex; }
|
||||
/** @internal */
|
||||
static void clearCurrentlyFocusedHandler() { currentlyFocusedHandler = nullptr; }
|
||||
|
||||
/** @internal
|
||||
|
||||
The following functions provide the means to associate JUCE Components with OS specific
|
||||
types that provide their own accessibility mechanisms. This way accessibility navigation
|
||||
can move from a JUCE Component to a native, embedded window and back.
|
||||
|
||||
These functions assume that the concrete types behind the void* are
|
||||
- Windows: HWND
|
||||
- MacOS: NSView*
|
||||
- iOS: UIView*
|
||||
- Android: GlobalRef that points to an android.view.View
|
||||
*/
|
||||
static void* getNativeChildForComponent (Component& component);
|
||||
/** @internal */
|
||||
static void setNativeChildForComponent (Component& component, void* nativeChild);
|
||||
/** @internal */
|
||||
static Component* getComponentForNativeChild (void* nativeChild);
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
|
|
|
|||
|
|
@ -2182,7 +2182,7 @@ void Component::internalMouseDown (MouseInputSource source,
|
|||
|
||||
if (! flags.dontFocusOnMouseClickFlag)
|
||||
{
|
||||
grabKeyboardFocusInternal (focusChangedByMouseClick, true);
|
||||
grabKeyboardFocusInternal (focusChangedByMouseClick, true, FocusChangeDirection::unknown);
|
||||
|
||||
if (checker.shouldBailOut())
|
||||
return;
|
||||
|
|
@ -2435,17 +2435,20 @@ void Component::internalBroughtToFront()
|
|||
|
||||
//==============================================================================
|
||||
void Component::focusGained (FocusChangeType) {}
|
||||
void Component::focusGainedWithDirection (FocusChangeType, FocusChangeDirection) {}
|
||||
void Component::focusLost (FocusChangeType) {}
|
||||
void Component::focusOfChildComponentChanged (FocusChangeType) {}
|
||||
|
||||
void Component::internalKeyboardFocusGain (FocusChangeType cause)
|
||||
{
|
||||
internalKeyboardFocusGain (cause, WeakReference<Component> (this));
|
||||
internalKeyboardFocusGain (cause, WeakReference<Component> (this), FocusChangeDirection::unknown);
|
||||
}
|
||||
|
||||
void Component::internalKeyboardFocusGain (FocusChangeType cause,
|
||||
const WeakReference<Component>& safePointer)
|
||||
const WeakReference<Component>& safePointer,
|
||||
FocusChangeDirection direction)
|
||||
{
|
||||
focusGainedWithDirection (cause, direction);
|
||||
focusGained (cause);
|
||||
|
||||
if (safePointer == nullptr)
|
||||
|
|
@ -2585,7 +2588,7 @@ std::unique_ptr<ComponentTraverser> Component::createKeyboardFocusTraverser()
|
|||
return parentComponent->createKeyboardFocusTraverser();
|
||||
}
|
||||
|
||||
void Component::takeKeyboardFocus (FocusChangeType cause)
|
||||
void Component::takeKeyboardFocus (FocusChangeType cause, FocusChangeDirection direction)
|
||||
{
|
||||
if (currentlyFocusedComponent == this)
|
||||
return;
|
||||
|
|
@ -2614,11 +2617,11 @@ void Component::takeKeyboardFocus (FocusChangeType cause)
|
|||
componentLosingFocus->internalKeyboardFocusLoss (cause);
|
||||
|
||||
if (currentlyFocusedComponent == this)
|
||||
internalKeyboardFocusGain (cause, safePointer);
|
||||
internalKeyboardFocusGain (cause, safePointer, direction);
|
||||
}
|
||||
}
|
||||
|
||||
void Component::grabKeyboardFocusInternal (FocusChangeType cause, bool canTryParent)
|
||||
void Component::grabKeyboardFocusInternal (FocusChangeType cause, bool canTryParent, FocusChangeDirection direction)
|
||||
{
|
||||
if (! isShowing())
|
||||
return;
|
||||
|
|
@ -2626,7 +2629,7 @@ void Component::grabKeyboardFocusInternal (FocusChangeType cause, bool canTryPar
|
|||
if (flags.wantsKeyboardFocusFlag
|
||||
&& (isEnabled() || parentComponent == nullptr))
|
||||
{
|
||||
takeKeyboardFocus (cause);
|
||||
takeKeyboardFocus (cause, direction);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -2637,7 +2640,7 @@ void Component::grabKeyboardFocusInternal (FocusChangeType cause, bool canTryPar
|
|||
{
|
||||
if (auto* defaultComp = traverser->getDefaultComponent (this))
|
||||
{
|
||||
defaultComp->grabKeyboardFocusInternal (cause, false);
|
||||
defaultComp->grabKeyboardFocusInternal (cause, false, direction);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -2645,7 +2648,7 @@ void Component::grabKeyboardFocusInternal (FocusChangeType cause, bool canTryPar
|
|||
// if no children want it and we're allowed to try our parent comp,
|
||||
// then pass up to parent, which will try our siblings.
|
||||
if (canTryParent && parentComponent != nullptr)
|
||||
parentComponent->grabKeyboardFocusInternal (cause, true);
|
||||
parentComponent->grabKeyboardFocusInternal (cause, true, direction);
|
||||
}
|
||||
|
||||
void Component::grabKeyboardFocus()
|
||||
|
|
@ -2654,7 +2657,7 @@ void Component::grabKeyboardFocus()
|
|||
// thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe.
|
||||
JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
|
||||
|
||||
grabKeyboardFocusInternal (focusChangedDirectly, true);
|
||||
grabKeyboardFocusInternal (focusChangedDirectly, true, FocusChangeDirection::unknown);
|
||||
|
||||
// A component can only be focused when it's actually on the screen!
|
||||
// If this fails then you're probably trying to grab the focus before you've
|
||||
|
|
@ -2730,7 +2733,10 @@ void Component::moveKeyboardFocusToSibling (bool moveToNext)
|
|||
return;
|
||||
}
|
||||
|
||||
nextComp->grabKeyboardFocusInternal (focusChangedByTabKey, true);
|
||||
nextComp->grabKeyboardFocusInternal (focusChangedByTabKey,
|
||||
true,
|
||||
moveToNext ? FocusChangeDirection::forward
|
||||
: FocusChangeDirection::backward);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1891,11 +1891,28 @@ public:
|
|||
focusChangedDirectly /**< Means that the focus was changed by a call to grabKeyboardFocus(). */
|
||||
};
|
||||
|
||||
/** Enumeration used by the focusGainedWithDirection() method. */
|
||||
enum class FocusChangeDirection
|
||||
{
|
||||
unknown,
|
||||
forward,
|
||||
backward
|
||||
};
|
||||
|
||||
/** Called to indicate that this component has just acquired the keyboard focus.
|
||||
@see focusLost, setWantsKeyboardFocus, getCurrentlyFocusedComponent, hasKeyboardFocus
|
||||
*/
|
||||
virtual void focusGained (FocusChangeType cause);
|
||||
|
||||
/** Called to indicate that this component has just acquired the keyboard focus.
|
||||
|
||||
This function is called every time focusGained() is called but it has an additional change
|
||||
direction parameter.
|
||||
|
||||
@see focusLost, setWantsKeyboardFocus, getCurrentlyFocusedComponent, hasKeyboardFocus
|
||||
*/
|
||||
virtual void focusGainedWithDirection (FocusChangeType cause, FocusChangeDirection direction);
|
||||
|
||||
/** Called to indicate that this component has just lost the keyboard focus.
|
||||
@see focusGained, setWantsKeyboardFocus, getCurrentlyFocusedComponent, hasKeyboardFocus
|
||||
*/
|
||||
|
|
@ -2614,7 +2631,7 @@ private:
|
|||
void internalMouseWheel (MouseInputSource, Point<float>, Time, const MouseWheelDetails&);
|
||||
void internalMagnifyGesture (MouseInputSource, Point<float>, Time, float);
|
||||
void internalBroughtToFront();
|
||||
void internalKeyboardFocusGain (FocusChangeType, const WeakReference<Component>&);
|
||||
void internalKeyboardFocusGain (FocusChangeType, const WeakReference<Component>&, FocusChangeDirection);
|
||||
void internalKeyboardFocusGain (FocusChangeType);
|
||||
void internalKeyboardFocusLoss (FocusChangeType);
|
||||
void internalChildKeyboardFocusChange (FocusChangeType, const WeakReference<Component>&);
|
||||
|
|
@ -2632,8 +2649,8 @@ private:
|
|||
void sendMovedResizedMessagesIfPending();
|
||||
void repaintParent();
|
||||
void sendFakeMouseMove() const;
|
||||
void takeKeyboardFocus (FocusChangeType);
|
||||
void grabKeyboardFocusInternal (FocusChangeType, bool canTryParent);
|
||||
void takeKeyboardFocus (FocusChangeType, FocusChangeDirection);
|
||||
void grabKeyboardFocusInternal (FocusChangeType, bool canTryParent, FocusChangeDirection);
|
||||
void giveAwayKeyboardFocusInternal (bool sendFocusLossEvent);
|
||||
void sendEnablementChangeMessage();
|
||||
void sendVisibilityChangeMessage();
|
||||
|
|
|
|||
|
|
@ -166,10 +166,15 @@ JUCE_COMRESULT AccessibilityNativeHandle::get_HostRawElementProvider (IRawElemen
|
|||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]
|
||||
{
|
||||
if (isFragmentRoot())
|
||||
if (auto* wrapper = WindowsUIAWrapper::getInstanceWithoutCreating())
|
||||
if (auto* wrapper = WindowsUIAWrapper::getInstanceWithoutCreating())
|
||||
{
|
||||
if (isFragmentRoot())
|
||||
return wrapper->hostProviderFromHwnd ((HWND) accessibilityHandler.getComponent().getWindowHandle(), pRetVal);
|
||||
|
||||
if (auto* embeddedWindow = static_cast<HWND> (AccessibilityHandler::getNativeChildForComponent (accessibilityHandler.getComponent())))
|
||||
return wrapper->hostProviderFromHwnd (embeddedWindow, pRetVal);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
|
@ -180,6 +185,10 @@ JUCE_COMRESULT AccessibilityNativeHandle::get_ProviderOptions (ProviderOptions*
|
|||
return E_INVALIDARG;
|
||||
|
||||
*options = (ProviderOptions) (ProviderOptions_ServerSideProvider | ProviderOptions_UseComThreading);
|
||||
|
||||
if (AccessibilityHandler::getNativeChildForComponent (accessibilityHandler.getComponent()) != nullptr)
|
||||
*options |= ProviderOptions_OverrideProvider;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
|
@ -618,6 +627,18 @@ JUCE_COMRESULT AccessibilityNativeHandle::GetFocus (ComTypes::IRawElementProvide
|
|||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT AccessibilityNativeHandle::GetOverrideProviderForHwnd (HWND hwnd, IRawElementProviderSimple** pRetVal)
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]
|
||||
{
|
||||
if (auto* component = AccessibilityHandler::getComponentForNativeChild (hwnd))
|
||||
if (auto* handler = component->getAccessibilityHandler())
|
||||
handler->getNativeImplementation()->QueryInterface (IID_PPV_ARGS (pRetVal));
|
||||
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
String AccessibilityNativeHandle::getElementName() const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ namespace juce
|
|||
{
|
||||
|
||||
class AccessibilityNativeHandle : public ComBaseClassHelper<IRawElementProviderSimple,
|
||||
IRawElementProviderHwndOverride,
|
||||
ComTypes::IRawElementProviderFragment,
|
||||
ComTypes::IRawElementProviderFragmentRoot>
|
||||
{
|
||||
|
|
@ -58,6 +59,8 @@ public:
|
|||
JUCE_COMRESULT ElementProviderFromPoint (double x, double y, ComTypes::IRawElementProviderFragment** pRetVal) override;
|
||||
JUCE_COMRESULT GetFocus (ComTypes::IRawElementProviderFragment** pRetVal) override;
|
||||
|
||||
JUCE_COMRESULT GetOverrideProviderForHwnd (HWND hwnd, IRawElementProviderSimple** pRetVal) override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
String getElementName() const;
|
||||
|
|
|
|||
|
|
@ -4147,6 +4147,21 @@ private:
|
|||
|
||||
//==============================================================================
|
||||
case WM_SETFOCUS:
|
||||
/* When the HWND receives Focus from the system it sends a
|
||||
UIA_AutomationFocusChangedEventId notification redirecting the focus to the HWND
|
||||
itself. This is a built-in behaviour of the HWND.
|
||||
|
||||
This means that whichever JUCE managed provider was active before the entire
|
||||
window lost and then regained the focus, loses its focused state, and the
|
||||
window's root element will become focused under which all JUCE managed providers
|
||||
can be found.
|
||||
|
||||
This needs to be reflected on currentlyFocusedHandler so that the JUCE
|
||||
accessibility mechanisms can detect that the root window got the focus, and send
|
||||
another FocusChanged event to the system to redirect focus to a JUCE managed
|
||||
provider if necessary.
|
||||
*/
|
||||
AccessibilityHandler::clearCurrentlyFocusedHandler();
|
||||
updateKeyModifiers();
|
||||
handleFocusGain();
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -264,12 +264,17 @@ public:
|
|||
/** @internal */
|
||||
void visibilityChanged() override;
|
||||
/** @internal */
|
||||
void focusGained (FocusChangeType) override;
|
||||
void focusGainedWithDirection (FocusChangeType, FocusChangeDirection) override;
|
||||
|
||||
/** @internal */
|
||||
class Pimpl;
|
||||
|
||||
private:
|
||||
std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override
|
||||
{
|
||||
return std::make_unique<AccessibilityHandler> (*this, AccessibilityRole::group);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
std::unique_ptr<Pimpl> browser;
|
||||
bool blankPageShown = false, unloadPageWhenHidden;
|
||||
|
|
|
|||
|
|
@ -676,7 +676,7 @@ void WebBrowserComponent::visibilityChanged()
|
|||
checkWindowAssociation();
|
||||
}
|
||||
|
||||
void WebBrowserComponent::focusGained (FocusChangeType)
|
||||
void WebBrowserComponent::focusGainedWithDirection (FocusChangeType, FocusChangeDirection)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1036,7 +1036,7 @@ void WebBrowserComponent::visibilityChanged()
|
|||
checkWindowAssociation();
|
||||
}
|
||||
|
||||
void WebBrowserComponent::focusGained (FocusChangeType)
|
||||
void WebBrowserComponent::focusGainedWithDirection (FocusChangeType, FocusChangeDirection)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -724,7 +724,7 @@ void WebBrowserComponent::visibilityChanged()
|
|||
checkWindowAssociation();
|
||||
}
|
||||
|
||||
void WebBrowserComponent::focusGained (FocusChangeType)
|
||||
void WebBrowserComponent::focusGainedWithDirection (FocusChangeType, FocusChangeDirection)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -351,11 +351,56 @@ private:
|
|||
|
||||
#if JUCE_USE_WIN_WEBVIEW2
|
||||
|
||||
#include <winuser.h>
|
||||
|
||||
using namespace Microsoft::WRL;
|
||||
|
||||
static std::vector<HWND> getDirectChildWindows (HWND hwnd)
|
||||
{
|
||||
std::vector<HWND> result;
|
||||
|
||||
const auto getNextChildWindow = [hwnd, &result]
|
||||
{
|
||||
return FindWindowExA (hwnd, result.empty() ? nullptr : result.back(), nullptr, nullptr);
|
||||
};
|
||||
|
||||
for (auto* next = getNextChildWindow(); next != nullptr; next = getNextChildWindow())
|
||||
result.push_back (next);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void forEachChildWindowRecursive (HWND hwnd, std::function<bool (HWND)> callback)
|
||||
{
|
||||
// EnumChildWindows itself provides the recursion
|
||||
EnumChildWindows (hwnd,
|
||||
[] (HWND hwnd, LPARAM lParam)
|
||||
{
|
||||
auto* callbackPtr = reinterpret_cast<std::function<bool (HWND)>*> (lParam);
|
||||
return (*callbackPtr) (hwnd) ? TRUE : FALSE;
|
||||
},
|
||||
reinterpret_cast<LPARAM> (&callback));
|
||||
}
|
||||
|
||||
static bool anyChildWindow (HWND hwnd, std::function<bool (HWND)> predicate)
|
||||
{
|
||||
auto result = false;
|
||||
|
||||
forEachChildWindowRecursive (hwnd,
|
||||
[&predicate, &result] (auto* child)
|
||||
{
|
||||
result = predicate (child);
|
||||
const auto keepGoing = ! result;
|
||||
return keepGoing;
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
class WebView2 : public InternalWebViewType,
|
||||
public Component,
|
||||
public ComponentMovementWatcher
|
||||
public ComponentMovementWatcher,
|
||||
private AsyncUpdater
|
||||
{
|
||||
public:
|
||||
WebView2 (WebBrowserComponent& o, const WebBrowserComponent::Options& prefs)
|
||||
|
|
@ -372,6 +417,26 @@ public:
|
|||
owner.addAndMakeVisible (this);
|
||||
}
|
||||
|
||||
void focusGainedWithDirection (FocusChangeType, FocusChangeDirection direction) override
|
||||
{
|
||||
if (inMoveFocusRequested)
|
||||
return;
|
||||
|
||||
const auto moveFocusReason = [&]
|
||||
{
|
||||
if (direction == FocusChangeDirection::backward)
|
||||
return COREWEBVIEW2_MOVE_FOCUS_REASON_PREVIOUS;
|
||||
|
||||
if (direction == FocusChangeDirection::forward)
|
||||
return COREWEBVIEW2_MOVE_FOCUS_REASON_NEXT;
|
||||
|
||||
return COREWEBVIEW2_MOVE_FOCUS_REASON_PROGRAMMATIC;
|
||||
}();
|
||||
|
||||
if (webViewController != nullptr)
|
||||
webViewController->MoveFocus (moveFocusReason);
|
||||
}
|
||||
|
||||
~WebView2() override
|
||||
{
|
||||
removeEventHandlers();
|
||||
|
|
@ -389,7 +454,9 @@ public:
|
|||
|
||||
bool hasBrowserBeenCreated() override
|
||||
{
|
||||
return webView != nullptr || isCreating;
|
||||
return webView != nullptr
|
||||
|| webView2ConstructionHelper.webView2BeingCreated == this
|
||||
|| webView2ConstructionHelper.viewsWaitingForCreation.contains (this);
|
||||
}
|
||||
|
||||
void goToURL (const String& url, const StringArray* headers, const MemoryBlock* postData) override
|
||||
|
|
@ -462,6 +529,11 @@ public:
|
|||
owner.visibilityChanged();
|
||||
}
|
||||
|
||||
std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override
|
||||
{
|
||||
return std::make_unique<AccessibilityHandler> (*this, AccessibilityRole::group);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct WebViewHandle
|
||||
{
|
||||
|
|
@ -470,7 +542,7 @@ public:
|
|||
ComSmartPtr<ICoreWebView2Environment> environment;
|
||||
};
|
||||
|
||||
static std::optional<WebViewHandle> createWebViewHandle(const WebBrowserComponent::Options::WinWebView2& options)
|
||||
static std::optional<WebViewHandle> createWebViewHandle (const WebBrowserComponent::Options::WinWebView2& options)
|
||||
{
|
||||
using CreateWebViewEnvironmentWithOptionsFunc = HRESULT (*) (PCWSTR, PCWSTR,
|
||||
ICoreWebView2EnvironmentOptions*,
|
||||
|
|
@ -645,6 +717,45 @@ private:
|
|||
return S_OK;
|
||||
}).Get(), &webResourceRequestedToken);
|
||||
}
|
||||
|
||||
if (webViewController != nullptr)
|
||||
{
|
||||
webViewController->add_MoveFocusRequested (Callback<ICoreWebView2MoveFocusRequestedEventHandler> (
|
||||
[this] (ICoreWebView2Controller*, ICoreWebView2MoveFocusRequestedEventArgs* args) -> HRESULT
|
||||
{
|
||||
ScopedValueSetter scope { inMoveFocusRequested, true };
|
||||
|
||||
auto* comp = [&]() -> Component*
|
||||
{
|
||||
auto* c = owner.getParentComponent();
|
||||
|
||||
if (c == nullptr)
|
||||
return nullptr;
|
||||
|
||||
const auto traverser = c->createFocusTraverser();
|
||||
|
||||
if (COREWEBVIEW2_MOVE_FOCUS_REASON reason;
|
||||
args->get_Reason (&reason) == S_OK && reason == COREWEBVIEW2_MOVE_FOCUS_REASON_PREVIOUS)
|
||||
{
|
||||
// The previous Component to the embedded WebView2 Component is the
|
||||
// WebBrowserComponent. Here we want to skip that and jump to the
|
||||
// Component that comes before it.
|
||||
return traverser->getPreviousComponent (&owner);
|
||||
}
|
||||
|
||||
// The Component that comes immediately after the WebBrowserComponent is the
|
||||
// embedded WebView2. We want to jump to the Component that comes after that.
|
||||
return traverser->getNextComponent (this);
|
||||
}();
|
||||
|
||||
if (comp != nullptr)
|
||||
comp->getAccessibilityHandler()->grabFocus();
|
||||
else
|
||||
giveAwayKeyboardFocus();
|
||||
|
||||
return S_OK;
|
||||
}).Get(), &moveFocusRequestedToken);
|
||||
}
|
||||
}
|
||||
|
||||
void removeEventHandlers()
|
||||
|
|
@ -669,6 +780,12 @@ private:
|
|||
webView->remove_WebResourceRequested (webResourceRequestedToken);
|
||||
}
|
||||
}
|
||||
|
||||
if (webViewController != nullptr)
|
||||
{
|
||||
if (moveFocusRequestedToken.value != 0)
|
||||
webViewController->remove_MoveFocusRequested (moveFocusRequestedToken);
|
||||
}
|
||||
}
|
||||
|
||||
void setWebViewPreferences()
|
||||
|
|
@ -710,7 +827,18 @@ private:
|
|||
{
|
||||
if (auto* peer = getPeer())
|
||||
{
|
||||
isCreating = true;
|
||||
// We enforce the serial creation of WebView2 instances so that our HWND association
|
||||
// logic can work. Multiple HWNDs can belong to the same browser process, so the only
|
||||
// way to identify which belongs to which WebView2 is to associate them with each other
|
||||
// in the order of creation.
|
||||
if (webView2ConstructionHelper.webView2BeingCreated != nullptr)
|
||||
{
|
||||
webView2ConstructionHelper.viewsWaitingForCreation.insert (this);
|
||||
return;
|
||||
}
|
||||
|
||||
webView2ConstructionHelper.viewsWaitingForCreation.erase (this);
|
||||
webView2ConstructionHelper.webView2BeingCreated = this;
|
||||
|
||||
WeakReference<WebView2> weakThis (this);
|
||||
|
||||
|
|
@ -720,7 +848,7 @@ private:
|
|||
{
|
||||
if (weakThis != nullptr)
|
||||
{
|
||||
weakThis->isCreating = false;
|
||||
webView2ConstructionHelper.webView2BeingCreated = nullptr;
|
||||
|
||||
if (controller != nullptr)
|
||||
{
|
||||
|
|
@ -729,6 +857,40 @@ private:
|
|||
|
||||
if (weakThis->webView != nullptr)
|
||||
{
|
||||
if (UINT32 browserProcessId;
|
||||
weakThis->webView->get_BrowserProcessId (&browserProcessId) == S_OK)
|
||||
{
|
||||
auto* self = weakThis.get();
|
||||
auto* webView2WindowHandle = static_cast<HWND> (self->getWindowHandle());
|
||||
|
||||
// There is no WebView2 API for getting the HWND hosting
|
||||
// the WebView2 content. So we iterate over all child
|
||||
// windows of the JUCE peer HWND, and try to figure out
|
||||
// which one belongs to a WebView2. What we are looking for
|
||||
// is a window that has a child window that belongs to the
|
||||
// browserProcessId.
|
||||
const auto directChildWindows = getDirectChildWindows (webView2WindowHandle);
|
||||
|
||||
for (auto* childWindow : directChildWindows)
|
||||
{
|
||||
if (! self->webView2ConstructionHelper.associatedWebViewNativeWindows.contains (childWindow))
|
||||
{
|
||||
if (anyChildWindow (childWindow,
|
||||
[browserProcessId] (auto* childOfChild)
|
||||
{
|
||||
if (DWORD procId; GetWindowThreadProcessId (childOfChild, &procId) != 0)
|
||||
return (UINT32) procId == browserProcessId;
|
||||
|
||||
return false;
|
||||
}))
|
||||
{
|
||||
webView2ConstructionHelper.associatedWebViewNativeWindows.insert (childWindow);
|
||||
AccessibilityHandler::setNativeChildForComponent (*self, childWindow);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
weakThis->addEventHandlers();
|
||||
weakThis->setWebViewPreferences();
|
||||
weakThis->componentMovedOrResized (true, true);
|
||||
|
|
@ -737,6 +899,9 @@ private:
|
|||
weakThis->webView->Navigate (weakThis->urlRequest.url.toWideCharPointer());
|
||||
}
|
||||
}
|
||||
|
||||
if (! weakThis->webView2ConstructionHelper.viewsWaitingForCreation.empty())
|
||||
(*weakThis->webView2ConstructionHelper.viewsWaitingForCreation.begin())->triggerAsyncUpdate();
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
|
|
@ -746,6 +911,11 @@ private:
|
|||
|
||||
void closeWebView()
|
||||
{
|
||||
if (auto* webViewNativeWindow = AccessibilityHandler::getNativeChildForComponent (*this))
|
||||
webView2ConstructionHelper.associatedWebViewNativeWindows.erase (webViewNativeWindow);
|
||||
|
||||
AccessibilityHandler::setNativeChildForComponent (*this, nullptr);
|
||||
|
||||
if (webViewController != nullptr)
|
||||
{
|
||||
webViewController->Close();
|
||||
|
|
@ -756,6 +926,12 @@ private:
|
|||
webViewHandle.environment = nullptr;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void handleAsyncUpdate() override
|
||||
{
|
||||
createWebView();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void setControlBounds (Rectangle<int> newBounds) const
|
||||
{
|
||||
|
|
@ -790,7 +966,10 @@ private:
|
|||
newWindowRequestedToken { 0 },
|
||||
windowCloseRequestedToken { 0 },
|
||||
navigationCompletedToken { 0 },
|
||||
webResourceRequestedToken { 0 };
|
||||
webResourceRequestedToken { 0 },
|
||||
moveFocusRequestedToken { 0 };
|
||||
|
||||
bool inMoveFocusRequested = false;
|
||||
|
||||
struct URLRequest
|
||||
{
|
||||
|
|
@ -801,7 +980,14 @@ private:
|
|||
|
||||
URLRequest urlRequest;
|
||||
|
||||
bool isCreating = false;
|
||||
struct WebView2ConstructionHelper
|
||||
{
|
||||
WebView2* webView2BeingCreated;
|
||||
std::set<WebView2*> viewsWaitingForCreation;
|
||||
std::set<void*> associatedWebViewNativeWindows;
|
||||
};
|
||||
|
||||
inline static WebView2ConstructionHelper webView2ConstructionHelper;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_WEAK_REFERENCEABLE (WebView2)
|
||||
|
|
@ -969,9 +1155,16 @@ void WebBrowserComponent::visibilityChanged()
|
|||
checkWindowAssociation();
|
||||
}
|
||||
|
||||
void WebBrowserComponent::focusGained (FocusChangeType)
|
||||
void WebBrowserComponent::focusGainedWithDirection (FocusChangeType type, FocusChangeDirection dir)
|
||||
{
|
||||
browser->getInternalWebView().focusGained();
|
||||
ignoreUnused (type, dir);
|
||||
|
||||
#if JUCE_USE_WIN_WEBVIEW2
|
||||
if (auto* webView2 = dynamic_cast<WebView2*> (&browser->getInternalWebView()))
|
||||
webView2->focusGainedWithDirection (type, dir);
|
||||
else
|
||||
#endif
|
||||
browser->getInternalWebView().focusGained();
|
||||
}
|
||||
|
||||
void WebBrowserComponent::clearCookies()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue