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/. */
6 #include "nsNativeThemeGTK.h"
7 #include "nsThemeConstants.h"
8 #include "gtkdrawing.h"
10 #include "nsIObserverService.h"
11 #include "nsIServiceManager.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"
21 #include "nsIDOMHTMLInputElement.h"
22 #include "nsRenderingContext.h"
23 #include "nsGkAtoms.h"
25 #include "mozilla/EventStates.h"
26 #include "mozilla/Services.h"
28 #include <gdk/gdkprivate.h>
31 #include "gfxContext.h"
32 #include "gfxPlatformGtk.h"
33 #include "gfxGdkNativeRenderer.h"
36 using namespace mozilla
;
38 NS_IMPL_ISUPPORTS_INHERITED(nsNativeThemeGTK
, nsNativeTheme
, nsITheme
,
41 static int gLastGdkError
;
43 nsNativeThemeGTK::nsNativeThemeGTK()
45 if (moz_gtk_init() != MOZ_GTK_SUCCESS
) {
46 memset(mDisabledWidgetTypes
, 0xff, sizeof(mDisabledWidgetTypes
));
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);
55 memset(mDisabledWidgetTypes
, 0, sizeof(mDisabledWidgetTypes
));
56 memset(mSafeWidgetStates
, 0, sizeof(mSafeWidgetStates
));
59 nsNativeThemeGTK::~nsNativeThemeGTK() {
63 nsNativeThemeGTK::Observe(nsISupports
*aSubject
, const char *aTopic
,
64 const char16_t
*aData
)
66 if (!nsCRT::strcmp(aTopic
, "xpcom-shutdown")) {
69 NS_NOTREACHED("unexpected topic");
70 return NS_ERROR_UNEXPECTED
;
77 nsNativeThemeGTK::RefreshWidgetWindow(nsIFrame
* aFrame
)
79 nsIPresShell
*shell
= GetPresShell(aFrame
);
83 nsViewManager
* vm
= shell
->GetViewManager();
87 vm
->InvalidateAllViews();
90 static bool IsFrameContentNodeInNamespace(nsIFrame
*aFrame
, uint32_t aNamespace
)
92 nsIContent
*content
= aFrame
? aFrame
->GetContent() : nullptr;
95 return content
->IsInNamespace(aNamespace
);
98 static bool IsWidgetTypeDisabled(uint8_t* aDisabledVector
, uint8_t aWidgetType
) {
99 return (aDisabledVector
[aWidgetType
>> 3] & (1 << (aWidgetType
& 7))) != 0;
102 static void SetWidgetTypeDisabled(uint8_t* aDisabledVector
, uint8_t aWidgetType
) {
103 aDisabledVector
[aWidgetType
>> 3] |= (1 << (aWidgetType
& 7));
106 static inline uint16_t
107 GetWidgetStateKey(uint8_t aWidgetType
, GtkWidgetState
*aWidgetState
)
109 return (aWidgetState
->active
|
110 aWidgetState
->focused
<< 1 |
111 aWidgetState
->inHover
<< 2 |
112 aWidgetState
->disabled
<< 3 |
113 aWidgetState
->isDefault
<< 4 |
117 static bool IsWidgetStateSafe(uint8_t* aSafeVector
,
119 GtkWidgetState
*aWidgetState
)
121 uint8_t key
= GetWidgetStateKey(aWidgetType
, aWidgetState
);
122 return (aSafeVector
[key
>> 3] & (1 << (key
& 7))) != 0;
125 static void SetWidgetStateSafe(uint8_t *aSafeVector
,
127 GtkWidgetState
*aWidgetState
)
129 uint8_t key
= GetWidgetStateKey(aWidgetType
, aWidgetState
);
130 aSafeVector
[key
>> 3] |= (1 << (key
& 7));
133 static GtkTextDirection
GetTextDirection(nsIFrame
* aFrame
)
136 return GTK_TEXT_DIR_NONE
;
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
;
145 return GTK_TEXT_DIR_NONE
;
148 // Returns positive for negative margins (otherwise 0).
150 nsNativeThemeGTK::GetTabMarginPixels(nsIFrame
* aFrame
)
153 IsBottomTab(aFrame
) ? aFrame
->GetUsedMargin().top
154 : aFrame
->GetUsedMargin().bottom
;
156 return std::min
<gint
>(MOZ_GTK_TAB_MARGIN_MASK
,
158 aFrame
->PresContext()->AppUnitsToDevPixels(-margin
)));
162 nsNativeThemeGTK::GetGtkWidgetAndState(uint8_t aWidgetType
, nsIFrame
* aFrame
,
163 GtkThemeWidgetType
& aGtkWidgetType
,
164 GtkWidgetState
* aState
,
169 // reset the entire struct to zero
170 memset(aState
, 0, sizeof(GtkWidgetState
));
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
)) {
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();
188 // GetContentState knows to look one frame up for radio/checkbox
189 // widgets, so don't adjust stateFrame here.
190 aFrame
= aFrame
->GetParent();
194 atom
= (aWidgetType
== NS_THEME_CHECKBOX
||
195 aWidgetType
== NS_THEME_CHECKBOX_LABEL
) ? nsGkAtoms::checked
196 : nsGkAtoms::selected
;
198 *aWidgetFlags
= CheckBooleanAttr(aFrame
, atom
);
202 nsCOMPtr
<nsIDOMHTMLInputElement
> inputElt(do_QueryInterface(aFrame
->GetContent()));
206 inputElt
->GetChecked(&isHTMLChecked
);
208 *aWidgetFlags
|= MOZ_GTK_WIDGET_CHECKED
;
211 if (GetIndeterminate(aFrame
))
212 *aWidgetFlags
|= MOZ_GTK_WIDGET_INCONSISTENT
;
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();
225 EventStates eventState
= GetContentState(stateFrame
, aWidgetType
);
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
;
235 if (aWidgetType
== NS_THEME_FOCUS_OUTLINE
) {
236 aState
->disabled
= FALSE
;
237 aState
->active
= FALSE
;
238 aState
->inHover
= FALSE
;
239 aState
->isDefault
= FALSE
;
240 aState
->canDefault
= FALSE
;
242 aState
->focused
= TRUE
;
243 aState
->depressed
= TRUE
; // see moz_gtk_entry_paint()
246 if (IsFrameContentNodeInNamespace(aFrame
, kNameSpaceID_XUL
)) {
247 // For these widget types, some element (either a child or parent)
248 // actually has element focus, so we check the focused attribute
249 // to see whether to draw in the focused state.
250 if (aWidgetType
== NS_THEME_NUMBER_INPUT
||
251 aWidgetType
== NS_THEME_TEXTFIELD
||
252 aWidgetType
== NS_THEME_TEXTFIELD_MULTILINE
||
253 aWidgetType
== NS_THEME_DROPDOWN_TEXTFIELD
||
254 aWidgetType
== NS_THEME_SPINNER_TEXTFIELD
||
255 aWidgetType
== NS_THEME_RADIO_CONTAINER
||
256 aWidgetType
== NS_THEME_RADIO_LABEL
) {
257 aState
->focused
= IsFocused(aFrame
);
258 } else if (aWidgetType
== NS_THEME_RADIO
||
259 aWidgetType
== NS_THEME_CHECKBOX
) {
260 // In XUL, checkboxes and radios shouldn't have focus rings, their labels do
261 aState
->focused
= FALSE
;
264 if (aWidgetType
== NS_THEME_SCROLLBAR_THUMB_VERTICAL
||
265 aWidgetType
== NS_THEME_SCROLLBAR_THUMB_HORIZONTAL
) {
266 // for scrollbars we need to go up two to go from the thumb to
267 // the slider to the actual scrollbar object
268 nsIFrame
*tmpFrame
= aFrame
->GetParent()->GetParent();
270 aState
->curpos
= CheckIntAttr(tmpFrame
, nsGkAtoms::curpos
, 0);
271 aState
->maxpos
= CheckIntAttr(tmpFrame
, nsGkAtoms::maxpos
, 100);
273 if (CheckBooleanAttr(aFrame
, nsGkAtoms::active
)) {
274 aState
->active
= TRUE
;
278 if (aWidgetType
== NS_THEME_SCROLLBAR_BUTTON_UP
||
279 aWidgetType
== NS_THEME_SCROLLBAR_BUTTON_DOWN
||
280 aWidgetType
== NS_THEME_SCROLLBAR_BUTTON_LEFT
||
281 aWidgetType
== NS_THEME_SCROLLBAR_BUTTON_RIGHT
) {
282 // set the state to disabled when the scrollbar is scrolled to
283 // the beginning or the end, depending on the button type.
284 int32_t curpos
= CheckIntAttr(aFrame
, nsGkAtoms::curpos
, 0);
285 int32_t maxpos
= CheckIntAttr(aFrame
, nsGkAtoms::maxpos
, 100);
286 if ((curpos
== 0 && (aWidgetType
== NS_THEME_SCROLLBAR_BUTTON_UP
||
287 aWidgetType
== NS_THEME_SCROLLBAR_BUTTON_LEFT
)) ||
289 (aWidgetType
== NS_THEME_SCROLLBAR_BUTTON_DOWN
||
290 aWidgetType
== NS_THEME_SCROLLBAR_BUTTON_RIGHT
)))
291 aState
->disabled
= true;
293 // In order to simulate native GTK scrollbar click behavior,
294 // we set the active attribute on the element to true if it's
295 // pressed with any mouse button.
296 // This allows us to show that it's active without setting :active
297 else if (CheckBooleanAttr(aFrame
, nsGkAtoms::active
))
298 aState
->active
= true;
301 *aWidgetFlags
= GetScrollbarButtonType(aFrame
);
302 if (aWidgetType
- NS_THEME_SCROLLBAR_BUTTON_UP
< 2)
303 *aWidgetFlags
|= MOZ_GTK_STEPPER_VERTICAL
;
307 // menu item state is determined by the attribute "_moz-menuactive",
308 // and not by the mouse hovering (accessibility). as a special case,
309 // menus which are children of a menu bar are only marked as prelight
310 // if they are open, not on normal hover.
312 if (aWidgetType
== NS_THEME_MENUITEM
||
313 aWidgetType
== NS_THEME_CHECKMENUITEM
||
314 aWidgetType
== NS_THEME_RADIOMENUITEM
||
315 aWidgetType
== NS_THEME_MENUSEPARATOR
||
316 aWidgetType
== NS_THEME_MENUARROW
) {
317 bool isTopLevel
= false;
318 nsMenuFrame
*menuFrame
= do_QueryFrame(aFrame
);
320 isTopLevel
= menuFrame
->IsOnMenuBar();
324 aState
->inHover
= menuFrame
->IsOpen();
325 *aWidgetFlags
|= MOZ_TOPLEVEL_MENU_ITEM
;
327 aState
->inHover
= CheckBooleanAttr(aFrame
, nsGkAtoms::menuactive
);
328 *aWidgetFlags
&= ~MOZ_TOPLEVEL_MENU_ITEM
;
331 aState
->active
= FALSE
;
333 if (aWidgetType
== NS_THEME_CHECKMENUITEM
||
334 aWidgetType
== NS_THEME_RADIOMENUITEM
) {
336 if (aFrame
&& aFrame
->GetContent()) {
337 *aWidgetFlags
= aFrame
->GetContent()->
338 AttrValueIs(kNameSpaceID_None
, nsGkAtoms::checked
,
339 nsGkAtoms::_true
, eIgnoreCase
);
344 // A button with drop down menu open or an activated toggle button
345 // should always appear depressed.
346 if (aWidgetType
== NS_THEME_BUTTON
||
347 aWidgetType
== NS_THEME_TOOLBAR_BUTTON
||
348 aWidgetType
== NS_THEME_TOOLBAR_DUAL_BUTTON
||
349 aWidgetType
== NS_THEME_TOOLBAR_BUTTON_DROPDOWN
||
350 aWidgetType
== NS_THEME_DROPDOWN
||
351 aWidgetType
== NS_THEME_DROPDOWN_BUTTON
) {
352 bool menuOpen
= IsOpenButton(aFrame
);
353 aState
->depressed
= IsCheckedButton(aFrame
) || menuOpen
;
354 // we must not highlight buttons with open drop down menus on hover.
355 aState
->inHover
= aState
->inHover
&& !menuOpen
;
358 // When the input field of the drop down button has focus, some themes
359 // should draw focus for the drop down button as well.
360 if (aWidgetType
== NS_THEME_DROPDOWN_BUTTON
&& aWidgetFlags
) {
361 *aWidgetFlags
= CheckBooleanAttr(aFrame
, nsGkAtoms::parentfocused
);
367 switch (aWidgetType
) {
368 case NS_THEME_BUTTON
:
369 case NS_THEME_TOOLBAR_BUTTON
:
370 case NS_THEME_TOOLBAR_DUAL_BUTTON
:
372 *aWidgetFlags
= (aWidgetType
== NS_THEME_BUTTON
) ? GTK_RELIEF_NORMAL
: GTK_RELIEF_NONE
;
373 aGtkWidgetType
= MOZ_GTK_BUTTON
;
375 case NS_THEME_FOCUS_OUTLINE
:
376 aGtkWidgetType
= MOZ_GTK_ENTRY
;
378 case NS_THEME_CHECKBOX
:
380 aGtkWidgetType
= (aWidgetType
== NS_THEME_RADIO
) ? MOZ_GTK_RADIOBUTTON
: MOZ_GTK_CHECKBUTTON
;
382 case NS_THEME_SCROLLBAR_BUTTON_UP
:
383 case NS_THEME_SCROLLBAR_BUTTON_DOWN
:
384 case NS_THEME_SCROLLBAR_BUTTON_LEFT
:
385 case NS_THEME_SCROLLBAR_BUTTON_RIGHT
:
386 aGtkWidgetType
= MOZ_GTK_SCROLLBAR_BUTTON
;
388 case NS_THEME_SCROLLBAR_TRACK_VERTICAL
:
389 aGtkWidgetType
= MOZ_GTK_SCROLLBAR_TRACK_VERTICAL
;
391 case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL
:
392 aGtkWidgetType
= MOZ_GTK_SCROLLBAR_TRACK_HORIZONTAL
;
394 case NS_THEME_SCROLLBAR_THUMB_VERTICAL
:
395 aGtkWidgetType
= MOZ_GTK_SCROLLBAR_THUMB_VERTICAL
;
397 case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL
:
398 aGtkWidgetType
= MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL
;
400 case NS_THEME_SPINNER
:
401 aGtkWidgetType
= MOZ_GTK_SPINBUTTON
;
403 case NS_THEME_SPINNER_UP_BUTTON
:
404 aGtkWidgetType
= MOZ_GTK_SPINBUTTON_UP
;
406 case NS_THEME_SPINNER_DOWN_BUTTON
:
407 aGtkWidgetType
= MOZ_GTK_SPINBUTTON_DOWN
;
409 case NS_THEME_SPINNER_TEXTFIELD
:
410 aGtkWidgetType
= MOZ_GTK_SPINBUTTON_ENTRY
;
414 if (IsRangeHorizontal(aFrame
)) {
416 *aWidgetFlags
= GTK_ORIENTATION_HORIZONTAL
;
417 aGtkWidgetType
= MOZ_GTK_SCALE_HORIZONTAL
;
420 *aWidgetFlags
= GTK_ORIENTATION_VERTICAL
;
421 aGtkWidgetType
= MOZ_GTK_SCALE_VERTICAL
;
425 case NS_THEME_RANGE_THUMB
:
427 if (IsRangeHorizontal(aFrame
)) {
429 *aWidgetFlags
= GTK_ORIENTATION_HORIZONTAL
;
430 aGtkWidgetType
= MOZ_GTK_SCALE_THUMB_HORIZONTAL
;
433 *aWidgetFlags
= GTK_ORIENTATION_VERTICAL
;
434 aGtkWidgetType
= MOZ_GTK_SCALE_THUMB_VERTICAL
;
438 case NS_THEME_SCALE_HORIZONTAL
:
440 *aWidgetFlags
= GTK_ORIENTATION_HORIZONTAL
;
441 aGtkWidgetType
= MOZ_GTK_SCALE_HORIZONTAL
;
443 case NS_THEME_SCALE_THUMB_HORIZONTAL
:
445 *aWidgetFlags
= GTK_ORIENTATION_HORIZONTAL
;
446 aGtkWidgetType
= MOZ_GTK_SCALE_THUMB_HORIZONTAL
;
448 case NS_THEME_SCALE_VERTICAL
:
450 *aWidgetFlags
= GTK_ORIENTATION_VERTICAL
;
451 aGtkWidgetType
= MOZ_GTK_SCALE_VERTICAL
;
453 case NS_THEME_TOOLBAR_SEPARATOR
:
454 aGtkWidgetType
= MOZ_GTK_TOOLBAR_SEPARATOR
;
456 case NS_THEME_SCALE_THUMB_VERTICAL
:
458 *aWidgetFlags
= GTK_ORIENTATION_VERTICAL
;
459 aGtkWidgetType
= MOZ_GTK_SCALE_THUMB_VERTICAL
;
461 case NS_THEME_TOOLBAR_GRIPPER
:
462 aGtkWidgetType
= MOZ_GTK_GRIPPER
;
464 case NS_THEME_RESIZER
:
465 aGtkWidgetType
= MOZ_GTK_RESIZER
;
467 case NS_THEME_NUMBER_INPUT
:
468 case NS_THEME_TEXTFIELD
:
469 case NS_THEME_TEXTFIELD_MULTILINE
:
470 aGtkWidgetType
= MOZ_GTK_ENTRY
;
472 case NS_THEME_LISTBOX
:
473 case NS_THEME_TREEVIEW
:
474 aGtkWidgetType
= MOZ_GTK_TREEVIEW
;
476 case NS_THEME_TREEVIEW_HEADER_CELL
:
478 // In this case, the flag denotes whether the header is the sorted one or not
479 if (GetTreeSortDirection(aFrame
) == eTreeSortDirection_Natural
)
480 *aWidgetFlags
= false;
482 *aWidgetFlags
= true;
484 aGtkWidgetType
= MOZ_GTK_TREE_HEADER_CELL
;
486 case NS_THEME_TREEVIEW_HEADER_SORTARROW
:
488 switch (GetTreeSortDirection(aFrame
)) {
489 case eTreeSortDirection_Ascending
:
490 *aWidgetFlags
= GTK_ARROW_DOWN
;
492 case eTreeSortDirection_Descending
:
493 *aWidgetFlags
= GTK_ARROW_UP
;
495 case eTreeSortDirection_Natural
:
497 /* This prevents the treecolums from getting smaller
498 * and wider when switching sort direction off and on
500 *aWidgetFlags
= GTK_ARROW_NONE
;
504 aGtkWidgetType
= MOZ_GTK_TREE_HEADER_SORTARROW
;
506 case NS_THEME_TREEVIEW_TWISTY
:
507 aGtkWidgetType
= MOZ_GTK_TREEVIEW_EXPANDER
;
509 *aWidgetFlags
= GTK_EXPANDER_COLLAPSED
;
511 case NS_THEME_TREEVIEW_TWISTY_OPEN
:
512 aGtkWidgetType
= MOZ_GTK_TREEVIEW_EXPANDER
;
514 *aWidgetFlags
= GTK_EXPANDER_EXPANDED
;
516 case NS_THEME_DROPDOWN
:
517 aGtkWidgetType
= MOZ_GTK_DROPDOWN
;
519 *aWidgetFlags
= IsFrameContentNodeInNamespace(aFrame
, kNameSpaceID_XHTML
);
521 case NS_THEME_DROPDOWN_TEXT
:
522 return false; // nothing to do, but prevents the bg from being drawn
523 case NS_THEME_DROPDOWN_TEXTFIELD
:
524 aGtkWidgetType
= MOZ_GTK_DROPDOWN_ENTRY
;
526 case NS_THEME_DROPDOWN_BUTTON
:
527 aGtkWidgetType
= MOZ_GTK_DROPDOWN_ARROW
;
529 case NS_THEME_TOOLBAR_BUTTON_DROPDOWN
:
530 case NS_THEME_BUTTON_ARROW_DOWN
:
531 case NS_THEME_BUTTON_ARROW_UP
:
532 case NS_THEME_BUTTON_ARROW_NEXT
:
533 case NS_THEME_BUTTON_ARROW_PREVIOUS
:
534 aGtkWidgetType
= MOZ_GTK_TOOLBARBUTTON_ARROW
;
536 *aWidgetFlags
= GTK_ARROW_DOWN
;
538 if (aWidgetType
== NS_THEME_BUTTON_ARROW_UP
)
539 *aWidgetFlags
= GTK_ARROW_UP
;
540 else if (aWidgetType
== NS_THEME_BUTTON_ARROW_NEXT
)
541 *aWidgetFlags
= GTK_ARROW_RIGHT
;
542 else if (aWidgetType
== NS_THEME_BUTTON_ARROW_PREVIOUS
)
543 *aWidgetFlags
= GTK_ARROW_LEFT
;
546 case NS_THEME_CHECKBOX_CONTAINER
:
547 aGtkWidgetType
= MOZ_GTK_CHECKBUTTON_CONTAINER
;
549 case NS_THEME_RADIO_CONTAINER
:
550 aGtkWidgetType
= MOZ_GTK_RADIOBUTTON_CONTAINER
;
552 case NS_THEME_CHECKBOX_LABEL
:
553 aGtkWidgetType
= MOZ_GTK_CHECKBUTTON_LABEL
;
555 case NS_THEME_RADIO_LABEL
:
556 aGtkWidgetType
= MOZ_GTK_RADIOBUTTON_LABEL
;
558 case NS_THEME_TOOLBAR
:
559 aGtkWidgetType
= MOZ_GTK_TOOLBAR
;
561 case NS_THEME_TOOLTIP
:
562 aGtkWidgetType
= MOZ_GTK_TOOLTIP
;
564 case NS_THEME_STATUSBAR_PANEL
:
565 case NS_THEME_STATUSBAR_RESIZER_PANEL
:
566 aGtkWidgetType
= MOZ_GTK_FRAME
;
568 case NS_THEME_PROGRESSBAR
:
569 case NS_THEME_PROGRESSBAR_VERTICAL
:
570 aGtkWidgetType
= MOZ_GTK_PROGRESSBAR
;
572 case NS_THEME_PROGRESSBAR_CHUNK
:
573 case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL
:
575 nsIFrame
* stateFrame
= aFrame
->GetParent();
576 EventStates eventStates
= GetContentState(stateFrame
, aWidgetType
);
578 aGtkWidgetType
= IsIndeterminateProgress(stateFrame
, eventStates
)
579 ? (stateFrame
->StyleDisplay()->mOrient
== NS_STYLE_ORIENT_VERTICAL
)
580 ? MOZ_GTK_PROGRESS_CHUNK_VERTICAL_INDETERMINATE
581 : MOZ_GTK_PROGRESS_CHUNK_INDETERMINATE
582 : MOZ_GTK_PROGRESS_CHUNK
;
585 case NS_THEME_TAB_SCROLLARROW_BACK
:
586 case NS_THEME_TAB_SCROLLARROW_FORWARD
:
588 *aWidgetFlags
= aWidgetType
== NS_THEME_TAB_SCROLLARROW_BACK
?
589 GTK_ARROW_LEFT
: GTK_ARROW_RIGHT
;
590 aGtkWidgetType
= MOZ_GTK_TAB_SCROLLARROW
;
592 case NS_THEME_TAB_PANELS
:
593 aGtkWidgetType
= MOZ_GTK_TABPANELS
;
598 /* First bits will be used to store max(0,-bmargin) where bmargin
599 * is the bottom margin of the tab in pixels (resp. top margin,
600 * for bottom tabs). */
601 if (IsBottomTab(aFrame
)) {
602 *aWidgetFlags
= MOZ_GTK_TAB_BOTTOM
;
607 *aWidgetFlags
|= GetTabMarginPixels(aFrame
);
609 if (IsSelectedTab(aFrame
))
610 *aWidgetFlags
|= MOZ_GTK_TAB_SELECTED
;
612 if (IsFirstTab(aFrame
))
613 *aWidgetFlags
|= MOZ_GTK_TAB_FIRST
;
616 aGtkWidgetType
= MOZ_GTK_TAB
;
619 case NS_THEME_SPLITTER
:
620 if (IsHorizontal(aFrame
))
621 aGtkWidgetType
= MOZ_GTK_SPLITTER_VERTICAL
;
623 aGtkWidgetType
= MOZ_GTK_SPLITTER_HORIZONTAL
;
625 case NS_THEME_MENUBAR
:
626 aGtkWidgetType
= MOZ_GTK_MENUBAR
;
628 case NS_THEME_MENUPOPUP
:
629 aGtkWidgetType
= MOZ_GTK_MENUPOPUP
;
631 case NS_THEME_MENUITEM
:
632 aGtkWidgetType
= MOZ_GTK_MENUITEM
;
634 case NS_THEME_MENUSEPARATOR
:
635 aGtkWidgetType
= MOZ_GTK_MENUSEPARATOR
;
637 case NS_THEME_MENUARROW
:
638 aGtkWidgetType
= MOZ_GTK_MENUARROW
;
640 case NS_THEME_CHECKMENUITEM
:
641 aGtkWidgetType
= MOZ_GTK_CHECKMENUITEM
;
643 case NS_THEME_RADIOMENUITEM
:
644 aGtkWidgetType
= MOZ_GTK_RADIOMENUITEM
;
646 case NS_THEME_WINDOW
:
647 case NS_THEME_DIALOG
:
648 aGtkWidgetType
= MOZ_GTK_WINDOW
;
657 #if (MOZ_WIDGET_GTK == 2)
658 class ThemeRenderer
: public gfxGdkNativeRenderer
{
660 ThemeRenderer(GtkWidgetState aState
, GtkThemeWidgetType aGTKWidgetType
,
661 gint aFlags
, GtkTextDirection aDirection
,
662 const GdkRectangle
& aGDKRect
, const GdkRectangle
& aGDKClip
)
663 : mState(aState
), mGTKWidgetType(aGTKWidgetType
), mFlags(aFlags
),
664 mDirection(aDirection
), mGDKRect(aGDKRect
), mGDKClip(aGDKClip
) {}
665 nsresult
DrawWithGDK(GdkDrawable
* drawable
, gint offsetX
, gint offsetY
,
666 GdkRectangle
* clipRects
, uint32_t numClipRects
);
668 GtkWidgetState mState
;
669 GtkThemeWidgetType mGTKWidgetType
;
671 GtkTextDirection mDirection
;
672 const GdkRectangle
& mGDKRect
;
673 const GdkRectangle
& mGDKClip
;
677 ThemeRenderer::DrawWithGDK(GdkDrawable
* drawable
, gint offsetX
,
678 gint offsetY
, GdkRectangle
* clipRects
, uint32_t numClipRects
)
680 GdkRectangle gdk_rect
= mGDKRect
;
681 gdk_rect
.x
+= offsetX
;
682 gdk_rect
.y
+= offsetY
;
684 GdkRectangle gdk_clip
= mGDKClip
;
685 gdk_clip
.x
+= offsetX
;
686 gdk_clip
.y
+= offsetY
;
688 GdkRectangle surfaceRect
;
691 gdk_drawable_get_size(drawable
, &surfaceRect
.width
, &surfaceRect
.height
);
692 gdk_rectangle_intersect(&gdk_clip
, &surfaceRect
, &gdk_clip
);
694 NS_ASSERTION(numClipRects
== 0, "We don't support clipping!!!");
695 moz_gtk_widget_paint(mGTKWidgetType
, drawable
, &gdk_rect
, &gdk_clip
,
696 &mState
, mFlags
, mDirection
);
703 nsNativeThemeGTK::GetExtraSizeForWidget(nsIFrame
* aFrame
, uint8_t aWidgetType
,
706 *aExtra
= nsIntMargin(0,0,0,0);
707 // Allow an extra one pixel above and below the thumb for certain
708 // GTK2 themes (Ximian Industrial, Bluecurve, Misty, at least);
709 // We modify the frame's overflow area. See bug 297508.
710 switch (aWidgetType
) {
711 case NS_THEME_SCROLLBAR_THUMB_VERTICAL
:
712 aExtra
->top
= aExtra
->bottom
= 1;
714 case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL
:
715 aExtra
->left
= aExtra
->right
= 1;
718 // Include the indicator spacing (the padding around the control).
719 case NS_THEME_CHECKBOX
:
722 gint indicator_size
, indicator_spacing
;
724 if (aWidgetType
== NS_THEME_CHECKBOX
) {
725 moz_gtk_checkbox_get_metrics(&indicator_size
, &indicator_spacing
);
727 moz_gtk_radio_get_metrics(&indicator_size
, &indicator_spacing
);
730 aExtra
->top
= indicator_spacing
;
731 aExtra
->right
= indicator_spacing
;
732 aExtra
->bottom
= indicator_spacing
;
733 aExtra
->left
= indicator_spacing
;
736 case NS_THEME_BUTTON
:
738 if (IsDefaultButton(aFrame
)) {
739 // Some themes draw a default indicator outside the widget,
740 // include that in overflow
741 gint top
, left
, bottom
, right
;
742 moz_gtk_button_get_default_overflow(&top
, &left
, &bottom
, &right
);
744 aExtra
->right
= right
;
745 aExtra
->bottom
= bottom
;
750 case NS_THEME_FOCUS_OUTLINE
:
752 moz_gtk_get_focus_outline_size(&aExtra
->left
, &aExtra
->top
);
753 aExtra
->right
= aExtra
->left
;
754 aExtra
->bottom
= aExtra
->top
;
759 if (!IsSelectedTab(aFrame
))
762 gint gap_height
= moz_gtk_get_tab_thickness();
764 int32_t extra
= gap_height
- GetTabMarginPixels(aFrame
);
768 if (IsBottomTab(aFrame
)) {
771 aExtra
->bottom
= extra
;
780 nsNativeThemeGTK::DrawWidgetBackground(nsRenderingContext
* aContext
,
784 const nsRect
& aDirtyRect
)
786 GtkWidgetState state
;
787 GtkThemeWidgetType gtkWidgetType
;
788 GtkTextDirection direction
= GetTextDirection(aFrame
);
790 if (!GetGtkWidgetAndState(aWidgetType
, aFrame
, gtkWidgetType
, &state
,
794 gfxContext
* ctx
= aContext
->ThebesContext();
795 nsPresContext
*presContext
= aFrame
->PresContext();
797 gfxRect rect
= presContext
->AppUnitsToGfxUnits(aRect
);
798 gfxRect dirtyRect
= presContext
->AppUnitsToGfxUnits(aDirtyRect
);
800 // Align to device pixels where sensible
801 // to provide crisper and faster drawing.
802 // Don't snap if it's a non-unit scale factor. We're going to have to take
803 // slow paths then in any case.
804 bool snapXY
= ctx
->UserToDevicePixelSnapped(rect
);
806 // Leave rect in device coords but make dirtyRect consistent.
807 dirtyRect
= ctx
->UserToDevice(dirtyRect
);
810 // Translate the dirty rect so that it is wrt the widget top-left.
811 dirtyRect
.MoveBy(-rect
.TopLeft());
812 // Round out the dirty rect to gdk pixels to ensure that gtk draws
813 // enough pixels for interpolation to device pixels.
814 dirtyRect
.RoundOut();
816 // GTK themes can only draw an integer number of pixels
817 // (even when not snapped).
818 nsIntRect
widgetRect(0, 0, NS_lround(rect
.Width()), NS_lround(rect
.Height()));
819 nsIntRect
overflowRect(widgetRect
);
820 nsIntMargin extraSize
;
821 if (GetExtraSizeForWidget(aFrame
, aWidgetType
, &extraSize
)) {
822 overflowRect
.Inflate(extraSize
);
825 // This is the rectangle that will actually be drawn, in gdk pixels
826 nsIntRect
drawingRect(int32_t(dirtyRect
.X()),
827 int32_t(dirtyRect
.Y()),
828 int32_t(dirtyRect
.Width()),
829 int32_t(dirtyRect
.Height()));
830 if (widgetRect
.IsEmpty()
831 || !drawingRect
.IntersectRect(overflowRect
, drawingRect
))
834 // gdk rectangles are wrt the drawing rect.
836 GdkRectangle gdk_rect
= {-drawingRect
.x
, -drawingRect
.y
,
837 widgetRect
.width
, widgetRect
.height
};
839 // translate everything so (0,0) is the top left of the drawingRect
840 gfxContextAutoSaveRestore
autoSR(ctx
);
842 // Rects are in device coords.
843 ctx
->IdentityMatrix();
845 ctx
->Translate(rect
.TopLeft() + gfxPoint(drawingRect
.x
, drawingRect
.y
));
847 NS_ASSERTION(!IsWidgetTypeDisabled(mDisabledWidgetTypes
, aWidgetType
),
848 "Trying to render an unsafe widget!");
850 bool safeState
= IsWidgetStateSafe(mSafeWidgetStates
, aWidgetType
, &state
);
853 gdk_error_trap_push ();
856 #if (MOZ_WIDGET_GTK == 2)
857 // The gdk_clip is just advisory here, meaning "you don't
858 // need to draw outside this rect if you don't feel like it!"
859 GdkRectangle gdk_clip
= {0, 0, drawingRect
.width
, drawingRect
.height
};
861 ThemeRenderer
renderer(state
, gtkWidgetType
, flags
, direction
,
864 // Some themes (e.g. Clearlooks) just don't clip properly to any
865 // clip rect we provide, so we cannot advertise support for clipping within
866 // the widget bounds.
867 uint32_t rendererFlags
= 0;
868 if (GetWidgetTransparency(aFrame
, aWidgetType
) == eOpaque
) {
869 rendererFlags
|= gfxGdkNativeRenderer::DRAW_IS_OPAQUE
;
872 // GtkStyles (used by the widget drawing backend) are created for a
873 // particular colormap/visual.
874 GdkColormap
* colormap
= moz_gtk_widget_get_colormap();
876 renderer
.Draw(ctx
, drawingRect
.Size(), rendererFlags
, colormap
);
878 moz_gtk_widget_paint(gtkWidgetType
, ctx
->GetCairo(), &gdk_rect
,
879 &state
, flags
, direction
);
884 gLastGdkError
= gdk_error_trap_pop ();
888 printf("GTK theme failed for widget type %d, error was %d, state was "
889 "[active=%d,focused=%d,inHover=%d,disabled=%d]\n",
890 aWidgetType
, gLastGdkError
, state
.active
, state
.focused
,
891 state
.inHover
, state
.disabled
);
893 NS_WARNING("GTK theme failed; disabling unsafe widget");
894 SetWidgetTypeDisabled(mDisabledWidgetTypes
, aWidgetType
);
895 // force refresh of the window, because the widget was not
896 // successfully drawn it must be redrawn using the default look
897 RefreshWidgetWindow(aFrame
);
899 SetWidgetStateSafe(mSafeWidgetStates
, aWidgetType
, &state
);
903 // Indeterminate progress bar are animated.
904 if (gtkWidgetType
== MOZ_GTK_PROGRESS_CHUNK_INDETERMINATE
||
905 gtkWidgetType
== MOZ_GTK_PROGRESS_CHUNK_VERTICAL_INDETERMINATE
) {
906 if (!QueueAnimatedContentForRefresh(aFrame
->GetContent(), 30)) {
907 NS_WARNING("unable to animate widget!");
915 nsNativeThemeGTK::GetWidgetBorder(nsDeviceContext
* aContext
, nsIFrame
* aFrame
,
916 uint8_t aWidgetType
, nsIntMargin
* aResult
)
918 GtkTextDirection direction
= GetTextDirection(aFrame
);
919 aResult
->top
= aResult
->left
= aResult
->right
= aResult
->bottom
= 0;
920 switch (aWidgetType
) {
921 case NS_THEME_SCROLLBAR_TRACK_VERTICAL
:
922 case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL
:
924 MozGtkScrollbarMetrics metrics
;
925 moz_gtk_get_scrollbar_metrics(&metrics
);
926 aResult
->top
= aResult
->left
= aResult
->right
= aResult
->bottom
= metrics
.trough_border
;
929 case NS_THEME_TOOLBOX
:
930 // gtk has no toolbox equivalent. So, although we map toolbox to
931 // gtk's 'toolbar' for purposes of painting the widget background,
932 // we don't use the toolbar border for toolbox.
934 case NS_THEME_TOOLBAR_DUAL_BUTTON
:
935 // TOOLBAR_DUAL_BUTTON is an interesting case. We want a border to draw
936 // around the entire button + dropdown, and also an inner border if you're
937 // over the button part. But, we want the inner button to be right up
938 // against the edge of the outer button so that the borders overlap.
939 // To make this happen, we draw a button border for the outer button,
940 // but don't reserve any space for it.
943 // Top tabs have no bottom border, bottom tabs have no top border
944 moz_gtk_get_widget_border(MOZ_GTK_TAB
, &aResult
->left
, &aResult
->top
,
945 &aResult
->right
, &aResult
->bottom
, direction
,
947 if (IsBottomTab(aFrame
))
952 case NS_THEME_MENUITEM
:
953 case NS_THEME_CHECKMENUITEM
:
954 case NS_THEME_RADIOMENUITEM
:
955 // For regular menuitems, we will be using GetWidgetPadding instead of
956 // GetWidgetBorder to pad up the widget's internals; other menuitems
957 // will need to fall through and use the default case as before.
958 if (IsRegularMenuItem(aFrame
))
962 GtkThemeWidgetType gtkWidgetType
;
963 if (GetGtkWidgetAndState(aWidgetType
, aFrame
, gtkWidgetType
, nullptr,
965 moz_gtk_get_widget_border(gtkWidgetType
, &aResult
->left
, &aResult
->top
,
966 &aResult
->right
, &aResult
->bottom
, direction
,
967 IsFrameContentNodeInNamespace(aFrame
, kNameSpaceID_XHTML
));
975 nsNativeThemeGTK::GetWidgetPadding(nsDeviceContext
* aContext
,
976 nsIFrame
* aFrame
, uint8_t aWidgetType
,
977 nsIntMargin
* aResult
)
979 switch (aWidgetType
) {
980 case NS_THEME_BUTTON_FOCUS
:
981 case NS_THEME_TOOLBAR_BUTTON
:
982 case NS_THEME_TOOLBAR_DUAL_BUTTON
:
983 case NS_THEME_TAB_SCROLLARROW_BACK
:
984 case NS_THEME_TAB_SCROLLARROW_FORWARD
:
985 case NS_THEME_DROPDOWN_BUTTON
:
986 case NS_THEME_TOOLBAR_BUTTON_DROPDOWN
:
987 case NS_THEME_BUTTON_ARROW_UP
:
988 case NS_THEME_BUTTON_ARROW_DOWN
:
989 case NS_THEME_BUTTON_ARROW_NEXT
:
990 case NS_THEME_BUTTON_ARROW_PREVIOUS
:
991 case NS_THEME_RANGE_THUMB
:
992 // Radios and checkboxes return a fixed size in GetMinimumWidgetSize
993 // and have a meaningful baseline, so they can't have
994 // author-specified padding.
995 case NS_THEME_CHECKBOX
:
997 aResult
->SizeTo(0, 0, 0, 0);
999 case NS_THEME_MENUITEM
:
1000 case NS_THEME_CHECKMENUITEM
:
1001 case NS_THEME_RADIOMENUITEM
:
1003 // Menubar and menulist have their padding specified in CSS.
1004 if (!IsRegularMenuItem(aFrame
))
1007 aResult
->SizeTo(0, 0, 0, 0);
1008 GtkThemeWidgetType gtkWidgetType
;
1009 if (GetGtkWidgetAndState(aWidgetType
, aFrame
, gtkWidgetType
, nullptr,
1011 moz_gtk_get_widget_border(gtkWidgetType
, &aResult
->left
, &aResult
->top
,
1012 &aResult
->right
, &aResult
->bottom
, GetTextDirection(aFrame
),
1013 IsFrameContentNodeInNamespace(aFrame
, kNameSpaceID_XHTML
));
1016 gint horizontal_padding
;
1018 if (aWidgetType
== NS_THEME_MENUITEM
)
1019 moz_gtk_menuitem_get_horizontal_padding(&horizontal_padding
);
1021 moz_gtk_checkmenuitem_get_horizontal_padding(&horizontal_padding
);
1023 aResult
->left
+= horizontal_padding
;
1024 aResult
->right
+= horizontal_padding
;
1034 nsNativeThemeGTK::GetWidgetOverflow(nsDeviceContext
* aContext
,
1035 nsIFrame
* aFrame
, uint8_t aWidgetType
,
1036 nsRect
* aOverflowRect
)
1038 nsIntMargin extraSize
;
1039 if (!GetExtraSizeForWidget(aFrame
, aWidgetType
, &extraSize
))
1042 int32_t p2a
= aContext
->AppUnitsPerDevPixel();
1043 nsMargin
m(NSIntPixelsToAppUnits(extraSize
.top
, p2a
),
1044 NSIntPixelsToAppUnits(extraSize
.right
, p2a
),
1045 NSIntPixelsToAppUnits(extraSize
.bottom
, p2a
),
1046 NSIntPixelsToAppUnits(extraSize
.left
, p2a
));
1048 aOverflowRect
->Inflate(m
);
1053 nsNativeThemeGTK::GetMinimumWidgetSize(nsPresContext
* aPresContext
,
1054 nsIFrame
* aFrame
, uint8_t aWidgetType
,
1055 nsIntSize
* aResult
, bool* aIsOverridable
)
1057 aResult
->width
= aResult
->height
= 0;
1058 *aIsOverridable
= true;
1060 switch (aWidgetType
) {
1061 case NS_THEME_SCROLLBAR_BUTTON_UP
:
1062 case NS_THEME_SCROLLBAR_BUTTON_DOWN
:
1064 MozGtkScrollbarMetrics metrics
;
1065 moz_gtk_get_scrollbar_metrics(&metrics
);
1067 aResult
->width
= metrics
.slider_width
;
1068 aResult
->height
= metrics
.stepper_size
;
1069 *aIsOverridable
= false;
1072 case NS_THEME_SCROLLBAR_BUTTON_LEFT
:
1073 case NS_THEME_SCROLLBAR_BUTTON_RIGHT
:
1075 MozGtkScrollbarMetrics metrics
;
1076 moz_gtk_get_scrollbar_metrics(&metrics
);
1078 aResult
->width
= metrics
.stepper_size
;
1079 aResult
->height
= metrics
.slider_width
;
1080 *aIsOverridable
= false;
1083 case NS_THEME_SPLITTER
:
1086 if (IsHorizontal(aFrame
)) {
1087 moz_gtk_splitter_get_metrics(GTK_ORIENTATION_HORIZONTAL
, &metrics
);
1088 aResult
->width
= metrics
;
1089 aResult
->height
= 0;
1091 moz_gtk_splitter_get_metrics(GTK_ORIENTATION_VERTICAL
, &metrics
);
1093 aResult
->height
= metrics
;
1095 *aIsOverridable
= false;
1098 case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL
:
1099 case NS_THEME_SCROLLBAR_TRACK_VERTICAL
:
1101 /* While we enforce a minimum size for the thumb, this is ignored
1102 * for the some scrollbars if buttons are hidden (bug 513006) because
1103 * the thumb isn't a direct child of the scrollbar, unlike the buttons
1104 * or track. So add a minimum size to the track as well to prevent a
1105 * 0-width scrollbar. */
1106 MozGtkScrollbarMetrics metrics
;
1107 moz_gtk_get_scrollbar_metrics(&metrics
);
1109 if (aWidgetType
== NS_THEME_SCROLLBAR_TRACK_VERTICAL
)
1110 aResult
->width
= metrics
.slider_width
+ 2 * metrics
.trough_border
;
1112 aResult
->height
= metrics
.slider_width
+ 2 * metrics
.trough_border
;
1114 *aIsOverridable
= false;
1117 case NS_THEME_SCROLLBAR_THUMB_VERTICAL
:
1118 case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL
:
1120 MozGtkScrollbarMetrics metrics
;
1121 moz_gtk_get_scrollbar_metrics(&metrics
);
1123 nsRect rect
= aFrame
->GetParent()->GetRect();
1124 int32_t p2a
= aFrame
->PresContext()->DeviceContext()->
1125 AppUnitsPerDevPixel();
1128 /* Get the available space, if that is smaller then the minimum size,
1129 * adjust the mininum size to fit into it.
1130 * Setting aIsOverridable to true has no effect for thumbs. */
1131 aFrame
->GetMargin(margin
);
1132 rect
.Deflate(margin
);
1133 aFrame
->GetParent()->GetBorderAndPadding(margin
);
1134 rect
.Deflate(margin
);
1136 if (aWidgetType
== NS_THEME_SCROLLBAR_THUMB_VERTICAL
) {
1137 aResult
->width
= metrics
.slider_width
;
1138 aResult
->height
= std::min(NSAppUnitsToIntPixels(rect
.height
, p2a
),
1139 metrics
.min_slider_size
);
1141 aResult
->height
= metrics
.slider_width
;
1142 aResult
->width
= std::min(NSAppUnitsToIntPixels(rect
.width
, p2a
),
1143 metrics
.min_slider_size
);
1146 *aIsOverridable
= false;
1149 case NS_THEME_RANGE_THUMB
:
1151 gint thumb_length
, thumb_height
;
1153 if (IsRangeHorizontal(aFrame
)) {
1154 moz_gtk_get_scalethumb_metrics(GTK_ORIENTATION_HORIZONTAL
, &thumb_length
, &thumb_height
);
1156 moz_gtk_get_scalethumb_metrics(GTK_ORIENTATION_VERTICAL
, &thumb_height
, &thumb_length
);
1158 aResult
->width
= thumb_length
;
1159 aResult
->height
= thumb_height
;
1161 *aIsOverridable
= false;
1164 case NS_THEME_SCALE_THUMB_HORIZONTAL
:
1165 case NS_THEME_SCALE_THUMB_VERTICAL
:
1167 gint thumb_length
, thumb_height
;
1169 if (aWidgetType
== NS_THEME_SCALE_THUMB_VERTICAL
) {
1170 moz_gtk_get_scalethumb_metrics(GTK_ORIENTATION_VERTICAL
, &thumb_length
, &thumb_height
);
1171 aResult
->width
= thumb_height
;
1172 aResult
->height
= thumb_length
;
1174 moz_gtk_get_scalethumb_metrics(GTK_ORIENTATION_HORIZONTAL
, &thumb_length
, &thumb_height
);
1175 aResult
->width
= thumb_length
;
1176 aResult
->height
= thumb_height
;
1179 *aIsOverridable
= false;
1182 case NS_THEME_TAB_SCROLLARROW_BACK
:
1183 case NS_THEME_TAB_SCROLLARROW_FORWARD
:
1185 moz_gtk_get_tab_scroll_arrow_size(&aResult
->width
, &aResult
->height
);
1186 *aIsOverridable
= false;
1189 case NS_THEME_DROPDOWN_BUTTON
:
1191 moz_gtk_get_combo_box_entry_button_size(&aResult
->width
,
1193 *aIsOverridable
= false;
1196 case NS_THEME_MENUSEPARATOR
:
1198 gint separator_height
;
1200 moz_gtk_get_menu_separator_height(&separator_height
);
1201 aResult
->height
= separator_height
;
1203 *aIsOverridable
= false;
1206 case NS_THEME_CHECKBOX
:
1207 case NS_THEME_RADIO
:
1209 gint indicator_size
, indicator_spacing
;
1211 if (aWidgetType
== NS_THEME_CHECKBOX
) {
1212 moz_gtk_checkbox_get_metrics(&indicator_size
, &indicator_spacing
);
1214 moz_gtk_radio_get_metrics(&indicator_size
, &indicator_spacing
);
1217 // Include space for the indicator and the padding around it.
1218 aResult
->width
= indicator_size
;
1219 aResult
->height
= indicator_size
;
1222 case NS_THEME_TOOLBAR_BUTTON_DROPDOWN
:
1223 case NS_THEME_BUTTON_ARROW_UP
:
1224 case NS_THEME_BUTTON_ARROW_DOWN
:
1225 case NS_THEME_BUTTON_ARROW_NEXT
:
1226 case NS_THEME_BUTTON_ARROW_PREVIOUS
:
1228 moz_gtk_get_arrow_size(&aResult
->width
, &aResult
->height
);
1229 *aIsOverridable
= false;
1232 case NS_THEME_CHECKBOX_CONTAINER
:
1233 case NS_THEME_RADIO_CONTAINER
:
1234 case NS_THEME_CHECKBOX_LABEL
:
1235 case NS_THEME_RADIO_LABEL
:
1236 case NS_THEME_BUTTON
:
1237 case NS_THEME_DROPDOWN
:
1238 case NS_THEME_TOOLBAR_BUTTON
:
1239 case NS_THEME_TREEVIEW_HEADER_CELL
:
1241 // Just include our border, and let the box code augment the size.
1243 nsNativeThemeGTK::GetWidgetBorder(aFrame
->PresContext()->DeviceContext(),
1244 aFrame
, aWidgetType
, &border
);
1245 aResult
->width
= border
.left
+ border
.right
;
1246 aResult
->height
= border
.top
+ border
.bottom
;
1249 case NS_THEME_TOOLBAR_SEPARATOR
:
1251 gint separator_width
;
1253 moz_gtk_get_toolbar_separator_width(&separator_width
);
1255 aResult
->width
= separator_width
;
1258 case NS_THEME_SPINNER
:
1259 // hard code these sizes
1260 aResult
->width
= 14;
1261 aResult
->height
= 26;
1263 case NS_THEME_TREEVIEW_HEADER_SORTARROW
:
1264 case NS_THEME_SPINNER_UP_BUTTON
:
1265 case NS_THEME_SPINNER_DOWN_BUTTON
:
1266 // hard code these sizes
1267 aResult
->width
= 14;
1268 aResult
->height
= 13;
1270 case NS_THEME_RESIZER
:
1271 // same as Windows to make our lives easier
1272 aResult
->width
= aResult
->height
= 15;
1273 *aIsOverridable
= false;
1275 case NS_THEME_TREEVIEW_TWISTY
:
1276 case NS_THEME_TREEVIEW_TWISTY_OPEN
:
1280 moz_gtk_get_treeview_expander_size(&expander_size
);
1281 aResult
->width
= aResult
->height
= expander_size
;
1282 *aIsOverridable
= false;
1290 nsNativeThemeGTK::WidgetStateChanged(nsIFrame
* aFrame
, uint8_t aWidgetType
,
1291 nsIAtom
* aAttribute
, bool* aShouldRepaint
)
1293 // Some widget types just never change state.
1294 if (aWidgetType
== NS_THEME_TOOLBOX
||
1295 aWidgetType
== NS_THEME_TOOLBAR
||
1296 aWidgetType
== NS_THEME_STATUSBAR
||
1297 aWidgetType
== NS_THEME_STATUSBAR_PANEL
||
1298 aWidgetType
== NS_THEME_STATUSBAR_RESIZER_PANEL
||
1299 aWidgetType
== NS_THEME_PROGRESSBAR_CHUNK
||
1300 aWidgetType
== NS_THEME_PROGRESSBAR_CHUNK_VERTICAL
||
1301 aWidgetType
== NS_THEME_PROGRESSBAR
||
1302 aWidgetType
== NS_THEME_PROGRESSBAR_VERTICAL
||
1303 aWidgetType
== NS_THEME_MENUBAR
||
1304 aWidgetType
== NS_THEME_MENUPOPUP
||
1305 aWidgetType
== NS_THEME_TOOLTIP
||
1306 aWidgetType
== NS_THEME_MENUSEPARATOR
||
1307 aWidgetType
== NS_THEME_WINDOW
||
1308 aWidgetType
== NS_THEME_DIALOG
) {
1309 *aShouldRepaint
= false;
1313 if ((aWidgetType
== NS_THEME_SCROLLBAR_THUMB_VERTICAL
||
1314 aWidgetType
== NS_THEME_SCROLLBAR_THUMB_HORIZONTAL
) &&
1315 aAttribute
== nsGkAtoms::active
) {
1316 *aShouldRepaint
= true;
1320 if ((aWidgetType
== NS_THEME_SCROLLBAR_BUTTON_UP
||
1321 aWidgetType
== NS_THEME_SCROLLBAR_BUTTON_DOWN
||
1322 aWidgetType
== NS_THEME_SCROLLBAR_BUTTON_LEFT
||
1323 aWidgetType
== NS_THEME_SCROLLBAR_BUTTON_RIGHT
) &&
1324 (aAttribute
== nsGkAtoms::curpos
||
1325 aAttribute
== nsGkAtoms::maxpos
)) {
1326 *aShouldRepaint
= true;
1330 // XXXdwh Not sure what can really be done here. Can at least guess for
1331 // specific widgets that they're highly unlikely to have certain states.
1332 // For example, a toolbar doesn't care about any states.
1334 // Hover/focus/active changed. Always repaint.
1335 *aShouldRepaint
= true;
1338 // Check the attribute to see if it's relevant.
1339 // disabled, checked, dlgtype, default, etc.
1340 *aShouldRepaint
= false;
1341 if (aAttribute
== nsGkAtoms::disabled
||
1342 aAttribute
== nsGkAtoms::checked
||
1343 aAttribute
== nsGkAtoms::selected
||
1344 aAttribute
== nsGkAtoms::focused
||
1345 aAttribute
== nsGkAtoms::readonly
||
1346 aAttribute
== nsGkAtoms::_default
||
1347 aAttribute
== nsGkAtoms::menuactive
||
1348 aAttribute
== nsGkAtoms::open
||
1349 aAttribute
== nsGkAtoms::parentfocused
)
1350 *aShouldRepaint
= true;
1357 nsNativeThemeGTK::ThemeChanged()
1359 memset(mDisabledWidgetTypes
, 0, sizeof(mDisabledWidgetTypes
));
1363 NS_IMETHODIMP_(bool)
1364 nsNativeThemeGTK::ThemeSupportsWidget(nsPresContext
* aPresContext
,
1366 uint8_t aWidgetType
)
1368 if (IsWidgetTypeDisabled(mDisabledWidgetTypes
, aWidgetType
))
1371 switch (aWidgetType
) {
1372 case NS_THEME_BUTTON
:
1373 case NS_THEME_BUTTON_FOCUS
:
1374 case NS_THEME_RADIO
:
1375 case NS_THEME_CHECKBOX
:
1376 case NS_THEME_TOOLBOX
: // N/A
1377 case NS_THEME_TOOLBAR
:
1378 case NS_THEME_TOOLBAR_BUTTON
:
1379 case NS_THEME_TOOLBAR_DUAL_BUTTON
: // so we can override the border with 0
1380 case NS_THEME_TOOLBAR_BUTTON_DROPDOWN
:
1381 case NS_THEME_BUTTON_ARROW_UP
:
1382 case NS_THEME_BUTTON_ARROW_DOWN
:
1383 case NS_THEME_BUTTON_ARROW_NEXT
:
1384 case NS_THEME_BUTTON_ARROW_PREVIOUS
:
1385 case NS_THEME_TOOLBAR_SEPARATOR
:
1386 case NS_THEME_TOOLBAR_GRIPPER
:
1387 case NS_THEME_STATUSBAR
:
1388 case NS_THEME_STATUSBAR_PANEL
:
1389 case NS_THEME_STATUSBAR_RESIZER_PANEL
:
1390 case NS_THEME_RESIZER
:
1391 case NS_THEME_LISTBOX
:
1392 // case NS_THEME_LISTBOX_LISTITEM:
1393 case NS_THEME_TREEVIEW
:
1394 // case NS_THEME_TREEVIEW_TREEITEM:
1395 case NS_THEME_TREEVIEW_TWISTY
:
1396 // case NS_THEME_TREEVIEW_LINE:
1397 // case NS_THEME_TREEVIEW_HEADER:
1398 case NS_THEME_TREEVIEW_HEADER_CELL
:
1399 case NS_THEME_TREEVIEW_HEADER_SORTARROW
:
1400 case NS_THEME_TREEVIEW_TWISTY_OPEN
:
1401 case NS_THEME_PROGRESSBAR
:
1402 case NS_THEME_PROGRESSBAR_CHUNK
:
1403 case NS_THEME_PROGRESSBAR_VERTICAL
:
1404 case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL
:
1406 // case NS_THEME_TAB_PANEL:
1407 case NS_THEME_TAB_PANELS
:
1408 case NS_THEME_TAB_SCROLLARROW_BACK
:
1409 case NS_THEME_TAB_SCROLLARROW_FORWARD
:
1410 case NS_THEME_TOOLTIP
:
1411 case NS_THEME_SPINNER
:
1412 case NS_THEME_SPINNER_UP_BUTTON
:
1413 case NS_THEME_SPINNER_DOWN_BUTTON
:
1414 case NS_THEME_SPINNER_TEXTFIELD
:
1415 // case NS_THEME_SCROLLBAR: (n/a for gtk)
1416 // case NS_THEME_SCROLLBAR_SMALL: (n/a for gtk)
1417 case NS_THEME_SCROLLBAR_BUTTON_UP
:
1418 case NS_THEME_SCROLLBAR_BUTTON_DOWN
:
1419 case NS_THEME_SCROLLBAR_BUTTON_LEFT
:
1420 case NS_THEME_SCROLLBAR_BUTTON_RIGHT
:
1421 case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL
:
1422 case NS_THEME_SCROLLBAR_TRACK_VERTICAL
:
1423 case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL
:
1424 case NS_THEME_SCROLLBAR_THUMB_VERTICAL
:
1425 case NS_THEME_NUMBER_INPUT
:
1426 case NS_THEME_TEXTFIELD
:
1427 case NS_THEME_TEXTFIELD_MULTILINE
:
1428 case NS_THEME_DROPDOWN_TEXTFIELD
:
1429 case NS_THEME_RANGE
:
1430 case NS_THEME_RANGE_THUMB
:
1431 case NS_THEME_SCALE_HORIZONTAL
:
1432 case NS_THEME_SCALE_THUMB_HORIZONTAL
:
1433 case NS_THEME_SCALE_VERTICAL
:
1434 case NS_THEME_SCALE_THUMB_VERTICAL
:
1435 // case NS_THEME_SCALE_THUMB_START:
1436 // case NS_THEME_SCALE_THUMB_END:
1437 // case NS_THEME_SCALE_TICK:
1438 case NS_THEME_CHECKBOX_CONTAINER
:
1439 case NS_THEME_RADIO_CONTAINER
:
1440 case NS_THEME_CHECKBOX_LABEL
:
1441 case NS_THEME_RADIO_LABEL
:
1442 case NS_THEME_MENUBAR
:
1443 case NS_THEME_MENUPOPUP
:
1444 case NS_THEME_MENUITEM
:
1445 case NS_THEME_MENUARROW
:
1446 case NS_THEME_MENUSEPARATOR
:
1447 case NS_THEME_CHECKMENUITEM
:
1448 case NS_THEME_RADIOMENUITEM
:
1449 case NS_THEME_SPLITTER
:
1450 case NS_THEME_WINDOW
:
1451 case NS_THEME_DIALOG
:
1452 case NS_THEME_DROPDOWN
:
1453 case NS_THEME_DROPDOWN_TEXT
:
1454 return !IsWidgetStyled(aPresContext
, aFrame
, aWidgetType
);
1456 case NS_THEME_DROPDOWN_BUTTON
:
1457 // "Native" dropdown buttons cause padding and margin problems, but only
1458 // in HTML so allow them in XUL.
1459 return (!aFrame
|| IsFrameContentNodeInNamespace(aFrame
, kNameSpaceID_XUL
)) &&
1460 !IsWidgetStyled(aPresContext
, aFrame
, aWidgetType
);
1462 case NS_THEME_FOCUS_OUTLINE
:
1469 NS_IMETHODIMP_(bool)
1470 nsNativeThemeGTK::WidgetIsContainer(uint8_t aWidgetType
)
1472 // XXXdwh At some point flesh all of this out.
1473 if (aWidgetType
== NS_THEME_DROPDOWN_BUTTON
||
1474 aWidgetType
== NS_THEME_RADIO
||
1475 aWidgetType
== NS_THEME_RANGE_THUMB
||
1476 aWidgetType
== NS_THEME_CHECKBOX
||
1477 aWidgetType
== NS_THEME_TAB_SCROLLARROW_BACK
||
1478 aWidgetType
== NS_THEME_TAB_SCROLLARROW_FORWARD
||
1479 aWidgetType
== NS_THEME_BUTTON_ARROW_UP
||
1480 aWidgetType
== NS_THEME_BUTTON_ARROW_DOWN
||
1481 aWidgetType
== NS_THEME_BUTTON_ARROW_NEXT
||
1482 aWidgetType
== NS_THEME_BUTTON_ARROW_PREVIOUS
)
1488 nsNativeThemeGTK::ThemeDrawsFocusForWidget(uint8_t aWidgetType
)
1490 if (aWidgetType
== NS_THEME_DROPDOWN
||
1491 aWidgetType
== NS_THEME_BUTTON
||
1492 aWidgetType
== NS_THEME_TREEVIEW_HEADER_CELL
)
1499 nsNativeThemeGTK::ThemeNeedsComboboxDropmarker()
1504 nsITheme::Transparency
1505 nsNativeThemeGTK::GetWidgetTransparency(nsIFrame
* aFrame
, uint8_t aWidgetType
)
1507 switch (aWidgetType
) {
1508 // These widgets always draw a default background.
1509 #if (MOZ_WIDGET_GTK == 2)
1510 case NS_THEME_SCROLLBAR_TRACK_VERTICAL
:
1511 case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL
:
1512 case NS_THEME_TOOLBAR
:
1513 case NS_THEME_MENUBAR
:
1515 case NS_THEME_MENUPOPUP
:
1516 case NS_THEME_WINDOW
:
1517 case NS_THEME_DIALOG
:
1518 // Tooltips use gtk_paint_flat_box().
1519 case NS_THEME_TOOLTIP
:
1523 return eUnknownTransparency
;