Bug 1700148, add an appearance type that forces a dropshadow on menus on Windows, and uses SetWindowRgn to clip the popup to the border radius, r=tnikkel

Differential Revision: https://phabricator.services.mozilla.com/D109833
This commit is contained in:
Neil Deakin 2021-04-01 00:13:09 +00:00
Родитель b540936658
Коммит f625b71d78
8 изменённых файлов: 78 добавлений и 10 удалений

Просмотреть файл

@ -6658,6 +6658,27 @@ bool nsLayoutUtils::HasNonZeroCornerOnSide(const BorderRadius& aCorners,
return false;
}
/* static */
LayoutDeviceIntSize nsLayoutUtils::GetBorderRadiusForMenuDropShadow(
const nsIFrame* aFrame) {
if (aFrame->StyleUIReset()->mWindowShadow == StyleWindowShadow::Cliprounded) {
const auto& corners = aFrame->StyleBorder()->mBorderRadius;
// Get the width and height of the top-left corner.
const LengthPercentage& cornerX = corners.Get(eCornerTopLeftX);
const LengthPercentage& cornerY = corners.Get(eCornerTopLeftY);
nscoord lengthX = (cornerX.IsLength() ? cornerX.ToLength() : 0);
nscoord lengthY = (cornerY.IsLength() ? cornerY.ToLength() : 0);
if (lengthX || lengthY) {
const nsPresContext* presContext = aFrame->PresContext();
return LayoutDeviceIntSize(presContext->AppUnitsToDevPixels(lengthX),
presContext->AppUnitsToDevPixels(lengthY));
}
}
return LayoutDeviceIntSize();
}
/* static */
nsTransparencyMode nsLayoutUtils::GetFrameTransparency(
nsIFrame* aBackgroundFrame, nsIFrame* aCSSRootFrame) {

Просмотреть файл

@ -2011,6 +2011,15 @@ class nsLayoutUtils {
static bool HasNonZeroCornerOnSide(const mozilla::BorderRadius& aCorners,
mozilla::Side aSide);
/**
* Return the border radius size (width, height) based only on the top-left
* corner. This is a special case used for drawing the Windows 10 drop-shadow,
* and only supports a specified length (not percentages) on the top-left
* corner.
*/
static LayoutDeviceIntSize GetBorderRadiusForMenuDropShadow(
const nsIFrame* aFrame);
/**
* Determine if a widget is likely to require transparency or translucency.
* @param aBackgroundFrame The frame that the background is set on. For

Просмотреть файл

@ -640,6 +640,7 @@ enum class StyleWindowShadow : uint8_t {
Menu,
Tooltip,
Sheet,
Cliprounded, // clip border to popup border-radius
};
// dominant-baseline

Просмотреть файл

@ -12943,7 +12943,7 @@ if (false) {
inherited: false,
type: CSS_TYPE_LONGHAND,
initial_values: ["default"],
other_values: ["none", "menu", "tooltip", "sheet"],
other_values: ["none", "menu", "tooltip", "sheet", "cliprounded"],
invalid_values: [],
};

Просмотреть файл

@ -315,10 +315,14 @@ nsresult nsMenuPopupFrame::CreateWidgetForView(nsView* aView) {
tag = parentContent->NodeInfo()->NameAtom();
widgetData.mHasRemoteContent = remote;
widgetData.mSupportTranslucency = mode == eTransparencyTransparent;
widgetData.mDropShadow =
!(mode == eTransparencyTransparent || tag == nsGkAtoms::menulist);
widgetData.mPopupLevel = PopupLevel(widgetData.mNoAutoHide);
// The special cases are menulists and handling the Windows 10
// drop-shadow on menus with rounded borders.
widgetData.mDropShadow =
!(mode == eTransparencyTransparent || tag == nsGkAtoms::menulist) ||
StyleUIReset()->mWindowShadow == StyleWindowShadow::Cliprounded;
// panels which have a parent level need a parent widget. This allows them to
// always appear in front of the parent window but behind other windows that
// should be in front of it.

Просмотреть файл

@ -53,7 +53,7 @@ ${helpers.single_keyword(
${helpers.single_keyword(
"-moz-window-shadow",
"default none menu tooltip sheet",
"default none menu tooltip sheet cliprounded",
engines="gecko",
gecko_ffi_name="mWindowShadow",
gecko_enum_prefix="StyleWindowShadow",

Просмотреть файл

@ -3899,6 +3899,7 @@ static const NSUInteger kWindowShadowOptionsTooltipMojaveOrLater = 4;
case StyleWindowShadow::Default: // we treat "default" as "default panel"
case StyleWindowShadow::Menu:
case StyleWindowShadow::Sheet:
case StyleWindowShadow::Cliprounded: // this is a Windows-only value.
return kWindowShadowOptionsMenu;
case StyleWindowShadow::Tooltip:

Просмотреть файл

@ -1757,28 +1757,60 @@ bool nsWindow::IsVisible() const { return mIsVisible; }
*
**************************************************************/
static bool ShouldHaveRoundedMenuDropShadow(nsWindow* aWindow) {
nsView* view = nsView::GetViewFor(aWindow);
return view && view->GetFrame() &&
view->GetFrame()->StyleUIReset()->mWindowShadow ==
StyleWindowShadow::Cliprounded;
}
// XP and Vista visual styles sometimes require window clipping regions to be
// applied for proper transparency. These routines are called on size and move
// operations.
// XXX this is apparently still needed in Windows 7 and later
void nsWindow::ClearThemeRegion() {
if (!HasGlass() &&
(mWindowType == eWindowType_popup && !IsPopupWithTitleBar() &&
(mPopupType == ePopupTypeTooltip || mPopupType == ePopupTypePanel))) {
if (mWindowType == eWindowType_popup && mPopupType == ePopupTypeMenu &&
ShouldHaveRoundedMenuDropShadow(this)) {
SetWindowRgn(mWnd, nullptr, false);
} else if (!HasGlass() &&
(mWindowType == eWindowType_popup && !IsPopupWithTitleBar() &&
(mPopupType == ePopupTypeTooltip ||
mPopupType == ePopupTypePanel))) {
SetWindowRgn(mWnd, nullptr, false);
}
}
void nsWindow::SetThemeRegion() {
// Clip the window to the rounded rect area of the popup if needed.
if (mWindowType == eWindowType_popup && mPopupType == ePopupTypeMenu) {
nsView* view = nsView::GetViewFor(this);
if (view) {
LayoutDeviceIntSize size =
nsLayoutUtils::GetBorderRadiusForMenuDropShadow(view->GetFrame());
if (size.width || size.height) {
int32_t width =
NSToIntRound(size.width * GetDesktopToDeviceScale().scale);
int32_t height =
NSToIntRound(size.height * GetDesktopToDeviceScale().scale);
HRGN region = CreateRoundRectRgn(0, 0, mBounds.Width(),
mBounds.Height(), width, height);
if (!SetWindowRgn(mWnd, region, false)) {
DeleteObject(region); // region setting failed so delete the region.
}
}
}
}
// Popup types that have a visual styles region applied (bug 376408). This can
// be expanded for other window types as needed. The regions are applied
// generically to the base window so default constants are used for part and
// state. At some point we might need part and state values from
// nsNativeThemeWin's GetThemePartAndState, but currently windows that change
// shape based on state haven't come up.
if (!HasGlass() &&
(mWindowType == eWindowType_popup && !IsPopupWithTitleBar() &&
(mPopupType == ePopupTypeTooltip || mPopupType == ePopupTypePanel))) {
else if (!HasGlass() &&
(mWindowType == eWindowType_popup && !IsPopupWithTitleBar() &&
(mPopupType == ePopupTypeTooltip ||
mPopupType == ePopupTypePanel))) {
HRGN hRgn = nullptr;
RECT rect = {0, 0, mBounds.Width(), mBounds.Height()};