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