blob: 1ae5b7be92e5df8e42bd7d280b2ff583554f539a [file] [log] [blame]
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/libgtkui/native_theme_gtk2.h"
#include <gtk/gtk.h>
#include "chrome/browser/ui/libgtkui/chrome_gtk_frame.h"
#include "chrome/browser/ui/libgtkui/chrome_gtk_menu_subclasses.h"
#include "chrome/browser/ui/libgtkui/gtk_ui.h"
#include "chrome/browser/ui/libgtkui/gtk_util.h"
#include "chrome/browser/ui/libgtkui/skia_utils_gtk.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/gfx/color_palette.h"
#include "ui/gfx/color_utils.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/path.h"
#include "ui/gfx/skia_util.h"
#include "ui/native_theme/common_theme.h"
#include "ui/native_theme/native_theme_aura.h"
#include "ui/native_theme/native_theme_dark_aura.h"
namespace libgtkui {
namespace {
enum WidgetState {
NORMAL = 0,
ACTIVE = 1,
PRELIGHT = 2,
SELECTED = 3,
INSENSITIVE = 4,
};
// Same order as enum WidgetState above
const GtkStateType stateMap[] = {
GTK_STATE_NORMAL, GTK_STATE_ACTIVE, GTK_STATE_PRELIGHT,
GTK_STATE_SELECTED, GTK_STATE_INSENSITIVE,
};
SkColor GetFgColor(GtkWidget* widget, WidgetState state) {
return GdkColorToSkColor(gtk_rc_get_style(widget)->fg[stateMap[state]]);
}
SkColor GetBgColor(GtkWidget* widget, WidgetState state) {
return GdkColorToSkColor(gtk_rc_get_style(widget)->bg[stateMap[state]]);
}
SkColor GetTextColor(GtkWidget* widget, WidgetState state) {
return GdkColorToSkColor(gtk_rc_get_style(widget)->text[stateMap[state]]);
}
SkColor GetTextAAColor(GtkWidget* widget, WidgetState state) {
return GdkColorToSkColor(gtk_rc_get_style(widget)->text_aa[stateMap[state]]);
}
SkColor GetBaseColor(GtkWidget* widget, WidgetState state) {
return GdkColorToSkColor(gtk_rc_get_style(widget)->base[stateMap[state]]);
}
} // namespace
// static
NativeThemeGtk2* NativeThemeGtk2::instance() {
CR_DEFINE_STATIC_LOCAL(NativeThemeGtk2, s_native_theme, ());
return &s_native_theme;
}
// Constructors automatically called
NativeThemeGtk2::NativeThemeGtk2() {}
// This doesn't actually get called
NativeThemeGtk2::~NativeThemeGtk2() {}
void NativeThemeGtk2::PaintMenuPopupBackground(
cc::PaintCanvas* canvas,
const gfx::Size& size,
const MenuBackgroundExtraParams& menu_background) const {
if (menu_background.corner_radius > 0) {
cc::PaintFlags flags;
flags.setStyle(cc::PaintFlags::kFill_Style);
flags.setFlags(cc::PaintFlags::kAntiAlias_Flag);
flags.setColor(GetSystemColor(kColorId_MenuBackgroundColor));
gfx::Path path;
SkRect rect = SkRect::MakeWH(SkIntToScalar(size.width()),
SkIntToScalar(size.height()));
SkScalar radius = SkIntToScalar(menu_background.corner_radius);
SkScalar radii[8] = {radius, radius, radius, radius,
radius, radius, radius, radius};
path.addRoundRect(rect, radii);
canvas->drawPath(path, flags);
} else {
canvas->drawColor(GetSystemColor(kColorId_MenuBackgroundColor),
SkBlendMode::kSrc);
}
}
void NativeThemeGtk2::PaintMenuItemBackground(
cc::PaintCanvas* canvas,
State state,
const gfx::Rect& rect,
const MenuItemExtraParams& menu_item) const {
SkColor color;
cc::PaintFlags flags;
switch (state) {
case NativeTheme::kNormal:
case NativeTheme::kDisabled:
color = GetSystemColor(NativeTheme::kColorId_MenuBackgroundColor);
flags.setColor(color);
break;
case NativeTheme::kHovered:
color =
GetSystemColor(NativeTheme::kColorId_FocusedMenuItemBackgroundColor);
flags.setColor(color);
break;
default:
NOTREACHED() << "Invalid state " << state;
break;
}
if (menu_item.corner_radius > 0) {
const SkScalar radius = SkIntToScalar(menu_item.corner_radius);
canvas->drawRoundRect(gfx::RectToSkRect(rect), radius, radius, flags);
return;
}
canvas->drawRect(gfx::RectToSkRect(rect), flags);
}
SkColor NativeThemeGtk2::GetSystemColor(ColorId color_id) const {
const SkColor kPositiveTextColor = SkColorSetRGB(0x0b, 0x80, 0x43);
const SkColor kNegativeTextColor = SkColorSetRGB(0xc5, 0x39, 0x29);
switch (color_id) {
// Windows
case kColorId_WindowBackground:
return GetBgColor(GetWindow(), SELECTED);
// Dialogs
case kColorId_DialogBackground:
case kColorId_BubbleBackground:
return GetBgColor(GetWindow(), NORMAL);
// FocusableBorder
case kColorId_FocusedBorderColor:
return GetBgColor(GetEntry(), SELECTED);
case kColorId_UnfocusedBorderColor:
return GetTextAAColor(GetEntry(), NORMAL);
// MenuItem
case kColorId_SelectedMenuItemForegroundColor:
return GetTextColor(GetMenuItem(), SELECTED);
case kColorId_FocusedMenuItemBackgroundColor:
return GetBgColor(GetMenuItem(), SELECTED);
case kColorId_EnabledMenuItemForegroundColor:
return GetTextColor(GetMenuItem(), NORMAL);
case kColorId_MenuItemSubtitleColor:
case kColorId_DisabledMenuItemForegroundColor:
return GetTextColor(GetMenuItem(), INSENSITIVE);
case kColorId_FocusedMenuButtonBorderColor:
return GetBgColor(GetEntry(), NORMAL);
case kColorId_HoverMenuButtonBorderColor:
return GetTextAAColor(GetEntry(), PRELIGHT);
case kColorId_MenuBorderColor:
case kColorId_EnabledMenuButtonBorderColor:
case kColorId_MenuSeparatorColor: {
return GetTextColor(GetMenuItem(), INSENSITIVE);
}
case kColorId_MenuBackgroundColor:
return GetBgColor(GetMenu(), NORMAL);
// Label
case kColorId_LabelEnabledColor:
return GetTextColor(GetEntry(), NORMAL);
case kColorId_LabelDisabledColor:
return GetTextColor(GetLabel(), INSENSITIVE);
case kColorId_LabelTextSelectionColor:
return GetTextColor(GetLabel(), SELECTED);
case kColorId_LabelTextSelectionBackgroundFocused:
return GetBaseColor(GetLabel(), SELECTED);
// Link
case kColorId_LinkDisabled:
return SkColorSetA(GetSystemColor(kColorId_LinkEnabled), 0xBB);
case kColorId_LinkEnabled: {
SkColor link_color = SK_ColorTRANSPARENT;
GdkColor* style_color = nullptr;
gtk_widget_style_get(GetWindow(), "link-color", &style_color, nullptr);
if (style_color) {
link_color = GdkColorToSkColor(*style_color);
gdk_color_free(style_color);
}
if (link_color != SK_ColorTRANSPARENT)
return link_color;
// Default color comes from gtklinkbutton.c.
return SkColorSetRGB(0x00, 0x00, 0xEE);
}
case kColorId_LinkPressed:
return SK_ColorRED;
// Separator
case kColorId_SeparatorColor:
return GetFgColor(GetSeparator(), INSENSITIVE);
// Button
case kColorId_ButtonEnabledColor:
return GetTextColor(GetButton(), NORMAL);
case kColorId_BlueButtonEnabledColor:
return GetTextColor(GetBlueButton(), NORMAL);
case kColorId_ButtonDisabledColor:
return GetTextColor(GetButton(), INSENSITIVE);
case kColorId_BlueButtonDisabledColor:
return GetTextColor(GetBlueButton(), INSENSITIVE);
case kColorId_ButtonHoverColor:
return GetTextColor(GetButton(), PRELIGHT);
case kColorId_BlueButtonHoverColor:
return GetTextColor(GetBlueButton(), PRELIGHT);
case kColorId_BlueButtonPressedColor:
return GetTextColor(GetBlueButton(), ACTIVE);
case kColorId_BlueButtonShadowColor:
return SK_ColorTRANSPARENT;
case kColorId_ProminentButtonColor:
return GetSystemColor(kColorId_LinkEnabled);
case kColorId_TextOnProminentButtonColor:
return GetTextColor(GetLabel(), SELECTED);
case kColorId_ButtonPressedShade:
return SK_ColorTRANSPARENT;
// Textfield
case kColorId_TextfieldDefaultColor:
return GetTextColor(GetEntry(), NORMAL);
case kColorId_TextfieldDefaultBackground:
return GetBaseColor(GetEntry(), NORMAL);
case kColorId_TextfieldReadOnlyColor:
return GetTextColor(GetEntry(), ACTIVE);
case kColorId_TextfieldReadOnlyBackground:
return GetBaseColor(GetEntry(), ACTIVE);
case kColorId_TextfieldSelectionColor:
return GetTextColor(GetEntry(), SELECTED);
case kColorId_TextfieldSelectionBackgroundFocused:
return GetBaseColor(GetEntry(), SELECTED);
// Tooltips
case kColorId_TooltipBackground:
return GetBgColor(GetTooltip(), NORMAL);
case kColorId_TooltipText:
return GetFgColor(GetTooltip(), NORMAL);
// Trees and Tables (implemented on GTK using the same class)
case kColorId_TableBackground:
case kColorId_TreeBackground:
return GetBgColor(GetTree(), NORMAL);
case kColorId_TableText:
case kColorId_TreeText:
return GetTextColor(GetTree(), NORMAL);
case kColorId_TableSelectedText:
case kColorId_TableSelectedTextUnfocused:
case kColorId_TreeSelectedText:
case kColorId_TreeSelectedTextUnfocused:
return GetTextColor(GetTree(), SELECTED);
case kColorId_TableSelectionBackgroundFocused:
case kColorId_TableSelectionBackgroundUnfocused:
case kColorId_TreeSelectionBackgroundFocused:
case kColorId_TreeSelectionBackgroundUnfocused:
return GetBgColor(GetTree(), SELECTED);
case kColorId_TableGroupingIndicatorColor:
return GetTextAAColor(GetTree(), NORMAL);
// Table Headers
case kColorId_TableHeaderText:
return GetTextColor(GetTree(), NORMAL);
case kColorId_TableHeaderBackground:
return GetBgColor(GetTree(), NORMAL);
case kColorId_TableHeaderSeparator:
return GetFgColor(GetSeparator(), INSENSITIVE);
// Results Table
case kColorId_ResultsTableNormalBackground:
return GetSystemColor(kColorId_TextfieldDefaultBackground);
case kColorId_ResultsTableHoveredBackground:
return color_utils::AlphaBlend(
GetSystemColor(kColorId_TextfieldDefaultBackground),
GetSystemColor(kColorId_TextfieldSelectionBackgroundFocused), 0x80);
case kColorId_ResultsTableSelectedBackground:
return GetSystemColor(kColorId_TextfieldSelectionBackgroundFocused);
case kColorId_ResultsTableNormalText:
case kColorId_ResultsTableHoveredText:
return GetSystemColor(kColorId_TextfieldDefaultColor);
case kColorId_ResultsTableSelectedText:
return GetSystemColor(kColorId_TextfieldSelectionColor);
case kColorId_ResultsTableNormalDimmedText:
case kColorId_ResultsTableHoveredDimmedText:
return color_utils::AlphaBlend(
GetSystemColor(kColorId_TextfieldDefaultColor),
GetSystemColor(kColorId_TextfieldDefaultBackground), 0x80);
case kColorId_ResultsTableSelectedDimmedText:
return color_utils::AlphaBlend(
GetSystemColor(kColorId_TextfieldSelectionColor),
GetSystemColor(kColorId_TextfieldDefaultBackground), 0x80);
case kColorId_ResultsTableNormalUrl:
case kColorId_ResultsTableHoveredUrl:
return NormalURLColor(GetSystemColor(kColorId_TextfieldDefaultColor));
case kColorId_ResultsTableSelectedUrl:
return SelectedURLColor(
GetSystemColor(kColorId_TextfieldSelectionColor),
GetSystemColor(kColorId_TextfieldSelectionBackgroundFocused));
case kColorId_ResultsTablePositiveText: {
return color_utils::GetReadableColor(kPositiveTextColor,
GetBaseColor(GetEntry(), NORMAL));
}
case kColorId_ResultsTablePositiveHoveredText: {
return color_utils::GetReadableColor(kPositiveTextColor,
GetBaseColor(GetEntry(), PRELIGHT));
}
case kColorId_ResultsTablePositiveSelectedText: {
return color_utils::GetReadableColor(kPositiveTextColor,
GetBaseColor(GetEntry(), SELECTED));
}
case kColorId_ResultsTableNegativeText: {
return color_utils::GetReadableColor(kNegativeTextColor,
GetBaseColor(GetEntry(), NORMAL));
}
case kColorId_ResultsTableNegativeHoveredText: {
return color_utils::GetReadableColor(kNegativeTextColor,
GetBaseColor(GetEntry(), PRELIGHT));
}
case kColorId_ResultsTableNegativeSelectedText: {
return color_utils::GetReadableColor(kNegativeTextColor,
GetBaseColor(GetEntry(), SELECTED));
}
// Throbber
case kColorId_ThrobberSpinningColor:
case kColorId_ThrobberLightColor:
return GetSystemColor(kColorId_TextfieldSelectionBackgroundFocused);
case kColorId_ThrobberWaitingColor:
return color_utils::AlphaBlend(
GetSystemColor(kColorId_TextfieldSelectionBackgroundFocused),
GetBgColor(GetWindow(), NORMAL), 0x80);
// Alert icons
// Just fall back to the same colors as Aura.
case kColorId_AlertSeverityLow:
case kColorId_AlertSeverityMedium:
case kColorId_AlertSeverityHigh: {
ui::NativeTheme* fallback_theme =
color_utils::IsDark(GetTextColor(GetEntry(), NORMAL))
? ui::NativeTheme::GetInstanceForNativeUi()
: ui::NativeThemeDarkAura::instance();
return fallback_theme->GetSystemColor(color_id);
}
case kColorId_NumColors:
NOTREACHED();
break;
}
return kInvalidColorIdColor;
}
GtkWidget* NativeThemeGtk2::GetWindow() const {
static GtkWidget* fake_window = nullptr;
if (!fake_window) {
fake_window = chrome_gtk_frame_new();
gtk_widget_realize(fake_window);
}
return fake_window;
}
GtkWidget* NativeThemeGtk2::GetEntry() const {
static GtkWidget* fake_entry = nullptr;
if (!fake_entry) {
fake_entry = gtk_entry_new();
// The fake entry needs to be in the window so it can be realized so we can
// use the computed parts of the style.
gtk_container_add(GTK_CONTAINER(GetWindow()), fake_entry);
gtk_widget_realize(fake_entry);
}
return fake_entry;
}
GtkWidget* NativeThemeGtk2::GetLabel() const {
static GtkWidget* fake_label = nullptr;
if (!fake_label)
fake_label = gtk_label_new("");
return fake_label;
}
GtkWidget* NativeThemeGtk2::GetButton() const {
static GtkWidget* fake_button = nullptr;
if (!fake_button)
fake_button = gtk_button_new();
return fake_button;
}
GtkWidget* NativeThemeGtk2::GetBlueButton() const {
static GtkWidget* fake_bluebutton = nullptr;
if (!fake_bluebutton) {
fake_bluebutton = gtk_button_new();
TurnButtonBlue(fake_bluebutton);
}
return fake_bluebutton;
}
GtkWidget* NativeThemeGtk2::GetTree() const {
static GtkWidget* fake_tree = nullptr;
if (!fake_tree)
fake_tree = gtk_tree_view_new();
return fake_tree;
}
GtkWidget* NativeThemeGtk2::GetTooltip() const {
static GtkWidget* fake_tooltip = nullptr;
if (!fake_tooltip) {
fake_tooltip = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_widget_set_name(fake_tooltip, "gtk-tooltip");
gtk_widget_realize(fake_tooltip);
}
return fake_tooltip;
}
GtkWidget* NativeThemeGtk2::GetMenu() const {
static GtkWidget* fake_menu = nullptr;
if (!fake_menu)
fake_menu = gtk_custom_menu_new();
return fake_menu;
}
GtkWidget* NativeThemeGtk2::GetMenuItem() const {
static GtkWidget* fake_menu_item = nullptr;
if (!fake_menu_item) {
fake_menu_item = gtk_custom_menu_item_new();
gtk_menu_shell_append(GTK_MENU_SHELL(GetMenu()), fake_menu_item);
}
return fake_menu_item;
}
GtkWidget* NativeThemeGtk2::GetSeparator() const {
static GtkWidget* fake_separator = nullptr;
if (!fake_separator)
fake_separator = gtk_hseparator_new();
return fake_separator;
}
} // namespace libgtkui