nsGroupBoxFrame.cpp 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. // YY need to pass isMultiple before create called
  6. #include "nsBoxFrame.h"
  7. #include "mozilla/gfx/2D.h"
  8. #include "nsCSSRendering.h"
  9. #include "nsLayoutUtils.h"
  10. #include "nsRenderingContext.h"
  11. #include "nsStyleContext.h"
  12. #include "nsDisplayList.h"
  13. using namespace mozilla;
  14. using namespace mozilla::gfx;
  15. using namespace mozilla::image;
  16. class nsGroupBoxFrame : public nsBoxFrame {
  17. public:
  18. NS_DECL_FRAMEARENA_HELPERS
  19. explicit nsGroupBoxFrame(nsStyleContext* aContext):
  20. nsBoxFrame(aContext) {}
  21. virtual nsresult GetXULBorderAndPadding(nsMargin& aBorderAndPadding) override;
  22. virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
  23. const nsDisplayListSet& aLists) override;
  24. #ifdef DEBUG_FRAME_DUMP
  25. virtual nsresult GetFrameName(nsAString& aResult) const override {
  26. return MakeFrameName(NS_LITERAL_STRING("GroupBoxFrame"), aResult);
  27. }
  28. #endif
  29. virtual bool HonorPrintBackgroundSettings() override { return false; }
  30. DrawResult PaintBorder(nsRenderingContext& aRenderingContext,
  31. nsPoint aPt,
  32. const nsRect& aDirtyRect);
  33. nsRect GetBackgroundRectRelativeToSelf(nscoord* aOutYOffset = nullptr, nsRect* aOutGroupRect = nullptr);
  34. // make sure we our kids get our orient and align instead of us.
  35. // our child box has no content node so it will search for a parent with one.
  36. // that will be us.
  37. virtual void GetInitialOrientation(bool& aHorizontal) override { aHorizontal = false; }
  38. virtual bool GetInitialHAlignment(Halignment& aHalign) override { aHalign = hAlign_Left; return true; }
  39. virtual bool GetInitialVAlignment(Valignment& aValign) override { aValign = vAlign_Top; return true; }
  40. virtual bool GetInitialAutoStretch(bool& aStretch) override { aStretch = true; return true; }
  41. nsIFrame* GetCaptionBox(nsRect& aCaptionRect);
  42. };
  43. /*
  44. class nsGroupBoxInnerFrame : public nsBoxFrame {
  45. public:
  46. nsGroupBoxInnerFrame(nsIPresShell* aShell, nsStyleContext* aContext):
  47. nsBoxFrame(aShell, aContext) {}
  48. #ifdef DEBUG_FRAME_DUMP
  49. NS_IMETHOD GetFrameName(nsString& aResult) const {
  50. return MakeFrameName("GroupBoxFrameInner", aResult);
  51. }
  52. #endif
  53. // we are always flexible
  54. virtual bool GetDefaultFlex(int32_t& aFlex) { aFlex = 1; return true; }
  55. };
  56. */
  57. nsIFrame*
  58. NS_NewGroupBoxFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
  59. {
  60. return new (aPresShell) nsGroupBoxFrame(aContext);
  61. }
  62. NS_IMPL_FRAMEARENA_HELPERS(nsGroupBoxFrame)
  63. class nsDisplayXULGroupBorder : public nsDisplayItem {
  64. public:
  65. nsDisplayXULGroupBorder(nsDisplayListBuilder* aBuilder,
  66. nsGroupBoxFrame* aFrame) :
  67. nsDisplayItem(aBuilder, aFrame) {
  68. MOZ_COUNT_CTOR(nsDisplayXULGroupBorder);
  69. }
  70. #ifdef NS_BUILD_REFCNT_LOGGING
  71. virtual ~nsDisplayXULGroupBorder() {
  72. MOZ_COUNT_DTOR(nsDisplayXULGroupBorder);
  73. }
  74. #endif
  75. nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) override;
  76. void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
  77. const nsDisplayItemGeometry* aGeometry,
  78. nsRegion *aInvalidRegion) override;
  79. virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
  80. HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) override {
  81. aOutFrames->AppendElement(mFrame);
  82. }
  83. virtual void Paint(nsDisplayListBuilder* aBuilder,
  84. nsRenderingContext* aCtx) override;
  85. NS_DISPLAY_DECL_NAME("XULGroupBackground", TYPE_XUL_GROUP_BACKGROUND)
  86. };
  87. nsDisplayItemGeometry*
  88. nsDisplayXULGroupBorder::AllocateGeometry(nsDisplayListBuilder* aBuilder)
  89. {
  90. return new nsDisplayItemGenericImageGeometry(this, aBuilder);
  91. }
  92. void
  93. nsDisplayXULGroupBorder::ComputeInvalidationRegion(
  94. nsDisplayListBuilder* aBuilder,
  95. const nsDisplayItemGeometry* aGeometry,
  96. nsRegion* aInvalidRegion)
  97. {
  98. auto geometry =
  99. static_cast<const nsDisplayItemGenericImageGeometry*>(aGeometry);
  100. if (aBuilder->ShouldSyncDecodeImages() &&
  101. geometry->ShouldInvalidateToSyncDecodeImages()) {
  102. bool snap;
  103. aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
  104. }
  105. nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
  106. }
  107. void
  108. nsDisplayXULGroupBorder::Paint(nsDisplayListBuilder* aBuilder,
  109. nsRenderingContext* aCtx)
  110. {
  111. DrawResult result = static_cast<nsGroupBoxFrame*>(mFrame)
  112. ->PaintBorder(*aCtx, ToReferenceFrame(), mVisibleRect);
  113. nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result);
  114. }
  115. void
  116. nsGroupBoxFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
  117. const nsDisplayListSet& aLists)
  118. {
  119. // Paint our background and border
  120. if (IsVisibleForPainting(aBuilder)) {
  121. nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
  122. aBuilder, this,
  123. GetBackgroundRectRelativeToSelf() + aBuilder->ToReferenceFrame(this),
  124. aLists.BorderBackground());
  125. aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
  126. nsDisplayXULGroupBorder(aBuilder, this));
  127. DisplayOutline(aBuilder, aLists);
  128. }
  129. BuildDisplayListForChildren(aBuilder, aLists);
  130. }
  131. nsRect
  132. nsGroupBoxFrame::GetBackgroundRectRelativeToSelf(nscoord* aOutYOffset, nsRect* aOutGroupRect)
  133. {
  134. const nsMargin& border = StyleBorder()->GetComputedBorder();
  135. nsRect groupRect;
  136. nsIFrame* groupBox = GetCaptionBox(groupRect);
  137. nscoord yoff = 0;
  138. if (groupBox) {
  139. // If the border is smaller than the legend, move the border down
  140. // to be centered on the legend.
  141. nsMargin groupMargin;
  142. groupBox->StyleMargin()->GetMargin(groupMargin);
  143. groupRect.Inflate(groupMargin);
  144. if (border.top < groupRect.height) {
  145. yoff = (groupRect.height - border.top) / 2 + groupRect.y;
  146. }
  147. }
  148. if (aOutYOffset) {
  149. *aOutYOffset = yoff;
  150. }
  151. if (aOutGroupRect) {
  152. *aOutGroupRect = groupRect;
  153. }
  154. return nsRect(0, yoff, mRect.width, mRect.height - yoff);
  155. }
  156. DrawResult
  157. nsGroupBoxFrame::PaintBorder(nsRenderingContext& aRenderingContext,
  158. nsPoint aPt, const nsRect& aDirtyRect) {
  159. DrawTarget* drawTarget = aRenderingContext.GetDrawTarget();
  160. gfxContext* gfx = aRenderingContext.ThebesContext();
  161. Sides skipSides;
  162. const nsStyleBorder* borderStyleData = StyleBorder();
  163. const nsMargin& border = borderStyleData->GetComputedBorder();
  164. nsPresContext* presContext = PresContext();
  165. nsRect groupRect;
  166. nsIFrame* groupBox = GetCaptionBox(groupRect);
  167. nscoord yoff = 0;
  168. nsRect rect = GetBackgroundRectRelativeToSelf(&yoff, &groupRect) + aPt;
  169. groupRect += aPt;
  170. DrawResult result = DrawResult::SUCCESS;
  171. if (groupBox) {
  172. int32_t appUnitsPerDevPixel = PresContext()->AppUnitsPerDevPixel();
  173. // we should probably use PaintBorderEdges to do this but for now just use clipping
  174. // to achieve the same effect.
  175. // draw left side
  176. nsRect clipRect(rect);
  177. clipRect.width = groupRect.x - rect.x;
  178. clipRect.height = border.top;
  179. gfx->Save();
  180. gfx->Clip(NSRectToSnappedRect(clipRect, appUnitsPerDevPixel, *drawTarget));
  181. result &=
  182. nsCSSRendering::PaintBorder(presContext, aRenderingContext, this,
  183. aDirtyRect, rect, mStyleContext,
  184. PaintBorderFlags::SYNC_DECODE_IMAGES, skipSides);
  185. gfx->Restore();
  186. // draw right side
  187. clipRect = rect;
  188. clipRect.x = groupRect.XMost();
  189. clipRect.width = rect.XMost() - groupRect.XMost();
  190. clipRect.height = border.top;
  191. gfx->Save();
  192. gfx->Clip(NSRectToSnappedRect(clipRect, appUnitsPerDevPixel, *drawTarget));
  193. result &=
  194. nsCSSRendering::PaintBorder(presContext, aRenderingContext, this,
  195. aDirtyRect, rect, mStyleContext,
  196. PaintBorderFlags::SYNC_DECODE_IMAGES, skipSides);
  197. gfx->Restore();
  198. // draw bottom
  199. clipRect = rect;
  200. clipRect.y += border.top;
  201. clipRect.height = mRect.height - (yoff + border.top);
  202. gfx->Save();
  203. gfx->Clip(NSRectToSnappedRect(clipRect, appUnitsPerDevPixel, *drawTarget));
  204. result &=
  205. nsCSSRendering::PaintBorder(presContext, aRenderingContext, this,
  206. aDirtyRect, rect, mStyleContext,
  207. PaintBorderFlags::SYNC_DECODE_IMAGES, skipSides);
  208. gfx->Restore();
  209. } else {
  210. result &=
  211. nsCSSRendering::PaintBorder(presContext, aRenderingContext, this,
  212. aDirtyRect, nsRect(aPt, GetSize()),
  213. mStyleContext,
  214. PaintBorderFlags::SYNC_DECODE_IMAGES, skipSides);
  215. }
  216. return result;
  217. }
  218. nsIFrame*
  219. nsGroupBoxFrame::GetCaptionBox(nsRect& aCaptionRect)
  220. {
  221. // first child is our grouped area
  222. nsIFrame* box = nsBox::GetChildXULBox(this);
  223. // no area fail.
  224. if (!box)
  225. return nullptr;
  226. // get the first child in the grouped area, that is the caption
  227. box = nsBox::GetChildXULBox(box);
  228. // nothing in the area? fail
  229. if (!box)
  230. return nullptr;
  231. // now get the caption itself. It is in the caption frame.
  232. nsIFrame* child = nsBox::GetChildXULBox(box);
  233. if (child) {
  234. // convert to our coordinates.
  235. nsRect parentRect(box->GetRect());
  236. aCaptionRect = child->GetRect();
  237. aCaptionRect.x += parentRect.x;
  238. aCaptionRect.y += parentRect.y;
  239. }
  240. return child;
  241. }
  242. nsresult
  243. nsGroupBoxFrame::GetXULBorderAndPadding(nsMargin& aBorderAndPadding)
  244. {
  245. aBorderAndPadding.SizeTo(0,0,0,0);
  246. return NS_OK;
  247. }