Bug 943759, [Australis], support mousethrough on panels to allow mouse events to pass through to the content behind, implemented on Windows, Mac and Linux, use this for the UI tour highlight, r=neil,jmathies,karlt

This commit is contained in:
Neil Deakin 2014-01-31 08:27:43 -05:00
parent 9e7a6ea388
commit ee782b7b75
9 changed files with 71 additions and 44 deletions

View File

@ -223,7 +223,8 @@
noautofocus="true"
noautohide="true"
flip="none"
consumeoutsideclicks="false">
consumeoutsideclicks="false"
mousethrough="always">
<box id="UITourHighlight"></box>
</panel>

View File

@ -96,7 +96,7 @@ nsMenuPopupFrame::nsMenuPopupFrame(nsIPresShell* aShell, nsStyleContext* aContex
mShouldAutoPosition(true),
mInContentShell(true),
mIsMenuLocked(false),
mIsDragPopup(false),
mMouseTransparent(false),
mHFlip(false),
mVFlip(false)
{
@ -141,12 +141,6 @@ nsMenuPopupFrame::Init(nsIContent* aContent,
mPopupType = ePopupTypeTooltip;
}
if (mPopupType == ePopupTypePanel &&
aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
nsGkAtoms::drag, eIgnoreCase)) {
mIsDragPopup = true;
}
nsCOMPtr<nsIDocShellTreeItem> dsti = PresContext()->GetDocShell();
if (dsti && dsti->ItemType() == nsIDocShellTreeItem::typeChrome) {
mInContentShell = false;
@ -243,7 +237,22 @@ nsMenuPopupFrame::CreateWidgetForView(nsView* aView)
widgetData.clipSiblings = true;
widgetData.mPopupHint = mPopupType;
widgetData.mNoAutoHide = IsNoAutoHide();
widgetData.mIsDragPopup = mIsDragPopup;
if (!mInContentShell) {
// A drag popup may be used for non-static translucent drag feedback
bool isDragPopup = false;
if (mPopupType == ePopupTypePanel &&
mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
nsGkAtoms::drag, eIgnoreCase)) {
widgetData.mIsDragPopup = true;
isDragPopup = true;
}
// If mousethrough="always" is set directly on the popup, then the widget
// should ignore mouse events, passing them through to the content behind.
mMouseTransparent = GetStateBits() & NS_FRAME_MOUSE_THROUGH_ALWAYS;
widgetData.mMouseTransparent = mMouseTransparent;
}
nsAutoString title;
if (mContent && widgetData.mNoAutoHide) {
@ -1814,19 +1823,6 @@ nsMenuPopupFrame::AttachedDismissalListener()
mConsumeRollupEvent = nsIPopupBoxObject::ROLLUP_DEFAULT;
}
void
nsMenuPopupFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
const nsRect& aDirtyRect,
const nsDisplayListSet& aLists)
{
// don't pass events to drag popups
if (aBuilder->IsForEventDelivery() && mIsDragPopup) {
return;
}
nsBoxFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
}
// helpers /////////////////////////////////////////////////////////////
NS_IMETHODIMP

View File

@ -230,7 +230,7 @@ public:
bool IsMenu() MOZ_OVERRIDE { return mPopupType == ePopupTypeMenu; }
bool IsOpen() MOZ_OVERRIDE { return mPopupState == ePopupOpen || mPopupState == ePopupOpenAndVisible; }
bool IsDragPopup() { return mIsDragPopup; }
bool IsMouseTransparent() { return mMouseTransparent; }
static nsIContent* GetTriggerContent(nsMenuPopupFrame* aMenuPopupFrame);
void ClearTriggerContent() { mTriggerContent = nullptr; }
@ -334,10 +334,6 @@ public:
// This position is in CSS pixels.
nsIntPoint ScreenPosition() const { return nsIntPoint(mScreenXPos, mScreenYPos); }
virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
const nsRect& aDirtyRect,
const nsDisplayListSet& aLists) MOZ_OVERRIDE;
nsIntPoint GetLastClientOffset() const { return mLastClientOffset; }
// Return the alignment of the popup
@ -480,7 +476,7 @@ protected:
bool mShouldAutoPosition; // Should SetPopupPosition be allowed to auto position popup?
bool mInContentShell; // True if the popup is in a content shell
bool mIsMenuLocked; // Should events inside this menu be ignored?
bool mIsDragPopup; // True if this is a popup used for drag feedback
bool mMouseTransparent; // True if this is a popup is transparent to mouse events
// the flip modes that were used when the popup was opened
bool mHFlip;

View File

@ -1385,21 +1385,21 @@ nsXULPopupManager::GetVisiblePopups(nsTArray<nsIFrame *>& aPopups)
{
aPopups.Clear();
// Iterate over both lists of popups
nsMenuChainItem* item = mPopups;
while (item) {
if (item->Frame()->PopupState() == ePopupOpenAndVisible)
aPopups.AppendElement(static_cast<nsIFrame*>(item->Frame()));
item = item->GetParent();
}
for (int32_t list = 0; list < 2; list++) {
while (item) {
// Skip panels which are not open and visible as well as popups that
// are transparent to mouse events.
if (item->Frame()->PopupState() == ePopupOpenAndVisible &&
!item->Frame()->IsMouseTransparent()) {
aPopups.AppendElement(item->Frame());
}
item = mNoHidePanels;
while (item) {
// skip panels which are not open and visible as well as draggable popups,
// as those don't respond to events.
if (item->Frame()->PopupState() == ePopupOpenAndVisible && !item->Frame()->IsDragPopup()) {
aPopups.AppendElement(static_cast<nsIFrame*>(item->Frame()));
item = item->GetParent();
}
item = item->GetParent();
item = mNoHidePanels;
}
}

View File

@ -290,7 +290,7 @@ nsresult nsCocoaWindow::Create(nsIWidget *aParent,
NS_ENSURE_SUCCESS(rv, rv);
if (mWindowType == eWindowType_popup) {
if (aInitData->mIsDragPopup) {
if (aInitData->mMouseTransparent) {
[mWindow setIgnoresMouseEvents:YES];
}
// now we can convert newBounds to device pixels for the window we created,

View File

@ -3586,6 +3586,23 @@ nsWindow::Create(nsIWidget *aParent,
if (wmd != -1)
gdk_window_set_decorations(gtk_widget_get_window(mShell), (GdkWMDecoration) wmd);
}
// If the popup ignores mouse events, set an empty input shape.
if (aInitData->mMouseTransparent) {
#if (MOZ_WIDGET_GTK == 2)
GdkRectangle rect = { 0, 0, 0, 0 };
GdkRegion *region = gdk_region_rectangle(&rect);
gdk_window_input_shape_combine_region(mGdkWindow, region, 0, 0);
gdk_region_destroy(region);
#else
cairo_rectangle_int_t rect = { 0, 0, 0, 0 };
cairo_region_t *region = cairo_region_create_rectangle(&rect);
gdk_window_input_shape_combine_region(mGdkWindow, region, 0, 0);
cairo_region_destroy(region);
#endif
}
}
}
break;

View File

@ -101,7 +101,8 @@ struct nsWidgetInitData {
mNoAutoHide(false),
mIsDragPopup(false),
mIsAnimationSuppressed(false),
mSupportTranslucency(false)
mSupportTranslucency(false),
mMouseTransparent(false)
{
}
@ -120,6 +121,9 @@ struct nsWidgetInitData {
bool mIsAnimationSuppressed;
// true if the window should support an alpha channel, if available.
bool mSupportTranslucency;
// true if the window should be transparent to mouse events. Currently this is
// only valid for eWindowType_popup widgets
bool mMouseTransparent;
};
#endif // nsWidgetInitData_h__

View File

@ -334,6 +334,8 @@ nsWindow::nsWindow() : nsWindowBase()
mFullscreenMode = false;
mMousePresent = false;
mDestroyCalled = false;
mHasTaskbarIconBeenCreated = false;
mMouseTransparent = false;
mPickerDisplayCount = 0;
mWindowType = eWindowType_child;
mBorderStyle = eBorderStyle_default;
@ -362,7 +364,6 @@ nsWindow::nsWindow() : nsWindowBase()
mForeground = ::GetSysColor(COLOR_WINDOWTEXT);
mTaskbarPreview = nullptr;
mHasTaskbarIconBeenCreated = false;
// Global initialization
if (!sInstanceCount) {
@ -490,8 +491,9 @@ nsWindow::Create(nsIWidget *aParent,
extendedStyle |= WS_EX_COMPOSITED;
}
if (aInitData->mIsDragPopup) {
if (aInitData->mMouseTransparent) {
// This flag makes the window transparent to mouse events
mMouseTransparent = true;
extendedStyle |= WS_EX_TRANSPARENT;
}
} else if (mWindowType == eWindowType_invisible) {
@ -4673,6 +4675,13 @@ nsWindow::ProcessMessage(UINT msg, WPARAM& wParam, LPARAM& lParam,
case WM_NCHITTEST:
{
if (mMouseTransparent) {
// Treat this window as transparent.
*aRetValue = HTTRANSPARENT;
result = true;
break;
}
/*
* If an nc client area margin has been moved, we are responsible
* for calculating where the resize margins are and returning the

View File

@ -572,6 +572,10 @@ protected:
// icon has been created on the taskbar.
bool mHasTaskbarIconBeenCreated;
// Indicates that mouse events should be ignored and pass through to the
// window below. This is currently only used for popups.
bool mMouseTransparent;
// The point in time at which the last paint completed. We use this to avoid
// painting too rapidly in response to frequent input events.
TimeStamp mLastPaintEndTime;