Bug 672050 - Add Mac rendering for -moz-appearance: toolbarbutton. r=josh

This commit is contained in:
Markus Stange 2011-08-08 16:42:45 +02:00
Родитель 58a18684cf
Коммит 6397112f91
4 изменённых файлов: 184 добавлений и 146 удалений

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

@ -51,6 +51,7 @@
@class CellDrawView;
@class NSProgressBarCell;
class nsDeviceContext;
struct SegmentedControlRenderSettings;
class nsNativeThemeCocoa : private nsNativeTheme,
public nsITheme
@ -96,6 +97,9 @@ protected:
nsresult GetSystemColor(PRUint8 aWidgetType, nsILookAndFeel::nsColorID& aColorID);
nsIntMargin RTLAwareMargin(const nsIntMargin& aMargin, nsIFrame* aFrame);
nsIFrame* SeparatorResponsibility(nsIFrame* aBefore, nsIFrame* aAfter);
CGRect SeparatorAdjustedRect(CGRect aRect, nsIFrame* aLeft,
nsIFrame* aCurrent, nsIFrame* aRight);
// Helpers for progressbar.
double GetProgressValue(nsIFrame* aFrame);
@ -108,8 +112,9 @@ protected:
void DrawProgress(CGContextRef context, const HIRect& inBoxRect,
PRBool inIsIndeterminate, PRBool inIsHorizontal,
double inValue, double inMaxValue, nsIFrame* aFrame);
void DrawTab(CGContextRef context, HIRect inBoxRect, nsEventStates inState,
nsIFrame* aFrame);
void DrawSegment(CGContextRef cgContext, const HIRect& inBoxRect,
nsEventStates inState, nsIFrame* aFrame,
const SegmentedControlRenderSettings& aSettings);
void DrawTabPanel(CGContextRef context, const HIRect& inBoxRect, nsIFrame* aFrame);
void DrawScale(CGContextRef context, const HIRect& inBoxRect,
nsEventStates inState, PRBool inDirection,

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

@ -289,6 +289,16 @@ static NSControlSize CocoaSizeForEnum(PRInt32 enumControlSize) {
return NSRegularControlSize;
}
static NSString* CUIControlSizeForCocoaSize(NSControlSize aControlSize)
{
if (aControlSize == NSRegularControlSize)
return @"regular";
else if (aControlSize == NSSmallControlSize)
return @"small";
else
return @"mini";
}
static void InflateControlRect(NSRect* rect, NSControlSize cocoaControlSize, const float marginSet[][3][4])
{
if (!marginSet)
@ -338,6 +348,15 @@ static BOOL FrameIsInActiveWindow(nsIFrame* aFrame)
return [win isMainWindow] && ![win attachedSheet];
}
// Toolbar controls and content controls respond to different window
// activeness states.
static BOOL IsActive(nsIFrame* aFrame, BOOL aIsToolbarControl)
{
if (aIsToolbarControl)
return [NativeWindowForFrame(aFrame) isMainWindow] || ![NSView focusView];
return FrameIsInActiveWindow(aFrame);
}
NS_IMPL_ISUPPORTS_INHERITED1(nsNativeThemeCocoa, nsNativeTheme, nsITheme)
@ -574,7 +593,7 @@ struct CellRenderSettings {
* tolerance - The tolerance as passed to DrawCellWithSnapping.
* NOTE: returns NSRegularControlSize if all values in 'sizes' are zero.
*/
static NSControlSize FindControlSize(CGFloat size, CGFloat* sizes, CGFloat tolerance)
static NSControlSize FindControlSize(CGFloat size, const CGFloat* sizes, CGFloat tolerance)
{
for (PRUint32 i = miniControlSize; i <= regularControlSize; ++i) {
if (sizes[i] == 0) {
@ -1344,86 +1363,107 @@ nsNativeThemeCocoa::DrawScale(CGContextRef cgContext, const HIRect& inBoxRect,
NS_OBJC_END_TRY_ABORT_BLOCK;
}
#define NATURAL_MINI_TAB_BUTTON_HEIGHT 17
#define NATURAL_SMALL_TAB_BUTTON_HEIGHT 20
#define NATURAL_REGULAR_TAB_BUTTON_HEIGHT 23
nsIFrame*
nsNativeThemeCocoa::SeparatorResponsibility(nsIFrame* aBefore, nsIFrame* aAfter)
{
// Usually a separator is drawn by the segment to the right of the
// separator, but pressed and selected segments have higher priority.
if (!aBefore || !aAfter)
return nsnull;
if (IsSelectedButton(aAfter))
return aAfter;
if (IsSelectedButton(aBefore) || IsPressedButton(aBefore))
return aBefore;
return aAfter;
}
CGRect
nsNativeThemeCocoa::SeparatorAdjustedRect(CGRect aRect, nsIFrame* aLeft,
nsIFrame* aCurrent, nsIFrame* aRight)
{
// A separator between two segments should always be located in the leftmost
// pixel column of the segment to the right of the separator, regardless of
// who ends up drawing it.
// CoreUI draws the separators inside the drawing rect.
if (aLeft && SeparatorResponsibility(aLeft, aCurrent) == aLeft) {
// The left button draws the separator, so we need to make room for it.
aRect.origin.x += 1;
aRect.size.width -= 1;
}
if (SeparatorResponsibility(aCurrent, aRight) == aCurrent) {
// We draw the right separator, so we need to extend the draw rect into the
// segment to our right.
aRect.size.width += 1;
}
return aRect;
}
static NSString* ToolbarButtonPosition(BOOL aIsFirst, BOOL aIsLast)
{
if (aIsFirst) {
if (aIsLast)
return @"kCUISegmentPositionOnly";
return @"kCUISegmentPositionFirst";
}
if (aIsLast)
return @"kCUISegmentPositionLast";
return @"kCUISegmentPositionMiddle";
}
struct SegmentedControlRenderSettings {
const CGFloat* heights;
const NSString* widgetName;
const BOOL ignoresPressedWhenSelected;
const BOOL isToolbarControl;
};
static const CGFloat tabHeights[3] = { 17, 20, 23 };
static const SegmentedControlRenderSettings tabRenderSettings = {
tabHeights, @"tab", YES, NO
};
static const CGFloat toolbarButtonHeights[3] = { 15, 18, 22 };
static const SegmentedControlRenderSettings toolbarButtonRenderSettings = {
toolbarButtonHeights, @"kCUIWidgetButtonSegmentedSCurve", NO, YES
};
void
nsNativeThemeCocoa::DrawTab(CGContextRef cgContext, HIRect inBoxRect,
nsEventStates inState, nsIFrame* aFrame)
nsNativeThemeCocoa::DrawSegment(CGContextRef cgContext, const HIRect& inBoxRect,
nsEventStates inState, nsIFrame* aFrame,
const SegmentedControlRenderSettings& aSettings)
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
HIThemeTabDrawInfo tdi;
tdi.version = 1;
tdi.kind = kHIThemeTabKindNormal;
PRBool isSelected = IsSelectedTab(aFrame);
PRBool isDisabled = IsDisabled(aFrame, inState);
if (isSelected) {
if (isDisabled)
tdi.style = kThemeTabFrontUnavailable;
else
tdi.style = FrameIsInActiveWindow(aFrame) ? kThemeTabFront : kThemeTabFrontInactive;
} else {
if (isDisabled)
tdi.style = kThemeTabNonFrontUnavailable;
else if (inState.HasAllStates(NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_HOVER))
tdi.style = kThemeTabNonFrontPressed;
else
tdi.style = FrameIsInActiveWindow(aFrame) ? kThemeTabNonFront : kThemeTabNonFrontInactive;
BOOL isActive = IsActive(aFrame, aSettings.isToolbarControl);
BOOL isFocused = inState.HasState(NS_EVENT_STATE_FOCUS);
BOOL isSelected = IsSelectedButton(aFrame);
BOOL isPressed = IsPressedButton(aFrame);
if (isSelected && aSettings.ignoresPressedWhenSelected) {
isPressed = NO;
}
tdi.direction = kThemeTabNorth;
tdi.size = kHIThemeTabSizeNormal;
if (inBoxRect.size.height < NATURAL_REGULAR_TAB_BUTTON_HEIGHT)
tdi.size = kHIThemeTabSizeSmall;
if (inBoxRect.size.height < NATURAL_SMALL_TAB_BUTTON_HEIGHT)
tdi.size = kHIThemeTabSizeMini;
BOOL isRTL = IsFrameRTL(aFrame);
nsIFrame* left = GetAdjacentSiblingFrameWithSameAppearance(aFrame, isRTL);
nsIFrame* right = GetAdjacentSiblingFrameWithSameAppearance(aFrame, !isRTL);
CGRect drawRect = SeparatorAdjustedRect(inBoxRect, left, aFrame, right);
BOOL drawLeftSeparator = SeparatorResponsibility(left, aFrame) == aFrame;
BOOL drawRightSeparator = SeparatorResponsibility(aFrame, right) == aFrame;
NSControlSize controlSize = FindControlSize(drawRect.size.height, aSettings.heights, 4.0f);
PRBool isRTL = IsFrameRTL(aFrame);
PRBool isFirst = isRTL ? IsLastTab(aFrame) : IsFirstTab(aFrame);
PRBool isLast = isRTL ? IsFirstTab(aFrame) : IsLastTab(aFrame);
if (isFirst && isLast)
tdi.position = kHIThemeTabPositionOnly;
else if (isFirst)
tdi.position = kHIThemeTabPositionFirst;
else if (isLast)
tdi.position = kHIThemeTabPositionLast;
else
tdi.position = kHIThemeTabPositionMiddle;
// Tab separator management:
// Normal tabs only draw their left separator, in the leftmost pixel row of
// their frame. Selected tabs additionally draw their right separator, outside
// of their frame. To prevent overlapping, the tab to the right of the
// selected tab shouldn't draw its left separator.
tdi.adornment = kHIThemeTabAdornmentNone;
if (isRTL ? IsBeforeSelectedTab(aFrame) : IsAfterSelectedTab(aFrame)) {
// On Leopard, the tab's left edge must be shifted 1px to the right.
// On Tiger, this happens automatically when no leading separator is drawn.
inBoxRect.origin.x += 1;
inBoxRect.size.width -= 1;
}
else {
tdi.adornment = kHIThemeTabAdornmentLeadingSeparator;
}
if (isSelected && !isLast) {
tdi.adornment |= kHIThemeTabAdornmentTrailingSeparator;
// On Tiger, the right separator is drawn outside of the frame.
// On Leopard, the right edge must be shifted 1px to the right.
inBoxRect.size.width += 1;
}
if (inState.HasState(NS_EVENT_STATE_FOCUS))
tdi.adornment |= kThemeAdornmentFocus;
HIThemeDrawTab(&inBoxRect, &tdi, cgContext, HITHEME_ORIENTATION, NULL);
NS_OBJC_END_TRY_ABORT_BLOCK;
CUIDraw([NSWindow coreUIRenderer], drawRect, cgContext,
(CFDictionaryRef)[NSDictionary dictionaryWithObjectsAndKeys:
aSettings.widgetName, @"widget",
ToolbarButtonPosition(!left, !right), @"kCUIPositionKey",
[NSNumber numberWithBool:drawLeftSeparator], @"kCUISegmentLeadingSeparatorKey",
[NSNumber numberWithBool:drawRightSeparator], @"kCUISegmentTrailingSeparatorKey",
[NSNumber numberWithBool:isSelected], @"value",
(isPressed ? @"pressed" : (isActive ? @"normal" : @"inactive")), @"state",
[NSNumber numberWithBool:isFocused], @"focus",
CUIControlSizeForCocoaSize(controlSize), @"size",
[NSNumber numberWithBool:YES], @"is.flipped",
@"up", @"direction",
nil],
nil);
}
static inline UInt8
@ -1629,8 +1669,6 @@ nsNativeThemeCocoa::DrawStatusBar(CGContextRef cgContext, const HIRect& inBoxRec
if (inBoxRect.size.height < 2.0f)
return;
BOOL isMain = [NativeWindowForFrame(aFrame) isMainWindow] || ![NSView focusView];
CGContextSaveGState(cgContext);
CGContextClipToRect(cgContext, inBoxRect);
@ -1645,7 +1683,7 @@ nsNativeThemeCocoa::DrawStatusBar(CGContextRef cgContext, const HIRect& inBoxRec
(CFDictionaryRef)[NSDictionary dictionaryWithObjectsAndKeys:
@"kCUIWidgetWindowFrame", @"widget",
@"regularwin", @"windowtype",
(isMain ? @"normal" : @"inactive"), @"state",
(IsActive(aFrame, YES) ? @"normal" : @"inactive"), @"state",
[NSNumber numberWithInt:inBoxRect.size.height], @"kCUIWindowFrameBottomBarHeightKey",
[NSNumber numberWithBool:YES], @"kCUIWindowFrameDrawBottomBarSeparatorKey",
[NSNumber numberWithBool:YES], @"is.flipped",
@ -1860,9 +1898,7 @@ nsNativeThemeCocoa::DrawWidgetBackground(nsRenderingContext* aContext,
break;
case NS_THEME_TOOLBAR_BUTTON:
DrawButton(cgContext, kThemePushButton, macRect,
IsDefaultButton(aFrame), kThemeButtonOn, kThemeAdornmentNone,
eventState, aFrame);
DrawSegment(cgContext, macRect, eventState, aFrame, toolbarButtonRenderSettings);
break;
case NS_THEME_TOOLBAR_SEPARATOR: {
@ -2109,7 +2145,7 @@ nsNativeThemeCocoa::DrawWidgetBackground(nsRenderingContext* aContext,
break;
case NS_THEME_TAB:
DrawTab(cgContext, macRect, eventState, aFrame);
DrawSegment(cgContext, macRect, eventState, aFrame, tabRenderSettings);
break;
case NS_THEME_TAB_PANELS:
@ -2162,6 +2198,12 @@ nsNativeThemeCocoa::GetWidgetBorder(nsDeviceContext* aContext,
break;
}
case NS_THEME_TOOLBAR_BUTTON:
{
aResult->SizeTo(4, 1, 4, 1);
break;
}
case NS_THEME_CHECKBOX:
case NS_THEME_RADIO:
{
@ -2278,6 +2320,7 @@ nsNativeThemeCocoa::GetWidgetOverflow(nsDeviceContext* aContext, nsIFrame* aFram
{
switch (aWidgetType) {
case NS_THEME_BUTTON:
case NS_THEME_TOOLBAR_BUTTON:
case NS_THEME_TEXTFIELD:
case NS_THEME_TEXTFIELD_MULTILINE:
case NS_THEME_SEARCHFIELD:
@ -2328,6 +2371,12 @@ nsNativeThemeCocoa::GetMinimumWidgetSize(nsRenderingContext* aContext,
break;
}
case NS_THEME_TOOLBAR_BUTTON:
{
aResult->SizeTo(0, toolbarButtonHeights[miniControlSize]);
break;
}
case NS_THEME_SPINNER:
{
SInt32 buttonHeight = 0, buttonWidth = 0;
@ -2388,7 +2437,7 @@ nsNativeThemeCocoa::GetMinimumWidgetSize(nsRenderingContext* aContext,
case NS_THEME_TAB:
{
aResult->SizeTo(0, NATURAL_MINI_TAB_BUTTON_HEIGHT);
aResult->SizeTo(0, tabHeights[miniControlSize]);
break;
}
@ -2511,7 +2560,6 @@ nsNativeThemeCocoa::WidgetStateChanged(nsIFrame* aFrame, PRUint8 aWidgetType,
case NS_THEME_TOOLBOX:
case NS_THEME_TOOLBAR:
case NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR:
case NS_THEME_TOOLBAR_BUTTON:
case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
case NS_THEME_STATUSBAR:
@ -2597,6 +2645,7 @@ nsNativeThemeCocoa::ThemeSupportsWidget(nsPresContext* aPresContext, nsIFrame* a
case NS_THEME_GROUPBOX:
case NS_THEME_BUTTON:
case NS_THEME_BUTTON_BEVEL:
case NS_THEME_TOOLBAR_BUTTON:
case NS_THEME_SPINNER:
case NS_THEME_TOOLBAR:
case NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR:

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

@ -207,6 +207,18 @@ nsNativeTheme::IsButtonTypeMenu(nsIFrame* aFrame)
NS_LITERAL_STRING("menu"), eCaseMatters);
}
PRBool
nsNativeTheme::IsPressedButton(nsIFrame* aFrame)
{
nsEventStates eventState = GetContentState(aFrame, NS_THEME_TOOLBAR_BUTTON);
if (IsDisabled(aFrame, eventState))
return PR_FALSE;
return IsOpenButton(aFrame) ||
eventState.HasAllStates(NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_HOVER);
}
PRBool
nsNativeTheme::GetIndeterminate(nsIFrame* aFrame)
{
@ -409,19 +421,6 @@ nsNativeTheme::IsFirstTab(nsIFrame* aFrame)
return PR_FALSE;
}
PRBool
nsNativeTheme::IsLastTab(nsIFrame* aFrame)
{
if (!aFrame)
return PR_FALSE;
while ((aFrame = aFrame->GetNextSibling())) {
if (aFrame->GetRect().width > 0 && aFrame->GetContent()->Tag() == nsWidgetAtoms::tab)
return PR_FALSE;
}
return PR_TRUE;
}
PRBool
nsNativeTheme::IsHorizontal(nsIFrame* aFrame)
{
@ -433,34 +432,6 @@ nsNativeTheme::IsHorizontal(nsIFrame* aFrame)
eCaseMatters);
}
PRBool
nsNativeTheme::IsNextToSelectedTab(nsIFrame* aFrame, PRInt32 aOffset)
{
if (!aFrame)
return PR_FALSE;
if (aOffset == 0)
return IsSelectedTab(aFrame);
PRInt32 thisTabIndex = -1, selectedTabIndex = -1;
nsIFrame* currentTab = aFrame->GetParent()->GetFirstChild(NULL);
for (PRInt32 i = 0; currentTab; currentTab = currentTab->GetNextSibling()) {
if (currentTab->GetRect().width == 0)
continue;
if (aFrame == currentTab)
thisTabIndex = i;
if (IsSelectedTab(currentTab))
selectedTabIndex = i;
++i;
}
if (thisTabIndex == -1 || selectedTabIndex == -1)
return PR_FALSE;
return (thisTabIndex - selectedTabIndex == aOffset);
}
// progressbar:
PRBool
nsNativeTheme::IsIndeterminateProgress(nsIFrame* aFrame,
@ -589,3 +560,25 @@ nsNativeTheme::Notify(nsITimer* aTimer)
mAnimatedContentTimeout = PR_UINT32_MAX;
return NS_OK;
}
nsIFrame*
nsNativeTheme::GetAdjacentSiblingFrameWithSameAppearance(nsIFrame* aFrame,
PRBool aNextSibling)
{
if (!aFrame)
return nsnull;
// Find the next visible sibling.
nsIFrame* sibling = aFrame;
do {
sibling = aNextSibling ? sibling->GetNextSibling() : sibling->GetPrevSibling();
} while (sibling && sibling->GetRect().width == 0);
// Check same appearance and adjacency.
if (!sibling ||
sibling->GetStyleDisplay()->mAppearance != aFrame->GetStyleDisplay()->mAppearance ||
(sibling->GetRect().XMost() != aFrame->GetRect().x &&
aFrame->GetRect().XMost() != sibling->GetRect().x))
return nsnull;
return sibling;
}

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

@ -122,33 +122,22 @@ class nsNativeTheme : public nsITimerCallback
return CheckBooleanAttr(aFrame, nsWidgetAtoms::selected);
}
PRBool IsNextToSelectedTab(nsIFrame* aFrame, PRInt32 aOffset);
PRBool IsBeforeSelectedTab(nsIFrame* aFrame) {
return IsNextToSelectedTab(aFrame, -1);
}
PRBool IsAfterSelectedTab(nsIFrame* aFrame) {
return IsNextToSelectedTab(aFrame, 1);
}
PRBool IsLeftToSelectedTab(nsIFrame* aFrame) {
return IsFrameRTL(aFrame) ? IsAfterSelectedTab(aFrame) : IsBeforeSelectedTab(aFrame);
}
PRBool IsRightToSelectedTab(nsIFrame* aFrame) {
return IsFrameRTL(aFrame) ? IsBeforeSelectedTab(aFrame) : IsAfterSelectedTab(aFrame);
}
// button / toolbarbutton:
PRBool IsCheckedButton(nsIFrame* aFrame) {
return CheckBooleanAttr(aFrame, nsWidgetAtoms::checked);
}
PRBool IsSelectedButton(nsIFrame* aFrame) {
return CheckBooleanAttr(aFrame, nsWidgetAtoms::checked) ||
CheckBooleanAttr(aFrame, nsWidgetAtoms::selected);
}
PRBool IsOpenButton(nsIFrame* aFrame) {
return CheckBooleanAttr(aFrame, nsWidgetAtoms::open);
}
PRBool IsPressedButton(nsIFrame* aFrame);
// treeheadercell:
TreeSortDirection GetTreeSortDirection(nsIFrame* aFrame);
PRBool IsLastTreeHeaderCell(nsIFrame* aFrame);
@ -156,7 +145,6 @@ class nsNativeTheme : public nsITimerCallback
// tab:
PRBool IsBottomTab(nsIFrame* aFrame);
PRBool IsFirstTab(nsIFrame* aFrame);
PRBool IsLastTab(nsIFrame* aFrame);
PRBool IsHorizontal(nsIFrame* aFrame);
@ -187,6 +175,9 @@ class nsNativeTheme : public nsITimerCallback
PRBool QueueAnimatedContentForRefresh(nsIContent* aContent,
PRUint32 aMinimumFrameRate);
nsIFrame* GetAdjacentSiblingFrameWithSameAppearance(nsIFrame* aFrame,
PRBool aNextSibling);
private:
PRUint32 mAnimatedContentTimeout;
nsCOMPtr<nsITimer> mAnimatedContentTimer;