michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: #include "nsButtonFrameRenderer.h" michael@0: #include "nsCSSRendering.h" michael@0: #include "nsPresContext.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "nsCSSPseudoElements.h" michael@0: #include "nsNameSpaceManager.h" michael@0: #include "nsStyleSet.h" michael@0: #include "nsDisplayList.h" michael@0: #include "nsITheme.h" michael@0: #include "nsFrame.h" michael@0: #include "mozilla/EventStates.h" michael@0: #include "mozilla/dom/Element.h" michael@0: michael@0: #define ACTIVE "active" michael@0: #define HOVER "hover" michael@0: #define FOCUS "focus" michael@0: michael@0: nsButtonFrameRenderer::nsButtonFrameRenderer() michael@0: { michael@0: MOZ_COUNT_CTOR(nsButtonFrameRenderer); michael@0: } michael@0: michael@0: nsButtonFrameRenderer::~nsButtonFrameRenderer() michael@0: { michael@0: MOZ_COUNT_DTOR(nsButtonFrameRenderer); michael@0: } michael@0: michael@0: void michael@0: nsButtonFrameRenderer::SetFrame(nsFrame* aFrame, nsPresContext* aPresContext) michael@0: { michael@0: mFrame = aFrame; michael@0: ReResolveStyles(aPresContext); michael@0: } michael@0: michael@0: nsIFrame* michael@0: nsButtonFrameRenderer::GetFrame() michael@0: { michael@0: return mFrame; michael@0: } michael@0: michael@0: void michael@0: nsButtonFrameRenderer::SetDisabled(bool aDisabled, bool notify) michael@0: { michael@0: if (aDisabled) michael@0: mFrame->GetContent()->SetAttr(kNameSpaceID_None, nsGkAtoms::disabled, EmptyString(), michael@0: notify); michael@0: else michael@0: mFrame->GetContent()->UnsetAttr(kNameSpaceID_None, nsGkAtoms::disabled, notify); michael@0: } michael@0: michael@0: bool michael@0: nsButtonFrameRenderer::isDisabled() michael@0: { michael@0: return mFrame->GetContent()->AsElement()-> michael@0: State().HasState(NS_EVENT_STATE_DISABLED); michael@0: } michael@0: michael@0: class nsDisplayButtonBoxShadowOuter : public nsDisplayItem { michael@0: public: michael@0: nsDisplayButtonBoxShadowOuter(nsDisplayListBuilder* aBuilder, michael@0: nsButtonFrameRenderer* aRenderer) michael@0: : nsDisplayItem(aBuilder, aRenderer->GetFrame()), mBFR(aRenderer) { michael@0: MOZ_COUNT_CTOR(nsDisplayButtonBoxShadowOuter); michael@0: } michael@0: #ifdef NS_BUILD_REFCNT_LOGGING michael@0: virtual ~nsDisplayButtonBoxShadowOuter() { michael@0: MOZ_COUNT_DTOR(nsDisplayButtonBoxShadowOuter); michael@0: } michael@0: #endif michael@0: michael@0: virtual void Paint(nsDisplayListBuilder* aBuilder, michael@0: nsRenderingContext* aCtx) MOZ_OVERRIDE; michael@0: virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, michael@0: bool* aSnap) MOZ_OVERRIDE; michael@0: NS_DISPLAY_DECL_NAME("ButtonBoxShadowOuter", TYPE_BUTTON_BOX_SHADOW_OUTER) michael@0: private: michael@0: nsButtonFrameRenderer* mBFR; michael@0: }; michael@0: michael@0: nsRect michael@0: nsDisplayButtonBoxShadowOuter::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) { michael@0: *aSnap = false; michael@0: return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame(); michael@0: } michael@0: michael@0: void michael@0: nsDisplayButtonBoxShadowOuter::Paint(nsDisplayListBuilder* aBuilder, michael@0: nsRenderingContext* aCtx) { michael@0: nsRect frameRect = nsRect(ToReferenceFrame(), mFrame->GetSize()); michael@0: michael@0: nsRect buttonRect; michael@0: mBFR->GetButtonRect(frameRect, buttonRect); michael@0: michael@0: nsCSSRendering::PaintBoxShadowOuter(mFrame->PresContext(), *aCtx, mFrame, michael@0: buttonRect, mVisibleRect); michael@0: } michael@0: michael@0: class nsDisplayButtonBorderBackground : public nsDisplayItem { michael@0: public: michael@0: nsDisplayButtonBorderBackground(nsDisplayListBuilder* aBuilder, michael@0: nsButtonFrameRenderer* aRenderer) michael@0: : nsDisplayItem(aBuilder, aRenderer->GetFrame()), mBFR(aRenderer) { michael@0: MOZ_COUNT_CTOR(nsDisplayButtonBorderBackground); michael@0: } michael@0: #ifdef NS_BUILD_REFCNT_LOGGING michael@0: virtual ~nsDisplayButtonBorderBackground() { michael@0: MOZ_COUNT_DTOR(nsDisplayButtonBorderBackground); michael@0: } michael@0: #endif michael@0: michael@0: virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, michael@0: HitTestState* aState, michael@0: nsTArray *aOutFrames) MOZ_OVERRIDE { michael@0: aOutFrames->AppendElement(mFrame); michael@0: } michael@0: virtual void Paint(nsDisplayListBuilder* aBuilder, michael@0: nsRenderingContext* aCtx) MOZ_OVERRIDE; michael@0: virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, michael@0: bool* aSnap) MOZ_OVERRIDE; michael@0: virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, michael@0: const nsDisplayItemGeometry* aGeometry, michael@0: nsRegion *aInvalidRegion) MOZ_OVERRIDE; michael@0: NS_DISPLAY_DECL_NAME("ButtonBorderBackground", TYPE_BUTTON_BORDER_BACKGROUND) michael@0: private: michael@0: nsButtonFrameRenderer* mBFR; michael@0: }; michael@0: michael@0: nsRect michael@0: nsDisplayButtonBorderBackground::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) { michael@0: *aSnap = false; michael@0: return aBuilder->IsForEventDelivery() ? nsRect(ToReferenceFrame(), mFrame->GetSize()) michael@0: : mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame(); michael@0: } michael@0: michael@0: class nsDisplayButtonForeground : public nsDisplayItem { michael@0: public: michael@0: nsDisplayButtonForeground(nsDisplayListBuilder* aBuilder, michael@0: nsButtonFrameRenderer* aRenderer) michael@0: : nsDisplayItem(aBuilder, aRenderer->GetFrame()), mBFR(aRenderer) { michael@0: MOZ_COUNT_CTOR(nsDisplayButtonForeground); michael@0: } michael@0: #ifdef NS_BUILD_REFCNT_LOGGING michael@0: virtual ~nsDisplayButtonForeground() { michael@0: MOZ_COUNT_DTOR(nsDisplayButtonForeground); michael@0: } michael@0: #endif michael@0: michael@0: virtual void Paint(nsDisplayListBuilder* aBuilder, michael@0: nsRenderingContext* aCtx) MOZ_OVERRIDE; michael@0: NS_DISPLAY_DECL_NAME("ButtonForeground", TYPE_BUTTON_FOREGROUND) michael@0: private: michael@0: nsButtonFrameRenderer* mBFR; michael@0: }; michael@0: michael@0: void michael@0: nsDisplayButtonBorderBackground::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, michael@0: const nsDisplayItemGeometry* aGeometry, michael@0: nsRegion *aInvalidRegion) michael@0: { michael@0: AddInvalidRegionForSyncDecodeBackgroundImages(aBuilder, aGeometry, aInvalidRegion); michael@0: michael@0: nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion); michael@0: } michael@0: michael@0: void nsDisplayButtonBorderBackground::Paint(nsDisplayListBuilder* aBuilder, michael@0: nsRenderingContext* aCtx) michael@0: { michael@0: NS_ASSERTION(mFrame, "No frame?"); michael@0: nsPresContext* pc = mFrame->PresContext(); michael@0: nsRect r = nsRect(ToReferenceFrame(), mFrame->GetSize()); michael@0: michael@0: // draw the border and background inside the focus and outline borders michael@0: mBFR->PaintBorderAndBackground(pc, *aCtx, mVisibleRect, r, michael@0: aBuilder->GetBackgroundPaintFlags()); michael@0: } michael@0: michael@0: void nsDisplayButtonForeground::Paint(nsDisplayListBuilder* aBuilder, michael@0: nsRenderingContext* aCtx) michael@0: { michael@0: nsPresContext *presContext = mFrame->PresContext(); michael@0: const nsStyleDisplay *disp = mFrame->StyleDisplay(); michael@0: if (!mFrame->IsThemed(disp) || michael@0: !presContext->GetTheme()->ThemeDrawsFocusForWidget(disp->mAppearance)) { michael@0: // draw the focus and outline borders michael@0: nsRect r = nsRect(ToReferenceFrame(), mFrame->GetSize()); michael@0: mBFR->PaintOutlineAndFocusBorders(presContext, *aCtx, mVisibleRect, r); michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: nsButtonFrameRenderer::DisplayButton(nsDisplayListBuilder* aBuilder, michael@0: nsDisplayList* aBackground, michael@0: nsDisplayList* aForeground) michael@0: { michael@0: if (mFrame->StyleBorder()->mBoxShadow) { michael@0: aBackground->AppendNewToTop(new (aBuilder) michael@0: nsDisplayButtonBoxShadowOuter(aBuilder, this)); michael@0: } michael@0: michael@0: // Almost all buttons draw some kind of background so there's not much michael@0: // point in checking whether we should create this item. michael@0: aBackground->AppendNewToTop(new (aBuilder) michael@0: nsDisplayButtonBorderBackground(aBuilder, this)); michael@0: michael@0: // Only display focus rings if we actually have them. Since at most one michael@0: // button would normally display a focus ring, most buttons won't have them. michael@0: if ((mOuterFocusStyle && mOuterFocusStyle->StyleBorder()->HasBorder()) || michael@0: (mInnerFocusStyle && mInnerFocusStyle->StyleBorder()->HasBorder())) { michael@0: aForeground->AppendNewToTop(new (aBuilder) michael@0: nsDisplayButtonForeground(aBuilder, this)); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsButtonFrameRenderer::PaintOutlineAndFocusBorders(nsPresContext* aPresContext, michael@0: nsRenderingContext& aRenderingContext, michael@0: const nsRect& aDirtyRect, michael@0: const nsRect& aRect) michael@0: { michael@0: // once we have all that we'll draw the focus if we have it. We will michael@0: // need to draw 2 focuses, the inner and the outer. This is so we michael@0: // can do any kind of look and feel. Some buttons have focus on the michael@0: // outside like mac and motif. While others like windows have it michael@0: // inside (dotted line). Usually only one will be specifed. But I michael@0: // guess you could have both if you wanted to. michael@0: michael@0: nsRect rect; michael@0: michael@0: if (mOuterFocusStyle) { michael@0: // ---------- paint the outer focus border ------------- michael@0: michael@0: GetButtonOuterFocusRect(aRect, rect); michael@0: michael@0: nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, mFrame, michael@0: aDirtyRect, rect, mOuterFocusStyle); michael@0: } michael@0: michael@0: if (mInnerFocusStyle) { michael@0: // ---------- paint the inner focus border ------------- michael@0: michael@0: GetButtonInnerFocusRect(aRect, rect); michael@0: michael@0: nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, mFrame, michael@0: aDirtyRect, rect, mInnerFocusStyle); michael@0: } michael@0: } michael@0: michael@0: michael@0: void michael@0: nsButtonFrameRenderer::PaintBorderAndBackground(nsPresContext* aPresContext, michael@0: nsRenderingContext& aRenderingContext, michael@0: const nsRect& aDirtyRect, michael@0: const nsRect& aRect, michael@0: uint32_t aBGFlags) michael@0: michael@0: { michael@0: // get the button rect this is inside the focus and outline rects michael@0: nsRect buttonRect; michael@0: GetButtonRect(aRect, buttonRect); michael@0: michael@0: nsStyleContext* context = mFrame->StyleContext(); michael@0: michael@0: nsCSSRendering::PaintBackground(aPresContext, aRenderingContext, mFrame, michael@0: aDirtyRect, buttonRect, aBGFlags); michael@0: nsCSSRendering::PaintBoxShadowInner(aPresContext, aRenderingContext, michael@0: mFrame, buttonRect, aDirtyRect); michael@0: nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, mFrame, michael@0: aDirtyRect, buttonRect, context); michael@0: } michael@0: michael@0: michael@0: void michael@0: nsButtonFrameRenderer::GetButtonOuterFocusRect(const nsRect& aRect, nsRect& focusRect) michael@0: { michael@0: focusRect = aRect; michael@0: } michael@0: michael@0: void michael@0: nsButtonFrameRenderer::GetButtonRect(const nsRect& aRect, nsRect& r) michael@0: { michael@0: r = aRect; michael@0: r.Deflate(GetButtonOuterFocusBorderAndPadding()); michael@0: } michael@0: michael@0: michael@0: void michael@0: nsButtonFrameRenderer::GetButtonInnerFocusRect(const nsRect& aRect, nsRect& focusRect) michael@0: { michael@0: GetButtonRect(aRect, focusRect); michael@0: focusRect.Deflate(GetButtonBorderAndPadding()); michael@0: focusRect.Deflate(GetButtonInnerFocusMargin()); michael@0: } michael@0: michael@0: michael@0: nsMargin michael@0: nsButtonFrameRenderer::GetButtonOuterFocusBorderAndPadding() michael@0: { michael@0: nsMargin result(0,0,0,0); michael@0: michael@0: if (mOuterFocusStyle) { michael@0: if (!mOuterFocusStyle->StylePadding()->GetPadding(result)) { michael@0: NS_NOTYETIMPLEMENTED("percentage padding"); michael@0: } michael@0: result += mOuterFocusStyle->StyleBorder()->GetComputedBorder(); michael@0: } michael@0: michael@0: return result; michael@0: } michael@0: michael@0: nsMargin michael@0: nsButtonFrameRenderer::GetButtonBorderAndPadding() michael@0: { michael@0: return mFrame->GetUsedBorderAndPadding(); michael@0: } michael@0: michael@0: /** michael@0: * Gets the size of the buttons border this is the union of the normal and disabled borders. michael@0: */ michael@0: nsMargin michael@0: nsButtonFrameRenderer::GetButtonInnerFocusMargin() michael@0: { michael@0: nsMargin innerFocusMargin(0,0,0,0); michael@0: michael@0: if (mInnerFocusStyle) { michael@0: const nsStyleMargin* margin = mInnerFocusStyle->StyleMargin(); michael@0: if (!margin->GetMargin(innerFocusMargin)) { michael@0: NS_NOTYETIMPLEMENTED("percentage margin"); michael@0: } michael@0: } michael@0: michael@0: return innerFocusMargin; michael@0: } michael@0: michael@0: nsMargin michael@0: nsButtonFrameRenderer::GetButtonInnerFocusBorderAndPadding() michael@0: { michael@0: nsMargin result(0,0,0,0); michael@0: michael@0: if (mInnerFocusStyle) { michael@0: if (!mInnerFocusStyle->StylePadding()->GetPadding(result)) { michael@0: NS_NOTYETIMPLEMENTED("percentage padding"); michael@0: } michael@0: result += mInnerFocusStyle->StyleBorder()->GetComputedBorder(); michael@0: } michael@0: michael@0: return result; michael@0: } michael@0: michael@0: // gets all the focus borders and padding that will be added to the regular border michael@0: nsMargin michael@0: nsButtonFrameRenderer::GetAddedButtonBorderAndPadding() michael@0: { michael@0: return GetButtonOuterFocusBorderAndPadding() + GetButtonInnerFocusMargin() + GetButtonInnerFocusBorderAndPadding(); michael@0: } michael@0: michael@0: /** michael@0: * Call this when styles change michael@0: */ michael@0: void michael@0: nsButtonFrameRenderer::ReResolveStyles(nsPresContext* aPresContext) michael@0: { michael@0: // get all the styles michael@0: nsStyleContext* context = mFrame->StyleContext(); michael@0: nsStyleSet *styleSet = aPresContext->StyleSet(); michael@0: michael@0: // style for the inner such as a dotted line (Windows) michael@0: mInnerFocusStyle = michael@0: styleSet->ProbePseudoElementStyle(mFrame->GetContent()->AsElement(), michael@0: nsCSSPseudoElements::ePseudo_mozFocusInner, michael@0: context); michael@0: michael@0: // style for outer focus like a ridged border (MAC). michael@0: mOuterFocusStyle = michael@0: styleSet->ProbePseudoElementStyle(mFrame->GetContent()->AsElement(), michael@0: nsCSSPseudoElements::ePseudo_mozFocusOuter, michael@0: context); michael@0: } michael@0: michael@0: nsStyleContext* michael@0: nsButtonFrameRenderer::GetStyleContext(int32_t aIndex) const michael@0: { michael@0: switch (aIndex) { michael@0: case NS_BUTTON_RENDERER_FOCUS_INNER_CONTEXT_INDEX: michael@0: return mInnerFocusStyle; michael@0: case NS_BUTTON_RENDERER_FOCUS_OUTER_CONTEXT_INDEX: michael@0: return mOuterFocusStyle; michael@0: default: michael@0: return nullptr; michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsButtonFrameRenderer::SetStyleContext(int32_t aIndex, nsStyleContext* aStyleContext) michael@0: { michael@0: switch (aIndex) { michael@0: case NS_BUTTON_RENDERER_FOCUS_INNER_CONTEXT_INDEX: michael@0: mInnerFocusStyle = aStyleContext; michael@0: break; michael@0: case NS_BUTTON_RENDERER_FOCUS_OUTER_CONTEXT_INDEX: michael@0: mOuterFocusStyle = aStyleContext; michael@0: break; michael@0: } michael@0: }