Bug 1833814 - change nsIWidget's cursor logic, r=emilio,win-reviewers,mhowell

Differential Revision: https://phabricator.services.mozilla.com/D195549
This commit is contained in:
Gijs Kruitbosch 2024-01-22 20:42:50 +00:00
parent e4df13ce23
commit 6674caa946
11 changed files with 101 additions and 26 deletions

View file

@ -209,7 +209,44 @@ UniquePtr<nsMenuChainItem> nsMenuChainItem::Detach() {
return std::move(mParent);
}
void nsXULPopupManager::AddMenuChainItem(UniquePtr<nsMenuChainItem> aItem) {
PopupType popupType = aItem->Frame()->GetPopupType();
if (StaticPrefs::layout_cursor_disable_for_popups() &&
popupType != PopupType::Tooltip) {
nsPresContext* rootPresContext =
aItem->Frame()->PresContext()->GetRootPresContext();
if (nsCOMPtr<nsIWidget> rootWidget = rootPresContext->GetRootWidget()) {
rootWidget->SetCustomCursorAllowed(false);
}
}
// popups normally hide when an outside click occurs. Panels may use
// the noautohide attribute to disable this behaviour. It is expected
// that the application will hide these popups manually. The tooltip
// listener will handle closing the tooltip also.
nsIContent* oldmenu = nullptr;
if (mPopups) {
oldmenu = mPopups->Element();
}
aItem->SetParent(std::move(mPopups));
mPopups = std::move(aItem);
SetCaptureState(oldmenu);
}
void nsXULPopupManager::RemoveMenuChainItem(nsMenuChainItem* aItem) {
nsPresContext* presContext =
aItem->Frame()->PresContext()->GetRootPresContext();
auto matcher = [&](nsMenuChainItem* aChainItem) -> bool {
return aChainItem != aItem &&
presContext ==
aChainItem->Frame()->PresContext()->GetRootPresContext();
};
nsCOMPtr<nsIWidget> rootWidget =
presContext->GetRootPresContext()->GetRootWidget();
if (!FirstMatchingPopup(matcher) && rootWidget) {
rootWidget->SetCustomCursorAllowed(true);
}
auto parent = aItem->Detach();
if (auto* child = aItem->GetChild()) {
MOZ_ASSERT(aItem != mPopups,
@ -225,6 +262,17 @@ void nsXULPopupManager::RemoveMenuChainItem(nsMenuChainItem* aItem) {
}
}
nsMenuChainItem* nsXULPopupManager::FirstMatchingPopup(
mozilla::FunctionRef<bool(nsMenuChainItem*)> aMatcher) const {
for (nsMenuChainItem* popup = mPopups.get(); popup;
popup = popup->GetParent()) {
if (aMatcher(popup)) {
return popup;
}
}
return nullptr;
}
void nsMenuChainItem::UpdateFollowAnchor() {
mFollowAnchor = mFrame->ShouldFollowAnchor(mCurrentRect);
}
@ -1140,17 +1188,7 @@ void nsXULPopupManager::ShowPopupCallback(Element* aPopup,
item->UpdateFollowAnchor();
// popups normally hide when an outside click occurs. Panels may use
// the noautohide attribute to disable this behaviour. It is expected
// that the application will hide these popups manually. The tooltip
// listener will handle closing the tooltip also.
nsIContent* oldmenu = nullptr;
if (mPopups) {
oldmenu = mPopups->Element();
}
item->SetParent(std::move(mPopups));
mPopups = std::move(item);
SetCaptureState(oldmenu);
AddMenuChainItem(std::move(item));
NS_ENSURE_TRUE_VOID(weakFrame.IsAlive());
RefPtr popup = &aPopupFrame->PopupElement();
@ -1166,12 +1204,10 @@ void nsXULPopupManager::ShowPopupCallback(Element* aPopup,
}
nsMenuChainItem* nsXULPopupManager::FindPopup(Element* aPopup) const {
for (nsMenuChainItem* item = mPopups.get(); item; item = item->GetParent()) {
if (item->Frame()->GetContent() == aPopup) {
return item;
}
}
return nullptr;
auto matcher = [&](nsMenuChainItem* aItem) -> bool {
return aItem->Frame()->GetContent() == aPopup;
};
return FirstMatchingPopup(matcher);
}
void nsXULPopupManager::HidePopup(Element* aPopup, HidePopupOptions aOptions,

View file

@ -23,6 +23,7 @@
#include "nsIObserver.h"
#include "nsThreadUtils.h"
#include "mozilla/Attributes.h"
#include "mozilla/FunctionRef.h"
#include "mozilla/widget/InitData.h"
#include "mozilla/widget/NativeMenu.h"
@ -550,6 +551,13 @@ class nsXULPopupManager final : public nsIDOMEventListener,
*/
void UpdatePopupPositions(nsRefreshDriver* aRefreshDriver);
/**
* Get the first nsMenuChainItem that is matched by the matching callback
* function provided.
*/
nsMenuChainItem* FirstMatchingPopup(
mozilla::FunctionRef<bool(nsMenuChainItem*)> aMatcher) const;
/**
* Enable or disable anchor following on the popup if needed.
*/
@ -733,6 +741,9 @@ class nsXULPopupManager final : public nsIDOMEventListener,
return GetRollupItem(RollupKind::Menu);
}
// Add the chain item to the chain and update mPopups to point to it.
void AddMenuChainItem(mozilla::UniquePtr<nsMenuChainItem>);
// Removes the chain item from the chain and deletes it.
void RemoveMenuChainItem(nsMenuChainItem*);

View file

@ -8844,6 +8844,11 @@
value: 32
mirror: always
- name: layout.cursor.disable-for-popups
type: bool
value: true
mirror: always
- name: layout.display-list.build-twice
type: RelaxedAtomicBool
value: false

View file

@ -625,9 +625,12 @@ void nsChildView::SetCursor(const Cursor& aCursor) {
nsBaseWidget::SetCursor(aCursor);
if (NS_SUCCEEDED([[nsCursorManager sharedInstance]
setCustomCursor:aCursor
widgetScaleFactor:BackingScaleFactor()])) {
bool forceUpdate = mUpdateCursor;
mUpdateCursor = false;
if (mCustomCursorAllowed && NS_SUCCEEDED([[nsCursorManager sharedInstance]
setCustomCursor:aCursor
widgetScaleFactor:BackingScaleFactor()
forceUpdate:forceUpdate])) {
return;
}

View file

@ -36,7 +36,8 @@
// As above, but returns an error if the cursor isn't custom or we couldn't set
// it for some reason.
- (nsresult)setCustomCursor:(const nsIWidget::Cursor&)aCursor
widgetScaleFactor:(CGFloat)aWidgetScaleFactor;
widgetScaleFactor:(CGFloat)aWidgetScaleFactor
forceUpdate:(bool)aForceUpdate;
/*! @method sharedInstance
@abstract Get the Singleton instance of the cursor manager.

View file

@ -276,13 +276,14 @@ static constexpr nsCursor kCustomCursor = eCursorCount;
}
- (nsresult)setCustomCursor:(const nsIWidget::Cursor&)aCursor
widgetScaleFactor:(CGFloat)scaleFactor {
widgetScaleFactor:(CGFloat)scaleFactor
forceUpdate:(bool)aForceUpdate {
NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
// As the user moves the mouse, this gets called repeatedly with the same
// aCursorImage
if (sCurrentCursor == aCursor && sCurrentCursorScaleFactor == scaleFactor &&
mCurrentMacCursor) {
if (!aForceUpdate && sCurrentCursor == aCursor &&
sCurrentCursorScaleFactor == scaleFactor && mCurrentMacCursor) {
// Native dragging can unset our cursor apparently (see bug 1739352).
if (MOZ_UNLIKELY(![mCurrentMacCursor isSet])) {
[mCurrentMacCursor set];

View file

@ -3328,7 +3328,10 @@ void nsWindow::SetCursor(const Cursor& aCursor) {
mCursor = aCursor;
// Try to set the cursor image first, and fall back to the numeric cursor.
GdkCursor* imageCursor = GetCursorForImage(aCursor, GdkCeiledScaleFactor());
GdkCursor* imageCursor = nullptr;
if (mCustomCursorAllowed) {
imageCursor = GetCursorForImage(aCursor, GdkCeiledScaleFactor());
}
// When using a custom cursor, clear the cursor first using eCursor_none, in
// order to work around https://gitlab.gnome.org/GNOME/gtk/-/issues/6242

View file

@ -701,6 +701,14 @@ void nsBaseWidget::MoveToWorkspace(const nsAString& workspaceID) {
void nsBaseWidget::SetCursor(const Cursor& aCursor) { mCursor = aCursor; }
void nsBaseWidget::SetCustomCursorAllowed(bool aIsAllowed) {
if (aIsAllowed != mCustomCursorAllowed) {
mCustomCursorAllowed = aIsAllowed;
mUpdateCursor = true;
SetCursor(mCursor);
}
}
//-------------------------------------------------------------------------
//
// Window transparency methods

View file

@ -187,6 +187,7 @@ class nsBaseWidget : public nsIWidget, public nsSupportsWeakReference {
bool IsFullyOccluded() const override { return mIsFullyOccluded; }
void SetCursor(const Cursor&) override;
void SetCustomCursorAllowed(bool) override;
void ClearCachedCursor() final {
mCursor = {};
mUpdateCursor = true;
@ -694,6 +695,7 @@ class nsBaseWidget : public nsIWidget, public nsSupportsWeakReference {
RefPtr<mozilla::SwipeTracker> mSwipeTracker;
mozilla::UniquePtr<mozilla::SwipeEventQueue> mSwipeEventQueue;
Cursor mCursor;
bool mCustomCursorAllowed = true;
BorderStyle mBorderStyle;
LayoutDeviceIntRect mBounds;
bool mIsTiled;

View file

@ -1014,6 +1014,8 @@ class nsIWidget : public nsISupports {
*/
virtual void SetCursor(const Cursor&) = 0;
virtual void SetCustomCursorAllowed(bool) = 0;
static nsIntSize CustomCursorSize(const Cursor&);
/**

View file

@ -3031,7 +3031,10 @@ void nsWindow::SetCursor(const Cursor& aCursor) {
sCurrentHCursorIsCustom = false;
sCurrentCursor = aCursor;
HCURSOR cursor = CursorForImage(aCursor, GetDefaultScale());
HCURSOR cursor = nullptr;
if (mCustomCursorAllowed) {
cursor = CursorForImage(aCursor, GetDefaultScale());
}
bool custom = false;
if (cursor) {
custom = true;