Revert Bug 1963446 - Unify button.css between Linux and Windows. r=desktop-theme-reviewers,dao

This reverts commit e35ce6ba6f.

This reverts commit cb24a82ae3.

This reverts commit 5cf47a8af2.

This reverts commit dc8cd0b077.

This reverts commit 00d023c923.
This commit is contained in:
Serban Stanca
2025-05-01 19:19:24 +03:00
committed by sstanca@mozilla.com
parent e6871be33e
commit 9b69fc90a0
14 changed files with 1030 additions and 71 deletions

View File

@@ -76,11 +76,12 @@
.expander-up > .button-box {
appearance: auto;
-moz-default-appearance: -moz-menulist-arrow-button;
-moz-default-appearance: button-arrow-up;
}
.expander-down > .button-box {
transform: scaleY(-1);
appearance: auto;
-moz-default-appearance: button-arrow-down;
}
/* Downloads pane */

View File

@@ -1498,6 +1498,18 @@ pub enum Appearance {
Textfield,
/// The dropdown button(s) that open up a dropdown list.
MenulistButton,
/// Various arrows that go in buttons
#[parse(condition = "ParserContext::chrome_rules_enabled")]
ButtonArrowDown,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
ButtonArrowNext,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
ButtonArrowPrevious,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
ButtonArrowUp,
/// A dual toolbar button (e.g., a Back button with a dropdown)
#[parse(condition = "ParserContext::chrome_rules_enabled")]
Dualbutton,
/// Menu Popup background.
#[parse(condition = "ParserContext::chrome_rules_enabled")]
Menupopup,
@@ -1572,6 +1584,9 @@ pub enum Appearance {
/// A single toolbar button (with no associated dropdown).
#[parse(condition = "ParserContext::chrome_rules_enabled")]
Toolbarbutton,
/// The dropdown portion of a toolbar button
#[parse(condition = "ParserContext::chrome_rules_enabled")]
ToolbarbuttonDropdown,
/// A tooltip.
#[parse(condition = "ParserContext::chrome_rules_enabled")]
Tooltip,

View File

@@ -0,0 +1,62 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* ===== button.css =====================================================
== Styles used by the XUL button element.
======================================================================= */
@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
/* :::::::::: button :::::::::: */
button {
margin: 1px 5px 2px;
min-width: 6.3em;
color: ButtonText;
text-shadow: none;
}
.button-text {
margin: 0;
margin-inline-start: 2px;
text-align: center;
}
/* .......... hover state .......... */
button:where(:hover:not([checked="true"])) {
color: -moz-buttonhovertext;
}
/* .......... active state .......... */
button:where(:hover:active, [open="true"]) {
color: -moz-buttonactivetext;
}
/* .......... disabled state .......... */
button:where([disabled="true"]) {
color: GrayText;
}
/* .......... focused state .......... */
button:where(:focus-visible) {
outline: auto;
}
/* ::::: menu buttons ::::: */
.button-menu-dropmarker {
appearance: auto;
-moz-default-appearance: toolbarbutton-dropdown;
}
/* ::::: plain buttons ::::: */
button.plain {
margin: 0 !important;
padding: 0 !important;
}

View File

@@ -6,6 +6,7 @@
toolkit.jar:
skin/classic/global/autocomplete.css
skin/classic/global/button.css
skin/classic/global/global.css
skin/classic/global/menulist.css
skin/classic/global/richlistbox.css

View File

@@ -10,7 +10,6 @@
#include desktop-jar.inc.mn
skin/classic/global/button.css (../../windows/global/button.css)
skin/classic/global/dialog.css (../../windows/global/dialog.css)
skin/classic/global/wizard.css (../../windows/global/wizard.css)

View File

@@ -49,10 +49,6 @@ input {
color: GrayText;
}
:host(:focus-within) {
outline: auto;
}
.textbox-search-icons {
align-items: center;
justify-items: center;

View File

@@ -29,8 +29,13 @@ button {
color: -moz-buttonactivetext;
}
&:where(:focus-visible) {
outline: auto;
&:focus-visible {
outline: var(--default-focusring);
outline-offset: calc(-1 * var(--default-focusring-width) - 2px);
@media (prefers-color-scheme: dark) {
outline: 1px auto;
outline-offset: initial;
}
}
&:where([disabled="true"]) {
@@ -48,3 +53,12 @@ button {
margin: 0;
text-align: center;
}
/* ::::: menu buttons ::::: */
.button-menu-dropmarker {
appearance: none;
content: url("chrome://global/skin/icons/arrow-down-12.svg");
-moz-context-properties: fill;
fill: currentColor;
}

View File

@@ -108,6 +108,220 @@ static GtkWidget* CreateButtonWidget() {
return widget;
}
static GtkWidget* CreateToggleButtonWidget() {
GtkWidget* widget = gtk_toggle_button_new();
AddToWindowContainer(widget);
return widget;
}
static GtkWidget* CreateButtonArrowWidget() {
GtkWidget* widget = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_OUT);
gtk_container_add(GTK_CONTAINER(GetWidget(MOZ_GTK_TOGGLE_BUTTON)), widget);
gtk_widget_show(widget);
return widget;
}
static GtkWidget* CreateEntryWidget() {
GtkWidget* widget = gtk_entry_new();
AddToWindowContainer(widget);
return widget;
}
static GtkWidget* CreateComboBoxWidget() {
GtkWidget* widget = gtk_combo_box_new();
AddToWindowContainer(widget);
return widget;
}
typedef struct {
GType type;
GtkWidget** widget;
} GtkInnerWidgetInfo;
static void GetInnerWidget(GtkWidget* widget, gpointer client_data) {
auto info = static_cast<GtkInnerWidgetInfo*>(client_data);
if (G_TYPE_CHECK_INSTANCE_TYPE(widget, info->type)) {
*info->widget = widget;
}
}
static GtkWidget* CreateComboBoxButtonWidget() {
GtkWidget* comboBox = GetWidget(MOZ_GTK_COMBOBOX);
GtkWidget* comboBoxButton = nullptr;
/* Get its inner Button */
GtkInnerWidgetInfo info = {GTK_TYPE_TOGGLE_BUTTON, &comboBoxButton};
gtk_container_forall(GTK_CONTAINER(comboBox), GetInnerWidget, &info);
if (!comboBoxButton) {
/* Shouldn't be reached with current internal gtk implementation; we
* use a generic toggle button as last resort fallback to avoid
* crashing. */
comboBoxButton = GetWidget(MOZ_GTK_TOGGLE_BUTTON);
} else {
/* We need to have pointers to the inner widgets (button, separator, arrow)
* of the ComboBox to get the correct rendering from theme engines which
* special cases their look. Since the inner layout can change, we ask GTK
* to NULL our pointers when they are about to become invalid because the
* corresponding widgets don't exist anymore. It's the role of
* g_object_add_weak_pointer().
* Note that if we don't find the inner widgets (which shouldn't happen), we
* fallback to use generic "non-inner" widgets, and they don't need that
* kind of weak pointer since they are explicit children of gProtoLayout and
* as such GTK holds a strong reference to them. */
g_object_add_weak_pointer(
G_OBJECT(comboBoxButton),
reinterpret_cast<gpointer*>(sWidgetStorage) + MOZ_GTK_COMBOBOX_BUTTON);
}
return comboBoxButton;
}
static GtkWidget* CreateComboBoxArrowWidget() {
GtkWidget* comboBoxButton = GetWidget(MOZ_GTK_COMBOBOX_BUTTON);
GtkWidget* comboBoxArrow = nullptr;
/* Get the widgets inside the Button */
GtkWidget* buttonChild = gtk_bin_get_child(GTK_BIN(comboBoxButton));
if (GTK_IS_BOX(buttonChild)) {
/* appears-as-list = FALSE, cell-view = TRUE; the button
* contains an hbox. This hbox is there because the ComboBox
* needs to place a cell renderer, a separator, and an arrow in
* the button when appears-as-list is FALSE. */
GtkInnerWidgetInfo info = {GTK_TYPE_ARROW, &comboBoxArrow};
gtk_container_forall(GTK_CONTAINER(buttonChild), GetInnerWidget, &info);
} else if (GTK_IS_ARROW(buttonChild)) {
/* appears-as-list = TRUE, or cell-view = FALSE;
* the button only contains an arrow */
comboBoxArrow = buttonChild;
}
if (!comboBoxArrow) {
/* Shouldn't be reached with current internal gtk implementation;
* we gButtonArrowWidget as last resort fallback to avoid
* crashing. */
comboBoxArrow = GetWidget(MOZ_GTK_BUTTON_ARROW);
} else {
g_object_add_weak_pointer(
G_OBJECT(comboBoxArrow),
reinterpret_cast<gpointer*>(sWidgetStorage) + MOZ_GTK_COMBOBOX_ARROW);
}
return comboBoxArrow;
}
static GtkWidget* CreateComboBoxSeparatorWidget() {
// Ensure to search for separator only once as it can fail
// TODO - it won't initialize after ResetWidgetCache() call
static bool isMissingSeparator = false;
if (isMissingSeparator) return nullptr;
/* Get the widgets inside the Button */
GtkWidget* comboBoxSeparator = nullptr;
GtkWidget* buttonChild =
gtk_bin_get_child(GTK_BIN(GetWidget(MOZ_GTK_COMBOBOX_BUTTON)));
if (GTK_IS_BOX(buttonChild)) {
/* appears-as-list = FALSE, cell-view = TRUE; the button
* contains an hbox. This hbox is there because the ComboBox
* needs to place a cell renderer, a separator, and an arrow in
* the button when appears-as-list is FALSE. */
GtkInnerWidgetInfo info = {GTK_TYPE_SEPARATOR, &comboBoxSeparator};
gtk_container_forall(GTK_CONTAINER(buttonChild), GetInnerWidget, &info);
}
if (comboBoxSeparator) {
g_object_add_weak_pointer(G_OBJECT(comboBoxSeparator),
reinterpret_cast<gpointer*>(sWidgetStorage) +
MOZ_GTK_COMBOBOX_SEPARATOR);
} else {
/* comboBoxSeparator may be NULL
* when "appears-as-list" = TRUE or "cell-view" = FALSE;
* if there is no separator, then we just won't paint it. */
isMissingSeparator = true;
}
return comboBoxSeparator;
}
static GtkWidget* CreateComboBoxEntryWidget() {
GtkWidget* widget = gtk_combo_box_new_with_entry();
AddToWindowContainer(widget);
return widget;
}
static GtkWidget* CreateComboBoxEntryTextareaWidget() {
GtkWidget* comboBoxTextarea = nullptr;
/* Get its inner Entry and Button */
GtkInnerWidgetInfo info = {GTK_TYPE_ENTRY, &comboBoxTextarea};
gtk_container_forall(GTK_CONTAINER(GetWidget(MOZ_GTK_COMBOBOX_ENTRY)),
GetInnerWidget, &info);
if (!comboBoxTextarea) {
comboBoxTextarea = GetWidget(MOZ_GTK_ENTRY);
} else {
g_object_add_weak_pointer(
G_OBJECT(comboBoxTextarea),
reinterpret_cast<gpointer*>(sWidgetStorage) + MOZ_GTK_COMBOBOX_ENTRY);
}
return comboBoxTextarea;
}
static GtkWidget* CreateComboBoxEntryButtonWidget() {
GtkWidget* comboBoxButton = nullptr;
/* Get its inner Entry and Button */
GtkInnerWidgetInfo info = {GTK_TYPE_TOGGLE_BUTTON, &comboBoxButton};
gtk_container_forall(GTK_CONTAINER(GetWidget(MOZ_GTK_COMBOBOX_ENTRY)),
GetInnerWidget, &info);
if (!comboBoxButton) {
comboBoxButton = GetWidget(MOZ_GTK_TOGGLE_BUTTON);
} else {
g_object_add_weak_pointer(G_OBJECT(comboBoxButton),
reinterpret_cast<gpointer*>(sWidgetStorage) +
MOZ_GTK_COMBOBOX_ENTRY_BUTTON);
}
return comboBoxButton;
}
static GtkWidget* CreateComboBoxEntryArrowWidget() {
GtkWidget* comboBoxArrow = nullptr;
/* Get the Arrow inside the Button */
GtkWidget* buttonChild =
gtk_bin_get_child(GTK_BIN(GetWidget(MOZ_GTK_COMBOBOX_ENTRY_BUTTON)));
if (GTK_IS_BOX(buttonChild)) {
/* appears-as-list = FALSE, cell-view = TRUE; the button
* contains an hbox. This hbox is there because the ComboBox
* needs to place a cell renderer, a separator, and an arrow in
* the button when appears-as-list is FALSE. */
GtkInnerWidgetInfo info = {GTK_TYPE_ARROW, &comboBoxArrow};
gtk_container_forall(GTK_CONTAINER(buttonChild), GetInnerWidget, &info);
} else if (GTK_IS_ARROW(buttonChild)) {
/* appears-as-list = TRUE, or cell-view = FALSE;
* the button only contains an arrow */
comboBoxArrow = buttonChild;
}
if (!comboBoxArrow) {
/* Shouldn't be reached with current internal gtk implementation;
* we gButtonArrowWidget as last resort fallback to avoid
* crashing. */
comboBoxArrow = GetWidget(MOZ_GTK_BUTTON_ARROW);
} else {
g_object_add_weak_pointer(G_OBJECT(comboBoxArrow),
reinterpret_cast<gpointer*>(sWidgetStorage) +
MOZ_GTK_COMBOBOX_ENTRY_ARROW);
}
return comboBoxArrow;
}
static GtkWidget* CreateScrolledWindowWidget() {
GtkWidget* widget = gtk_scrolled_window_new(nullptr, nullptr);
AddToWindowContainer(widget);
@@ -470,6 +684,13 @@ static GtkWidget* CreateWidget(WidgetNodeType aAppearance) {
return CreateFrameWidget();
case MOZ_GTK_BUTTON:
return CreateButtonWidget();
case MOZ_GTK_TOGGLE_BUTTON:
return CreateToggleButtonWidget();
case MOZ_GTK_BUTTON_ARROW:
return CreateButtonArrowWidget();
case MOZ_GTK_ENTRY:
case MOZ_GTK_DROPDOWN_ENTRY:
return CreateEntryWidget();
case MOZ_GTK_SCROLLED_WINDOW:
return CreateScrolledWindowWidget();
case MOZ_GTK_TREEVIEW:
@@ -486,6 +707,22 @@ static GtkWidget* CreateWidget(WidgetNodeType aAppearance) {
return CreateScaleWidget(GTK_ORIENTATION_VERTICAL);
case MOZ_GTK_NOTEBOOK:
return CreateNotebookWidget();
case MOZ_GTK_COMBOBOX:
return CreateComboBoxWidget();
case MOZ_GTK_COMBOBOX_BUTTON:
return CreateComboBoxButtonWidget();
case MOZ_GTK_COMBOBOX_ARROW:
return CreateComboBoxArrowWidget();
case MOZ_GTK_COMBOBOX_SEPARATOR:
return CreateComboBoxSeparatorWidget();
case MOZ_GTK_COMBOBOX_ENTRY:
return CreateComboBoxEntryWidget();
case MOZ_GTK_COMBOBOX_ENTRY_TEXTAREA:
return CreateComboBoxEntryTextareaWidget();
case MOZ_GTK_COMBOBOX_ENTRY_BUTTON:
return CreateComboBoxEntryButtonWidget();
case MOZ_GTK_COMBOBOX_ENTRY_ARROW:
return CreateComboBoxEntryArrowWidget();
case MOZ_GTK_HEADERBAR_WINDOW:
case MOZ_GTK_HEADERBAR_WINDOW_MAXIMIZED:
case MOZ_GTK_HEADERBAR_FIXED:

View File

@@ -54,6 +54,8 @@ style_path_print(GtkStyleContext *context)
static gint moz_gtk_get_tab_thickness(GtkStyleContext* style);
static void Inset(GdkRectangle*, const GtkBorder&);
static void moz_gtk_add_style_border(GtkStyleContext* style, gint* left,
gint* top, gint* right, gint* bottom) {
GtkBorder border;
@@ -86,6 +88,19 @@ static void moz_gtk_add_border_padding(GtkStyleContext* style, gint* left,
moz_gtk_add_style_padding(style, left, top, right, bottom);
}
// In case there's an error in Gtk theme and preferred size is zero,
// return some sane values to pass mozilla automation tests.
// It should not happen in real-life.
#define MIN_WIDGET_SIZE 10
static void moz_gtk_sanity_preferred_size(GtkRequisition* requisition) {
if (requisition->width <= 0) {
requisition->width = MIN_WIDGET_SIZE;
}
if (requisition->height <= 0) {
requisition->height = MIN_WIDGET_SIZE;
}
}
// GetStateFlagsFromGtkWidgetState() can be safely used for the specific
// GtkWidgets that set both prelight and active flags. For other widgets,
// either the GtkStateFlags or Gecko's GtkWidgetState need to be carefully
@@ -153,6 +168,49 @@ void moz_gtk_refresh() {
ResetWidgetCache();
}
gint moz_gtk_button_get_default_overflow(gint* border_top, gint* border_left,
gint* border_bottom,
gint* border_right) {
GtkBorder* default_outside_border;
GtkStyleContext* style = GetStyleContext(MOZ_GTK_BUTTON);
gtk_style_context_get_style(style, "default-outside-border",
&default_outside_border, NULL);
if (default_outside_border) {
*border_top = default_outside_border->top;
*border_left = default_outside_border->left;
*border_bottom = default_outside_border->bottom;
*border_right = default_outside_border->right;
gtk_border_free(default_outside_border);
} else {
*border_top = *border_left = *border_bottom = *border_right = 0;
}
return MOZ_GTK_SUCCESS;
}
static gint moz_gtk_button_get_default_border(gint* border_top,
gint* border_left,
gint* border_bottom,
gint* border_right) {
GtkBorder* default_border;
GtkStyleContext* style = GetStyleContext(MOZ_GTK_BUTTON);
gtk_style_context_get_style(style, "default-border", &default_border, NULL);
if (default_border) {
*border_top = default_border->top;
*border_left = default_border->left;
*border_bottom = default_border->bottom;
*border_right = default_border->right;
gtk_border_free(default_border);
} else {
/* see gtkbutton.c */
*border_top = *border_left = *border_bottom = *border_right = 1;
}
return MOZ_GTK_SUCCESS;
}
gint moz_gtk_splitter_get_metrics(gint orientation, gint* size) {
GtkStyleContext* style;
if (orientation == GTK_ORIENTATION_HORIZONTAL) {
@@ -326,49 +384,6 @@ static gint moz_gtk_window_decoration_paint(cairo_t* cr,
return MOZ_GTK_SUCCESS;
}
gint moz_gtk_button_get_default_overflow(gint* border_top, gint* border_left,
gint* border_bottom,
gint* border_right) {
GtkBorder* default_outside_border;
GtkStyleContext* style = GetStyleContext(MOZ_GTK_BUTTON);
gtk_style_context_get_style(style, "default-outside-border",
&default_outside_border, NULL);
if (default_outside_border) {
*border_top = default_outside_border->top;
*border_left = default_outside_border->left;
*border_bottom = default_outside_border->bottom;
*border_right = default_outside_border->right;
gtk_border_free(default_outside_border);
} else {
*border_top = *border_left = *border_bottom = *border_right = 0;
}
return MOZ_GTK_SUCCESS;
}
static gint moz_gtk_button_get_default_border(gint* border_top,
gint* border_left,
gint* border_bottom,
gint* border_right) {
GtkBorder* default_border;
GtkStyleContext* style = GetStyleContext(MOZ_GTK_BUTTON);
gtk_style_context_get_style(style, "default-border", &default_border, NULL);
if (default_border) {
*border_top = default_border->top;
*border_left = default_border->left;
*border_bottom = default_border->bottom;
*border_right = default_border->right;
gtk_border_free(default_border);
} else {
/* see gtkbutton.c */
*border_top = *border_left = *border_bottom = *border_right = 1;
}
return MOZ_GTK_SUCCESS;
}
static gint moz_gtk_button_paint(cairo_t* cr, const GdkRectangle* rect,
GtkWidgetState* state, GtkReliefStyle relief,
GtkWidget* widget,
@@ -475,6 +490,64 @@ static gint moz_gtk_header_bar_button_paint(cairo_t* cr, GdkRectangle* aRect,
return MOZ_GTK_SUCCESS;
}
static gint calculate_button_inner_rect(GtkWidget* button,
const GdkRectangle* rect,
GdkRectangle* inner_rect,
GtkTextDirection direction) {
GtkStyleContext* style;
GtkBorder border;
GtkBorder padding = {0, 0, 0, 0};
style = gtk_widget_get_style_context(button);
/* This mirrors gtkbutton's child positioning */
gtk_style_context_get_border(style, gtk_style_context_get_state(style),
&border);
gtk_style_context_get_padding(style, gtk_style_context_get_state(style),
&padding);
inner_rect->x = rect->x + border.left + padding.left;
inner_rect->y = rect->y + padding.top + border.top;
inner_rect->width =
MAX(1, rect->width - padding.left - padding.right - border.left * 2);
inner_rect->height =
MAX(1, rect->height - padding.top - padding.bottom - border.top * 2);
return MOZ_GTK_SUCCESS;
}
static gint calculate_arrow_rect(GtkWidget* arrow, GdkRectangle* rect,
GdkRectangle* arrow_rect,
GtkTextDirection direction) {
/* defined in gtkarrow.c */
gfloat arrow_scaling = 0.7;
gfloat xalign, xpad;
gint extent;
gint mxpad, mypad;
gfloat mxalign, myalign;
GtkMisc* misc = GTK_MISC(arrow);
gtk_style_context_get_style(gtk_widget_get_style_context(arrow),
"arrow_scaling", &arrow_scaling, NULL);
gtk_misc_get_padding(misc, &mxpad, &mypad);
extent = MIN((rect->width - mxpad * 2), (rect->height - mypad * 2)) *
arrow_scaling;
gtk_misc_get_alignment(misc, &mxalign, &myalign);
xalign = direction == GTK_TEXT_DIR_LTR ? mxalign : 1.0 - mxalign;
xpad = mxpad + (rect->width - extent) * xalign;
arrow_rect->x = direction == GTK_TEXT_DIR_LTR ? floor(rect->x + xpad)
: ceil(rect->x + xpad);
arrow_rect->y = floor(rect->y + mypad + ((rect->height - extent) * myalign));
arrow_rect->width = arrow_rect->height = extent;
return MOZ_GTK_SUCCESS;
}
/**
* Get minimum widget size as sum of margin, padding, border and
* min-width/min-height.
@@ -496,7 +569,26 @@ static void moz_gtk_get_widget_min_size(GtkStyleContext* style, int* width,
padding.top + padding.bottom;
}
/* See gtk_range_draw() for reference. */
static void Inset(GdkRectangle* rect, const GtkBorder& aBorder) {
rect->x += aBorder.left;
rect->y += aBorder.top;
rect->width -= aBorder.left + aBorder.right;
rect->height -= aBorder.top + aBorder.bottom;
}
// Inset a rectangle by the border and padding specified in a style context.
static void InsetByBorderPadding(GdkRectangle* rect, GtkStyleContext* style) {
GtkStateFlags state = gtk_style_context_get_state(style);
GtkBorder padding, border;
gtk_style_context_get_padding(style, state, &padding);
Inset(rect, padding);
gtk_style_context_get_border(style, state, &border);
Inset(rect, border);
}
/* See gtk_range_draw() for reference.
*/
static gint moz_gtk_scale_paint(cairo_t* cr, GdkRectangle* rect,
GtkWidgetState* state, GtkOrientation flags,
GtkTextDirection direction) {
@@ -586,6 +678,56 @@ static gint moz_gtk_vpaned_paint(cairo_t* cr, GdkRectangle* rect,
return MOZ_GTK_SUCCESS;
}
// See gtk_entry_draw() for reference.
static gint moz_gtk_entry_paint(cairo_t* cr, const GdkRectangle* aRect,
GtkWidgetState* state, GtkStyleContext* style,
WidgetNodeType widget) {
GdkRectangle rect = *aRect;
gtk_render_background(style, cr, rect.x, rect.y, rect.width, rect.height);
// Paint the border, except for 'menulist-textfield' that isn't focused:
if (widget != MOZ_GTK_DROPDOWN_ENTRY || state->focused) {
gtk_render_frame(style, cr, rect.x, rect.y, rect.width, rect.height);
}
return MOZ_GTK_SUCCESS;
}
static gint moz_gtk_text_view_paint(cairo_t* cr, GdkRectangle* aRect,
GtkWidgetState* state,
GtkTextDirection direction) {
// GtkTextView and GtkScrolledWindow do not set active and prelight flags.
// The use of focus with MOZ_GTK_SCROLLED_WINDOW here is questionable
// because a parent widget will not have focus when its child GtkTextView
// has focus, but perhaps this may help identify a focused textarea with
// some themes as GtkTextView backgrounds do not typically render
// differently with focus.
GtkStateFlags state_flags = state->disabled ? GTK_STATE_FLAG_INSENSITIVE
: state->focused ? GTK_STATE_FLAG_FOCUSED
: GTK_STATE_FLAG_NORMAL;
GtkStyleContext* style_frame = GetStyleContext(
MOZ_GTK_SCROLLED_WINDOW, state->image_scale, direction, state_flags);
gtk_render_frame(style_frame, cr, aRect->x, aRect->y, aRect->width,
aRect->height);
GdkRectangle rect = *aRect;
InsetByBorderPadding(&rect, style_frame);
GtkStyleContext* style = GetStyleContext(
MOZ_GTK_TEXT_VIEW, state->image_scale, direction, state_flags);
gtk_render_background(style, cr, rect.x, rect.y, rect.width, rect.height);
// There is a separate "text" window, which usually provides the
// background behind the text. However, this is transparent in Ambiance
// for GTK 3.20, in which case the MOZ_GTK_TEXT_VIEW background is
// visible.
style = GetStyleContext(MOZ_GTK_TEXT_VIEW_TEXT, state->image_scale, direction,
state_flags);
gtk_render_background(style, cr, rect.x, rect.y, rect.width, rect.height);
return MOZ_GTK_SUCCESS;
}
static gint moz_gtk_treeview_paint(cairo_t* cr, GdkRectangle* rect,
GtkWidgetState* state,
GtkTextDirection direction) {
@@ -618,6 +760,118 @@ static gint moz_gtk_treeview_paint(cairo_t* cr, GdkRectangle* rect,
return MOZ_GTK_SUCCESS;
}
/* See gtk_separator_draw() for reference. */
static gint moz_gtk_combo_box_paint(cairo_t* cr, const GdkRectangle* aRect,
GtkWidgetState* state,
GtkTextDirection direction) {
GdkRectangle arrow_rect, real_arrow_rect;
gint separator_width;
gboolean wide_separators;
GtkStyleContext* style;
GtkRequisition arrow_req;
GtkWidget* comboBoxButton = GetWidget(MOZ_GTK_COMBOBOX_BUTTON);
GtkWidget* comboBoxArrow = GetWidget(MOZ_GTK_COMBOBOX_ARROW);
if (!comboBoxButton || !comboBoxArrow) {
return MOZ_GTK_UNKNOWN_WIDGET;
}
/* Also sets the direction on gComboBoxButtonWidget, which is then
* inherited by the separator and arrow */
moz_gtk_button_paint(cr, aRect, state, GTK_RELIEF_NORMAL, comboBoxButton,
direction);
calculate_button_inner_rect(comboBoxButton, aRect, &arrow_rect, direction);
/* Now arrow_rect contains the inner rect ; we want to correct the width
* to what the arrow needs (see gtk_combo_box_size_allocate) */
gtk_widget_get_preferred_size(comboBoxArrow, NULL, &arrow_req);
moz_gtk_sanity_preferred_size(&arrow_req);
if (direction == GTK_TEXT_DIR_LTR)
arrow_rect.x += arrow_rect.width - arrow_req.width;
arrow_rect.width = arrow_req.width;
calculate_arrow_rect(comboBoxArrow, &arrow_rect, &real_arrow_rect, direction);
style = GetStyleContext(MOZ_GTK_COMBOBOX_ARROW, state->image_scale);
gtk_render_arrow(style, cr, ARROW_DOWN, real_arrow_rect.x, real_arrow_rect.y,
real_arrow_rect.width);
/* If there is no separator in the theme, there's nothing left to do. */
GtkWidget* widget = GetWidget(MOZ_GTK_COMBOBOX_SEPARATOR);
if (!widget) {
return MOZ_GTK_SUCCESS;
}
style = gtk_widget_get_style_context(widget);
StyleContextSetScale(style, state->image_scale);
gtk_style_context_get_style(style, "wide-separators", &wide_separators,
"separator-width", &separator_width, NULL);
if (wide_separators) {
if (direction == GTK_TEXT_DIR_LTR)
arrow_rect.x -= separator_width;
else
arrow_rect.x += arrow_rect.width;
gtk_render_frame(style, cr, arrow_rect.x, arrow_rect.y, separator_width,
arrow_rect.height);
} else {
if (direction == GTK_TEXT_DIR_LTR) {
GtkBorder padding;
GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state);
gtk_style_context_get_padding(style, state_flags, &padding);
arrow_rect.x -= padding.left;
} else
arrow_rect.x += arrow_rect.width;
gtk_render_line(style, cr, arrow_rect.x, arrow_rect.y, arrow_rect.x,
arrow_rect.y + arrow_rect.height);
}
return MOZ_GTK_SUCCESS;
}
static gint moz_gtk_arrow_paint(cairo_t* cr, GdkRectangle* rect,
GtkWidgetState* state, GtkArrowType arrow_type,
GtkTextDirection direction) {
GdkRectangle arrow_rect;
gdouble arrow_angle;
if (direction == GTK_TEXT_DIR_RTL) {
if (arrow_type == GTK_ARROW_LEFT) {
arrow_type = GTK_ARROW_RIGHT;
} else if (arrow_type == GTK_ARROW_RIGHT) {
arrow_type = GTK_ARROW_LEFT;
}
}
switch (arrow_type) {
case GTK_ARROW_LEFT:
arrow_angle = ARROW_LEFT;
break;
case GTK_ARROW_RIGHT:
arrow_angle = ARROW_RIGHT;
break;
case GTK_ARROW_DOWN:
arrow_angle = ARROW_DOWN;
break;
default:
arrow_angle = ARROW_UP;
break;
}
if (arrow_type == GTK_ARROW_NONE) return MOZ_GTK_SUCCESS;
GtkWidget* widget = GetWidget(MOZ_GTK_BUTTON_ARROW);
if (!widget) {
return MOZ_GTK_UNKNOWN_WIDGET;
}
calculate_arrow_rect(widget, rect, &arrow_rect, direction);
GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state);
GtkStyleContext* style = GetStyleContext(
MOZ_GTK_BUTTON_ARROW, state->image_scale, direction, state_flags);
gtk_render_arrow(style, cr, arrow_angle, arrow_rect.x, arrow_rect.y,
arrow_rect.width);
return MOZ_GTK_SUCCESS;
}
static gint moz_gtk_resizer_paint(cairo_t* cr, GdkRectangle* rect,
GtkWidgetState* state,
GtkTextDirection direction) {
@@ -973,11 +1227,83 @@ gint moz_gtk_get_widget_border(WidgetNodeType widget, gint* left, gint* top,
*left = *top = *right = *bottom = 0;
switch (widget) {
case MOZ_GTK_BUTTON:
case MOZ_GTK_TOOLBAR_BUTTON: {
style = GetStyleContext(MOZ_GTK_BUTTON);
*left = *top = *right = *bottom = gtk_container_get_border_width(
GTK_CONTAINER(GetWidget(MOZ_GTK_BUTTON)));
if (widget == MOZ_GTK_TOOLBAR_BUTTON) {
gtk_style_context_save(style);
gtk_style_context_add_class(style, "image-button");
}
moz_gtk_add_style_padding(style, left, top, right, bottom);
if (widget == MOZ_GTK_TOOLBAR_BUTTON) gtk_style_context_restore(style);
moz_gtk_add_style_border(style, left, top, right, bottom);
return MOZ_GTK_SUCCESS;
}
case MOZ_GTK_ENTRY:
case MOZ_GTK_DROPDOWN_ENTRY: {
style = GetStyleContext(widget);
// XXX: Subtract 1 pixel from the padding to account for the default
// padding in forms.css. See bug 1187385.
*left = *top = *right = *bottom = -1;
moz_gtk_add_border_padding(style, left, top, right, bottom);
return MOZ_GTK_SUCCESS;
}
case MOZ_GTK_TEXT_VIEW:
case MOZ_GTK_TREEVIEW: {
style = GetStyleContext(MOZ_GTK_SCROLLED_WINDOW);
moz_gtk_add_style_border(style, left, top, right, bottom);
return MOZ_GTK_SUCCESS;
}
case MOZ_GTK_DROPDOWN: {
/* We need to account for the arrow on the dropdown, so text
* doesn't come too close to the arrow, or in some cases spill
* into the arrow. */
gboolean wide_separators;
gint separator_width;
GtkRequisition arrow_req;
GtkBorder border;
*left = *top = *right = *bottom = gtk_container_get_border_width(
GTK_CONTAINER(GetWidget(MOZ_GTK_COMBOBOX_BUTTON)));
style = GetStyleContext(MOZ_GTK_COMBOBOX_BUTTON);
moz_gtk_add_border_padding(style, left, top, right, bottom);
/* If there is no separator, don't try to count its width. */
separator_width = 0;
GtkWidget* comboBoxSeparator = GetWidget(MOZ_GTK_COMBOBOX_SEPARATOR);
if (comboBoxSeparator) {
style = gtk_widget_get_style_context(comboBoxSeparator);
gtk_style_context_get_style(style, "wide-separators", &wide_separators,
"separator-width", &separator_width, NULL);
if (!wide_separators) {
gtk_style_context_get_border(
style, gtk_style_context_get_state(style), &border);
separator_width = border.left;
}
}
gtk_widget_get_preferred_size(GetWidget(MOZ_GTK_COMBOBOX_ARROW), NULL,
&arrow_req);
moz_gtk_sanity_preferred_size(&arrow_req);
if (direction == GTK_TEXT_DIR_RTL)
*left += separator_width + arrow_req.width;
else
*right += separator_width + arrow_req.width;
return MOZ_GTK_SUCCESS;
}
case MOZ_GTK_TABPANELS:
w = GetWidget(MOZ_GTK_TABPANELS);
break;
@@ -1079,6 +1405,51 @@ gint moz_gtk_get_tab_scroll_arrow_size(gint* width, gint* height) {
return MOZ_GTK_SUCCESS;
}
void moz_gtk_get_arrow_size(WidgetNodeType widgetType, gint* width,
gint* height) {
GtkWidget* widget;
switch (widgetType) {
case MOZ_GTK_DROPDOWN:
widget = GetWidget(MOZ_GTK_COMBOBOX_ARROW);
break;
default:
widget = GetWidget(MOZ_GTK_BUTTON_ARROW);
break;
}
if (widget) {
GtkRequisition requisition;
gtk_widget_get_preferred_size(widget, NULL, &requisition);
moz_gtk_sanity_preferred_size(&requisition);
*width = requisition.width;
*height = requisition.height;
} else {
*width = 0;
*height = 0;
}
}
void moz_gtk_get_entry_min_height(gint* min_content_height,
gint* border_padding_height) {
GtkStyleContext* style = GetStyleContext(MOZ_GTK_ENTRY);
if (!gtk_check_version(3, 20, 0)) {
gtk_style_context_get(style, gtk_style_context_get_state(style),
"min-height", min_content_height, nullptr);
} else {
*min_content_height = 0;
}
GtkBorder border;
GtkBorder padding;
gtk_style_context_get_border(style, gtk_style_context_get_state(style),
&border);
gtk_style_context_get_padding(style, gtk_style_context_get_state(style),
&padding);
*border_padding_height =
(border.top + border.bottom + padding.top + padding.bottom);
}
void moz_gtk_get_scale_metrics(GtkOrientation orient, gint* scale_width,
gint* scale_height) {
if (gtk_check_version(3, 20, 0) != nullptr) {
@@ -1153,6 +1524,15 @@ gint moz_gtk_widget_paint(WidgetNodeType widget, cairo_t* cr,
cairo_new_path(cr);
switch (widget) {
case MOZ_GTK_BUTTON:
case MOZ_GTK_TOOLBAR_BUTTON:
if (state->depressed) {
return moz_gtk_button_paint(cr, rect, state, (GtkReliefStyle)flags,
GetWidget(MOZ_GTK_TOGGLE_BUTTON),
direction);
}
return moz_gtk_button_paint(cr, rect, state, (GtkReliefStyle)flags,
GetWidget(MOZ_GTK_BUTTON), direction);
case MOZ_GTK_HEADER_BAR_BUTTON_CLOSE:
case MOZ_GTK_HEADER_BAR_BUTTON_MINIMIZE:
case MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE:
@@ -1169,6 +1549,18 @@ gint moz_gtk_widget_paint(WidgetNodeType widget, cairo_t* cr,
direction);
case MOZ_GTK_TREEVIEW:
return moz_gtk_treeview_paint(cr, rect, state, direction);
case MOZ_GTK_ENTRY:
case MOZ_GTK_DROPDOWN_ENTRY: {
GtkStyleContext* style =
GetStyleContext(widget, state->image_scale, direction,
GetStateFlagsFromGtkWidgetState(state));
gint ret = moz_gtk_entry_paint(cr, rect, state, style, widget);
return ret;
}
case MOZ_GTK_TEXT_VIEW:
return moz_gtk_text_view_paint(cr, rect, state, direction);
case MOZ_GTK_DROPDOWN:
return moz_gtk_combo_box_paint(cr, rect, state, direction);
case MOZ_GTK_FRAME:
return moz_gtk_frame_paint(cr, rect, state, direction);
case MOZ_GTK_RESIZER:
@@ -1185,6 +1577,9 @@ gint moz_gtk_widget_paint(WidgetNodeType widget, cairo_t* cr,
widget);
case MOZ_GTK_TABPANELS:
return moz_gtk_tabpanels_paint(cr, rect, state, direction);
case MOZ_GTK_TOOLBARBUTTON_ARROW:
return moz_gtk_arrow_paint(cr, rect, state, (GtkArrowType)flags,
direction);
case MOZ_GTK_SPLITTER_HORIZONTAL:
return moz_gtk_vpaned_paint(cr, rect, state);
case MOZ_GTK_SPLITTER_VERTICAL:

View File

@@ -110,6 +110,12 @@ enum GtkTabFlags {
enum WidgetNodeType : int {
/* Paints a GtkButton. flags is a GtkReliefStyle. */
MOZ_GTK_BUTTON,
/* Paints a button with image and no text */
MOZ_GTK_TOOLBAR_BUTTON,
/* Paints a toggle button */
MOZ_GTK_TOGGLE_BUTTON,
/* Paints a button arrow */
MOZ_GTK_BUTTON_ARROW,
/* Vertical GtkScrollbar counterparts */
MOZ_GTK_SCROLLBAR_VERTICAL,
@@ -128,6 +134,8 @@ enum WidgetNodeType : int {
/* Paints a GtkScale thumb. */
MOZ_GTK_SCALE_THUMB_HORIZONTAL,
MOZ_GTK_SCALE_THUMB_VERTICAL,
/* Paints a GtkEntry. */
MOZ_GTK_ENTRY,
/* Paints a GtkExpander. */
MOZ_GTK_EXPANDER,
/* Paints a GtkTextView or gets the style context corresponding to the
@@ -137,6 +145,10 @@ enum WidgetNodeType : int {
MOZ_GTK_TEXT_VIEW_TEXT,
/* The "selection" node of a GtkTextView.text */
MOZ_GTK_TEXT_VIEW_TEXT_SELECTION,
/* Paints a GtkOptionMenu. */
MOZ_GTK_DROPDOWN,
/* Paints an entry in an editable option menu */
MOZ_GTK_DROPDOWN_ENTRY,
/* Paints a GtkToolTip */
MOZ_GTK_TOOLTIP,
@@ -207,6 +219,22 @@ enum WidgetNodeType : int {
MOZ_GTK_HEADERBAR_FIXED_MAXIMIZED,
/* Window container for all widgets */
MOZ_GTK_WINDOW_CONTAINER,
/* Used for widget tree construction. */
MOZ_GTK_COMBOBOX,
/* Paints a GtkComboBox button widget. */
MOZ_GTK_COMBOBOX_BUTTON,
/* Paints a GtkComboBox arrow widget. */
MOZ_GTK_COMBOBOX_ARROW,
/* Paints a GtkComboBox separator widget. */
MOZ_GTK_COMBOBOX_SEPARATOR,
/* Used for widget tree construction. */
MOZ_GTK_COMBOBOX_ENTRY,
/* Paints a GtkComboBox entry widget. */
MOZ_GTK_COMBOBOX_ENTRY_TEXTAREA,
/* Paints a GtkComboBox entry button widget. */
MOZ_GTK_COMBOBOX_ENTRY_BUTTON,
/* Paints a GtkComboBox entry arrow widget. */
MOZ_GTK_COMBOBOX_ENTRY_ARROW,
/* Used for scrolled window shell. */
MOZ_GTK_SCROLLED_WINDOW,
/* Paints a GtkHeaderBar */
@@ -351,6 +379,16 @@ gint moz_gtk_get_scalethumb_metrics(GtkOrientation orient, gint* thumb_length,
*/
gint moz_gtk_get_tab_scroll_arrow_size(gint* width, gint* height);
/**
* Get the desired size of an arrow in a button
*
* widgetType: [IN] the widget for which to get the arrow size
* width: [OUT] the desired width
* height: [OUT] the desired height
*/
void moz_gtk_get_arrow_size(WidgetNodeType widgetType, gint* width,
gint* height);
/**
* Get the minimum height of a entry widget
* min_content_height: [OUT] the minimum height of the content box.

View File

@@ -696,7 +696,6 @@ nsresult nsLookAndFeel::PerThemeData::GetColor(ColorID aID,
case ColorID::MozCombobox:
aColor = mWindow.mBg;
break;
case ColorID::MozComboboxtext:
case ColorID::Windowtext:
aColor = mWindow.mFg;
break;
@@ -926,6 +925,9 @@ nsresult nsLookAndFeel::PerThemeData::GetColor(ColorID aID,
case ColorID::Visitedtext:
aColor = mNativeVisitedHyperLinkText;
break;
case ColorID::MozComboboxtext:
aColor = mComboBoxText;
break;
case ColorID::MozColheader:
aColor = mMozColHeader.mBg;
break;
@@ -2313,6 +2315,11 @@ void nsLookAndFeel::PerThemeData::Init() {
mButtonActive.mBg = mWindow.mBg;
}
// Combobox text color
style = GetStyleContext(MOZ_GTK_COMBOBOX_ENTRY_TEXTAREA);
gtk_style_context_get_color(style, GTK_STATE_FLAG_NORMAL, &color);
mComboBoxText = GDK_RGBA_TO_NS_RGBA(color);
// GTK's guide to fancy odd row background colors:
// 1) Check if a theme explicitly defines an odd row color
// 2) If not, check if it defines an even row color, and darken it

View File

@@ -140,6 +140,10 @@ class nsLookAndFeel final : public nsXPLookAndFeel {
nscolor mOddCellBackground = kWhite;
nscolor mNativeHyperLinkText = kBlack;
nscolor mNativeVisitedHyperLinkText = kBlack;
// FIXME: This doesn't seem like it'd be sound since we use Window for
// -moz-Combobox... But I guess we rely on chrome code not setting
// appearance: none on selects or overriding the color if they do.
nscolor mComboBoxText = kBlack;
ColorPair mField;
ColorPair mWindow;
ColorPair mDialog;

View File

@@ -184,10 +184,15 @@ bool nsNativeThemeGTK::GetGtkWidgetAndState(StyleAppearance aAppearance,
aState->isDefault = IsDefaultButton(aFrame);
aState->canDefault = FALSE; // XXX fix me
if (aAppearance == StyleAppearance::MozWindowButtonMinimize ||
if (aAppearance == StyleAppearance::Button ||
aAppearance == StyleAppearance::Toolbarbutton ||
aAppearance == StyleAppearance::Dualbutton ||
aAppearance == StyleAppearance::ToolbarbuttonDropdown ||
aAppearance == StyleAppearance::MozWindowButtonMinimize ||
aAppearance == StyleAppearance::MozWindowButtonRestore ||
aAppearance == StyleAppearance::MozWindowButtonMaximize ||
aAppearance == StyleAppearance::MozWindowButtonClose) {
aAppearance == StyleAppearance::MozWindowButtonClose ||
aAppearance == StyleAppearance::Menulist) {
aState->active &= aState->inHover;
}
@@ -196,6 +201,19 @@ bool nsNativeThemeGTK::GetGtkWidgetAndState(StyleAppearance aAppearance,
// actually has element focus, so we check the focused attribute
// to see whether to draw in the focused state.
aState->focused = elementState.HasState(ElementState::FOCUSRING);
// A button with drop down menu open or an activated toggle button
// should always appear depressed.
if (aAppearance == StyleAppearance::Button ||
aAppearance == StyleAppearance::Toolbarbutton ||
aAppearance == StyleAppearance::Dualbutton ||
aAppearance == StyleAppearance::ToolbarbuttonDropdown ||
aAppearance == StyleAppearance::Menulist) {
bool menuOpen = IsOpenButton(aFrame);
aState->depressed = IsCheckedButton(aFrame) || menuOpen;
// we must not highlight buttons with open drop down menus on hover.
aState->inHover = aState->inHover && !menuOpen;
}
}
if (aAppearance == StyleAppearance::MozWindowTitlebar ||
@@ -210,6 +228,15 @@ bool nsNativeThemeGTK::GetGtkWidgetAndState(StyleAppearance aAppearance,
}
switch (aAppearance) {
case StyleAppearance::Button:
if (aWidgetFlags) *aWidgetFlags = GTK_RELIEF_NORMAL;
aGtkWidgetType = MOZ_GTK_BUTTON;
break;
case StyleAppearance::Toolbarbutton:
case StyleAppearance::Dualbutton:
if (aWidgetFlags) *aWidgetFlags = GTK_RELIEF_NONE;
aGtkWidgetType = MOZ_GTK_TOOLBAR_BUTTON;
break;
case StyleAppearance::Range: {
if (IsRangeHorizontal(aFrame)) {
if (aWidgetFlags) *aWidgetFlags = GTK_ORIENTATION_HORIZONTAL;
@@ -230,9 +257,40 @@ bool nsNativeThemeGTK::GetGtkWidgetAndState(StyleAppearance aAppearance,
}
break;
}
case StyleAppearance::NumberInput:
case StyleAppearance::PasswordInput:
case StyleAppearance::Textfield:
aGtkWidgetType = MOZ_GTK_ENTRY;
break;
case StyleAppearance::Textarea:
aGtkWidgetType = MOZ_GTK_TEXT_VIEW;
break;
case StyleAppearance::Listbox:
aGtkWidgetType = MOZ_GTK_TREEVIEW;
break;
case StyleAppearance::Menulist:
aGtkWidgetType = MOZ_GTK_DROPDOWN;
if (aWidgetFlags)
*aWidgetFlags =
IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XHTML);
break;
case StyleAppearance::ToolbarbuttonDropdown:
case StyleAppearance::ButtonArrowDown:
case StyleAppearance::ButtonArrowUp:
case StyleAppearance::ButtonArrowNext:
case StyleAppearance::ButtonArrowPrevious:
aGtkWidgetType = MOZ_GTK_TOOLBARBUTTON_ARROW;
if (aWidgetFlags) {
*aWidgetFlags = GTK_ARROW_DOWN;
if (aAppearance == StyleAppearance::ButtonArrowUp)
*aWidgetFlags = GTK_ARROW_UP;
else if (aAppearance == StyleAppearance::ButtonArrowNext)
*aWidgetFlags = GTK_ARROW_RIGHT;
else if (aAppearance == StyleAppearance::ButtonArrowPrevious)
*aWidgetFlags = GTK_ARROW_LEFT;
}
break;
case StyleAppearance::ProgressBar:
aGtkWidgetType = MOZ_GTK_PROGRESSBAR;
break;
@@ -539,6 +597,30 @@ static void DrawThemeWithCairo(gfxContext* aContext, DrawTarget* aDrawTarget,
}
}
CSSIntMargin nsNativeThemeGTK::GetExtraSizeForWidget(
nsIFrame* aFrame, StyleAppearance aAppearance) {
CSSIntMargin extra;
// Allow an extra one pixel above and below the thumb for certain
// GTK2 themes (Ximian Industrial, Bluecurve, Misty, at least);
// We modify the frame's overflow area. See bug 297508.
switch (aAppearance) {
case StyleAppearance::Button: {
if (IsDefaultButton(aFrame)) {
// Some themes draw a default indicator outside the widget,
// include that in overflow
moz_gtk_button_get_default_overflow(&extra.top.value, &extra.left.value,
&extra.bottom.value,
&extra.right.value);
break;
}
return {};
}
default:
return {};
}
return extra;
}
NS_IMETHODIMP
nsNativeThemeGTK::DrawWidgetBackground(gfxContext* aContext, nsIFrame* aFrame,
StyleAppearance aAppearance,
@@ -727,10 +809,12 @@ CSSIntMargin nsNativeThemeGTK::GetCachedWidgetBorder(
moz_gtk_get_widget_border(gtkWidgetType, &result.left.value,
&result.top.value, &result.right.value,
&result.bottom.value, aDirection);
if (gtkWidgetType != MOZ_GTK_DROPDOWN) { // depends on aDirection
mBorderCacheValid[cacheIndex] |= cacheBit;
mBorderCache[gtkWidgetType] = result;
}
}
}
FixupForVerticalWritingMode(aFrame->GetWritingMode(), &result);
return result;
}
@@ -744,6 +828,14 @@ LayoutDeviceIntMargin nsNativeThemeGTK::GetWidgetBorder(
CSSIntMargin result;
GtkTextDirection direction = GetTextDirection(aFrame);
switch (aAppearance) {
case StyleAppearance::Dualbutton:
// TOOLBAR_DUAL_BUTTON is an interesting case. We want a border to draw
// around the entire button + dropdown, and also an inner border if you're
// over the button part. But, we want the inner button to be right up
// against the edge of the outer button so that the borders overlap.
// To make this happen, we draw a button border for the outer button,
// but don't reserve any space for it.
break;
case StyleAppearance::Tab: {
WidgetNodeType gtkWidgetType;
gint flags;
@@ -777,6 +869,12 @@ bool nsNativeThemeGTK::GetWidgetPadding(nsDeviceContext* aContext,
case StyleAppearance::MozWindowButtonMinimize:
case StyleAppearance::MozWindowButtonMaximize:
case StyleAppearance::MozWindowButtonRestore:
case StyleAppearance::Dualbutton:
case StyleAppearance::ToolbarbuttonDropdown:
case StyleAppearance::ButtonArrowUp:
case StyleAppearance::ButtonArrowDown:
case StyleAppearance::ButtonArrowNext:
case StyleAppearance::ButtonArrowPrevious:
case StyleAppearance::RangeThumb:
aResult->SizeTo(0, 0, 0, 0);
return true;
@@ -795,7 +893,12 @@ bool nsNativeThemeGTK::GetWidgetOverflow(nsDeviceContext* aContext,
return Theme::GetWidgetOverflow(aContext, aFrame, aAppearance,
aOverflowRect);
}
auto overflow = GetExtraSizeForWidget(aFrame, aAppearance);
if (overflow == CSSIntMargin()) {
return false;
}
aOverflowRect->Inflate(CSSIntMargin::ToAppUnits(overflow));
return true;
}
auto nsNativeThemeGTK::IsWidgetNonNative(nsIFrame* aFrame,
@@ -824,15 +927,8 @@ bool nsNativeThemeGTK::IsWidgetAlwaysNonNative(nsIFrame* aFrame,
StyleAppearance aAppearance) {
return Theme::IsWidgetAlwaysNonNative(aFrame, aAppearance) ||
aAppearance == StyleAppearance::MozMenulistArrowButton ||
aAppearance == StyleAppearance::Textfield ||
aAppearance == StyleAppearance::NumberInput ||
aAppearance == StyleAppearance::PasswordInput ||
aAppearance == StyleAppearance::Textarea ||
aAppearance == StyleAppearance::Checkbox ||
aAppearance == StyleAppearance::Radio ||
aAppearance == StyleAppearance::Button ||
aAppearance == StyleAppearance::Toolbarbutton ||
aAppearance == StyleAppearance::Menulist;
aAppearance == StyleAppearance::Radio;
}
LayoutDeviceIntSize nsNativeThemeGTK::GetMinimumWidgetSize(
@@ -860,6 +956,14 @@ LayoutDeviceIntSize nsNativeThemeGTK::GetMinimumWidgetSize(
&result.width);
}
} break;
case StyleAppearance::ToolbarbuttonDropdown:
case StyleAppearance::ButtonArrowUp:
case StyleAppearance::ButtonArrowDown:
case StyleAppearance::ButtonArrowNext:
case StyleAppearance::ButtonArrowPrevious: {
moz_gtk_get_arrow_size(MOZ_GTK_TOOLBARBUTTON_ARROW, &result.width,
&result.height);
} break;
case StyleAppearance::MozWindowButtonClose: {
const ToolbarButtonGTKMetrics* metrics =
GetToolbarButtonMetrics(MOZ_GTK_HEADER_BAR_BUTTON_CLOSE);
@@ -882,6 +986,56 @@ LayoutDeviceIntSize nsNativeThemeGTK::GetMinimumWidgetSize(
result.height = metrics->minSizeWithBorder.height;
break;
}
case StyleAppearance::Button:
case StyleAppearance::Menulist: {
if (aAppearance == StyleAppearance::Menulist) {
// Include the arrow size.
moz_gtk_get_arrow_size(MOZ_GTK_DROPDOWN, &result.width, &result.height);
}
// else the minimum size is missing consideration of container
// descendants; the value returned here will not be helpful, but the
// box model may consider border and padding with child minimum sizes.
CSSIntMargin border =
GetCachedWidgetBorder(aFrame, aAppearance, GetTextDirection(aFrame));
result.width += border.LeftRight();
result.height += border.TopBottom();
} break;
case StyleAppearance::NumberInput:
case StyleAppearance::PasswordInput:
case StyleAppearance::Textfield: {
gint contentHeight = 0;
gint borderPaddingHeight = 0;
moz_gtk_get_entry_min_height(&contentHeight, &borderPaddingHeight);
// Scale the min content height proportionately with the font-size if it's
// smaller than the default one. This prevents <input type=text
// style="font-size: .5em"> from keeping a ridiculously large size, for
// example.
const gfxFloat fieldFontSizeInCSSPixels = [] {
gfxFontStyle fieldFontStyle;
nsAutoString unusedFontName;
DebugOnly<bool> result = LookAndFeel::GetFont(
LookAndFeel::FontID::MozField, unusedFontName, fieldFontStyle);
MOZ_ASSERT(result, "GTK look and feel supports the field font");
// NOTE: GetFont returns font sizes in CSS pixels, and we want just
// that.
return fieldFontStyle.size;
}();
const gfxFloat fontSize = aFrame->StyleFont()->mFont.size.ToCSSPixels();
if (fieldFontSizeInCSSPixels > fontSize) {
contentHeight =
std::round(contentHeight * fontSize / fieldFontSizeInCSSPixels);
}
gint height = contentHeight + borderPaddingHeight;
if (aFrame->GetWritingMode().IsVertical()) {
result.width = height;
} else {
result.height = height;
}
} break;
default:
break;
}
@@ -922,12 +1076,31 @@ nsNativeThemeGTK::ThemeSupportsWidget(nsPresContext* aPresContext,
}
switch (aAppearance) {
// Combobox dropdowns don't support native theming in vertical mode.
case StyleAppearance::Menulist:
if (aFrame && aFrame->GetWritingMode().IsVertical()) {
return false;
}
[[fallthrough]];
case StyleAppearance::Button:
case StyleAppearance::Toolbarbutton:
case StyleAppearance::Dualbutton: // so we can override the border with 0
case StyleAppearance::ToolbarbuttonDropdown:
case StyleAppearance::ButtonArrowUp:
case StyleAppearance::ButtonArrowDown:
case StyleAppearance::ButtonArrowNext:
case StyleAppearance::ButtonArrowPrevious:
case StyleAppearance::Listbox:
case StyleAppearance::ProgressBar:
case StyleAppearance::Progresschunk:
case StyleAppearance::Tab:
// case StyleAppearance::Tabpanel:
case StyleAppearance::Tabpanels:
case StyleAppearance::NumberInput:
case StyleAppearance::PasswordInput:
case StyleAppearance::Textfield:
case StyleAppearance::Textarea:
case StyleAppearance::Range:
case StyleAppearance::RangeThumb:
case StyleAppearance::Splitter:
@@ -949,9 +1122,12 @@ nsNativeThemeGTK::ThemeSupportsWidget(nsPresContext* aPresContext,
NS_IMETHODIMP_(bool)
nsNativeThemeGTK::WidgetIsContainer(StyleAppearance aAppearance) {
// XXXdwh At some point flesh all of this out.
if (aAppearance == StyleAppearance::RangeThumb) {
if (aAppearance == StyleAppearance::RangeThumb ||
aAppearance == StyleAppearance::ButtonArrowUp ||
aAppearance == StyleAppearance::ButtonArrowDown ||
aAppearance == StyleAppearance::ButtonArrowNext ||
aAppearance == StyleAppearance::ButtonArrowPrevious)
return false;
}
return true;
}
@@ -960,7 +1136,17 @@ bool nsNativeThemeGTK::ThemeDrawsFocusForWidget(nsIFrame* aFrame,
if (IsWidgetNonNative(aFrame, aAppearance) != NonNative::No) {
return Theme::ThemeDrawsFocusForWidget(aFrame, aAppearance);
}
switch (aAppearance) {
case StyleAppearance::Button:
case StyleAppearance::Menulist:
case StyleAppearance::Textarea:
case StyleAppearance::Textfield:
case StyleAppearance::NumberInput:
case StyleAppearance::PasswordInput:
return true;
default:
return false;
}
}
bool nsNativeThemeGTK::ThemeNeedsComboboxDropmarker() { return false; }

View File

@@ -53,13 +53,17 @@ NS_IMPL_ISUPPORTS(nsNativeTheme, nsITimerCallback, nsINamed)
if (isXULElement) {
if (aAppearance == StyleAppearance::Checkbox ||
aAppearance == StyleAppearance::Radio ||
aAppearance == StyleAppearance::ToolbarbuttonDropdown ||
aAppearance == StyleAppearance::ButtonArrowPrevious ||
aAppearance == StyleAppearance::ButtonArrowNext ||
aAppearance == StyleAppearance::ButtonArrowUp ||
#ifdef MOZ_WIDGET_GTK
aAppearance == StyleAppearance::MozWindowButtonClose ||
aAppearance == StyleAppearance::MozWindowButtonMinimize ||
aAppearance == StyleAppearance::MozWindowButtonRestore ||
aAppearance == StyleAppearance::MozWindowButtonMaximize ||
#endif
false) {
aAppearance == StyleAppearance::ButtonArrowDown) {
aFrame = aFrame->GetParent();
frameContent = aFrame->GetContent();
}