0

Gtk3: More fixes and refactorings

This CL
* Uses entry colors instead of window colors for the NTP
* Fixes a leak involving ScopedGObject
* Simplifies how background and border colors are calculated
* Adds the ".label" class only on versions of Gtk before 3.20
* Fixes text selection colors on one theme
* Updates the doc to reflect the new way colors are calculated

BUG=132847

Review-Url: https://codereview.chromium.org/2683953005
Cr-Commit-Position: refs/heads/master@{#449709}
This commit is contained in:
thomasanderson
2017-02-10 12:05:48 -08:00
committed by Commit bot
parent b0e1c97b05
commit 419a91a17b
5 changed files with 235 additions and 224 deletions

@ -822,14 +822,15 @@ void GtkUi::LoadGtkValues() {
colors_[ThemeProperties::COLOR_BACKGROUND_TAB_TEXT] =
color_utils::BlendTowardOppositeLuma(toolbar_text_color, 50);
// Color drawn around the location bar.
colors_[ThemeProperties::COLOR_LOCATION_BAR_BORDER] =
SkColor location_bar_border =
GetBorderColor("GtkToolbar#toolbar GtkEntry#entry");
if (SkColorGetA(location_bar_border)) {
colors_[ThemeProperties::COLOR_LOCATION_BAR_BORDER] = location_bar_border;
}
inactive_selection_bg_color_ =
GetBgColor("GtkEntry#entry:backdrop #selection:selected");
inactive_selection_bg_color_ = GetSelectedBgColor("GtkEntry#entry:backdrop");
inactive_selection_fg_color_ =
GetFgColor("GtkEntry#entry:backdrop #selection:selected");
GetSelectedTextColor("GtkEntry#entry:backdrop");
SkColor toolbar_separator_horizontal =
GetSeparatorColor("GtkToolbar#toolbar GtkSeparator#separator.horizontal");
@ -865,10 +866,8 @@ void GtkUi::LoadGtkValues() {
header_button_inactive_border;
}
SkColor ntp_bg = GetBgColor("");
colors_[ThemeProperties::COLOR_NTP_BACKGROUND] = ntp_bg;
colors_[ThemeProperties::COLOR_NTP_TEXT] =
color_utils::GetReadableColor(GetFgColor("GtkLabel#label"), ntp_bg);
colors_[ThemeProperties::COLOR_NTP_BACKGROUND] = GetBgColor("GtkEntry#entry");
colors_[ThemeProperties::COLOR_NTP_TEXT] = GetFgColor("GtkEntry#entry");
colors_[ThemeProperties::COLOR_NTP_HEADER] =
GetBorderColor("GtkButton#button");
#endif
@ -876,13 +875,13 @@ void GtkUi::LoadGtkValues() {
colors_[ThemeProperties::COLOR_TOOLBAR] = toolbar_color;
colors_[ThemeProperties::COLOR_CONTROL_BACKGROUND] = toolbar_color;
colors_[ThemeProperties::COLOR_NTP_LINK] =
native_theme_->GetSystemColor(ui::NativeTheme::kColorId_LinkEnabled);
colors_[ThemeProperties::COLOR_NTP_LINK] = native_theme_->GetSystemColor(
ui::NativeTheme::kColorId_TextfieldSelectionBackgroundFocused);
// Generate the colors that we pass to WebKit.
SetScrollbarColors();
focus_ring_color_ = native_theme_->GetSystemColor(
ui::NativeTheme::kColorId_TextfieldSelectionBackgroundFocused);
focus_ring_color_ =
native_theme_->GetSystemColor(ui::NativeTheme::kColorId_LinkEnabled);
// Some GTK themes only define the text selection colors on the GtkEntry
// class, so we need to use that for getting selection colors.

@ -220,49 +220,42 @@ CairoSurface::CairoSurface(const gfx::Size& size)
: surface_(cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
size.width(),
size.height())),
cairo_(cairo_create(surface_)) {}
cairo_(cairo_create(surface_)) {
DCHECK(cairo_surface_status(surface_) == CAIRO_STATUS_SUCCESS);
// Clear the surface.
cairo_save(cairo_);
cairo_set_source_rgba(cairo_, 0, 0, 0, 0);
cairo_set_operator(cairo_, CAIRO_OPERATOR_SOURCE);
cairo_paint(cairo_);
cairo_restore(cairo_);
}
CairoSurface::~CairoSurface() {
cairo_destroy(cairo_);
cairo_surface_destroy(surface_);
}
SkColor CairoSurface::GetAveragePixelValue(bool only_frame_pixels) {
SkColor CairoSurface::GetAveragePixelValue(bool frame) {
cairo_surface_flush(surface_);
int num_samples = 0;
long a = 0, r = 0, g = 0, b = 0;
SkColor* data =
reinterpret_cast<SkColor*>(cairo_image_surface_get_data(surface_));
int width = cairo_image_surface_get_width(surface_);
int height = cairo_image_surface_get_height(surface_);
DCHECK(4 * width == cairo_image_surface_get_stride(surface_));
auto accumulate = [&](int x, int y) mutable {
SkColor color = data[y * width + x];
int alpha = SkColorGetA(color);
a += alpha;
r += alpha * SkColorGetR(color);
g += alpha * SkColorGetG(color);
b += alpha * SkColorGetB(color);
num_samples++;
};
if (width == 1 || height == 1 || !only_frame_pixels) {
// Count every pixel in the surface.
for (int x = 0; x < width; x++)
for (int y = 0; y < height; y++)
accumulate(x, y);
} else {
// Count the pixels in the top and bottom rows.
for (int x = 0; x < width; x++)
for (int y : {0, height - 1})
accumulate(x, y);
// Count the pixels in the left and right columns.
for (int x : {0, width - 1})
for (int y = 1; y < height - 1; y++)
accumulate(x, y);
long a = 0, r = 0, g = 0, b = 0;
unsigned int max_alpha = 0;
for (int i = 0; i < width * height; i++) {
SkColor color = data[i];
max_alpha = std::max(SkColorGetA(color), max_alpha);
a += SkColorGetA(color);
r += SkColorGetR(color);
g += SkColorGetG(color);
b += SkColorGetB(color);
}
if (a == 0)
return SK_ColorTRANSPARENT;
return SkColorSetARGB(a / num_samples, r / a, g / a, b / a);
return SkColorSetARGB(frame ? max_alpha : a / (width * height), r * 255 / a,
g * 255 / a, b * 255 / a);
}
bool GtkVersionCheck(int major, int minor, int micro) {
@ -336,14 +329,12 @@ ScopedStyleContext AppendCssNodeToStyleContext(GtkStyleContext* context,
NOTREACHED();
}
} else {
static auto* _gtk_widget_path_iter_set_object_name =
reinterpret_cast<void (*)(GtkWidgetPath*, gint, const char*)>(dlsym(
GetGtk3SharedLibrary(), "gtk_widget_path_iter_set_object_name"));
switch (part_type) {
case CSS_NAME: {
if (GtkVersionCheck(3, 20)) {
static auto* _gtk_widget_path_iter_set_object_name =
reinterpret_cast<void (*)(GtkWidgetPath*, gint, const char*)>(
dlsym(GetGtk3SharedLibrary(),
"gtk_widget_path_iter_set_object_name"));
DCHECK(_gtk_widget_path_iter_set_object_name);
_gtk_widget_path_iter_set_object_name(path, -1, t.token().c_str());
} else {
gtk_widget_path_iter_add_class(path, -1, t.token().c_str());
@ -354,6 +345,10 @@ ScopedStyleContext AppendCssNodeToStyleContext(GtkStyleContext* context,
GType type = g_type_from_name(t.token().c_str());
DCHECK(type);
gtk_widget_path_append_type(path, type);
if (GtkVersionCheck(3, 20)) {
if (t.token() == "GtkLabel")
_gtk_widget_path_iter_set_object_name(path, -1, "label");
}
break;
}
case CSS_CLASS: {
@ -379,7 +374,7 @@ ScopedStyleContext AppendCssNodeToStyleContext(GtkStyleContext* context,
// widgets specially if they want to.
gtk_widget_path_iter_add_class(path, -1, "chromium");
auto child_context = ScopedStyleContext(gtk_style_context_new());
ScopedStyleContext child_context(gtk_style_context_new());
gtk_style_context_set_path(child_context, path);
gtk_style_context_set_state(child_context, state);
gtk_style_context_set_parent(child_context, context);
@ -388,9 +383,10 @@ ScopedStyleContext AppendCssNodeToStyleContext(GtkStyleContext* context,
}
ScopedStyleContext GetStyleContextFromCss(const char* css_selector) {
// Prepend "GtkWindow.background" to the selector since all widgets must live
// Prepend a window node to the selector since all widgets must live
// in a window, but we don't want to specify that every time.
auto context = AppendCssNodeToStyleContext(nullptr, "GtkWindow.background");
auto context =
AppendCssNodeToStyleContext(nullptr, "GtkWindow#window.background");
for (const auto& widget_type :
base::SplitString(css_selector, base::kWhitespaceASCII,
@ -405,26 +401,46 @@ SkColor GdkRgbaToSkColor(const GdkRGBA& color) {
color.blue * 255);
}
SkColor SkColorFromStyleContext(GtkStyleContext* context) {
SkColor GetFgColorFromStyleContext(GtkStyleContext* context) {
GdkRGBA color;
gtk_style_context_get_color(context, gtk_style_context_get_state(context),
&color);
return GdkRgbaToSkColor(color);
}
SkColor GetFgColor(const char* css_selector) {
return SkColorFromStyleContext(GetStyleContextFromCss(css_selector));
SkColor GetBgColorFromStyleContext(GtkStyleContext* context) {
// Backgrounds are more general than solid colors (eg. gradients),
// but chromium requires us to boil this down to one color. We
// cannot use the background-color here because some themes leave it
// set to a garbage color because a background-image will cover it
// anyway. So we instead render the background into a 24x24 bitmap,
// removing any borders, and hope that we get a good color.
ApplyCssToContext(context,
"* {"
"border-radius: 0px;"
"border-style: none;"
"box-shadow: none;"
"}");
gfx::Size size(24, 24);
CairoSurface surface(size);
RenderBackground(size, surface.cairo(), context);
return surface.GetAveragePixelValue(false);
}
GtkCssProvider* GetCssProvider(const char* css) {
SkColor GetFgColor(const char* css_selector) {
return GetFgColorFromStyleContext(GetStyleContextFromCss(css_selector));
}
ScopedCssProvider GetCssProvider(const char* css) {
GtkCssProvider* provider = gtk_css_provider_new();
GError* error = nullptr;
gtk_css_provider_load_from_data(provider, css, -1, &error);
DCHECK(!error);
return provider;
return ScopedCssProvider(provider);
}
void ApplyCssToContext(GtkStyleContext* context, GtkCssProvider* provider) {
void ApplyCssProviderToContext(GtkStyleContext* context,
GtkCssProvider* provider) {
while (context) {
gtk_style_context_add_provider(context, GTK_STYLE_PROVIDER(provider),
G_MAXUINT);
@ -432,35 +448,9 @@ void ApplyCssToContext(GtkStyleContext* context, GtkCssProvider* provider) {
}
}
void RemoveBorders(GtkStyleContext* context) {
static GtkCssProvider* provider = GetCssProvider(
"* {"
"border-style: none;"
"border-radius: 0px;"
"border-width: 0px;"
"border-image-width: 0px;"
"box-shadow: none;"
"padding: 0px;"
"margin: 0px;"
"outline: none;"
"outline-width: 0px;"
"}");
ApplyCssToContext(context, provider);
}
void AddBorders(GtkStyleContext* context) {
static GtkCssProvider* provider = GetCssProvider(
"* {"
"border-style: solid;"
"border-radius: 0px;"
"border-width: 1px;"
"box-shadow: none;"
"padding: 0px;"
"margin: 0px;"
"outline: none;"
"outline-width: 0px;"
"}");
ApplyCssToContext(context, provider);
void ApplyCssToContext(GtkStyleContext* context, const char* css) {
auto provider = GetCssProvider(css);
ApplyCssProviderToContext(context, provider);
}
void RenderBackground(const gfx::Size& size,
@ -472,66 +462,78 @@ void RenderBackground(const gfx::Size& size,
gtk_render_background(context, cr, 0, 0, size.width(), size.height());
}
gfx::Size GetMinimumWidgetSize(GtkStyleContext* context) {
if (!GtkVersionCheck(3, 20))
return gfx::Size(1, 1);
int w = 1, h = 1;
gtk_style_context_get(context, gtk_style_context_get_state(context),
"min-width", &w, "min-height", &h, NULL);
DCHECK(w >= 0 && h >= 0);
return gfx::Size(w ? w : 1, h ? h : 1);
}
SkColor GetBgColor(const char* css_selector) {
// Backgrounds are more general than solid colors (eg. gradients),
// but chromium requires us to boil this down to one color. We
// cannot use the background-color here because some themes leave it
// set to a garbage color because a background-image will cover it
// anyway. So we instead render the background into a single pixel,
// removing any borders, and hope that we get a good color.
auto context = GetStyleContextFromCss(css_selector);
RemoveBorders(context);
gfx::Size size = GetMinimumWidgetSize(context);
CairoSurface surface(size);
RenderBackground(size, surface.cairo(), context);
return surface.GetAveragePixelValue(false);
return GetBgColorFromStyleContext(GetStyleContextFromCss(css_selector));
}
SkColor GetBorderColor(const char* css_selector) {
// Borders have the same issue as backgrounds, due to the
// border-image property.
auto context = GetStyleContextFromCss(css_selector);
GtkStateFlags state = gtk_style_context_get_state(context);
GtkBorderStyle border_style = GTK_BORDER_STYLE_NONE;
gtk_style_context_get(context, state, GTK_STYLE_PROPERTY_BORDER_STYLE,
&border_style, nullptr);
GtkBorder border;
gtk_style_context_get_border(context, state, &border);
if ((border_style == GTK_BORDER_STYLE_NONE ||
border_style == GTK_BORDER_STYLE_HIDDEN) ||
(!border.left && !border.right && !border.top && !border.bottom)) {
return SK_ColorTRANSPARENT;
}
AddBorders(context);
gfx::Size size = GetMinimumWidgetSize(context);
gfx::Size size(24, 24);
CairoSurface surface(size);
RenderBackground(size, surface.cairo(), context);
gtk_render_frame(context, surface.cairo(), 0, 0, size.width(), size.height());
return surface.GetAveragePixelValue(true);
}
ScopedStyleContext GetSelectedStyleContext(const char* css_selector) {
auto context = GetStyleContextFromCss(css_selector);
if (GtkVersionCheck(3, 20)) {
context = AppendCssNodeToStyleContext(context, "#selection");
} else {
GtkStateFlags state = gtk_style_context_get_state(context);
state = static_cast<GtkStateFlags>(state | GTK_STATE_FLAG_SELECTED);
gtk_style_context_set_state(context, state);
}
return context;
}
SkColor GetSelectedTextColor(const char* css_selector) {
return GetFgColorFromStyleContext(GetSelectedStyleContext(css_selector));
}
SkColor GetSelectedBgColor(const char* css_selector) {
auto context = GetSelectedStyleContext(css_selector);
if (GtkVersionCheck(3, 20))
return GetBgColorFromStyleContext(context);
// This is verbatim how Gtk gets the selection color on versions before 3.20.
GdkRGBA selection_color;
G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
gtk_style_context_get_background_color(
context, gtk_style_context_get_state(context), &selection_color);
G_GNUC_END_IGNORE_DEPRECATIONS;
return GdkRgbaToSkColor(selection_color);
}
SkColor GetSeparatorColor(const char* css_selector) {
if (!GtkVersionCheck(3, 20))
return GetFgColor(css_selector);
// Some themes use borders to render separators, others use a
// background color. Only return the border color if there is one.
SkColor border = GetBorderColor(css_selector);
if (SkColorGetA(border))
return border;
auto context = GetStyleContextFromCss(css_selector);
int w = 1, h = 1;
gtk_style_context_get(context, gtk_style_context_get_state(context),
"min-width", &w, "min-height", &h, nullptr);
GtkBorder border, padding;
GtkStateFlags state = gtk_style_context_get_state(context);
gtk_style_context_get_border(context, state, &border);
gtk_style_context_get_padding(context, state, &padding);
w += border.left + padding.left + padding.right + border.right;
h += border.top + padding.top + padding.bottom + border.bottom;
return GetBgColor(css_selector);
bool horizontal = gtk_style_context_has_class(context, "horizontal");
if (horizontal) {
w = 24;
h = std::max(h, 1);
} else {
DCHECK(gtk_style_context_has_class(context, "vertical"));
h = 24;
w = std::max(w, 1);
}
CairoSurface surface(gfx::Size(w, h));
gtk_render_background(context, surface.cairo(), 0, 0, w, h);
gtk_render_frame(context, surface.cairo(), 0, 0, w, h);
return surface.GetAveragePixelValue(false);
}
#endif

@ -98,10 +98,10 @@ class CairoSurface {
// Get the drawing context for GTK to use.
cairo_t* cairo() { return cairo_; }
// If |only_frame_pixels| is false, returns the average of all
// pixels in the surface, otherwise returns the average of only the
// edge pixels.
SkColor GetAveragePixelValue(bool only_frame_pixels);
// Returns the average of all pixels in the surface. If |frame| is
// true, the resulting alpha will be the average alpha, otherwise it
// will be the max alpha across all pixels.
SkColor GetAveragePixelValue(bool frame);
private:
cairo_surface_t* surface_;
@ -112,18 +112,18 @@ class CairoSurface {
// |major|.|minor|.|micro|.
bool GtkVersionCheck(int major, int minor = 0, int micro = 0);
// Similar in spirit to a std::unique_ptr.
template <typename T>
class ScopedGObject {
public:
explicit ScopedGObject(T* obj) : obj_(obj) {
// Increase the reference count of |obj_|, removing the floating
// reference if it has one.
g_object_ref_sink(obj_);
// Remove the floating reference from |obj_| if it has one.
if (g_object_is_floating(obj_))
g_object_ref_sink(obj_);
DCHECK(G_OBJECT(obj_)->ref_count == 1);
}
ScopedGObject(const ScopedGObject<T>& other) : obj_(other.obj_) {
g_object_ref(obj_);
}
ScopedGObject(const ScopedGObject<T>& other) = delete;
ScopedGObject(ScopedGObject<T>&& other) : obj_(other.obj_) {
other.obj_ = nullptr;
@ -131,15 +131,10 @@ class ScopedGObject {
~ScopedGObject() {
if (obj_)
g_object_unref(obj_);
Unref();
}
ScopedGObject<T>& operator=(const ScopedGObject<T>& other) {
g_object_ref(other.obj_);
g_object_unref(obj_);
obj_ = other.obj_;
return *this;
}
ScopedGObject<T>& operator=(const ScopedGObject<T>& other) = delete;
ScopedGObject<T>& operator=(ScopedGObject<T>&& other) {
g_object_unref(obj_);
@ -151,10 +146,35 @@ class ScopedGObject {
operator T*() { return obj_; }
private:
void Unref() { g_object_unref(obj_); }
T* obj_;
};
template <>
inline void ScopedGObject<GtkStyleContext>::Unref() {
// Versions of GTK earlier than 3.15.4 had a bug where a g_assert
// would be triggered when trying to free a GtkStyleContext that had
// a parent whose only reference was the child context in question.
// This is a hack to work around that case. See GTK commit
// "gtkstylecontext: Don't try to emit a signal when finalizing".
GtkStyleContext* context = obj_;
while (context) {
GtkStyleContext* parent = gtk_style_context_get_parent(context);
if (parent && G_OBJECT(context)->ref_count == 1 &&
!GtkVersionCheck(3, 15, 4)) {
g_object_ref(parent);
gtk_style_context_set_parent(context, nullptr);
} else {
g_object_unref(context);
return;
}
context = parent;
}
}
typedef ScopedGObject<GtkStyleContext> ScopedStyleContext;
typedef ScopedGObject<GtkCssProvider> ScopedCssProvider;
// If |context| is NULL, creates a new top-level style context
// specified by parsing |css_node|. Otherwise, creates the child
@ -170,10 +190,11 @@ ScopedStyleContext AppendCssNodeToStyleContext(GtkStyleContext* context,
// must g_object_unref() the returned context.
ScopedStyleContext GetStyleContextFromCss(const char* css_selector);
SkColor SkColorFromStyleContext(GtkStyleContext* context);
SkColor GetFgColorFromStyleContext(GtkStyleContext* context);
// Removes all border-type properties on |context| and all of its parents.
void RemoveBorders(GtkStyleContext* context);
// Overrides properties on |context| and all its parents with those
// provided by |css|.
void ApplyCssToContext(GtkStyleContext* context, const char* css);
// Get the 'color' property from the style context created by
// GetStyleContextFromCss(|css_selector|).
@ -186,15 +207,18 @@ void RenderBackground(const gfx::Size& size,
GtkStyleContext* context);
// Renders a background from the style context created by
// GetStyleContextFromCss(|css_selector|) into a single pixel and
// returns the color.
// GetStyleContextFromCss(|css_selector|) into a 24x24 bitmap and
// returns the average color.
SkColor GetBgColor(const char* css_selector);
// If there is a border, renders the border from the style context
// created by GetStyleContextFromCss(|css_selector|) into a single
// pixel and returns the color. Otherwise returns kInvalidColor.
// Renders the border from the style context created by
// GetStyleContextFromCss(|css_selector|) into a 24x24 bitmap and
// returns the average color.
SkColor GetBorderColor(const char* css_selector);
SkColor GetSelectedTextColor(const char* css_selector);
SkColor GetSelectedBgColor(const char* css_selector);
// Get the color of the GtkSeparator specified by |css_selector|.
SkColor GetSeparatorColor(const char* css_selector);
#endif

@ -106,35 +106,35 @@ SkColor SkColorFromColorId(ui::NativeTheme::ColorId color_id) {
case ui::NativeTheme::kColorId_FocusedMenuItemBackgroundColor:
return GetBgColor("GtkMenu#menu GtkMenuItem#menuitem:hover");
case ui::NativeTheme::kColorId_EnabledMenuItemForegroundColor:
return GetFgColor("GtkMenu#menu GtkMenuItem#menuitem GtkLabel#label");
return GetFgColor("GtkMenu#menu GtkMenuItem#menuitem GtkLabel");
case ui::NativeTheme::kColorId_SelectedMenuItemForegroundColor:
return GetFgColor(
"GtkMenu#menu GtkMenuItem#menuitem:hover GtkLabel#label");
return GetFgColor("GtkMenu#menu GtkMenuItem#menuitem:hover GtkLabel");
case ui::NativeTheme::kColorId_DisabledMenuItemForegroundColor:
return GetFgColor(
"GtkMenu#menu GtkMenuItem#menuitem:disabled GtkLabel#label");
return GetFgColor("GtkMenu#menu GtkMenuItem#menuitem:disabled GtkLabel");
case ui::NativeTheme::kColorId_MenuItemSubtitleColor:
return GetFgColor(
"GtkMenu#menu GtkMenuItem#menuitem GtkLabel#label.accelerator");
"GtkMenu#menu GtkMenuItem#menuitem GtkLabel.accelerator");
case ui::NativeTheme::kColorId_MenuSeparatorColor:
// MenuButton borders are used as vertical menu separators in Chrome.
case ui::NativeTheme::kColorId_EnabledMenuButtonBorderColor:
case ui::NativeTheme::kColorId_FocusedMenuButtonBorderColor:
case ui::NativeTheme::kColorId_HoverMenuButtonBorderColor:
if (GtkVersionCheck(3, 20))
return GetBgColor("GtkMenu#menu GtkSeparator#separator");
else
if (GtkVersionCheck(3, 20)) {
return GetSeparatorColor(
"GtkMenu#menu GtkSeparator#separator.horizontal");
} else {
return GetFgColor("GtkMenu#menu GtkMenuItem#menuitem.separator");
}
// Label
case ui::NativeTheme::kColorId_LabelEnabledColor:
return GetFgColor("GtkLabel#label");
return GetFgColor("GtkLabel");
case ui::NativeTheme::kColorId_LabelDisabledColor:
return GetFgColor("GtkLabel#label:disabled");
return GetFgColor("GtkLabel:disabled");
case ui::NativeTheme::kColorId_LabelTextSelectionColor:
return GetFgColor("GtkLabel#label #selection:selected");
return GetSelectedTextColor("GtkLabel");
case ui::NativeTheme::kColorId_LabelTextSelectionBackgroundFocused:
return GetBgColor("GtkLabel#label #selection:selected");
return GetSelectedBgColor("GtkLabel");
// Link
case ui::NativeTheme::kColorId_LinkDisabled:
@ -142,13 +142,13 @@ SkColor SkColorFromColorId(ui::NativeTheme::ColorId color_id) {
SkColorFromColorId(ui::NativeTheme::kColorId_LinkEnabled), 0xBB);
case ui::NativeTheme::kColorId_LinkPressed:
if (GtkVersionCheck(3, 12))
return GetFgColor("GtkLabel#label.link:link:hover:active");
return GetFgColor("GtkLabel.link:link:hover:active");
// fallthrough
case ui::NativeTheme::kColorId_LinkEnabled: {
if (GtkVersionCheck(3, 12)) {
return GetFgColor("GtkLabel#label.link:link");
return GetFgColor("GtkLabel.link:link");
}
auto link_context = GetStyleContextFromCss("GtkLabel#label.view");
auto link_context = GetStyleContextFromCss("GtkLabel.view");
GdkColor* color;
gtk_style_context_get_style(link_context, "link-color", &color, nullptr);
if (color) {
@ -173,28 +173,28 @@ SkColor SkColorFromColorId(ui::NativeTheme::ColorId color_id) {
// Button
case ui::NativeTheme::kColorId_ButtonEnabledColor:
return GetFgColor("GtkButton#button.text-button GtkLabel#label");
return GetFgColor("GtkButton#button.text-button GtkLabel");
case ui::NativeTheme::kColorId_ButtonDisabledColor:
return GetFgColor("GtkButton#button.text-button:disabled GtkLabel#label");
return GetFgColor("GtkButton#button.text-button:disabled GtkLabel");
case ui::NativeTheme::kColorId_ButtonHoverColor:
return GetFgColor("GtkButton#button.text-button:hover GtkLabel#label");
return GetFgColor("GtkButton#button.text-button:hover GtkLabel");
case ui::NativeTheme::kColorId_ButtonPressedShade:
return SK_ColorTRANSPARENT;
case ui::NativeTheme::kColorId_BlueButtonEnabledColor:
return GetFgColor(
"GtkButton#button.text-button.suggested-action GtkLabel#label");
"GtkButton#button.text-button.suggested-action GtkLabel");
case ui::NativeTheme::kColorId_BlueButtonDisabledColor:
return GetFgColor(
"GtkButton#button.text-button.suggested-action:disabled "
"GtkLabel#label");
"GtkLabel");
case ui::NativeTheme::kColorId_BlueButtonHoverColor:
return GetFgColor(
"GtkButton#button.text-button.suggested-action:hover GtkLabel#label");
"GtkButton#button.text-button.suggested-action:hover GtkLabel");
case ui::NativeTheme::kColorId_BlueButtonPressedColor:
return GetFgColor(
"GtkButton#button.text-button.suggested-action:hover:active "
"GtkLabel#label");
"GtkLabel");
case ui::NativeTheme::kColorId_BlueButtonShadowColor:
return SK_ColorTRANSPARENT;
@ -202,7 +202,7 @@ SkColor SkColorFromColorId(ui::NativeTheme::ColorId color_id) {
return GetBgColor("GtkButton#button.text-button.destructive-action");
case ui::NativeTheme::kColorId_TextOnProminentButtonColor:
return GetFgColor(
"GtkButton#button.text-button.destructive-action GtkLabel#label");
"GtkButton#button.text-button.destructive-action GtkLabel");
// Textfield
case ui::NativeTheme::kColorId_TextfieldDefaultColor:
@ -214,9 +214,9 @@ SkColor SkColorFromColorId(ui::NativeTheme::ColorId color_id) {
case ui::NativeTheme::kColorId_TextfieldReadOnlyBackground:
return GetBgColor("GtkEntry#entry:disabled");
case ui::NativeTheme::kColorId_TextfieldSelectionColor:
return GetFgColor("GtkEntry#entry #selection:selected");
return GetSelectedTextColor("GtkEntry#entry");
case ui::NativeTheme::kColorId_TextfieldSelectionBackgroundFocused:
return GetBgColor("GtkEntry#entry #selection:selected");
return GetSelectedBgColor("GtkEntry#entry");
// Tooltips
case ui::NativeTheme::kColorId_TooltipBackground:
@ -233,13 +233,13 @@ SkColor SkColorFromColorId(ui::NativeTheme::ColorId color_id) {
case ui::NativeTheme::kColorId_TreeText:
case ui::NativeTheme::kColorId_TreeArrow:
case ui::NativeTheme::kColorId_TableGroupingIndicatorColor:
return GetFgColor("GtkTreeView#treeview.view .view.cell GtkLabel#label");
return GetFgColor("GtkTreeView#treeview.view .view.cell GtkLabel");
case ui::NativeTheme::kColorId_TableSelectedText:
case ui::NativeTheme::kColorId_TableSelectedTextUnfocused:
case ui::NativeTheme::kColorId_TreeSelectedText:
case ui::NativeTheme::kColorId_TreeSelectedTextUnfocused:
return GetFgColor(
"GtkTreeView#treeview.view .view.cell:selected:focus GtkLabel#label");
"GtkTreeView#treeview.view .view.cell:selected:focus GtkLabel");
case ui::NativeTheme::kColorId_TableSelectionBackgroundFocused:
case ui::NativeTheme::kColorId_TableSelectionBackgroundUnfocused:
case ui::NativeTheme::kColorId_TreeSelectionBackgroundFocused:
@ -248,43 +248,39 @@ SkColor SkColorFromColorId(ui::NativeTheme::ColorId color_id) {
// Table Header
case ui::NativeTheme::kColorId_TableHeaderText:
return GetFgColor(
"GtkTreeView#treeview.view GtkButton#button GtkLabel#label");
return GetFgColor("GtkTreeView#treeview.view GtkButton#button GtkLabel");
case ui::NativeTheme::kColorId_TableHeaderBackground:
return GetBgColor("GtkTreeView#treeview.view GtkButton#button");
case ui::NativeTheme::kColorId_TableHeaderSeparator:
return GetBorderColor("GtkTreeView#treeview.view GtkButton#button");
// Results Table
// TODO(thomasanderson): The GtkEntry selectors was how the gtk2 theme got
// these colors. Update this code to use a different widget.
case ui::NativeTheme::kColorId_ResultsTableNormalBackground:
return GetBgColor("GtkEntry#entry");
case ui::NativeTheme::kColorId_ResultsTableHoveredBackground:
return color_utils::AlphaBlend(
GetBgColor("GtkEntry#entry"),
GetBgColor("GtkEntry#entry #selection:selected"), 0x80);
return color_utils::AlphaBlend(GetBgColor("GtkEntry#entry"),
GetSelectedBgColor("GtkEntry#entry"),
0x80);
case ui::NativeTheme::kColorId_ResultsTableSelectedBackground:
return GetBgColor("GtkEntry#entry #selection:selected");
return GetSelectedBgColor("GtkEntry#entry");
case ui::NativeTheme::kColorId_ResultsTableNormalText:
case ui::NativeTheme::kColorId_ResultsTableHoveredText:
return GetFgColor("GtkEntry#entry");
case ui::NativeTheme::kColorId_ResultsTableSelectedText:
return GetFgColor("GtkEntry#entry #selection:selected");
return GetSelectedTextColor("GtkEntry#entry");
case ui::NativeTheme::kColorId_ResultsTableNormalDimmedText:
case ui::NativeTheme::kColorId_ResultsTableHoveredDimmedText:
return color_utils::AlphaBlend(GetFgColor("GtkEntry#entry"),
GetBgColor("GtkEntry#entry"), 0x80);
case ui::NativeTheme::kColorId_ResultsTableSelectedDimmedText:
return color_utils::AlphaBlend(
GetFgColor("GtkEntry#entry #selection:selected"),
GetBgColor("GtkEntry#entry"), 0x80);
return color_utils::AlphaBlend(GetSelectedTextColor("GtkEntry#entry"),
GetBgColor("GtkEntry#entry"), 0x80);
case ui::NativeTheme::kColorId_ResultsTableNormalUrl:
case ui::NativeTheme::kColorId_ResultsTableHoveredUrl:
return NormalURLColor(GetFgColor("GtkEntry#entry"));
case ui::NativeTheme::kColorId_ResultsTableSelectedUrl:
return SelectedURLColor(GetFgColor("GtkEntry#entry #selection:selected"),
GetBgColor("GtkEntry#entry #selection:selected"));
return SelectedURLColor(GetSelectedTextColor("GtkEntry#entry"),
GetSelectedBgColor("GtkEntry#entry"));
case ui::NativeTheme::kColorId_ResultsTablePositiveText:
return color_utils::GetReadableColor(kPositiveTextColor,
@ -413,7 +409,7 @@ void NativeThemeGtk3::PaintArrowButton(SkCanvas* canvas,
}
PaintWidget(canvas, rect, context, BG_RENDER_NORMAL, true);
PaintArrow(canvas, rect, direction, SkColorFromStyleContext(context));
PaintArrow(canvas, rect, direction, GetFgColorFromStyleContext(context));
}
void NativeThemeGtk3::PaintScrollbarTrack(
@ -483,28 +479,29 @@ void NativeThemeGtk3::PaintMenuSeparator(
case ui::UPPER_SEPARATOR:
return 0;
default:
return rect.height() / 2;
return (rect.height() - separator_thickness) / 2;
}
};
if (GtkVersionCheck(3, 20)) {
auto context =
GetStyleContextFromCss("GtkMenu#menu GtkSeparator#separator");
auto context = GetStyleContextFromCss(
"GtkMenu#menu GtkSeparator#separator.horizontal");
GtkBorder margin, border, padding;
GtkStateFlags state = gtk_style_context_get_state(context);
gtk_style_context_get_margin(context, state, &margin);
gtk_style_context_get_border(context, state, &border);
gtk_style_context_get_padding(context, state, &padding);
int min_height = 0;
int min_height = 1;
gtk_style_context_get(context, state, "min-height", &min_height, NULL);
int w = rect.width() - margin.left - margin.right;
int h =
min_height + padding.top + padding.bottom + border.top + border.bottom;
int h = std::max(
min_height + padding.top + padding.bottom + border.top + border.bottom,
1);
int x = margin.left;
int y = separator_offset(h);
PaintWidget(canvas, gfx::Rect(x, y, w, h), context, BG_RENDER_NORMAL, true);
} else {
auto context =
GetStyleContextFromCss("GtkMenu#menu GtkMenuItem#menuitem.separator");
auto context = GetStyleContextFromCss(
"GtkMenu#menu GtkMenuItem#menuitem.separator.horizontal");
gboolean wide_separators = false;
gint separator_height = 0;
gtk_style_context_get_style(context, "wide-separators", &wide_separators,
@ -522,7 +519,7 @@ void NativeThemeGtk3::PaintMenuSeparator(
PaintWidget(canvas, gfx::Rect(x, y, w, h), context, BG_RENDER_NONE, true);
} else {
SkPaint paint;
paint.setColor(SkColorFromStyleContext(context));
paint.setColor(GetFgColorFromStyleContext(context));
canvas->drawLine(x, y, x + w, y, paint);
}
}
@ -536,7 +533,7 @@ void NativeThemeGtk3::PaintFrameTopArea(
auto context = GetStyleContextFromCss(frame_top_area.use_custom_frame
? "#headerbar.header-bar.titlebar"
: "GtkMenuBar#menubar");
RemoveBorders(context);
ApplyCssToContext(context, "* { border-radius: 0px; border-style: none; }");
gtk_style_context_set_state(context, frame_top_area.is_active
? GTK_STATE_FLAG_NORMAL
: GTK_STATE_FLAG_BACKDROP);

@ -16,24 +16,18 @@ mostly a list of about 80 colors (see //src/ui/native_theme/native_theme.h)
overridden by the theme. Chromium usually doesn't use GTK to render entire
widgets, but instead tries to determine colors from them.
There are three types of colors Chromium needs from widgets:
* Foreground color: determined by the CSS "color" property
* Background color: determined by the CSS "background-color" and "background-image" properties
* Border color: determined by the "border-color", "border-image",
"border-style", and "border-width" properties
Backgrounds and borders are complicated because in general they might have
multiple gradients or images. To get the color, Chromium uses GTK to render the
background or border into a single pixel and uses the resulting color for
theming. This mostly gives reasonable results, but in case theme authors do not
like the resulting color, they have the option to theme Chromium widgets
specially.
Chromium needs foreground, background and border colors from widgets. The
foreground color is simply taken from the CSS "color" property. Backgrounds and
borders are complicated because in general they might have multiple gradients or
images. To get the color, Chromium uses GTK to render the background or border
into a 24x24 bitmap and uses the average color for theming. This mostly gives
reasonable results, but in case theme authors do not like the resulting color,
they have the option to theme Chromium widgets specially.
## Note to GTK theme authors: How to theme Chromium widgets
Every widget Chromium uses will have a "chromium" style class added to it. For
example, a texfield selector might look like:
example, a textfield selector might look like:
```
.window.background.chromium .entry.chromium
@ -59,11 +53,6 @@ they might use:
For GTK3.20 or later, themes will as usual have to replace ".entry" with
"entry".
Additional requirements for border colors to be picked up:
* Must have a border-style that is not none.
* Must have a border-width that is nonzero.
The list of CSS selectors that Chromium uses to determine its colors is in
//src/chrome/browser/ui/libgtkui/native_theme_gtk3.cc.