|
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 |
|
6 #include "nsNativeThemeGTK.h" |
|
7 #include "nsThemeConstants.h" |
|
8 #include "gtkdrawing.h" |
|
9 |
|
10 #include "nsIObserverService.h" |
|
11 #include "nsIServiceManager.h" |
|
12 #include "nsIFrame.h" |
|
13 #include "nsIPresShell.h" |
|
14 #include "nsIContent.h" |
|
15 #include "nsViewManager.h" |
|
16 #include "nsNameSpaceManager.h" |
|
17 #include "nsGfxCIID.h" |
|
18 #include "nsTransform2D.h" |
|
19 #include "nsMenuFrame.h" |
|
20 #include "prlink.h" |
|
21 #include "nsIDOMHTMLInputElement.h" |
|
22 #include "nsRenderingContext.h" |
|
23 #include "nsGkAtoms.h" |
|
24 |
|
25 #include "mozilla/EventStates.h" |
|
26 #include "mozilla/Services.h" |
|
27 |
|
28 #include <gdk/gdkprivate.h> |
|
29 #include <gtk/gtk.h> |
|
30 |
|
31 #include "gfxContext.h" |
|
32 #include "gfxPlatformGtk.h" |
|
33 #include "gfxGdkNativeRenderer.h" |
|
34 #include <algorithm> |
|
35 |
|
36 using namespace mozilla; |
|
37 |
|
38 NS_IMPL_ISUPPORTS_INHERITED(nsNativeThemeGTK, nsNativeTheme, nsITheme, |
|
39 nsIObserver) |
|
40 |
|
41 static int gLastGdkError; |
|
42 |
|
43 nsNativeThemeGTK::nsNativeThemeGTK() |
|
44 { |
|
45 if (moz_gtk_init() != MOZ_GTK_SUCCESS) { |
|
46 memset(mDisabledWidgetTypes, 0xff, sizeof(mDisabledWidgetTypes)); |
|
47 return; |
|
48 } |
|
49 |
|
50 // We have to call moz_gtk_shutdown before the event loop stops running. |
|
51 nsCOMPtr<nsIObserverService> obsServ = |
|
52 mozilla::services::GetObserverService(); |
|
53 obsServ->AddObserver(this, "xpcom-shutdown", false); |
|
54 |
|
55 memset(mDisabledWidgetTypes, 0, sizeof(mDisabledWidgetTypes)); |
|
56 memset(mSafeWidgetStates, 0, sizeof(mSafeWidgetStates)); |
|
57 } |
|
58 |
|
59 nsNativeThemeGTK::~nsNativeThemeGTK() { |
|
60 } |
|
61 |
|
62 NS_IMETHODIMP |
|
63 nsNativeThemeGTK::Observe(nsISupports *aSubject, const char *aTopic, |
|
64 const char16_t *aData) |
|
65 { |
|
66 if (!nsCRT::strcmp(aTopic, "xpcom-shutdown")) { |
|
67 moz_gtk_shutdown(); |
|
68 } else { |
|
69 NS_NOTREACHED("unexpected topic"); |
|
70 return NS_ERROR_UNEXPECTED; |
|
71 } |
|
72 |
|
73 return NS_OK; |
|
74 } |
|
75 |
|
76 void |
|
77 nsNativeThemeGTK::RefreshWidgetWindow(nsIFrame* aFrame) |
|
78 { |
|
79 nsIPresShell *shell = GetPresShell(aFrame); |
|
80 if (!shell) |
|
81 return; |
|
82 |
|
83 nsViewManager* vm = shell->GetViewManager(); |
|
84 if (!vm) |
|
85 return; |
|
86 |
|
87 vm->InvalidateAllViews(); |
|
88 } |
|
89 |
|
90 static bool IsFrameContentNodeInNamespace(nsIFrame *aFrame, uint32_t aNamespace) |
|
91 { |
|
92 nsIContent *content = aFrame ? aFrame->GetContent() : nullptr; |
|
93 if (!content) |
|
94 return false; |
|
95 return content->IsInNamespace(aNamespace); |
|
96 } |
|
97 |
|
98 static bool IsWidgetTypeDisabled(uint8_t* aDisabledVector, uint8_t aWidgetType) { |
|
99 return (aDisabledVector[aWidgetType >> 3] & (1 << (aWidgetType & 7))) != 0; |
|
100 } |
|
101 |
|
102 static void SetWidgetTypeDisabled(uint8_t* aDisabledVector, uint8_t aWidgetType) { |
|
103 aDisabledVector[aWidgetType >> 3] |= (1 << (aWidgetType & 7)); |
|
104 } |
|
105 |
|
106 static inline uint16_t |
|
107 GetWidgetStateKey(uint8_t aWidgetType, GtkWidgetState *aWidgetState) |
|
108 { |
|
109 return (aWidgetState->active | |
|
110 aWidgetState->focused << 1 | |
|
111 aWidgetState->inHover << 2 | |
|
112 aWidgetState->disabled << 3 | |
|
113 aWidgetState->isDefault << 4 | |
|
114 aWidgetType << 5); |
|
115 } |
|
116 |
|
117 static bool IsWidgetStateSafe(uint8_t* aSafeVector, |
|
118 uint8_t aWidgetType, |
|
119 GtkWidgetState *aWidgetState) |
|
120 { |
|
121 uint8_t key = GetWidgetStateKey(aWidgetType, aWidgetState); |
|
122 return (aSafeVector[key >> 3] & (1 << (key & 7))) != 0; |
|
123 } |
|
124 |
|
125 static void SetWidgetStateSafe(uint8_t *aSafeVector, |
|
126 uint8_t aWidgetType, |
|
127 GtkWidgetState *aWidgetState) |
|
128 { |
|
129 uint8_t key = GetWidgetStateKey(aWidgetType, aWidgetState); |
|
130 aSafeVector[key >> 3] |= (1 << (key & 7)); |
|
131 } |
|
132 |
|
133 static GtkTextDirection GetTextDirection(nsIFrame* aFrame) |
|
134 { |
|
135 if (!aFrame) |
|
136 return GTK_TEXT_DIR_NONE; |
|
137 |
|
138 switch (aFrame->StyleVisibility()->mDirection) { |
|
139 case NS_STYLE_DIRECTION_RTL: |
|
140 return GTK_TEXT_DIR_RTL; |
|
141 case NS_STYLE_DIRECTION_LTR: |
|
142 return GTK_TEXT_DIR_LTR; |
|
143 } |
|
144 |
|
145 return GTK_TEXT_DIR_NONE; |
|
146 } |
|
147 |
|
148 // Returns positive for negative margins (otherwise 0). |
|
149 gint |
|
150 nsNativeThemeGTK::GetTabMarginPixels(nsIFrame* aFrame) |
|
151 { |
|
152 nscoord margin = |
|
153 IsBottomTab(aFrame) ? aFrame->GetUsedMargin().top |
|
154 : aFrame->GetUsedMargin().bottom; |
|
155 |
|
156 return std::min<gint>(MOZ_GTK_TAB_MARGIN_MASK, |
|
157 std::max(0, |
|
158 aFrame->PresContext()->AppUnitsToDevPixels(-margin))); |
|
159 } |
|
160 |
|
161 bool |
|
162 nsNativeThemeGTK::GetGtkWidgetAndState(uint8_t aWidgetType, nsIFrame* aFrame, |
|
163 GtkThemeWidgetType& aGtkWidgetType, |
|
164 GtkWidgetState* aState, |
|
165 gint* aWidgetFlags) |
|
166 { |
|
167 if (aState) { |
|
168 if (!aFrame) { |
|
169 // reset the entire struct to zero |
|
170 memset(aState, 0, sizeof(GtkWidgetState)); |
|
171 } else { |
|
172 |
|
173 // For XUL checkboxes and radio buttons, the state of the parent |
|
174 // determines our state. |
|
175 nsIFrame *stateFrame = aFrame; |
|
176 if (aFrame && ((aWidgetFlags && (aWidgetType == NS_THEME_CHECKBOX || |
|
177 aWidgetType == NS_THEME_RADIO)) || |
|
178 aWidgetType == NS_THEME_CHECKBOX_LABEL || |
|
179 aWidgetType == NS_THEME_RADIO_LABEL)) { |
|
180 |
|
181 nsIAtom* atom = nullptr; |
|
182 if (IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XUL)) { |
|
183 if (aWidgetType == NS_THEME_CHECKBOX_LABEL || |
|
184 aWidgetType == NS_THEME_RADIO_LABEL) { |
|
185 // Adjust stateFrame so GetContentState finds the correct state. |
|
186 stateFrame = aFrame = aFrame->GetParent()->GetParent(); |
|
187 } else { |
|
188 // GetContentState knows to look one frame up for radio/checkbox |
|
189 // widgets, so don't adjust stateFrame here. |
|
190 aFrame = aFrame->GetParent(); |
|
191 } |
|
192 if (aWidgetFlags) { |
|
193 if (!atom) { |
|
194 atom = (aWidgetType == NS_THEME_CHECKBOX || |
|
195 aWidgetType == NS_THEME_CHECKBOX_LABEL) ? nsGkAtoms::checked |
|
196 : nsGkAtoms::selected; |
|
197 } |
|
198 *aWidgetFlags = CheckBooleanAttr(aFrame, atom); |
|
199 } |
|
200 } else { |
|
201 if (aWidgetFlags) { |
|
202 nsCOMPtr<nsIDOMHTMLInputElement> inputElt(do_QueryInterface(aFrame->GetContent())); |
|
203 *aWidgetFlags = 0; |
|
204 if (inputElt) { |
|
205 bool isHTMLChecked; |
|
206 inputElt->GetChecked(&isHTMLChecked); |
|
207 if (isHTMLChecked) |
|
208 *aWidgetFlags |= MOZ_GTK_WIDGET_CHECKED; |
|
209 } |
|
210 |
|
211 if (GetIndeterminate(aFrame)) |
|
212 *aWidgetFlags |= MOZ_GTK_WIDGET_INCONSISTENT; |
|
213 } |
|
214 } |
|
215 } else if (aWidgetType == NS_THEME_TOOLBAR_BUTTON_DROPDOWN || |
|
216 aWidgetType == NS_THEME_TREEVIEW_HEADER_SORTARROW || |
|
217 aWidgetType == NS_THEME_BUTTON_ARROW_PREVIOUS || |
|
218 aWidgetType == NS_THEME_BUTTON_ARROW_NEXT || |
|
219 aWidgetType == NS_THEME_BUTTON_ARROW_UP || |
|
220 aWidgetType == NS_THEME_BUTTON_ARROW_DOWN) { |
|
221 // The state of an arrow comes from its parent. |
|
222 stateFrame = aFrame = aFrame->GetParent(); |
|
223 } |
|
224 |
|
225 EventStates eventState = GetContentState(stateFrame, aWidgetType); |
|
226 |
|
227 aState->disabled = IsDisabled(aFrame, eventState) || IsReadOnly(aFrame); |
|
228 aState->active = eventState.HasState(NS_EVENT_STATE_ACTIVE); |
|
229 aState->focused = eventState.HasState(NS_EVENT_STATE_FOCUS); |
|
230 aState->inHover = eventState.HasState(NS_EVENT_STATE_HOVER); |
|
231 aState->isDefault = IsDefaultButton(aFrame); |
|
232 aState->canDefault = FALSE; // XXX fix me |
|
233 aState->depressed = FALSE; |
|
234 |
|
235 if (IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XUL)) { |
|
236 // For these widget types, some element (either a child or parent) |
|
237 // actually has element focus, so we check the focused attribute |
|
238 // to see whether to draw in the focused state. |
|
239 if (aWidgetType == NS_THEME_NUMBER_INPUT || |
|
240 aWidgetType == NS_THEME_TEXTFIELD || |
|
241 aWidgetType == NS_THEME_TEXTFIELD_MULTILINE || |
|
242 aWidgetType == NS_THEME_DROPDOWN_TEXTFIELD || |
|
243 aWidgetType == NS_THEME_SPINNER_TEXTFIELD || |
|
244 aWidgetType == NS_THEME_RADIO_CONTAINER || |
|
245 aWidgetType == NS_THEME_RADIO_LABEL) { |
|
246 aState->focused = IsFocused(aFrame); |
|
247 } else if (aWidgetType == NS_THEME_RADIO || |
|
248 aWidgetType == NS_THEME_CHECKBOX) { |
|
249 // In XUL, checkboxes and radios shouldn't have focus rings, their labels do |
|
250 aState->focused = FALSE; |
|
251 } |
|
252 |
|
253 if (aWidgetType == NS_THEME_SCROLLBAR_THUMB_VERTICAL || |
|
254 aWidgetType == NS_THEME_SCROLLBAR_THUMB_HORIZONTAL) { |
|
255 // for scrollbars we need to go up two to go from the thumb to |
|
256 // the slider to the actual scrollbar object |
|
257 nsIFrame *tmpFrame = aFrame->GetParent()->GetParent(); |
|
258 |
|
259 aState->curpos = CheckIntAttr(tmpFrame, nsGkAtoms::curpos, 0); |
|
260 aState->maxpos = CheckIntAttr(tmpFrame, nsGkAtoms::maxpos, 100); |
|
261 } |
|
262 |
|
263 if (aWidgetType == NS_THEME_SCROLLBAR_BUTTON_UP || |
|
264 aWidgetType == NS_THEME_SCROLLBAR_BUTTON_DOWN || |
|
265 aWidgetType == NS_THEME_SCROLLBAR_BUTTON_LEFT || |
|
266 aWidgetType == NS_THEME_SCROLLBAR_BUTTON_RIGHT) { |
|
267 // set the state to disabled when the scrollbar is scrolled to |
|
268 // the beginning or the end, depending on the button type. |
|
269 int32_t curpos = CheckIntAttr(aFrame, nsGkAtoms::curpos, 0); |
|
270 int32_t maxpos = CheckIntAttr(aFrame, nsGkAtoms::maxpos, 100); |
|
271 if ((curpos == 0 && (aWidgetType == NS_THEME_SCROLLBAR_BUTTON_UP || |
|
272 aWidgetType == NS_THEME_SCROLLBAR_BUTTON_LEFT)) || |
|
273 (curpos == maxpos && |
|
274 (aWidgetType == NS_THEME_SCROLLBAR_BUTTON_DOWN || |
|
275 aWidgetType == NS_THEME_SCROLLBAR_BUTTON_RIGHT))) |
|
276 aState->disabled = true; |
|
277 |
|
278 // In order to simulate native GTK scrollbar click behavior, |
|
279 // we set the active attribute on the element to true if it's |
|
280 // pressed with any mouse button. |
|
281 // This allows us to show that it's active without setting :active |
|
282 else if (CheckBooleanAttr(aFrame, nsGkAtoms::active)) |
|
283 aState->active = true; |
|
284 |
|
285 if (aWidgetFlags) { |
|
286 *aWidgetFlags = GetScrollbarButtonType(aFrame); |
|
287 if (aWidgetType - NS_THEME_SCROLLBAR_BUTTON_UP < 2) |
|
288 *aWidgetFlags |= MOZ_GTK_STEPPER_VERTICAL; |
|
289 } |
|
290 } |
|
291 |
|
292 // menu item state is determined by the attribute "_moz-menuactive", |
|
293 // and not by the mouse hovering (accessibility). as a special case, |
|
294 // menus which are children of a menu bar are only marked as prelight |
|
295 // if they are open, not on normal hover. |
|
296 |
|
297 if (aWidgetType == NS_THEME_MENUITEM || |
|
298 aWidgetType == NS_THEME_CHECKMENUITEM || |
|
299 aWidgetType == NS_THEME_RADIOMENUITEM || |
|
300 aWidgetType == NS_THEME_MENUSEPARATOR || |
|
301 aWidgetType == NS_THEME_MENUARROW) { |
|
302 bool isTopLevel = false; |
|
303 nsMenuFrame *menuFrame = do_QueryFrame(aFrame); |
|
304 if (menuFrame) { |
|
305 isTopLevel = menuFrame->IsOnMenuBar(); |
|
306 } |
|
307 |
|
308 if (isTopLevel) { |
|
309 aState->inHover = menuFrame->IsOpen(); |
|
310 *aWidgetFlags |= MOZ_TOPLEVEL_MENU_ITEM; |
|
311 } else { |
|
312 aState->inHover = CheckBooleanAttr(aFrame, nsGkAtoms::menuactive); |
|
313 *aWidgetFlags &= ~MOZ_TOPLEVEL_MENU_ITEM; |
|
314 } |
|
315 |
|
316 aState->active = FALSE; |
|
317 |
|
318 if (aWidgetType == NS_THEME_CHECKMENUITEM || |
|
319 aWidgetType == NS_THEME_RADIOMENUITEM) { |
|
320 *aWidgetFlags = 0; |
|
321 if (aFrame && aFrame->GetContent()) { |
|
322 *aWidgetFlags = aFrame->GetContent()-> |
|
323 AttrValueIs(kNameSpaceID_None, nsGkAtoms::checked, |
|
324 nsGkAtoms::_true, eIgnoreCase); |
|
325 } |
|
326 } |
|
327 } |
|
328 |
|
329 // A button with drop down menu open or an activated toggle button |
|
330 // should always appear depressed. |
|
331 if (aWidgetType == NS_THEME_BUTTON || |
|
332 aWidgetType == NS_THEME_TOOLBAR_BUTTON || |
|
333 aWidgetType == NS_THEME_TOOLBAR_DUAL_BUTTON || |
|
334 aWidgetType == NS_THEME_TOOLBAR_BUTTON_DROPDOWN || |
|
335 aWidgetType == NS_THEME_DROPDOWN || |
|
336 aWidgetType == NS_THEME_DROPDOWN_BUTTON) { |
|
337 bool menuOpen = IsOpenButton(aFrame); |
|
338 aState->depressed = IsCheckedButton(aFrame) || menuOpen; |
|
339 // we must not highlight buttons with open drop down menus on hover. |
|
340 aState->inHover = aState->inHover && !menuOpen; |
|
341 } |
|
342 |
|
343 // When the input field of the drop down button has focus, some themes |
|
344 // should draw focus for the drop down button as well. |
|
345 if (aWidgetType == NS_THEME_DROPDOWN_BUTTON && aWidgetFlags) { |
|
346 *aWidgetFlags = CheckBooleanAttr(aFrame, nsGkAtoms::parentfocused); |
|
347 } |
|
348 } |
|
349 } |
|
350 } |
|
351 |
|
352 switch (aWidgetType) { |
|
353 case NS_THEME_BUTTON: |
|
354 case NS_THEME_TOOLBAR_BUTTON: |
|
355 case NS_THEME_TOOLBAR_DUAL_BUTTON: |
|
356 if (aWidgetFlags) |
|
357 *aWidgetFlags = (aWidgetType == NS_THEME_BUTTON) ? GTK_RELIEF_NORMAL : GTK_RELIEF_NONE; |
|
358 aGtkWidgetType = MOZ_GTK_BUTTON; |
|
359 break; |
|
360 case NS_THEME_CHECKBOX: |
|
361 case NS_THEME_RADIO: |
|
362 aGtkWidgetType = (aWidgetType == NS_THEME_RADIO) ? MOZ_GTK_RADIOBUTTON : MOZ_GTK_CHECKBUTTON; |
|
363 break; |
|
364 case NS_THEME_SCROLLBAR_BUTTON_UP: |
|
365 case NS_THEME_SCROLLBAR_BUTTON_DOWN: |
|
366 case NS_THEME_SCROLLBAR_BUTTON_LEFT: |
|
367 case NS_THEME_SCROLLBAR_BUTTON_RIGHT: |
|
368 aGtkWidgetType = MOZ_GTK_SCROLLBAR_BUTTON; |
|
369 break; |
|
370 case NS_THEME_SCROLLBAR_TRACK_VERTICAL: |
|
371 aGtkWidgetType = MOZ_GTK_SCROLLBAR_TRACK_VERTICAL; |
|
372 break; |
|
373 case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL: |
|
374 aGtkWidgetType = MOZ_GTK_SCROLLBAR_TRACK_HORIZONTAL; |
|
375 break; |
|
376 case NS_THEME_SCROLLBAR_THUMB_VERTICAL: |
|
377 aGtkWidgetType = MOZ_GTK_SCROLLBAR_THUMB_VERTICAL; |
|
378 break; |
|
379 case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL: |
|
380 aGtkWidgetType = MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL; |
|
381 break; |
|
382 case NS_THEME_SPINNER: |
|
383 aGtkWidgetType = MOZ_GTK_SPINBUTTON; |
|
384 break; |
|
385 case NS_THEME_SPINNER_UP_BUTTON: |
|
386 aGtkWidgetType = MOZ_GTK_SPINBUTTON_UP; |
|
387 break; |
|
388 case NS_THEME_SPINNER_DOWN_BUTTON: |
|
389 aGtkWidgetType = MOZ_GTK_SPINBUTTON_DOWN; |
|
390 break; |
|
391 case NS_THEME_SPINNER_TEXTFIELD: |
|
392 aGtkWidgetType = MOZ_GTK_SPINBUTTON_ENTRY; |
|
393 break; |
|
394 case NS_THEME_RANGE: |
|
395 { |
|
396 if (IsRangeHorizontal(aFrame)) { |
|
397 if (aWidgetFlags) |
|
398 *aWidgetFlags = GTK_ORIENTATION_HORIZONTAL; |
|
399 aGtkWidgetType = MOZ_GTK_SCALE_HORIZONTAL; |
|
400 } else { |
|
401 if (aWidgetFlags) |
|
402 *aWidgetFlags = GTK_ORIENTATION_VERTICAL; |
|
403 aGtkWidgetType = MOZ_GTK_SCALE_VERTICAL; |
|
404 } |
|
405 break; |
|
406 } |
|
407 case NS_THEME_RANGE_THUMB: |
|
408 { |
|
409 if (IsRangeHorizontal(aFrame)) { |
|
410 if (aWidgetFlags) |
|
411 *aWidgetFlags = GTK_ORIENTATION_HORIZONTAL; |
|
412 aGtkWidgetType = MOZ_GTK_SCALE_THUMB_HORIZONTAL; |
|
413 } else { |
|
414 if (aWidgetFlags) |
|
415 *aWidgetFlags = GTK_ORIENTATION_VERTICAL; |
|
416 aGtkWidgetType = MOZ_GTK_SCALE_THUMB_VERTICAL; |
|
417 } |
|
418 break; |
|
419 } |
|
420 case NS_THEME_SCALE_HORIZONTAL: |
|
421 if (aWidgetFlags) |
|
422 *aWidgetFlags = GTK_ORIENTATION_HORIZONTAL; |
|
423 aGtkWidgetType = MOZ_GTK_SCALE_HORIZONTAL; |
|
424 break; |
|
425 case NS_THEME_SCALE_THUMB_HORIZONTAL: |
|
426 if (aWidgetFlags) |
|
427 *aWidgetFlags = GTK_ORIENTATION_HORIZONTAL; |
|
428 aGtkWidgetType = MOZ_GTK_SCALE_THUMB_HORIZONTAL; |
|
429 break; |
|
430 case NS_THEME_SCALE_VERTICAL: |
|
431 if (aWidgetFlags) |
|
432 *aWidgetFlags = GTK_ORIENTATION_VERTICAL; |
|
433 aGtkWidgetType = MOZ_GTK_SCALE_VERTICAL; |
|
434 break; |
|
435 case NS_THEME_TOOLBAR_SEPARATOR: |
|
436 aGtkWidgetType = MOZ_GTK_TOOLBAR_SEPARATOR; |
|
437 break; |
|
438 case NS_THEME_SCALE_THUMB_VERTICAL: |
|
439 if (aWidgetFlags) |
|
440 *aWidgetFlags = GTK_ORIENTATION_VERTICAL; |
|
441 aGtkWidgetType = MOZ_GTK_SCALE_THUMB_VERTICAL; |
|
442 break; |
|
443 case NS_THEME_TOOLBAR_GRIPPER: |
|
444 aGtkWidgetType = MOZ_GTK_GRIPPER; |
|
445 break; |
|
446 case NS_THEME_RESIZER: |
|
447 aGtkWidgetType = MOZ_GTK_RESIZER; |
|
448 break; |
|
449 case NS_THEME_NUMBER_INPUT: |
|
450 case NS_THEME_TEXTFIELD: |
|
451 case NS_THEME_TEXTFIELD_MULTILINE: |
|
452 aGtkWidgetType = MOZ_GTK_ENTRY; |
|
453 break; |
|
454 case NS_THEME_LISTBOX: |
|
455 case NS_THEME_TREEVIEW: |
|
456 aGtkWidgetType = MOZ_GTK_TREEVIEW; |
|
457 break; |
|
458 case NS_THEME_TREEVIEW_HEADER_CELL: |
|
459 if (aWidgetFlags) { |
|
460 // In this case, the flag denotes whether the header is the sorted one or not |
|
461 if (GetTreeSortDirection(aFrame) == eTreeSortDirection_Natural) |
|
462 *aWidgetFlags = false; |
|
463 else |
|
464 *aWidgetFlags = true; |
|
465 } |
|
466 aGtkWidgetType = MOZ_GTK_TREE_HEADER_CELL; |
|
467 break; |
|
468 case NS_THEME_TREEVIEW_HEADER_SORTARROW: |
|
469 if (aWidgetFlags) { |
|
470 switch (GetTreeSortDirection(aFrame)) { |
|
471 case eTreeSortDirection_Ascending: |
|
472 *aWidgetFlags = GTK_ARROW_DOWN; |
|
473 break; |
|
474 case eTreeSortDirection_Descending: |
|
475 *aWidgetFlags = GTK_ARROW_UP; |
|
476 break; |
|
477 case eTreeSortDirection_Natural: |
|
478 default: |
|
479 /* This prevents the treecolums from getting smaller |
|
480 * and wider when switching sort direction off and on |
|
481 * */ |
|
482 *aWidgetFlags = GTK_ARROW_NONE; |
|
483 break; |
|
484 } |
|
485 } |
|
486 aGtkWidgetType = MOZ_GTK_TREE_HEADER_SORTARROW; |
|
487 break; |
|
488 case NS_THEME_TREEVIEW_TWISTY: |
|
489 aGtkWidgetType = MOZ_GTK_TREEVIEW_EXPANDER; |
|
490 if (aWidgetFlags) |
|
491 *aWidgetFlags = GTK_EXPANDER_COLLAPSED; |
|
492 break; |
|
493 case NS_THEME_TREEVIEW_TWISTY_OPEN: |
|
494 aGtkWidgetType = MOZ_GTK_TREEVIEW_EXPANDER; |
|
495 if (aWidgetFlags) |
|
496 *aWidgetFlags = GTK_EXPANDER_EXPANDED; |
|
497 break; |
|
498 case NS_THEME_DROPDOWN: |
|
499 aGtkWidgetType = MOZ_GTK_DROPDOWN; |
|
500 if (aWidgetFlags) |
|
501 *aWidgetFlags = IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XHTML); |
|
502 break; |
|
503 case NS_THEME_DROPDOWN_TEXT: |
|
504 return false; // nothing to do, but prevents the bg from being drawn |
|
505 case NS_THEME_DROPDOWN_TEXTFIELD: |
|
506 aGtkWidgetType = MOZ_GTK_DROPDOWN_ENTRY; |
|
507 break; |
|
508 case NS_THEME_DROPDOWN_BUTTON: |
|
509 aGtkWidgetType = MOZ_GTK_DROPDOWN_ARROW; |
|
510 break; |
|
511 case NS_THEME_TOOLBAR_BUTTON_DROPDOWN: |
|
512 case NS_THEME_BUTTON_ARROW_DOWN: |
|
513 case NS_THEME_BUTTON_ARROW_UP: |
|
514 case NS_THEME_BUTTON_ARROW_NEXT: |
|
515 case NS_THEME_BUTTON_ARROW_PREVIOUS: |
|
516 aGtkWidgetType = MOZ_GTK_TOOLBARBUTTON_ARROW; |
|
517 if (aWidgetFlags) { |
|
518 *aWidgetFlags = GTK_ARROW_DOWN; |
|
519 |
|
520 if (aWidgetType == NS_THEME_BUTTON_ARROW_UP) |
|
521 *aWidgetFlags = GTK_ARROW_UP; |
|
522 else if (aWidgetType == NS_THEME_BUTTON_ARROW_NEXT) |
|
523 *aWidgetFlags = GTK_ARROW_RIGHT; |
|
524 else if (aWidgetType == NS_THEME_BUTTON_ARROW_PREVIOUS) |
|
525 *aWidgetFlags = GTK_ARROW_LEFT; |
|
526 } |
|
527 break; |
|
528 case NS_THEME_CHECKBOX_CONTAINER: |
|
529 aGtkWidgetType = MOZ_GTK_CHECKBUTTON_CONTAINER; |
|
530 break; |
|
531 case NS_THEME_RADIO_CONTAINER: |
|
532 aGtkWidgetType = MOZ_GTK_RADIOBUTTON_CONTAINER; |
|
533 break; |
|
534 case NS_THEME_CHECKBOX_LABEL: |
|
535 aGtkWidgetType = MOZ_GTK_CHECKBUTTON_LABEL; |
|
536 break; |
|
537 case NS_THEME_RADIO_LABEL: |
|
538 aGtkWidgetType = MOZ_GTK_RADIOBUTTON_LABEL; |
|
539 break; |
|
540 case NS_THEME_TOOLBAR: |
|
541 aGtkWidgetType = MOZ_GTK_TOOLBAR; |
|
542 break; |
|
543 case NS_THEME_TOOLTIP: |
|
544 aGtkWidgetType = MOZ_GTK_TOOLTIP; |
|
545 break; |
|
546 case NS_THEME_STATUSBAR_PANEL: |
|
547 case NS_THEME_STATUSBAR_RESIZER_PANEL: |
|
548 aGtkWidgetType = MOZ_GTK_FRAME; |
|
549 break; |
|
550 case NS_THEME_PROGRESSBAR: |
|
551 case NS_THEME_PROGRESSBAR_VERTICAL: |
|
552 aGtkWidgetType = MOZ_GTK_PROGRESSBAR; |
|
553 break; |
|
554 case NS_THEME_PROGRESSBAR_CHUNK: |
|
555 case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL: |
|
556 { |
|
557 nsIFrame* stateFrame = aFrame->GetParent(); |
|
558 EventStates eventStates = GetContentState(stateFrame, aWidgetType); |
|
559 |
|
560 aGtkWidgetType = IsIndeterminateProgress(stateFrame, eventStates) |
|
561 ? (stateFrame->StyleDisplay()->mOrient == NS_STYLE_ORIENT_VERTICAL) |
|
562 ? MOZ_GTK_PROGRESS_CHUNK_VERTICAL_INDETERMINATE |
|
563 : MOZ_GTK_PROGRESS_CHUNK_INDETERMINATE |
|
564 : MOZ_GTK_PROGRESS_CHUNK; |
|
565 } |
|
566 break; |
|
567 case NS_THEME_TAB_SCROLLARROW_BACK: |
|
568 case NS_THEME_TAB_SCROLLARROW_FORWARD: |
|
569 if (aWidgetFlags) |
|
570 *aWidgetFlags = aWidgetType == NS_THEME_TAB_SCROLLARROW_BACK ? |
|
571 GTK_ARROW_LEFT : GTK_ARROW_RIGHT; |
|
572 aGtkWidgetType = MOZ_GTK_TAB_SCROLLARROW; |
|
573 break; |
|
574 case NS_THEME_TAB_PANELS: |
|
575 aGtkWidgetType = MOZ_GTK_TABPANELS; |
|
576 break; |
|
577 case NS_THEME_TAB: |
|
578 { |
|
579 if (aWidgetFlags) { |
|
580 /* First bits will be used to store max(0,-bmargin) where bmargin |
|
581 * is the bottom margin of the tab in pixels (resp. top margin, |
|
582 * for bottom tabs). */ |
|
583 if (IsBottomTab(aFrame)) { |
|
584 *aWidgetFlags = MOZ_GTK_TAB_BOTTOM; |
|
585 } else { |
|
586 *aWidgetFlags = 0; |
|
587 } |
|
588 |
|
589 *aWidgetFlags |= GetTabMarginPixels(aFrame); |
|
590 |
|
591 if (IsSelectedTab(aFrame)) |
|
592 *aWidgetFlags |= MOZ_GTK_TAB_SELECTED; |
|
593 |
|
594 if (IsFirstTab(aFrame)) |
|
595 *aWidgetFlags |= MOZ_GTK_TAB_FIRST; |
|
596 } |
|
597 |
|
598 aGtkWidgetType = MOZ_GTK_TAB; |
|
599 } |
|
600 break; |
|
601 case NS_THEME_SPLITTER: |
|
602 if (IsHorizontal(aFrame)) |
|
603 aGtkWidgetType = MOZ_GTK_SPLITTER_VERTICAL; |
|
604 else |
|
605 aGtkWidgetType = MOZ_GTK_SPLITTER_HORIZONTAL; |
|
606 break; |
|
607 case NS_THEME_MENUBAR: |
|
608 aGtkWidgetType = MOZ_GTK_MENUBAR; |
|
609 break; |
|
610 case NS_THEME_MENUPOPUP: |
|
611 aGtkWidgetType = MOZ_GTK_MENUPOPUP; |
|
612 break; |
|
613 case NS_THEME_MENUITEM: |
|
614 aGtkWidgetType = MOZ_GTK_MENUITEM; |
|
615 break; |
|
616 case NS_THEME_MENUSEPARATOR: |
|
617 aGtkWidgetType = MOZ_GTK_MENUSEPARATOR; |
|
618 break; |
|
619 case NS_THEME_MENUARROW: |
|
620 aGtkWidgetType = MOZ_GTK_MENUARROW; |
|
621 break; |
|
622 case NS_THEME_CHECKMENUITEM: |
|
623 aGtkWidgetType = MOZ_GTK_CHECKMENUITEM; |
|
624 break; |
|
625 case NS_THEME_RADIOMENUITEM: |
|
626 aGtkWidgetType = MOZ_GTK_RADIOMENUITEM; |
|
627 break; |
|
628 case NS_THEME_WINDOW: |
|
629 case NS_THEME_DIALOG: |
|
630 aGtkWidgetType = MOZ_GTK_WINDOW; |
|
631 break; |
|
632 default: |
|
633 return false; |
|
634 } |
|
635 |
|
636 return true; |
|
637 } |
|
638 |
|
639 #if (MOZ_WIDGET_GTK == 2) |
|
640 class ThemeRenderer : public gfxGdkNativeRenderer { |
|
641 public: |
|
642 ThemeRenderer(GtkWidgetState aState, GtkThemeWidgetType aGTKWidgetType, |
|
643 gint aFlags, GtkTextDirection aDirection, |
|
644 const GdkRectangle& aGDKRect, const GdkRectangle& aGDKClip) |
|
645 : mState(aState), mGTKWidgetType(aGTKWidgetType), mFlags(aFlags), |
|
646 mDirection(aDirection), mGDKRect(aGDKRect), mGDKClip(aGDKClip) {} |
|
647 nsresult DrawWithGDK(GdkDrawable * drawable, gint offsetX, gint offsetY, |
|
648 GdkRectangle * clipRects, uint32_t numClipRects); |
|
649 private: |
|
650 GtkWidgetState mState; |
|
651 GtkThemeWidgetType mGTKWidgetType; |
|
652 gint mFlags; |
|
653 GtkTextDirection mDirection; |
|
654 const GdkRectangle& mGDKRect; |
|
655 const GdkRectangle& mGDKClip; |
|
656 }; |
|
657 |
|
658 nsresult |
|
659 ThemeRenderer::DrawWithGDK(GdkDrawable * drawable, gint offsetX, |
|
660 gint offsetY, GdkRectangle * clipRects, uint32_t numClipRects) |
|
661 { |
|
662 GdkRectangle gdk_rect = mGDKRect; |
|
663 gdk_rect.x += offsetX; |
|
664 gdk_rect.y += offsetY; |
|
665 |
|
666 GdkRectangle gdk_clip = mGDKClip; |
|
667 gdk_clip.x += offsetX; |
|
668 gdk_clip.y += offsetY; |
|
669 |
|
670 GdkRectangle surfaceRect; |
|
671 surfaceRect.x = 0; |
|
672 surfaceRect.y = 0; |
|
673 gdk_drawable_get_size(drawable, &surfaceRect.width, &surfaceRect.height); |
|
674 gdk_rectangle_intersect(&gdk_clip, &surfaceRect, &gdk_clip); |
|
675 |
|
676 NS_ASSERTION(numClipRects == 0, "We don't support clipping!!!"); |
|
677 moz_gtk_widget_paint(mGTKWidgetType, drawable, &gdk_rect, &gdk_clip, |
|
678 &mState, mFlags, mDirection); |
|
679 |
|
680 return NS_OK; |
|
681 } |
|
682 #endif |
|
683 |
|
684 bool |
|
685 nsNativeThemeGTK::GetExtraSizeForWidget(nsIFrame* aFrame, uint8_t aWidgetType, |
|
686 nsIntMargin* aExtra) |
|
687 { |
|
688 *aExtra = nsIntMargin(0,0,0,0); |
|
689 // Allow an extra one pixel above and below the thumb for certain |
|
690 // GTK2 themes (Ximian Industrial, Bluecurve, Misty, at least); |
|
691 // We modify the frame's overflow area. See bug 297508. |
|
692 switch (aWidgetType) { |
|
693 case NS_THEME_SCROLLBAR_THUMB_VERTICAL: |
|
694 aExtra->top = aExtra->bottom = 1; |
|
695 return true; |
|
696 case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL: |
|
697 aExtra->left = aExtra->right = 1; |
|
698 return true; |
|
699 |
|
700 // Include the indicator spacing (the padding around the control). |
|
701 case NS_THEME_CHECKBOX: |
|
702 case NS_THEME_RADIO: |
|
703 { |
|
704 gint indicator_size, indicator_spacing; |
|
705 |
|
706 if (aWidgetType == NS_THEME_CHECKBOX) { |
|
707 moz_gtk_checkbox_get_metrics(&indicator_size, &indicator_spacing); |
|
708 } else { |
|
709 moz_gtk_radio_get_metrics(&indicator_size, &indicator_spacing); |
|
710 } |
|
711 |
|
712 aExtra->top = indicator_spacing; |
|
713 aExtra->right = indicator_spacing; |
|
714 aExtra->bottom = indicator_spacing; |
|
715 aExtra->left = indicator_spacing; |
|
716 return true; |
|
717 } |
|
718 case NS_THEME_BUTTON : |
|
719 { |
|
720 if (IsDefaultButton(aFrame)) { |
|
721 // Some themes draw a default indicator outside the widget, |
|
722 // include that in overflow |
|
723 gint top, left, bottom, right; |
|
724 moz_gtk_button_get_default_overflow(&top, &left, &bottom, &right); |
|
725 aExtra->top = top; |
|
726 aExtra->right = right; |
|
727 aExtra->bottom = bottom; |
|
728 aExtra->left = left; |
|
729 return true; |
|
730 } |
|
731 } |
|
732 case NS_THEME_TAB : |
|
733 { |
|
734 if (!IsSelectedTab(aFrame)) |
|
735 return false; |
|
736 |
|
737 gint gap_height = moz_gtk_get_tab_thickness(); |
|
738 |
|
739 int32_t extra = gap_height - GetTabMarginPixels(aFrame); |
|
740 if (extra <= 0) |
|
741 return false; |
|
742 |
|
743 if (IsBottomTab(aFrame)) { |
|
744 aExtra->top = extra; |
|
745 } else { |
|
746 aExtra->bottom = extra; |
|
747 } |
|
748 } |
|
749 default: |
|
750 return false; |
|
751 } |
|
752 } |
|
753 |
|
754 NS_IMETHODIMP |
|
755 nsNativeThemeGTK::DrawWidgetBackground(nsRenderingContext* aContext, |
|
756 nsIFrame* aFrame, |
|
757 uint8_t aWidgetType, |
|
758 const nsRect& aRect, |
|
759 const nsRect& aDirtyRect) |
|
760 { |
|
761 GtkWidgetState state; |
|
762 GtkThemeWidgetType gtkWidgetType; |
|
763 GtkTextDirection direction = GetTextDirection(aFrame); |
|
764 gint flags; |
|
765 if (!GetGtkWidgetAndState(aWidgetType, aFrame, gtkWidgetType, &state, |
|
766 &flags)) |
|
767 return NS_OK; |
|
768 |
|
769 gfxContext* ctx = aContext->ThebesContext(); |
|
770 nsPresContext *presContext = aFrame->PresContext(); |
|
771 |
|
772 gfxRect rect = presContext->AppUnitsToGfxUnits(aRect); |
|
773 gfxRect dirtyRect = presContext->AppUnitsToGfxUnits(aDirtyRect); |
|
774 |
|
775 // Align to device pixels where sensible |
|
776 // to provide crisper and faster drawing. |
|
777 // Don't snap if it's a non-unit scale factor. We're going to have to take |
|
778 // slow paths then in any case. |
|
779 bool snapXY = ctx->UserToDevicePixelSnapped(rect); |
|
780 if (snapXY) { |
|
781 // Leave rect in device coords but make dirtyRect consistent. |
|
782 dirtyRect = ctx->UserToDevice(dirtyRect); |
|
783 } |
|
784 |
|
785 // Translate the dirty rect so that it is wrt the widget top-left. |
|
786 dirtyRect.MoveBy(-rect.TopLeft()); |
|
787 // Round out the dirty rect to gdk pixels to ensure that gtk draws |
|
788 // enough pixels for interpolation to device pixels. |
|
789 dirtyRect.RoundOut(); |
|
790 |
|
791 // GTK themes can only draw an integer number of pixels |
|
792 // (even when not snapped). |
|
793 nsIntRect widgetRect(0, 0, NS_lround(rect.Width()), NS_lround(rect.Height())); |
|
794 nsIntRect overflowRect(widgetRect); |
|
795 nsIntMargin extraSize; |
|
796 if (GetExtraSizeForWidget(aFrame, aWidgetType, &extraSize)) { |
|
797 overflowRect.Inflate(extraSize); |
|
798 } |
|
799 |
|
800 // This is the rectangle that will actually be drawn, in gdk pixels |
|
801 nsIntRect drawingRect(int32_t(dirtyRect.X()), |
|
802 int32_t(dirtyRect.Y()), |
|
803 int32_t(dirtyRect.Width()), |
|
804 int32_t(dirtyRect.Height())); |
|
805 if (widgetRect.IsEmpty() |
|
806 || !drawingRect.IntersectRect(overflowRect, drawingRect)) |
|
807 return NS_OK; |
|
808 |
|
809 // gdk rectangles are wrt the drawing rect. |
|
810 |
|
811 GdkRectangle gdk_rect = {-drawingRect.x, -drawingRect.y, |
|
812 widgetRect.width, widgetRect.height}; |
|
813 |
|
814 // translate everything so (0,0) is the top left of the drawingRect |
|
815 gfxContextAutoSaveRestore autoSR(ctx); |
|
816 if (snapXY) { |
|
817 // Rects are in device coords. |
|
818 ctx->IdentityMatrix(); |
|
819 } |
|
820 ctx->Translate(rect.TopLeft() + gfxPoint(drawingRect.x, drawingRect.y)); |
|
821 |
|
822 NS_ASSERTION(!IsWidgetTypeDisabled(mDisabledWidgetTypes, aWidgetType), |
|
823 "Trying to render an unsafe widget!"); |
|
824 |
|
825 bool safeState = IsWidgetStateSafe(mSafeWidgetStates, aWidgetType, &state); |
|
826 if (!safeState) { |
|
827 gLastGdkError = 0; |
|
828 gdk_error_trap_push (); |
|
829 } |
|
830 |
|
831 #if (MOZ_WIDGET_GTK == 2) |
|
832 // The gdk_clip is just advisory here, meaning "you don't |
|
833 // need to draw outside this rect if you don't feel like it!" |
|
834 GdkRectangle gdk_clip = {0, 0, drawingRect.width, drawingRect.height}; |
|
835 |
|
836 ThemeRenderer renderer(state, gtkWidgetType, flags, direction, |
|
837 gdk_rect, gdk_clip); |
|
838 |
|
839 // Some themes (e.g. Clearlooks) just don't clip properly to any |
|
840 // clip rect we provide, so we cannot advertise support for clipping within |
|
841 // the widget bounds. |
|
842 uint32_t rendererFlags = 0; |
|
843 if (GetWidgetTransparency(aFrame, aWidgetType) == eOpaque) { |
|
844 rendererFlags |= gfxGdkNativeRenderer::DRAW_IS_OPAQUE; |
|
845 } |
|
846 |
|
847 // GtkStyles (used by the widget drawing backend) are created for a |
|
848 // particular colormap/visual. |
|
849 GdkColormap* colormap = moz_gtk_widget_get_colormap(); |
|
850 |
|
851 renderer.Draw(ctx, drawingRect.Size(), rendererFlags, colormap); |
|
852 #else |
|
853 moz_gtk_widget_paint(gtkWidgetType, ctx->GetCairo(), &gdk_rect, |
|
854 &state, flags, direction); |
|
855 #endif |
|
856 |
|
857 if (!safeState) { |
|
858 gdk_flush(); |
|
859 gLastGdkError = gdk_error_trap_pop (); |
|
860 |
|
861 if (gLastGdkError) { |
|
862 #ifdef DEBUG |
|
863 printf("GTK theme failed for widget type %d, error was %d, state was " |
|
864 "[active=%d,focused=%d,inHover=%d,disabled=%d]\n", |
|
865 aWidgetType, gLastGdkError, state.active, state.focused, |
|
866 state.inHover, state.disabled); |
|
867 #endif |
|
868 NS_WARNING("GTK theme failed; disabling unsafe widget"); |
|
869 SetWidgetTypeDisabled(mDisabledWidgetTypes, aWidgetType); |
|
870 // force refresh of the window, because the widget was not |
|
871 // successfully drawn it must be redrawn using the default look |
|
872 RefreshWidgetWindow(aFrame); |
|
873 } else { |
|
874 SetWidgetStateSafe(mSafeWidgetStates, aWidgetType, &state); |
|
875 } |
|
876 } |
|
877 |
|
878 // Indeterminate progress bar are animated. |
|
879 if (gtkWidgetType == MOZ_GTK_PROGRESS_CHUNK_INDETERMINATE || |
|
880 gtkWidgetType == MOZ_GTK_PROGRESS_CHUNK_VERTICAL_INDETERMINATE) { |
|
881 if (!QueueAnimatedContentForRefresh(aFrame->GetContent(), 30)) { |
|
882 NS_WARNING("unable to animate widget!"); |
|
883 } |
|
884 } |
|
885 |
|
886 return NS_OK; |
|
887 } |
|
888 |
|
889 NS_IMETHODIMP |
|
890 nsNativeThemeGTK::GetWidgetBorder(nsDeviceContext* aContext, nsIFrame* aFrame, |
|
891 uint8_t aWidgetType, nsIntMargin* aResult) |
|
892 { |
|
893 GtkTextDirection direction = GetTextDirection(aFrame); |
|
894 aResult->top = aResult->left = aResult->right = aResult->bottom = 0; |
|
895 switch (aWidgetType) { |
|
896 case NS_THEME_SCROLLBAR_TRACK_VERTICAL: |
|
897 case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL: |
|
898 { |
|
899 MozGtkScrollbarMetrics metrics; |
|
900 moz_gtk_get_scrollbar_metrics(&metrics); |
|
901 aResult->top = aResult->left = aResult->right = aResult->bottom = metrics.trough_border; |
|
902 } |
|
903 break; |
|
904 case NS_THEME_TOOLBOX: |
|
905 // gtk has no toolbox equivalent. So, although we map toolbox to |
|
906 // gtk's 'toolbar' for purposes of painting the widget background, |
|
907 // we don't use the toolbar border for toolbox. |
|
908 break; |
|
909 case NS_THEME_TOOLBAR_DUAL_BUTTON: |
|
910 // TOOLBAR_DUAL_BUTTON is an interesting case. We want a border to draw |
|
911 // around the entire button + dropdown, and also an inner border if you're |
|
912 // over the button part. But, we want the inner button to be right up |
|
913 // against the edge of the outer button so that the borders overlap. |
|
914 // To make this happen, we draw a button border for the outer button, |
|
915 // but don't reserve any space for it. |
|
916 break; |
|
917 case NS_THEME_TAB: |
|
918 // Top tabs have no bottom border, bottom tabs have no top border |
|
919 moz_gtk_get_widget_border(MOZ_GTK_TAB, &aResult->left, &aResult->top, |
|
920 &aResult->right, &aResult->bottom, direction, |
|
921 FALSE); |
|
922 if (IsBottomTab(aFrame)) |
|
923 aResult->top = 0; |
|
924 else |
|
925 aResult->bottom = 0; |
|
926 break; |
|
927 case NS_THEME_MENUITEM: |
|
928 case NS_THEME_CHECKMENUITEM: |
|
929 case NS_THEME_RADIOMENUITEM: |
|
930 // For regular menuitems, we will be using GetWidgetPadding instead of |
|
931 // GetWidgetBorder to pad up the widget's internals; other menuitems |
|
932 // will need to fall through and use the default case as before. |
|
933 if (IsRegularMenuItem(aFrame)) |
|
934 break; |
|
935 default: |
|
936 { |
|
937 GtkThemeWidgetType gtkWidgetType; |
|
938 if (GetGtkWidgetAndState(aWidgetType, aFrame, gtkWidgetType, nullptr, |
|
939 nullptr)) { |
|
940 moz_gtk_get_widget_border(gtkWidgetType, &aResult->left, &aResult->top, |
|
941 &aResult->right, &aResult->bottom, direction, |
|
942 IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XHTML)); |
|
943 } |
|
944 } |
|
945 } |
|
946 return NS_OK; |
|
947 } |
|
948 |
|
949 bool |
|
950 nsNativeThemeGTK::GetWidgetPadding(nsDeviceContext* aContext, |
|
951 nsIFrame* aFrame, uint8_t aWidgetType, |
|
952 nsIntMargin* aResult) |
|
953 { |
|
954 switch (aWidgetType) { |
|
955 case NS_THEME_BUTTON_FOCUS: |
|
956 case NS_THEME_TOOLBAR_BUTTON: |
|
957 case NS_THEME_TOOLBAR_DUAL_BUTTON: |
|
958 case NS_THEME_TAB_SCROLLARROW_BACK: |
|
959 case NS_THEME_TAB_SCROLLARROW_FORWARD: |
|
960 case NS_THEME_DROPDOWN_BUTTON: |
|
961 case NS_THEME_TOOLBAR_BUTTON_DROPDOWN: |
|
962 case NS_THEME_BUTTON_ARROW_UP: |
|
963 case NS_THEME_BUTTON_ARROW_DOWN: |
|
964 case NS_THEME_BUTTON_ARROW_NEXT: |
|
965 case NS_THEME_BUTTON_ARROW_PREVIOUS: |
|
966 case NS_THEME_RANGE_THUMB: |
|
967 // Radios and checkboxes return a fixed size in GetMinimumWidgetSize |
|
968 // and have a meaningful baseline, so they can't have |
|
969 // author-specified padding. |
|
970 case NS_THEME_CHECKBOX: |
|
971 case NS_THEME_RADIO: |
|
972 aResult->SizeTo(0, 0, 0, 0); |
|
973 return true; |
|
974 case NS_THEME_MENUITEM: |
|
975 case NS_THEME_CHECKMENUITEM: |
|
976 case NS_THEME_RADIOMENUITEM: |
|
977 { |
|
978 // Menubar and menulist have their padding specified in CSS. |
|
979 if (!IsRegularMenuItem(aFrame)) |
|
980 return false; |
|
981 |
|
982 aResult->SizeTo(0, 0, 0, 0); |
|
983 GtkThemeWidgetType gtkWidgetType; |
|
984 if (GetGtkWidgetAndState(aWidgetType, aFrame, gtkWidgetType, nullptr, |
|
985 nullptr)) { |
|
986 moz_gtk_get_widget_border(gtkWidgetType, &aResult->left, &aResult->top, |
|
987 &aResult->right, &aResult->bottom, GetTextDirection(aFrame), |
|
988 IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XHTML)); |
|
989 } |
|
990 |
|
991 gint horizontal_padding; |
|
992 |
|
993 if (aWidgetType == NS_THEME_MENUITEM) |
|
994 moz_gtk_menuitem_get_horizontal_padding(&horizontal_padding); |
|
995 else |
|
996 moz_gtk_checkmenuitem_get_horizontal_padding(&horizontal_padding); |
|
997 |
|
998 aResult->left += horizontal_padding; |
|
999 aResult->right += horizontal_padding; |
|
1000 |
|
1001 return true; |
|
1002 } |
|
1003 } |
|
1004 |
|
1005 return false; |
|
1006 } |
|
1007 |
|
1008 bool |
|
1009 nsNativeThemeGTK::GetWidgetOverflow(nsDeviceContext* aContext, |
|
1010 nsIFrame* aFrame, uint8_t aWidgetType, |
|
1011 nsRect* aOverflowRect) |
|
1012 { |
|
1013 nsIntMargin extraSize; |
|
1014 if (!GetExtraSizeForWidget(aFrame, aWidgetType, &extraSize)) |
|
1015 return false; |
|
1016 |
|
1017 int32_t p2a = aContext->AppUnitsPerDevPixel(); |
|
1018 nsMargin m(NSIntPixelsToAppUnits(extraSize.top, p2a), |
|
1019 NSIntPixelsToAppUnits(extraSize.right, p2a), |
|
1020 NSIntPixelsToAppUnits(extraSize.bottom, p2a), |
|
1021 NSIntPixelsToAppUnits(extraSize.left, p2a)); |
|
1022 |
|
1023 aOverflowRect->Inflate(m); |
|
1024 return true; |
|
1025 } |
|
1026 |
|
1027 NS_IMETHODIMP |
|
1028 nsNativeThemeGTK::GetMinimumWidgetSize(nsRenderingContext* aContext, |
|
1029 nsIFrame* aFrame, uint8_t aWidgetType, |
|
1030 nsIntSize* aResult, bool* aIsOverridable) |
|
1031 { |
|
1032 aResult->width = aResult->height = 0; |
|
1033 *aIsOverridable = true; |
|
1034 |
|
1035 switch (aWidgetType) { |
|
1036 case NS_THEME_SCROLLBAR_BUTTON_UP: |
|
1037 case NS_THEME_SCROLLBAR_BUTTON_DOWN: |
|
1038 { |
|
1039 MozGtkScrollbarMetrics metrics; |
|
1040 moz_gtk_get_scrollbar_metrics(&metrics); |
|
1041 |
|
1042 aResult->width = metrics.slider_width; |
|
1043 aResult->height = metrics.stepper_size; |
|
1044 *aIsOverridable = false; |
|
1045 } |
|
1046 break; |
|
1047 case NS_THEME_SCROLLBAR_BUTTON_LEFT: |
|
1048 case NS_THEME_SCROLLBAR_BUTTON_RIGHT: |
|
1049 { |
|
1050 MozGtkScrollbarMetrics metrics; |
|
1051 moz_gtk_get_scrollbar_metrics(&metrics); |
|
1052 |
|
1053 aResult->width = metrics.stepper_size; |
|
1054 aResult->height = metrics.slider_width; |
|
1055 *aIsOverridable = false; |
|
1056 } |
|
1057 break; |
|
1058 case NS_THEME_SPLITTER: |
|
1059 { |
|
1060 gint metrics; |
|
1061 if (IsHorizontal(aFrame)) { |
|
1062 moz_gtk_splitter_get_metrics(GTK_ORIENTATION_HORIZONTAL, &metrics); |
|
1063 aResult->width = metrics; |
|
1064 aResult->height = 0; |
|
1065 } else { |
|
1066 moz_gtk_splitter_get_metrics(GTK_ORIENTATION_VERTICAL, &metrics); |
|
1067 aResult->width = 0; |
|
1068 aResult->height = metrics; |
|
1069 } |
|
1070 *aIsOverridable = false; |
|
1071 } |
|
1072 break; |
|
1073 case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL: |
|
1074 case NS_THEME_SCROLLBAR_TRACK_VERTICAL: |
|
1075 { |
|
1076 /* While we enforce a minimum size for the thumb, this is ignored |
|
1077 * for the some scrollbars if buttons are hidden (bug 513006) because |
|
1078 * the thumb isn't a direct child of the scrollbar, unlike the buttons |
|
1079 * or track. So add a minimum size to the track as well to prevent a |
|
1080 * 0-width scrollbar. */ |
|
1081 MozGtkScrollbarMetrics metrics; |
|
1082 moz_gtk_get_scrollbar_metrics(&metrics); |
|
1083 |
|
1084 if (aWidgetType == NS_THEME_SCROLLBAR_TRACK_VERTICAL) |
|
1085 aResult->width = metrics.slider_width + 2 * metrics.trough_border; |
|
1086 else |
|
1087 aResult->height = metrics.slider_width + 2 * metrics.trough_border; |
|
1088 |
|
1089 *aIsOverridable = false; |
|
1090 } |
|
1091 break; |
|
1092 case NS_THEME_SCROLLBAR_THUMB_VERTICAL: |
|
1093 case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL: |
|
1094 { |
|
1095 MozGtkScrollbarMetrics metrics; |
|
1096 moz_gtk_get_scrollbar_metrics(&metrics); |
|
1097 |
|
1098 nsRect rect = aFrame->GetParent()->GetRect(); |
|
1099 int32_t p2a = aFrame->PresContext()->DeviceContext()-> |
|
1100 AppUnitsPerDevPixel(); |
|
1101 nsMargin margin; |
|
1102 |
|
1103 /* Get the available space, if that is smaller then the minimum size, |
|
1104 * adjust the mininum size to fit into it. |
|
1105 * Setting aIsOverridable to true has no effect for thumbs. */ |
|
1106 aFrame->GetMargin(margin); |
|
1107 rect.Deflate(margin); |
|
1108 aFrame->GetParent()->GetBorderAndPadding(margin); |
|
1109 rect.Deflate(margin); |
|
1110 |
|
1111 if (aWidgetType == NS_THEME_SCROLLBAR_THUMB_VERTICAL) { |
|
1112 aResult->width = metrics.slider_width; |
|
1113 aResult->height = std::min(NSAppUnitsToIntPixels(rect.height, p2a), |
|
1114 metrics.min_slider_size); |
|
1115 } else { |
|
1116 aResult->height = metrics.slider_width; |
|
1117 aResult->width = std::min(NSAppUnitsToIntPixels(rect.width, p2a), |
|
1118 metrics.min_slider_size); |
|
1119 } |
|
1120 |
|
1121 *aIsOverridable = false; |
|
1122 } |
|
1123 break; |
|
1124 case NS_THEME_RANGE_THUMB: |
|
1125 { |
|
1126 gint thumb_length, thumb_height; |
|
1127 |
|
1128 if (IsRangeHorizontal(aFrame)) { |
|
1129 moz_gtk_get_scalethumb_metrics(GTK_ORIENTATION_HORIZONTAL, &thumb_length, &thumb_height); |
|
1130 } else { |
|
1131 moz_gtk_get_scalethumb_metrics(GTK_ORIENTATION_VERTICAL, &thumb_height, &thumb_length); |
|
1132 } |
|
1133 aResult->width = thumb_length; |
|
1134 aResult->height = thumb_height; |
|
1135 |
|
1136 *aIsOverridable = false; |
|
1137 } |
|
1138 break; |
|
1139 case NS_THEME_SCALE_THUMB_HORIZONTAL: |
|
1140 case NS_THEME_SCALE_THUMB_VERTICAL: |
|
1141 { |
|
1142 gint thumb_length, thumb_height; |
|
1143 |
|
1144 if (aWidgetType == NS_THEME_SCALE_THUMB_VERTICAL) { |
|
1145 moz_gtk_get_scalethumb_metrics(GTK_ORIENTATION_VERTICAL, &thumb_length, &thumb_height); |
|
1146 aResult->width = thumb_height; |
|
1147 aResult->height = thumb_length; |
|
1148 } else { |
|
1149 moz_gtk_get_scalethumb_metrics(GTK_ORIENTATION_HORIZONTAL, &thumb_length, &thumb_height); |
|
1150 aResult->width = thumb_length; |
|
1151 aResult->height = thumb_height; |
|
1152 } |
|
1153 |
|
1154 *aIsOverridable = false; |
|
1155 } |
|
1156 break; |
|
1157 case NS_THEME_TAB_SCROLLARROW_BACK: |
|
1158 case NS_THEME_TAB_SCROLLARROW_FORWARD: |
|
1159 { |
|
1160 moz_gtk_get_tab_scroll_arrow_size(&aResult->width, &aResult->height); |
|
1161 *aIsOverridable = false; |
|
1162 } |
|
1163 break; |
|
1164 case NS_THEME_DROPDOWN_BUTTON: |
|
1165 { |
|
1166 moz_gtk_get_combo_box_entry_button_size(&aResult->width, |
|
1167 &aResult->height); |
|
1168 *aIsOverridable = false; |
|
1169 } |
|
1170 break; |
|
1171 case NS_THEME_MENUSEPARATOR: |
|
1172 { |
|
1173 gint separator_height; |
|
1174 |
|
1175 moz_gtk_get_menu_separator_height(&separator_height); |
|
1176 aResult->height = separator_height; |
|
1177 |
|
1178 *aIsOverridable = false; |
|
1179 } |
|
1180 break; |
|
1181 case NS_THEME_CHECKBOX: |
|
1182 case NS_THEME_RADIO: |
|
1183 { |
|
1184 gint indicator_size, indicator_spacing; |
|
1185 |
|
1186 if (aWidgetType == NS_THEME_CHECKBOX) { |
|
1187 moz_gtk_checkbox_get_metrics(&indicator_size, &indicator_spacing); |
|
1188 } else { |
|
1189 moz_gtk_radio_get_metrics(&indicator_size, &indicator_spacing); |
|
1190 } |
|
1191 |
|
1192 // Include space for the indicator and the padding around it. |
|
1193 aResult->width = indicator_size; |
|
1194 aResult->height = indicator_size; |
|
1195 } |
|
1196 break; |
|
1197 case NS_THEME_TOOLBAR_BUTTON_DROPDOWN: |
|
1198 case NS_THEME_BUTTON_ARROW_UP: |
|
1199 case NS_THEME_BUTTON_ARROW_DOWN: |
|
1200 case NS_THEME_BUTTON_ARROW_NEXT: |
|
1201 case NS_THEME_BUTTON_ARROW_PREVIOUS: |
|
1202 { |
|
1203 moz_gtk_get_arrow_size(&aResult->width, &aResult->height); |
|
1204 *aIsOverridable = false; |
|
1205 } |
|
1206 break; |
|
1207 case NS_THEME_CHECKBOX_CONTAINER: |
|
1208 case NS_THEME_RADIO_CONTAINER: |
|
1209 case NS_THEME_CHECKBOX_LABEL: |
|
1210 case NS_THEME_RADIO_LABEL: |
|
1211 case NS_THEME_BUTTON: |
|
1212 case NS_THEME_DROPDOWN: |
|
1213 case NS_THEME_TOOLBAR_BUTTON: |
|
1214 case NS_THEME_TREEVIEW_HEADER_CELL: |
|
1215 { |
|
1216 // Just include our border, and let the box code augment the size. |
|
1217 nsIntMargin border; |
|
1218 nsNativeThemeGTK::GetWidgetBorder(aContext->DeviceContext(), |
|
1219 aFrame, aWidgetType, &border); |
|
1220 aResult->width = border.left + border.right; |
|
1221 aResult->height = border.top + border.bottom; |
|
1222 } |
|
1223 break; |
|
1224 case NS_THEME_TOOLBAR_SEPARATOR: |
|
1225 { |
|
1226 gint separator_width; |
|
1227 |
|
1228 moz_gtk_get_toolbar_separator_width(&separator_width); |
|
1229 |
|
1230 aResult->width = separator_width; |
|
1231 } |
|
1232 break; |
|
1233 case NS_THEME_SPINNER: |
|
1234 // hard code these sizes |
|
1235 aResult->width = 14; |
|
1236 aResult->height = 26; |
|
1237 break; |
|
1238 case NS_THEME_TREEVIEW_HEADER_SORTARROW: |
|
1239 case NS_THEME_SPINNER_UP_BUTTON: |
|
1240 case NS_THEME_SPINNER_DOWN_BUTTON: |
|
1241 // hard code these sizes |
|
1242 aResult->width = 14; |
|
1243 aResult->height = 13; |
|
1244 break; |
|
1245 case NS_THEME_RESIZER: |
|
1246 // same as Windows to make our lives easier |
|
1247 aResult->width = aResult->height = 15; |
|
1248 *aIsOverridable = false; |
|
1249 break; |
|
1250 case NS_THEME_TREEVIEW_TWISTY: |
|
1251 case NS_THEME_TREEVIEW_TWISTY_OPEN: |
|
1252 { |
|
1253 gint expander_size; |
|
1254 |
|
1255 moz_gtk_get_treeview_expander_size(&expander_size); |
|
1256 aResult->width = aResult->height = expander_size; |
|
1257 *aIsOverridable = false; |
|
1258 } |
|
1259 break; |
|
1260 } |
|
1261 return NS_OK; |
|
1262 } |
|
1263 |
|
1264 NS_IMETHODIMP |
|
1265 nsNativeThemeGTK::WidgetStateChanged(nsIFrame* aFrame, uint8_t aWidgetType, |
|
1266 nsIAtom* aAttribute, bool* aShouldRepaint) |
|
1267 { |
|
1268 // Some widget types just never change state. |
|
1269 if (aWidgetType == NS_THEME_TOOLBOX || |
|
1270 aWidgetType == NS_THEME_TOOLBAR || |
|
1271 aWidgetType == NS_THEME_STATUSBAR || |
|
1272 aWidgetType == NS_THEME_STATUSBAR_PANEL || |
|
1273 aWidgetType == NS_THEME_STATUSBAR_RESIZER_PANEL || |
|
1274 aWidgetType == NS_THEME_PROGRESSBAR_CHUNK || |
|
1275 aWidgetType == NS_THEME_PROGRESSBAR_CHUNK_VERTICAL || |
|
1276 aWidgetType == NS_THEME_PROGRESSBAR || |
|
1277 aWidgetType == NS_THEME_PROGRESSBAR_VERTICAL || |
|
1278 aWidgetType == NS_THEME_MENUBAR || |
|
1279 aWidgetType == NS_THEME_MENUPOPUP || |
|
1280 aWidgetType == NS_THEME_TOOLTIP || |
|
1281 aWidgetType == NS_THEME_MENUSEPARATOR || |
|
1282 aWidgetType == NS_THEME_WINDOW || |
|
1283 aWidgetType == NS_THEME_DIALOG) { |
|
1284 *aShouldRepaint = false; |
|
1285 return NS_OK; |
|
1286 } |
|
1287 |
|
1288 if ((aWidgetType == NS_THEME_SCROLLBAR_BUTTON_UP || |
|
1289 aWidgetType == NS_THEME_SCROLLBAR_BUTTON_DOWN || |
|
1290 aWidgetType == NS_THEME_SCROLLBAR_BUTTON_LEFT || |
|
1291 aWidgetType == NS_THEME_SCROLLBAR_BUTTON_RIGHT) && |
|
1292 (aAttribute == nsGkAtoms::curpos || |
|
1293 aAttribute == nsGkAtoms::maxpos)) { |
|
1294 *aShouldRepaint = true; |
|
1295 return NS_OK; |
|
1296 } |
|
1297 |
|
1298 // XXXdwh Not sure what can really be done here. Can at least guess for |
|
1299 // specific widgets that they're highly unlikely to have certain states. |
|
1300 // For example, a toolbar doesn't care about any states. |
|
1301 if (!aAttribute) { |
|
1302 // Hover/focus/active changed. Always repaint. |
|
1303 *aShouldRepaint = true; |
|
1304 } |
|
1305 else { |
|
1306 // Check the attribute to see if it's relevant. |
|
1307 // disabled, checked, dlgtype, default, etc. |
|
1308 *aShouldRepaint = false; |
|
1309 if (aAttribute == nsGkAtoms::disabled || |
|
1310 aAttribute == nsGkAtoms::checked || |
|
1311 aAttribute == nsGkAtoms::selected || |
|
1312 aAttribute == nsGkAtoms::focused || |
|
1313 aAttribute == nsGkAtoms::readonly || |
|
1314 aAttribute == nsGkAtoms::_default || |
|
1315 aAttribute == nsGkAtoms::menuactive || |
|
1316 aAttribute == nsGkAtoms::open || |
|
1317 aAttribute == nsGkAtoms::parentfocused) |
|
1318 *aShouldRepaint = true; |
|
1319 } |
|
1320 |
|
1321 return NS_OK; |
|
1322 } |
|
1323 |
|
1324 NS_IMETHODIMP |
|
1325 nsNativeThemeGTK::ThemeChanged() |
|
1326 { |
|
1327 memset(mDisabledWidgetTypes, 0, sizeof(mDisabledWidgetTypes)); |
|
1328 return NS_OK; |
|
1329 } |
|
1330 |
|
1331 NS_IMETHODIMP_(bool) |
|
1332 nsNativeThemeGTK::ThemeSupportsWidget(nsPresContext* aPresContext, |
|
1333 nsIFrame* aFrame, |
|
1334 uint8_t aWidgetType) |
|
1335 { |
|
1336 if (IsWidgetTypeDisabled(mDisabledWidgetTypes, aWidgetType)) |
|
1337 return false; |
|
1338 |
|
1339 switch (aWidgetType) { |
|
1340 case NS_THEME_BUTTON: |
|
1341 case NS_THEME_BUTTON_FOCUS: |
|
1342 case NS_THEME_RADIO: |
|
1343 case NS_THEME_CHECKBOX: |
|
1344 case NS_THEME_TOOLBOX: // N/A |
|
1345 case NS_THEME_TOOLBAR: |
|
1346 case NS_THEME_TOOLBAR_BUTTON: |
|
1347 case NS_THEME_TOOLBAR_DUAL_BUTTON: // so we can override the border with 0 |
|
1348 case NS_THEME_TOOLBAR_BUTTON_DROPDOWN: |
|
1349 case NS_THEME_BUTTON_ARROW_UP: |
|
1350 case NS_THEME_BUTTON_ARROW_DOWN: |
|
1351 case NS_THEME_BUTTON_ARROW_NEXT: |
|
1352 case NS_THEME_BUTTON_ARROW_PREVIOUS: |
|
1353 case NS_THEME_TOOLBAR_SEPARATOR: |
|
1354 case NS_THEME_TOOLBAR_GRIPPER: |
|
1355 case NS_THEME_STATUSBAR: |
|
1356 case NS_THEME_STATUSBAR_PANEL: |
|
1357 case NS_THEME_STATUSBAR_RESIZER_PANEL: |
|
1358 case NS_THEME_RESIZER: |
|
1359 case NS_THEME_LISTBOX: |
|
1360 // case NS_THEME_LISTBOX_LISTITEM: |
|
1361 case NS_THEME_TREEVIEW: |
|
1362 // case NS_THEME_TREEVIEW_TREEITEM: |
|
1363 case NS_THEME_TREEVIEW_TWISTY: |
|
1364 // case NS_THEME_TREEVIEW_LINE: |
|
1365 // case NS_THEME_TREEVIEW_HEADER: |
|
1366 case NS_THEME_TREEVIEW_HEADER_CELL: |
|
1367 case NS_THEME_TREEVIEW_HEADER_SORTARROW: |
|
1368 case NS_THEME_TREEVIEW_TWISTY_OPEN: |
|
1369 case NS_THEME_PROGRESSBAR: |
|
1370 case NS_THEME_PROGRESSBAR_CHUNK: |
|
1371 case NS_THEME_PROGRESSBAR_VERTICAL: |
|
1372 case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL: |
|
1373 case NS_THEME_TAB: |
|
1374 // case NS_THEME_TAB_PANEL: |
|
1375 case NS_THEME_TAB_PANELS: |
|
1376 case NS_THEME_TAB_SCROLLARROW_BACK: |
|
1377 case NS_THEME_TAB_SCROLLARROW_FORWARD: |
|
1378 case NS_THEME_TOOLTIP: |
|
1379 case NS_THEME_SPINNER: |
|
1380 case NS_THEME_SPINNER_UP_BUTTON: |
|
1381 case NS_THEME_SPINNER_DOWN_BUTTON: |
|
1382 case NS_THEME_SPINNER_TEXTFIELD: |
|
1383 // case NS_THEME_SCROLLBAR: (n/a for gtk) |
|
1384 // case NS_THEME_SCROLLBAR_SMALL: (n/a for gtk) |
|
1385 case NS_THEME_SCROLLBAR_BUTTON_UP: |
|
1386 case NS_THEME_SCROLLBAR_BUTTON_DOWN: |
|
1387 case NS_THEME_SCROLLBAR_BUTTON_LEFT: |
|
1388 case NS_THEME_SCROLLBAR_BUTTON_RIGHT: |
|
1389 case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL: |
|
1390 case NS_THEME_SCROLLBAR_TRACK_VERTICAL: |
|
1391 case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL: |
|
1392 case NS_THEME_SCROLLBAR_THUMB_VERTICAL: |
|
1393 case NS_THEME_NUMBER_INPUT: |
|
1394 case NS_THEME_TEXTFIELD: |
|
1395 case NS_THEME_TEXTFIELD_MULTILINE: |
|
1396 case NS_THEME_DROPDOWN_TEXTFIELD: |
|
1397 case NS_THEME_RANGE: |
|
1398 case NS_THEME_RANGE_THUMB: |
|
1399 case NS_THEME_SCALE_HORIZONTAL: |
|
1400 case NS_THEME_SCALE_THUMB_HORIZONTAL: |
|
1401 case NS_THEME_SCALE_VERTICAL: |
|
1402 case NS_THEME_SCALE_THUMB_VERTICAL: |
|
1403 // case NS_THEME_SCALE_THUMB_START: |
|
1404 // case NS_THEME_SCALE_THUMB_END: |
|
1405 // case NS_THEME_SCALE_TICK: |
|
1406 case NS_THEME_CHECKBOX_CONTAINER: |
|
1407 case NS_THEME_RADIO_CONTAINER: |
|
1408 case NS_THEME_CHECKBOX_LABEL: |
|
1409 case NS_THEME_RADIO_LABEL: |
|
1410 case NS_THEME_MENUBAR: |
|
1411 case NS_THEME_MENUPOPUP: |
|
1412 case NS_THEME_MENUITEM: |
|
1413 case NS_THEME_MENUARROW: |
|
1414 case NS_THEME_MENUSEPARATOR: |
|
1415 case NS_THEME_CHECKMENUITEM: |
|
1416 case NS_THEME_RADIOMENUITEM: |
|
1417 case NS_THEME_SPLITTER: |
|
1418 case NS_THEME_WINDOW: |
|
1419 case NS_THEME_DIALOG: |
|
1420 case NS_THEME_DROPDOWN: |
|
1421 case NS_THEME_DROPDOWN_TEXT: |
|
1422 return !IsWidgetStyled(aPresContext, aFrame, aWidgetType); |
|
1423 |
|
1424 case NS_THEME_DROPDOWN_BUTTON: |
|
1425 // "Native" dropdown buttons cause padding and margin problems, but only |
|
1426 // in HTML so allow them in XUL. |
|
1427 return (!aFrame || IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XUL)) && |
|
1428 !IsWidgetStyled(aPresContext, aFrame, aWidgetType); |
|
1429 |
|
1430 } |
|
1431 |
|
1432 return false; |
|
1433 } |
|
1434 |
|
1435 NS_IMETHODIMP_(bool) |
|
1436 nsNativeThemeGTK::WidgetIsContainer(uint8_t aWidgetType) |
|
1437 { |
|
1438 // XXXdwh At some point flesh all of this out. |
|
1439 if (aWidgetType == NS_THEME_DROPDOWN_BUTTON || |
|
1440 aWidgetType == NS_THEME_RADIO || |
|
1441 aWidgetType == NS_THEME_RANGE_THUMB || |
|
1442 aWidgetType == NS_THEME_CHECKBOX || |
|
1443 aWidgetType == NS_THEME_TAB_SCROLLARROW_BACK || |
|
1444 aWidgetType == NS_THEME_TAB_SCROLLARROW_FORWARD || |
|
1445 aWidgetType == NS_THEME_BUTTON_ARROW_UP || |
|
1446 aWidgetType == NS_THEME_BUTTON_ARROW_DOWN || |
|
1447 aWidgetType == NS_THEME_BUTTON_ARROW_NEXT || |
|
1448 aWidgetType == NS_THEME_BUTTON_ARROW_PREVIOUS) |
|
1449 return false; |
|
1450 return true; |
|
1451 } |
|
1452 |
|
1453 bool |
|
1454 nsNativeThemeGTK::ThemeDrawsFocusForWidget(uint8_t aWidgetType) |
|
1455 { |
|
1456 if (aWidgetType == NS_THEME_DROPDOWN || |
|
1457 aWidgetType == NS_THEME_BUTTON || |
|
1458 aWidgetType == NS_THEME_TREEVIEW_HEADER_CELL) |
|
1459 return true; |
|
1460 |
|
1461 return false; |
|
1462 } |
|
1463 |
|
1464 bool |
|
1465 nsNativeThemeGTK::ThemeNeedsComboboxDropmarker() |
|
1466 { |
|
1467 return false; |
|
1468 } |
|
1469 |
|
1470 nsITheme::Transparency |
|
1471 nsNativeThemeGTK::GetWidgetTransparency(nsIFrame* aFrame, uint8_t aWidgetType) |
|
1472 { |
|
1473 switch (aWidgetType) { |
|
1474 // These widgets always draw a default background. |
|
1475 #if (MOZ_WIDGET_GTK == 2) |
|
1476 case NS_THEME_SCROLLBAR_TRACK_VERTICAL: |
|
1477 case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL: |
|
1478 case NS_THEME_TOOLBAR: |
|
1479 case NS_THEME_MENUBAR: |
|
1480 #endif |
|
1481 case NS_THEME_MENUPOPUP: |
|
1482 case NS_THEME_WINDOW: |
|
1483 case NS_THEME_DIALOG: |
|
1484 // Tooltips use gtk_paint_flat_box(). |
|
1485 case NS_THEME_TOOLTIP: |
|
1486 return eOpaque; |
|
1487 } |
|
1488 |
|
1489 return eUnknownTransparency; |
|
1490 } |