--- a/widget/src/windows/nsNativeThemeWin.cpp
+++ b/widget/src/windows/nsNativeThemeWin.cpp
@@ -17,16 +17,17 @@
* The Initial Developer of the Original Code is
* David Hyatt (hyatt@netscape.com).
* Portions created by the Initial Developer are Copyright (C) 2001
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Tim Hill (tim@prismelite.com)
* James Ross (silver@warwickcompsoc.co.uk)
+ * Simon Bünzli (zeniko@gmail.com)
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
@@ -132,16 +133,19 @@
#define TABP_PANEL 10
// Tooltip constants
#define TTP_STANDARD 1
// Dropdown constants
#define CBP_DROPMARKER 1
+// Rebar constants
+#define RP_BAND 3
+
// Constants only found in new (98+, 2K+, XP+, etc.) Windows.
#ifdef DFCS_HOT
#undef DFCS_HOT
#endif
#define DFCS_HOT 0x00001000
#ifdef COLOR_MENUHILIGHT
#undef COLOR_MENUHILIGHT
@@ -150,29 +154,31 @@
#ifdef SPI_GETFLATMENU
#undef SPI_GETFLATMENU
#endif
#define SPI_GETFLATMENU 0x1022
// Our extra constants for passing a little bit more info to the renderer.
#define DFCS_RTL 0x00010000
-#define DFCS_CONTAINER 0x00020000
+
+// Toolbar separator dimension which can't be gotten from Windows
+#define TB_SEPARATOR_HEIGHT 2
NS_IMPL_ISUPPORTS1(nsNativeThemeWin, nsITheme)
typedef HANDLE (WINAPI*OpenThemeDataPtr)(HWND hwnd, LPCWSTR pszClassList);
typedef HRESULT (WINAPI*CloseThemeDataPtr)(HANDLE hTheme);
typedef HRESULT (WINAPI*DrawThemeBackgroundPtr)(HANDLE hTheme, HDC hdc, int iPartId,
int iStateId, const RECT *pRect,
const RECT* pClipRect);
typedef HRESULT (WINAPI*DrawThemeEdgePtr)(HANDLE hTheme, HDC hdc, int iPartId,
- int iStateId, const RECT *pRect,
+ int iStateId, const RECT *pDestRect,
uint uEdge, uint uFlags,
- const RECT* pClipRect);
+ const RECT* pContentRect);
typedef HRESULT (WINAPI*GetThemeContentRectPtr)(HANDLE hTheme, HDC hdc, int iPartId,
int iStateId, const RECT* pRect,
RECT* pContentRect);
typedef HRESULT (WINAPI*GetThemePartSizePtr)(HANDLE hTheme, HDC hdc, int iPartId,
int iStateId, RECT* prc, int ts,
SIZE* psz);
typedef HRESULT (WINAPI*GetThemeSysFontPtr)(HANDLE hTheme, int iFontId, OUT LOGFONT* pFont);
typedef HRESULT (WINAPI*GetThemeColorPtr)(HANDLE hTheme, HDC hdc, int iPartId,
@@ -693,16 +699,17 @@ nsNativeThemeWin::GetThemePartAndState(n
// Use -1 to indicate we don't wish to have the theme background drawn
// for this item. We will pass any nessessary information via aState,
// and will render the item using separate code.
aPart = -1;
aState = 0;
if (aFrame) {
nsIContent* content = aFrame->GetContent();
nsIContent* parent = content->GetParent();
+ // XXXzeniko hiding the first toolbar will result in an unwanted margin
if (parent && parent->GetChildAt(0) == content) {
aState = 1;
}
}
return NS_OK;
}
case NS_THEME_STATUSBAR_PANEL:
case NS_THEME_STATUSBAR_RESIZER_PANEL:
@@ -887,23 +894,16 @@ RENDER_AGAIN:
// effectively shifting the edge out of view (such that it won't be painted).
if (aWidgetType == NS_THEME_TAB_LEFT_EDGE)
// The right edge should not be drawn. Extend our rect by the edge size.
widgetRect.right += edgeSize;
else
// The left edge should not be drawn. Move the widget rect's left coord back.
widgetRect.left -= edgeSize;
}
- else if (aWidgetType == NS_THEME_TOOLBOX) {
- // The toolbox's toolbar elements will show a 1px border top and bottom.
- // We want the toolbox's background to end right up against the bottom
- // border of the last toolbar, so we simply make it leave a 1px gap at the
- // bottom. This gap will get the bottom border of the last toolbar in it.
- widgetRect.bottom -= 1;
- }
// widgetRect is the bounding box for a widget, yet the scale track is only
// a small portion of this size, so the edges of the scale need to be
// adjusted to the real size of the track.
if (aWidgetType == NS_THEME_SCALE_HORIZONTAL ||
aWidgetType == NS_THEME_SCALE_VERTICAL) {
RECT contentRect;
getThemeContentRect(theme, hdc, part, state, &widgetRect, &contentRect);
@@ -948,24 +948,25 @@ RENDER_AGAIN:
::SetBrushOrgEx(hdc, vpOrg.x + widgetRect.left, vpOrg.y + widgetRect.top, NULL);
PRInt32 oldColor;
oldColor = ::SetTextColor(hdc, 0);
// draw focus rectangle
::DrawFocusRect(hdc, &widgetRect);
::SetTextColor(hdc, oldColor);
}
}
- else if (aWidgetType == NS_THEME_TOOLBAR) {
- // state == 1 iff this toolbar is the first inside the toolbox, which
- // means we should omit the top border for correct rendering.
- if (state == 1) {
- drawThemeEdge(theme, hdc, 0, 0, &widgetRect, BDR_RAISEDINNER, BF_BOTTOM, &clipRect);
- } else {
- drawThemeEdge(theme, hdc, 0, 0, &widgetRect, BDR_RAISEDINNER, BF_TOP | BF_BOTTOM, &clipRect);
- }
+ else if (aWidgetType == NS_THEME_TOOLBAR && state == 0) {
+ // Draw toolbar separator lines above all toolbars except the first one.
+ // The lines are part of the Rebar theme, which is loaded for NS_THEME_TOOLBOX.
+ theme = GetTheme(NS_THEME_TOOLBOX);
+ if (!theme)
+ return NS_ERROR_FAILURE;
+
+ widgetRect.bottom = widgetRect.top + TB_SEPARATOR_HEIGHT;
+ drawThemeEdge(theme, hdc, RP_BAND, 0, &widgetRect, EDGE_ETCHED, BF_TOP, NULL);
}
nativeDrawing.EndNativeDrawing();
if (nativeDrawing.ShouldRenderAgain())
goto RENDER_AGAIN;
nativeDrawing.PaintToContext();
@@ -988,39 +989,31 @@ nsNativeThemeWin::GetWidgetBorder(nsIDev
if (!WidgetIsContainer(aWidgetType) ||
aWidgetType == NS_THEME_TOOLBOX ||
aWidgetType == NS_THEME_STATUSBAR ||
aWidgetType == NS_THEME_RESIZER || aWidgetType == NS_THEME_TAB_PANEL ||
aWidgetType == NS_THEME_SCROLLBAR_TRACK_HORIZONTAL ||
aWidgetType == NS_THEME_SCROLLBAR_TRACK_VERTICAL)
return NS_OK; // Don't worry about it.
- if (aWidgetType == NS_THEME_TOOLBAR) {
- // A normal toolbar has a 1px border above and below it, with 2px of
- // space either size. If it is the first toolbar, no top border is needed.
- aResult->top = aResult->bottom = 1;
- aResult->left = 2;
- if (aFrame) {
- nsIContent* content = aFrame->GetContent();
- nsIContent* parent = content->GetParent();
- if (parent && parent->GetChildAt(0) == content) {
- aResult->top = 0;
- }
- }
- return NS_OK;
- }
-
if (!getThemeContentRect)
return NS_ERROR_FAILURE;
PRInt32 part, state;
nsresult rv = GetThemePartAndState(aFrame, aWidgetType, part, state);
if (NS_FAILED(rv))
return rv;
+ if (aWidgetType == NS_THEME_TOOLBAR) {
+ // make space for the separator line above all toolbars but the first
+ if (state == 0)
+ aResult->top = TB_SEPARATOR_HEIGHT;
+ return NS_OK;
+ }
+
// Get our info.
RECT outerRect; // Create a fake outer rect.
outerRect.top = outerRect.left = 100;
outerRect.right = outerRect.bottom = 200;
RECT contentRect(outerRect);
HRESULT res = getThemeContentRect(theme, NULL, part, state, &outerRect, &contentRect);
if (FAILED(res))
@@ -1303,16 +1296,21 @@ nsNativeThemeWin::ThemeNeedsComboboxDrop
/* Windows 9x/NT/2000/Classic XP Theme Support */
PRBool
nsNativeThemeWin::ClassicThemeSupportsWidget(nsPresContext* aPresContext,
nsIFrame* aFrame,
PRUint8 aWidgetType)
{
switch (aWidgetType) {
+ case NS_THEME_MENUBAR:
+ case NS_THEME_MENUPOPUP:
+ // Classic non-flat menus are handled almost entirely through CSS.
+ if (!mFlatMenus)
+ return PR_FALSE;
case NS_THEME_BUTTON:
case NS_THEME_TEXTFIELD:
case NS_THEME_TEXTFIELD_MULTILINE:
case NS_THEME_CHECKBOX:
case NS_THEME_CHECKBOX_SMALL:
case NS_THEME_RADIO:
case NS_THEME_RADIO_SMALL:
case NS_THEME_SCROLLBAR_BUTTON_UP:
@@ -1343,21 +1341,22 @@ nsNativeThemeWin::ClassicThemeSupportsWi
case NS_THEME_PROGRESSBAR_VERTICAL:
case NS_THEME_PROGRESSBAR_CHUNK:
case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL:
case NS_THEME_TAB:
case NS_THEME_TAB_LEFT_EDGE:
case NS_THEME_TAB_RIGHT_EDGE:
case NS_THEME_TAB_PANEL:
case NS_THEME_TAB_PANELS:
- case NS_THEME_MENUBAR:
- case NS_THEME_MENUPOPUP:
case NS_THEME_MENUITEM:
case NS_THEME_CHECKMENUITEM:
case NS_THEME_RADIOMENUITEM:
+ case NS_THEME_MENUCHECKBOX:
+ case NS_THEME_MENURADIO:
+ case NS_THEME_MENUARROW:
return PR_TRUE;
}
return PR_FALSE;
}
nsresult
nsNativeThemeWin::ClassicGetWidgetBorder(nsIDeviceContext* aContext,
nsIFrame* aFrame,
@@ -1405,40 +1404,43 @@ nsNativeThemeWin::ClassicGetWidgetBorder
case NS_THEME_PROGRESSBAR:
case NS_THEME_PROGRESSBAR_VERTICAL:
(*aResult).top = (*aResult).left = (*aResult).bottom = (*aResult).right = 1;
break;
case NS_THEME_MENUBAR:
(*aResult).top = (*aResult).left = (*aResult).bottom = (*aResult).right = 0;
break;
case NS_THEME_MENUPOPUP:
- (*aResult).top = (*aResult).left = (*aResult).bottom = (*aResult).right = 2;
+ (*aResult).top = (*aResult).left = (*aResult).bottom = (*aResult).right = 3;
break;
case NS_THEME_MENUITEM:
case NS_THEME_CHECKMENUITEM:
case NS_THEME_RADIOMENUITEM: {
- PRBool isTopLevel = PR_FALSE;
- nsIMenuFrame *menuFrame = nsnull;
- CallQueryInterface(aFrame, &menuFrame);
+ PRInt32 part, state;
+ PRBool focused;
+ nsresult rv;
- if (menuFrame) {
- // If this is a real menu item, we should check if it is part of the
- // main menu bar or not, as this affects rendering.
- isTopLevel = menuFrame->IsOnMenuBar();
- }
+ rv = ClassicGetThemePartAndState(aFrame, aWidgetType, part, state, focused);
+ if (NS_FAILED(rv))
+ return rv;
- // These values are obtained from visual inspection of equivelant
- // native components.
- if (isTopLevel) {
- (*aResult).top = (*aResult).bottom = 1;
- (*aResult).left = 3;
- (*aResult).right = 4;
- } else {
- (*aResult).top = 0;
- (*aResult).bottom = (*aResult).left = (*aResult).right = 1;
+ if (part == 1) { // top level menu
+ if (mFlatMenus || !(state & DFCS_PUSHED)) {
+ (*aResult).top = (*aResult).bottom = (*aResult).left = (*aResult).right = 2;
+ }
+ else {
+ // make top-level menus look sunken when pushed in the Classic look
+ (*aResult).top = (*aResult).left = 3;
+ (*aResult).bottom = (*aResult).right = 1;
+ }
+ }
+ else {
+ (*aResult).top = 1;
+ (*aResult).bottom = 3;
+ (*aResult).left = (*aResult).right = 2;
}
break;
}
default:
(*aResult).top = (*aResult).bottom = (*aResult).left = (*aResult).right = 0;
break;
}
return NS_OK;
@@ -1453,16 +1455,22 @@ nsNativeThemeWin::ClassicGetMinimumWidge
*aIsOverridable = PR_TRUE;
switch (aWidgetType) {
case NS_THEME_RADIO:
case NS_THEME_RADIO_SMALL:
case NS_THEME_CHECKBOX:
case NS_THEME_CHECKBOX_SMALL:
(*aResult).width = (*aResult).height = 13;
break;
+ case NS_THEME_MENUCHECKBOX:
+ case NS_THEME_MENURADIO:
+ case NS_THEME_MENUARROW:
+ (*aResult).width = ::GetSystemMetrics(SM_CXMENUCHECK);
+ (*aResult).height = ::GetSystemMetrics(SM_CYMENUCHECK);
+ break;
case NS_THEME_SCROLLBAR_BUTTON_UP:
case NS_THEME_SCROLLBAR_BUTTON_DOWN:
(*aResult).width = ::GetSystemMetrics(SM_CXVSCROLL);
(*aResult).height = ::GetSystemMetrics(SM_CYVSCROLL);
*aIsOverridable = PR_FALSE;
break;
case NS_THEME_SCROLLBAR_BUTTON_LEFT:
case NS_THEME_SCROLLBAR_BUTTON_RIGHT:
@@ -1649,35 +1657,39 @@ nsresult nsNativeThemeWin::ClassicGetThe
if (IsDisabled(aFrame))
aState |= DFCS_INACTIVE;
if (isTopLevel) {
aPart = 1;
if (isOpen)
aState |= DFCS_PUSHED;
- } else {
- if (isContainer)
- aState |= DFCS_CONTAINER;
- if (aFrame->GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL)
- aState |= DFCS_RTL;
}
if (CheckBooleanAttr(aFrame, nsWidgetAtoms::mozmenuactive))
aState |= DFCS_HOT;
- // Only menu items of the appropriate type may have tick or bullet marks.
- if (aWidgetType == NS_THEME_CHECKMENUITEM ||
- aWidgetType == NS_THEME_RADIOMENUITEM) {
+ return NS_OK;
+ }
+ case NS_THEME_MENUCHECKBOX:
+ case NS_THEME_MENURADIO:
+ case NS_THEME_MENUARROW:
+ aState = 0;
+ if (IsDisabled(aFrame))
+ aState |= DFCS_INACTIVE;
+ if (CheckBooleanAttr(aFrame, nsWidgetAtoms::mozmenuactive))
+ aState |= DFCS_HOT;
+ if (aWidgetType == NS_THEME_MENUCHECKBOX || aWidgetType == NS_THEME_MENURADIO) {
if (IsCheckedButton(aFrame))
aState |= DFCS_CHECKED;
+ } else {
+ if (aFrame->GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL)
+ aState |= DFCS_RTL;
}
-
return NS_OK;
- }
case NS_THEME_LISTBOX:
case NS_THEME_TREEVIEW:
case NS_THEME_TEXTFIELD:
case NS_THEME_TEXTFIELD_MULTILINE:
case NS_THEME_DROPDOWN:
case NS_THEME_DROPDOWN_TEXTFIELD:
case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
@@ -1908,16 +1920,20 @@ static void DrawMenuImage(HDC hdc, const
// XXXjgr This will go pear-shaped if the image is bigger than the
// provided rect. What should we do?
RECT imgRect = { 0, 0, checkW, checkH };
POINT imgPos = {
rc.left + (rc.right - rc.left - checkW) / 2,
rc.top + (rc.bottom - rc.top - checkH) / 2
};
+ // XXXzeniko Windows renders these 1px lower than you'd expect
+ if (aComponent == DFCS_MENUCHECK || aComponent == DFCS_MENUBULLET)
+ imgPos.y++;
+
::DrawFrameControl(hMemoryDC, &imgRect, DFC_MENU, aComponent);
COLORREF oldTextCol = ::SetTextColor(hdc, 0x00000000);
COLORREF oldBackCol = ::SetBkColor(hdc, 0x00FFFFFF);
::BitBlt(hdc, imgPos.x, imgPos.y, checkW, checkH, hMemoryDC, 0, 0, SRCAND);
::SetTextColor(hdc, ::GetSysColor(aColor));
::SetBkColor(hdc, 0x00000000);
::BitBlt(hdc, imgPos.x, imgPos.y, checkW, checkH, hMemoryDC, 0, 0, SRCPAINT);
::SetTextColor(hdc, oldTextCol);
@@ -2183,26 +2199,23 @@ RENDER_AGAIN:
case NS_THEME_TAB_PANELS:
::DrawEdge(hdc, &widgetRect, EDGE_RAISED, BF_SOFT | BF_MIDDLE |
BF_LEFT | BF_RIGHT | BF_BOTTOM);
break;
case NS_THEME_MENUBAR:
break;
case NS_THEME_MENUPOPUP:
- if (mFlatMenus) {
- ::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_MENU+1));
- ::FrameRect(hdc, &widgetRect, ::GetSysColorBrush(COLOR_BTNSHADOW));
- } else {
- ::DrawEdge(hdc, &widgetRect, EDGE_RAISED, BF_RECT | BF_MIDDLE);
- }
+ NS_ASSERTION(mFlatMenus, "Classic menus are styled entirely through CSS");
+ ::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_MENU+1));
+ ::FrameRect(hdc, &widgetRect, ::GetSysColorBrush(COLOR_BTNSHADOW));
break;
case NS_THEME_MENUITEM:
case NS_THEME_CHECKMENUITEM:
- case NS_THEME_RADIOMENUITEM: {
+ case NS_THEME_RADIOMENUITEM:
// part == 0 for normal items
// part == 1 for top-level menu items
if (mFlatMenus) {
// Not disabled and hot/pushed.
if ((state & (DFCS_HOT | DFCS_PUSHED)) != 0) {
::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_MENUHILIGHT+1));
::FrameRect(hdc, &widgetRect, ::GetSysColorBrush(COLOR_HIGHLIGHT));
}
@@ -2216,56 +2229,40 @@ RENDER_AGAIN:
}
}
} else {
if ((state & (DFCS_HOT | DFCS_PUSHED)) != 0) {
::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_HIGHLIGHT+1));
}
}
}
- if (((state & DFCS_CHECKED) != 0) || ((state & DFCS_CONTAINER) != 0)) {
- RECT menuRectStart, menuRectEnd;
- PRUint32 color = COLOR_MENUTEXT;
-
- if ((state & DFCS_INACTIVE) != 0)
- color = COLOR_GRAYTEXT;
- else if ((state & DFCS_HOT) != 0)
- color = COLOR_HIGHLIGHTTEXT;
-
- ::CopyRect(&menuRectStart, &widgetRect);
- ::InflateRect(&menuRectStart, -1, -1);
- ::CopyRect(&menuRectEnd, &menuRectStart);
-
- // WARNING: This value of 15 must match the value in menu.css for the min-width of .menu-iconic-left
- if ((state & DFCS_RTL) == 0) {
- menuRectStart.right = menuRectStart.left + 15; // Left box
- menuRectEnd.left = menuRectEnd.right - 15; // Right box
- } else {
- menuRectStart.left = menuRectStart.right - 15; // Right box
- menuRectEnd.right = menuRectEnd.left + 15; // left box
- }
-
+ break;
#ifndef WINCE
- if ((state & DFCS_CHECKED) != 0) {
- if (aWidgetType == NS_THEME_CHECKMENUITEM) {
- DrawMenuImage(hdc, menuRectStart, DFCS_MENUCHECK, color);
- } else if (aWidgetType == NS_THEME_RADIOMENUITEM) {
- DrawMenuImage(hdc, menuRectStart, DFCS_MENUBULLET, color);
- }
- }
- if ((state & DFCS_CONTAINER) != 0) {
- if ((state & DFCS_RTL) == 0)
- DrawMenuImage(hdc, menuRectEnd, DFCS_MENUARROW, color);
- else
- DrawMenuImage(hdc, menuRectEnd, DFCS_MENUARROWRIGHT, color);
- }
-#endif
- }
+ case NS_THEME_MENUCHECKBOX:
+ case NS_THEME_MENURADIO:
+ if (!(state & DFCS_CHECKED))
+ break; // nothin' to do
+ case NS_THEME_MENUARROW: {
+ PRUint32 color = COLOR_MENUTEXT;
+ if ((state & DFCS_INACTIVE))
+ color = COLOR_GRAYTEXT;
+ else if ((state & DFCS_HOT))
+ color = COLOR_HIGHLIGHTTEXT;
+
+ if (aWidgetType == NS_THEME_MENUCHECKBOX)
+ DrawMenuImage(hdc, widgetRect, DFCS_MENUCHECK, color);
+ else if (aWidgetType == NS_THEME_MENURADIO)
+ DrawMenuImage(hdc, widgetRect, DFCS_MENUBULLET, color);
+ else if (aWidgetType == NS_THEME_MENUARROW)
+ DrawMenuImage(hdc, widgetRect,
+ (state & DFCS_RTL) ? DFCS_MENUARROWRIGHT : DFCS_MENUARROW,
+ color);
break;
}
+#endif
default:
rv = NS_ERROR_FAILURE;
break;
}
nativeDrawing.EndNativeDrawing();
if (NS_FAILED(rv))
@@ -2336,16 +2333,19 @@ nsNativeThemeWin::GetWidgetNativeDrawing
case NS_THEME_DROPDOWN_BUTTON:
// these are definitely no; they're all graphics that don't get scaled up
case NS_THEME_CHECKBOX:
case NS_THEME_CHECKBOX_SMALL:
case NS_THEME_RADIO:
case NS_THEME_RADIO_SMALL:
case NS_THEME_CHECKMENUITEM:
case NS_THEME_RADIOMENUITEM:
+ case NS_THEME_MENUCHECKBOX:
+ case NS_THEME_MENURADIO:
+ case NS_THEME_MENUARROW:
return
gfxWindowsNativeDrawing::CANNOT_DRAW_TO_COLOR_ALPHA |
gfxWindowsNativeDrawing::CANNOT_AXIS_ALIGNED_SCALE |
gfxWindowsNativeDrawing::CANNOT_COMPLEX_TRANSFORM;
}
return
gfxWindowsNativeDrawing::CANNOT_DRAW_TO_COLOR_ALPHA |