|
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 #include "nsButtonFrameRenderer.h" |
|
6 #include "nsCSSRendering.h" |
|
7 #include "nsPresContext.h" |
|
8 #include "nsGkAtoms.h" |
|
9 #include "nsCSSPseudoElements.h" |
|
10 #include "nsNameSpaceManager.h" |
|
11 #include "nsStyleSet.h" |
|
12 #include "nsDisplayList.h" |
|
13 #include "nsITheme.h" |
|
14 #include "nsFrame.h" |
|
15 #include "mozilla/EventStates.h" |
|
16 #include "mozilla/dom/Element.h" |
|
17 |
|
18 #define ACTIVE "active" |
|
19 #define HOVER "hover" |
|
20 #define FOCUS "focus" |
|
21 |
|
22 nsButtonFrameRenderer::nsButtonFrameRenderer() |
|
23 { |
|
24 MOZ_COUNT_CTOR(nsButtonFrameRenderer); |
|
25 } |
|
26 |
|
27 nsButtonFrameRenderer::~nsButtonFrameRenderer() |
|
28 { |
|
29 MOZ_COUNT_DTOR(nsButtonFrameRenderer); |
|
30 } |
|
31 |
|
32 void |
|
33 nsButtonFrameRenderer::SetFrame(nsFrame* aFrame, nsPresContext* aPresContext) |
|
34 { |
|
35 mFrame = aFrame; |
|
36 ReResolveStyles(aPresContext); |
|
37 } |
|
38 |
|
39 nsIFrame* |
|
40 nsButtonFrameRenderer::GetFrame() |
|
41 { |
|
42 return mFrame; |
|
43 } |
|
44 |
|
45 void |
|
46 nsButtonFrameRenderer::SetDisabled(bool aDisabled, bool notify) |
|
47 { |
|
48 if (aDisabled) |
|
49 mFrame->GetContent()->SetAttr(kNameSpaceID_None, nsGkAtoms::disabled, EmptyString(), |
|
50 notify); |
|
51 else |
|
52 mFrame->GetContent()->UnsetAttr(kNameSpaceID_None, nsGkAtoms::disabled, notify); |
|
53 } |
|
54 |
|
55 bool |
|
56 nsButtonFrameRenderer::isDisabled() |
|
57 { |
|
58 return mFrame->GetContent()->AsElement()-> |
|
59 State().HasState(NS_EVENT_STATE_DISABLED); |
|
60 } |
|
61 |
|
62 class nsDisplayButtonBoxShadowOuter : public nsDisplayItem { |
|
63 public: |
|
64 nsDisplayButtonBoxShadowOuter(nsDisplayListBuilder* aBuilder, |
|
65 nsButtonFrameRenderer* aRenderer) |
|
66 : nsDisplayItem(aBuilder, aRenderer->GetFrame()), mBFR(aRenderer) { |
|
67 MOZ_COUNT_CTOR(nsDisplayButtonBoxShadowOuter); |
|
68 } |
|
69 #ifdef NS_BUILD_REFCNT_LOGGING |
|
70 virtual ~nsDisplayButtonBoxShadowOuter() { |
|
71 MOZ_COUNT_DTOR(nsDisplayButtonBoxShadowOuter); |
|
72 } |
|
73 #endif |
|
74 |
|
75 virtual void Paint(nsDisplayListBuilder* aBuilder, |
|
76 nsRenderingContext* aCtx) MOZ_OVERRIDE; |
|
77 virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, |
|
78 bool* aSnap) MOZ_OVERRIDE; |
|
79 NS_DISPLAY_DECL_NAME("ButtonBoxShadowOuter", TYPE_BUTTON_BOX_SHADOW_OUTER) |
|
80 private: |
|
81 nsButtonFrameRenderer* mBFR; |
|
82 }; |
|
83 |
|
84 nsRect |
|
85 nsDisplayButtonBoxShadowOuter::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) { |
|
86 *aSnap = false; |
|
87 return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame(); |
|
88 } |
|
89 |
|
90 void |
|
91 nsDisplayButtonBoxShadowOuter::Paint(nsDisplayListBuilder* aBuilder, |
|
92 nsRenderingContext* aCtx) { |
|
93 nsRect frameRect = nsRect(ToReferenceFrame(), mFrame->GetSize()); |
|
94 |
|
95 nsRect buttonRect; |
|
96 mBFR->GetButtonRect(frameRect, buttonRect); |
|
97 |
|
98 nsCSSRendering::PaintBoxShadowOuter(mFrame->PresContext(), *aCtx, mFrame, |
|
99 buttonRect, mVisibleRect); |
|
100 } |
|
101 |
|
102 class nsDisplayButtonBorderBackground : public nsDisplayItem { |
|
103 public: |
|
104 nsDisplayButtonBorderBackground(nsDisplayListBuilder* aBuilder, |
|
105 nsButtonFrameRenderer* aRenderer) |
|
106 : nsDisplayItem(aBuilder, aRenderer->GetFrame()), mBFR(aRenderer) { |
|
107 MOZ_COUNT_CTOR(nsDisplayButtonBorderBackground); |
|
108 } |
|
109 #ifdef NS_BUILD_REFCNT_LOGGING |
|
110 virtual ~nsDisplayButtonBorderBackground() { |
|
111 MOZ_COUNT_DTOR(nsDisplayButtonBorderBackground); |
|
112 } |
|
113 #endif |
|
114 |
|
115 virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, |
|
116 HitTestState* aState, |
|
117 nsTArray<nsIFrame*> *aOutFrames) MOZ_OVERRIDE { |
|
118 aOutFrames->AppendElement(mFrame); |
|
119 } |
|
120 virtual void Paint(nsDisplayListBuilder* aBuilder, |
|
121 nsRenderingContext* aCtx) MOZ_OVERRIDE; |
|
122 virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, |
|
123 bool* aSnap) MOZ_OVERRIDE; |
|
124 virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, |
|
125 const nsDisplayItemGeometry* aGeometry, |
|
126 nsRegion *aInvalidRegion) MOZ_OVERRIDE; |
|
127 NS_DISPLAY_DECL_NAME("ButtonBorderBackground", TYPE_BUTTON_BORDER_BACKGROUND) |
|
128 private: |
|
129 nsButtonFrameRenderer* mBFR; |
|
130 }; |
|
131 |
|
132 nsRect |
|
133 nsDisplayButtonBorderBackground::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) { |
|
134 *aSnap = false; |
|
135 return aBuilder->IsForEventDelivery() ? nsRect(ToReferenceFrame(), mFrame->GetSize()) |
|
136 : mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame(); |
|
137 } |
|
138 |
|
139 class nsDisplayButtonForeground : public nsDisplayItem { |
|
140 public: |
|
141 nsDisplayButtonForeground(nsDisplayListBuilder* aBuilder, |
|
142 nsButtonFrameRenderer* aRenderer) |
|
143 : nsDisplayItem(aBuilder, aRenderer->GetFrame()), mBFR(aRenderer) { |
|
144 MOZ_COUNT_CTOR(nsDisplayButtonForeground); |
|
145 } |
|
146 #ifdef NS_BUILD_REFCNT_LOGGING |
|
147 virtual ~nsDisplayButtonForeground() { |
|
148 MOZ_COUNT_DTOR(nsDisplayButtonForeground); |
|
149 } |
|
150 #endif |
|
151 |
|
152 virtual void Paint(nsDisplayListBuilder* aBuilder, |
|
153 nsRenderingContext* aCtx) MOZ_OVERRIDE; |
|
154 NS_DISPLAY_DECL_NAME("ButtonForeground", TYPE_BUTTON_FOREGROUND) |
|
155 private: |
|
156 nsButtonFrameRenderer* mBFR; |
|
157 }; |
|
158 |
|
159 void |
|
160 nsDisplayButtonBorderBackground::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, |
|
161 const nsDisplayItemGeometry* aGeometry, |
|
162 nsRegion *aInvalidRegion) |
|
163 { |
|
164 AddInvalidRegionForSyncDecodeBackgroundImages(aBuilder, aGeometry, aInvalidRegion); |
|
165 |
|
166 nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion); |
|
167 } |
|
168 |
|
169 void nsDisplayButtonBorderBackground::Paint(nsDisplayListBuilder* aBuilder, |
|
170 nsRenderingContext* aCtx) |
|
171 { |
|
172 NS_ASSERTION(mFrame, "No frame?"); |
|
173 nsPresContext* pc = mFrame->PresContext(); |
|
174 nsRect r = nsRect(ToReferenceFrame(), mFrame->GetSize()); |
|
175 |
|
176 // draw the border and background inside the focus and outline borders |
|
177 mBFR->PaintBorderAndBackground(pc, *aCtx, mVisibleRect, r, |
|
178 aBuilder->GetBackgroundPaintFlags()); |
|
179 } |
|
180 |
|
181 void nsDisplayButtonForeground::Paint(nsDisplayListBuilder* aBuilder, |
|
182 nsRenderingContext* aCtx) |
|
183 { |
|
184 nsPresContext *presContext = mFrame->PresContext(); |
|
185 const nsStyleDisplay *disp = mFrame->StyleDisplay(); |
|
186 if (!mFrame->IsThemed(disp) || |
|
187 !presContext->GetTheme()->ThemeDrawsFocusForWidget(disp->mAppearance)) { |
|
188 // draw the focus and outline borders |
|
189 nsRect r = nsRect(ToReferenceFrame(), mFrame->GetSize()); |
|
190 mBFR->PaintOutlineAndFocusBorders(presContext, *aCtx, mVisibleRect, r); |
|
191 } |
|
192 } |
|
193 |
|
194 nsresult |
|
195 nsButtonFrameRenderer::DisplayButton(nsDisplayListBuilder* aBuilder, |
|
196 nsDisplayList* aBackground, |
|
197 nsDisplayList* aForeground) |
|
198 { |
|
199 if (mFrame->StyleBorder()->mBoxShadow) { |
|
200 aBackground->AppendNewToTop(new (aBuilder) |
|
201 nsDisplayButtonBoxShadowOuter(aBuilder, this)); |
|
202 } |
|
203 |
|
204 // Almost all buttons draw some kind of background so there's not much |
|
205 // point in checking whether we should create this item. |
|
206 aBackground->AppendNewToTop(new (aBuilder) |
|
207 nsDisplayButtonBorderBackground(aBuilder, this)); |
|
208 |
|
209 // Only display focus rings if we actually have them. Since at most one |
|
210 // button would normally display a focus ring, most buttons won't have them. |
|
211 if ((mOuterFocusStyle && mOuterFocusStyle->StyleBorder()->HasBorder()) || |
|
212 (mInnerFocusStyle && mInnerFocusStyle->StyleBorder()->HasBorder())) { |
|
213 aForeground->AppendNewToTop(new (aBuilder) |
|
214 nsDisplayButtonForeground(aBuilder, this)); |
|
215 } |
|
216 return NS_OK; |
|
217 } |
|
218 |
|
219 void |
|
220 nsButtonFrameRenderer::PaintOutlineAndFocusBorders(nsPresContext* aPresContext, |
|
221 nsRenderingContext& aRenderingContext, |
|
222 const nsRect& aDirtyRect, |
|
223 const nsRect& aRect) |
|
224 { |
|
225 // once we have all that we'll draw the focus if we have it. We will |
|
226 // need to draw 2 focuses, the inner and the outer. This is so we |
|
227 // can do any kind of look and feel. Some buttons have focus on the |
|
228 // outside like mac and motif. While others like windows have it |
|
229 // inside (dotted line). Usually only one will be specifed. But I |
|
230 // guess you could have both if you wanted to. |
|
231 |
|
232 nsRect rect; |
|
233 |
|
234 if (mOuterFocusStyle) { |
|
235 // ---------- paint the outer focus border ------------- |
|
236 |
|
237 GetButtonOuterFocusRect(aRect, rect); |
|
238 |
|
239 nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, mFrame, |
|
240 aDirtyRect, rect, mOuterFocusStyle); |
|
241 } |
|
242 |
|
243 if (mInnerFocusStyle) { |
|
244 // ---------- paint the inner focus border ------------- |
|
245 |
|
246 GetButtonInnerFocusRect(aRect, rect); |
|
247 |
|
248 nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, mFrame, |
|
249 aDirtyRect, rect, mInnerFocusStyle); |
|
250 } |
|
251 } |
|
252 |
|
253 |
|
254 void |
|
255 nsButtonFrameRenderer::PaintBorderAndBackground(nsPresContext* aPresContext, |
|
256 nsRenderingContext& aRenderingContext, |
|
257 const nsRect& aDirtyRect, |
|
258 const nsRect& aRect, |
|
259 uint32_t aBGFlags) |
|
260 |
|
261 { |
|
262 // get the button rect this is inside the focus and outline rects |
|
263 nsRect buttonRect; |
|
264 GetButtonRect(aRect, buttonRect); |
|
265 |
|
266 nsStyleContext* context = mFrame->StyleContext(); |
|
267 |
|
268 nsCSSRendering::PaintBackground(aPresContext, aRenderingContext, mFrame, |
|
269 aDirtyRect, buttonRect, aBGFlags); |
|
270 nsCSSRendering::PaintBoxShadowInner(aPresContext, aRenderingContext, |
|
271 mFrame, buttonRect, aDirtyRect); |
|
272 nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, mFrame, |
|
273 aDirtyRect, buttonRect, context); |
|
274 } |
|
275 |
|
276 |
|
277 void |
|
278 nsButtonFrameRenderer::GetButtonOuterFocusRect(const nsRect& aRect, nsRect& focusRect) |
|
279 { |
|
280 focusRect = aRect; |
|
281 } |
|
282 |
|
283 void |
|
284 nsButtonFrameRenderer::GetButtonRect(const nsRect& aRect, nsRect& r) |
|
285 { |
|
286 r = aRect; |
|
287 r.Deflate(GetButtonOuterFocusBorderAndPadding()); |
|
288 } |
|
289 |
|
290 |
|
291 void |
|
292 nsButtonFrameRenderer::GetButtonInnerFocusRect(const nsRect& aRect, nsRect& focusRect) |
|
293 { |
|
294 GetButtonRect(aRect, focusRect); |
|
295 focusRect.Deflate(GetButtonBorderAndPadding()); |
|
296 focusRect.Deflate(GetButtonInnerFocusMargin()); |
|
297 } |
|
298 |
|
299 |
|
300 nsMargin |
|
301 nsButtonFrameRenderer::GetButtonOuterFocusBorderAndPadding() |
|
302 { |
|
303 nsMargin result(0,0,0,0); |
|
304 |
|
305 if (mOuterFocusStyle) { |
|
306 if (!mOuterFocusStyle->StylePadding()->GetPadding(result)) { |
|
307 NS_NOTYETIMPLEMENTED("percentage padding"); |
|
308 } |
|
309 result += mOuterFocusStyle->StyleBorder()->GetComputedBorder(); |
|
310 } |
|
311 |
|
312 return result; |
|
313 } |
|
314 |
|
315 nsMargin |
|
316 nsButtonFrameRenderer::GetButtonBorderAndPadding() |
|
317 { |
|
318 return mFrame->GetUsedBorderAndPadding(); |
|
319 } |
|
320 |
|
321 /** |
|
322 * Gets the size of the buttons border this is the union of the normal and disabled borders. |
|
323 */ |
|
324 nsMargin |
|
325 nsButtonFrameRenderer::GetButtonInnerFocusMargin() |
|
326 { |
|
327 nsMargin innerFocusMargin(0,0,0,0); |
|
328 |
|
329 if (mInnerFocusStyle) { |
|
330 const nsStyleMargin* margin = mInnerFocusStyle->StyleMargin(); |
|
331 if (!margin->GetMargin(innerFocusMargin)) { |
|
332 NS_NOTYETIMPLEMENTED("percentage margin"); |
|
333 } |
|
334 } |
|
335 |
|
336 return innerFocusMargin; |
|
337 } |
|
338 |
|
339 nsMargin |
|
340 nsButtonFrameRenderer::GetButtonInnerFocusBorderAndPadding() |
|
341 { |
|
342 nsMargin result(0,0,0,0); |
|
343 |
|
344 if (mInnerFocusStyle) { |
|
345 if (!mInnerFocusStyle->StylePadding()->GetPadding(result)) { |
|
346 NS_NOTYETIMPLEMENTED("percentage padding"); |
|
347 } |
|
348 result += mInnerFocusStyle->StyleBorder()->GetComputedBorder(); |
|
349 } |
|
350 |
|
351 return result; |
|
352 } |
|
353 |
|
354 // gets all the focus borders and padding that will be added to the regular border |
|
355 nsMargin |
|
356 nsButtonFrameRenderer::GetAddedButtonBorderAndPadding() |
|
357 { |
|
358 return GetButtonOuterFocusBorderAndPadding() + GetButtonInnerFocusMargin() + GetButtonInnerFocusBorderAndPadding(); |
|
359 } |
|
360 |
|
361 /** |
|
362 * Call this when styles change |
|
363 */ |
|
364 void |
|
365 nsButtonFrameRenderer::ReResolveStyles(nsPresContext* aPresContext) |
|
366 { |
|
367 // get all the styles |
|
368 nsStyleContext* context = mFrame->StyleContext(); |
|
369 nsStyleSet *styleSet = aPresContext->StyleSet(); |
|
370 |
|
371 // style for the inner such as a dotted line (Windows) |
|
372 mInnerFocusStyle = |
|
373 styleSet->ProbePseudoElementStyle(mFrame->GetContent()->AsElement(), |
|
374 nsCSSPseudoElements::ePseudo_mozFocusInner, |
|
375 context); |
|
376 |
|
377 // style for outer focus like a ridged border (MAC). |
|
378 mOuterFocusStyle = |
|
379 styleSet->ProbePseudoElementStyle(mFrame->GetContent()->AsElement(), |
|
380 nsCSSPseudoElements::ePseudo_mozFocusOuter, |
|
381 context); |
|
382 } |
|
383 |
|
384 nsStyleContext* |
|
385 nsButtonFrameRenderer::GetStyleContext(int32_t aIndex) const |
|
386 { |
|
387 switch (aIndex) { |
|
388 case NS_BUTTON_RENDERER_FOCUS_INNER_CONTEXT_INDEX: |
|
389 return mInnerFocusStyle; |
|
390 case NS_BUTTON_RENDERER_FOCUS_OUTER_CONTEXT_INDEX: |
|
391 return mOuterFocusStyle; |
|
392 default: |
|
393 return nullptr; |
|
394 } |
|
395 } |
|
396 |
|
397 void |
|
398 nsButtonFrameRenderer::SetStyleContext(int32_t aIndex, nsStyleContext* aStyleContext) |
|
399 { |
|
400 switch (aIndex) { |
|
401 case NS_BUTTON_RENDERER_FOCUS_INNER_CONTEXT_INDEX: |
|
402 mInnerFocusStyle = aStyleContext; |
|
403 break; |
|
404 case NS_BUTTON_RENDERER_FOCUS_OUTER_CONTEXT_INDEX: |
|
405 mOuterFocusStyle = aStyleContext; |
|
406 break; |
|
407 } |
|
408 } |