gecko-dev/layout/xul/nsGroupBoxFrame.cpp

250 строки
7.8 KiB
C++

/* -*- 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/. */
// YY need to pass isMultiple before create called
#include "nsBoxFrame.h"
#include "mozilla/gfx/2D.h"
#include "nsCSSRendering.h"
#include "nsLayoutUtils.h"
#include "nsRenderingContext.h"
#include "nsStyleContext.h"
#include "nsDisplayList.h"
using namespace mozilla;
using namespace mozilla::gfx;
class nsGroupBoxFrame : public nsBoxFrame {
public:
NS_DECL_FRAMEARENA_HELPERS
explicit nsGroupBoxFrame(nsStyleContext* aContext):
nsBoxFrame(aContext) {}
virtual nsresult GetBorderAndPadding(nsMargin& aBorderAndPadding) override;
virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
const nsRect& aDirtyRect,
const nsDisplayListSet& aLists) override;
#ifdef DEBUG_FRAME_DUMP
virtual nsresult GetFrameName(nsAString& aResult) const override {
return MakeFrameName(NS_LITERAL_STRING("GroupBoxFrame"), aResult);
}
#endif
virtual bool HonorPrintBackgroundSettings() override { return false; }
void PaintBorderBackground(nsRenderingContext& aRenderingContext,
nsPoint aPt, const nsRect& aDirtyRect);
// make sure we our kids get our orient and align instead of us.
// our child box has no content node so it will search for a parent with one.
// that will be us.
virtual void GetInitialOrientation(bool& aHorizontal) override { aHorizontal = false; }
virtual bool GetInitialHAlignment(Halignment& aHalign) override { aHalign = hAlign_Left; return true; }
virtual bool GetInitialVAlignment(Valignment& aValign) override { aValign = vAlign_Top; return true; }
virtual bool GetInitialAutoStretch(bool& aStretch) override { aStretch = true; return true; }
nsIFrame* GetCaptionBox(nsPresContext* aPresContext, nsRect& aCaptionRect);
};
/*
class nsGroupBoxInnerFrame : public nsBoxFrame {
public:
nsGroupBoxInnerFrame(nsIPresShell* aShell, nsStyleContext* aContext):
nsBoxFrame(aShell, aContext) {}
#ifdef DEBUG_FRAME_DUMP
NS_IMETHOD GetFrameName(nsString& aResult) const {
return MakeFrameName("GroupBoxFrameInner", aResult);
}
#endif
// we are always flexible
virtual bool GetDefaultFlex(int32_t& aFlex) { aFlex = 1; return true; }
};
*/
nsIFrame*
NS_NewGroupBoxFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
{
return new (aPresShell) nsGroupBoxFrame(aContext);
}
NS_IMPL_FRAMEARENA_HELPERS(nsGroupBoxFrame)
class nsDisplayXULGroupBackground : public nsDisplayItem {
public:
nsDisplayXULGroupBackground(nsDisplayListBuilder* aBuilder,
nsGroupBoxFrame* aFrame) :
nsDisplayItem(aBuilder, aFrame) {
MOZ_COUNT_CTOR(nsDisplayXULGroupBackground);
}
#ifdef NS_BUILD_REFCNT_LOGGING
virtual ~nsDisplayXULGroupBackground() {
MOZ_COUNT_DTOR(nsDisplayXULGroupBackground);
}
#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;
NS_DISPLAY_DECL_NAME("XULGroupBackground", TYPE_XUL_GROUP_BACKGROUND)
};
void
nsDisplayXULGroupBackground::Paint(nsDisplayListBuilder* aBuilder,
nsRenderingContext* aCtx)
{
static_cast<nsGroupBoxFrame*>(mFrame)->
PaintBorderBackground(*aCtx, ToReferenceFrame(), mVisibleRect);
}
void
nsGroupBoxFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
const nsRect& aDirtyRect,
const nsDisplayListSet& aLists)
{
// Paint our background and border
if (IsVisibleForPainting(aBuilder)) {
aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
nsDisplayXULGroupBackground(aBuilder, this));
DisplayOutline(aBuilder, aLists);
}
BuildDisplayListForChildren(aBuilder, aDirtyRect, aLists);
}
void
nsGroupBoxFrame::PaintBorderBackground(nsRenderingContext& aRenderingContext,
nsPoint aPt, const nsRect& aDirtyRect) {
DrawTarget* drawTarget = aRenderingContext.GetDrawTarget();
gfxContext* gfx = aRenderingContext.ThebesContext();
Sides skipSides;
const nsStyleBorder* borderStyleData = StyleBorder();
const nsMargin& border = borderStyleData->GetComputedBorder();
nscoord yoff = 0;
nsPresContext* presContext = PresContext();
nsRect groupRect;
nsIFrame* groupBox = GetCaptionBox(presContext, groupRect);
if (groupBox) {
// if the border is smaller than the legend. Move the border down
// to be centered on the legend.
nsMargin groupMargin;
groupBox->StyleMargin()->GetMargin(groupMargin);
groupRect.Inflate(groupMargin);
if (border.top < groupRect.height)
yoff = (groupRect.height - border.top)/2 + groupRect.y;
}
nsRect rect(aPt.x, aPt.y + yoff, mRect.width, mRect.height - yoff);
groupRect += aPt;
nsCSSRendering::PaintBackground(presContext, aRenderingContext, this,
aDirtyRect, rect,
nsCSSRendering::PAINTBG_SYNC_DECODE_IMAGES);
if (groupBox) {
int32_t appUnitsPerDevPixel = PresContext()->AppUnitsPerDevPixel();
// we should probably use PaintBorderEdges to do this but for now just use clipping
// to achieve the same effect.
// draw left side
nsRect clipRect(rect);
clipRect.width = groupRect.x - rect.x;
clipRect.height = border.top;
gfx->Save();
gfx->Clip(NSRectToSnappedRect(clipRect, appUnitsPerDevPixel, *drawTarget));
nsCSSRendering::PaintBorder(presContext, aRenderingContext, this,
aDirtyRect, rect, mStyleContext, skipSides);
gfx->Restore();
// draw right side
clipRect = rect;
clipRect.x = groupRect.XMost();
clipRect.width = rect.XMost() - groupRect.XMost();
clipRect.height = border.top;
gfx->Save();
gfx->Clip(NSRectToSnappedRect(clipRect, appUnitsPerDevPixel, *drawTarget));
nsCSSRendering::PaintBorder(presContext, aRenderingContext, this,
aDirtyRect, rect, mStyleContext, skipSides);
gfx->Restore();
// draw bottom
clipRect = rect;
clipRect.y += border.top;
clipRect.height = mRect.height - (yoff + border.top);
gfx->Save();
gfx->Clip(NSRectToSnappedRect(clipRect, appUnitsPerDevPixel, *drawTarget));
nsCSSRendering::PaintBorder(presContext, aRenderingContext, this,
aDirtyRect, rect, mStyleContext, skipSides);
gfx->Restore();
} else {
nsCSSRendering::PaintBorder(presContext, aRenderingContext, this,
aDirtyRect, nsRect(aPt, GetSize()),
mStyleContext, skipSides);
}
}
nsIFrame*
nsGroupBoxFrame::GetCaptionBox(nsPresContext* aPresContext, nsRect& aCaptionRect)
{
// first child is our grouped area
nsIFrame* box = nsBox::GetChildBox(this);
// no area fail.
if (!box)
return nullptr;
// get the first child in the grouped area, that is the caption
box = nsBox::GetChildBox(box);
// nothing in the area? fail
if (!box)
return nullptr;
// now get the caption itself. It is in the caption frame.
nsIFrame* child = nsBox::GetChildBox(box);
if (child) {
// convert to our coordinates.
nsRect parentRect(box->GetRect());
aCaptionRect = child->GetRect();
aCaptionRect.x += parentRect.x;
aCaptionRect.y += parentRect.y;
}
return child;
}
nsresult
nsGroupBoxFrame::GetBorderAndPadding(nsMargin& aBorderAndPadding)
{
aBorderAndPadding.SizeTo(0,0,0,0);
return NS_OK;
}