summaryrefslogtreecommitdiff
path: root/chromium/ui/native_theme
diff options
context:
space:
mode:
authorZeno Albisser <zeno.albisser@digia.com>2013-08-15 21:46:11 +0200
committerZeno Albisser <zeno.albisser@digia.com>2013-08-15 21:46:11 +0200
commit679147eead574d186ebf3069647b4c23e8ccace6 (patch)
treefc247a0ac8ff119f7c8550879ebb6d3dd8d1ff69 /chromium/ui/native_theme
downloadqtwebengine-chromium-679147eead574d186ebf3069647b4c23e8ccace6.tar.gz
Initial import.
Diffstat (limited to 'chromium/ui/native_theme')
-rw-r--r--chromium/ui/native_theme/common_theme.cc186
-rw-r--r--chromium/ui/native_theme/common_theme.h51
-rw-r--r--chromium/ui/native_theme/fallback_theme.cc195
-rw-r--r--chromium/ui/native_theme/fallback_theme.h28
-rw-r--r--chromium/ui/native_theme/native_theme.cc52
-rw-r--r--chromium/ui/native_theme/native_theme.gyp45
-rw-r--r--chromium/ui/native_theme/native_theme.h314
-rw-r--r--chromium/ui/native_theme/native_theme_android.cc34
-rw-r--r--chromium/ui/native_theme/native_theme_android.h28
-rw-r--r--chromium/ui/native_theme/native_theme_aura.cc229
-rw-r--r--chromium/ui/native_theme/native_theme_aura.h49
-rw-r--r--chromium/ui/native_theme/native_theme_base.cc1114
-rw-r--r--chromium/ui/native_theme/native_theme_base.h180
-rw-r--r--chromium/ui/native_theme/native_theme_export.h36
-rw-r--r--chromium/ui/native_theme/native_theme_gtk.cc108
-rw-r--r--chromium/ui/native_theme/native_theme_gtk.h28
-rw-r--r--chromium/ui/native_theme/native_theme_mac.h28
-rw-r--r--chromium/ui/native_theme/native_theme_mac.mm51
-rw-r--r--chromium/ui/native_theme/native_theme_win.cc2098
-rw-r--r--chromium/ui/native_theme/native_theme_win.h364
-rw-r--r--chromium/ui/native_theme/native_theme_win_unittest.cc15
21 files changed, 5233 insertions, 0 deletions
diff --git a/chromium/ui/native_theme/common_theme.cc b/chromium/ui/native_theme/common_theme.cc
new file mode 100644
index 00000000000..b589c000b82
--- /dev/null
+++ b/chromium/ui/native_theme/common_theme.cc
@@ -0,0 +1,186 @@
+// Copyright (c) 2012 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 "ui/native_theme/common_theme.h"
+
+#include "base/logging.h"
+#include "grit/ui_resources.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/color_utils.h"
+#include "ui/gfx/image/image_skia.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/skia_util.h"
+#include "ui/gfx/sys_color_change_listener.h"
+#include "ui/views/controls/menu/menu_config.h"
+
+namespace {
+
+// Theme colors returned by GetSystemColor().
+
+// MenuItem:
+const SkColor kMenuBackgroundColor = SK_ColorWHITE;
+const SkColor kMenuHighlightBackgroundColor = SkColorSetARGB(15, 0, 0, 0);
+const SkColor kMenuHighlightBackgroundColor2 = SkColorSetRGB(0x42, 0x81, 0xF4);
+const SkColor kMenuInvertedSchemeHighlightBackgroundColor =
+ SkColorSetRGB(48, 48, 48);
+const SkColor kMenuBorderColor = SkColorSetRGB(0xBA, 0xBA, 0xBA);
+const SkColor kEnabledMenuButtonBorderColor = SkColorSetARGB(36, 0, 0, 0);
+const SkColor kFocusedMenuButtonBorderColor = SkColorSetARGB(72, 0, 0, 0);
+const SkColor kHoverMenuButtonBorderColor = SkColorSetARGB(72, 0, 0, 0);
+const SkColor kMenuSeparatorColor = SkColorSetRGB(0xE9, 0xE9, 0xE9);
+const SkColor kEnabledMenuItemForegroundColor = SK_ColorBLACK;
+const SkColor kDisabledMenuItemForegroundColor = SkColorSetRGB(161, 161, 146);
+const SkColor kHoverMenuItemBackgroundColor =
+ SkColorSetARGB(204, 255, 255, 255);
+
+} // namespace
+
+namespace ui {
+
+bool CommonThemeGetSystemColor(NativeTheme::ColorId color_id, SkColor* color) {
+ switch (color_id) {
+ // MenuItem
+ case NativeTheme::kColorId_MenuBorderColor:
+ *color = kMenuBorderColor;
+ break;
+ case NativeTheme::kColorId_EnabledMenuButtonBorderColor:
+ *color = kEnabledMenuButtonBorderColor;
+ break;
+ case NativeTheme::kColorId_FocusedMenuButtonBorderColor:
+ *color = kFocusedMenuButtonBorderColor;
+ break;
+ case NativeTheme::kColorId_HoverMenuButtonBorderColor:
+ *color = kHoverMenuButtonBorderColor;
+ break;
+ case NativeTheme::kColorId_MenuSeparatorColor:
+ *color = kMenuSeparatorColor;
+ break;
+ case NativeTheme::kColorId_MenuBackgroundColor:
+ *color = kMenuBackgroundColor;
+ break;
+ case NativeTheme::kColorId_FocusedMenuItemBackgroundColor:
+ *color = kMenuHighlightBackgroundColor;
+ break;
+ case NativeTheme::kColorId_HoverMenuItemBackgroundColor:
+ *color = kHoverMenuItemBackgroundColor;
+ break;
+ case NativeTheme::kColorId_EnabledMenuItemForegroundColor:
+ *color = kEnabledMenuItemForegroundColor;
+ break;
+ case NativeTheme::kColorId_DisabledMenuItemForegroundColor:
+ *color = kDisabledMenuItemForegroundColor;
+ break;
+ case NativeTheme::kColorId_SelectedMenuItemForegroundColor:
+ *color = kEnabledMenuItemForegroundColor;
+ break;
+ case NativeTheme::kColorId_ButtonDisabledColor:
+ *color = kDisabledMenuItemForegroundColor;
+ break;
+ default:
+ return false;
+ }
+
+ *color = AdjustColorForMenuVariations(color_id, *color);
+
+ if (gfx::IsInvertedColorScheme()) {
+ switch (color_id) {
+ case NativeTheme::kColorId_FocusedMenuItemBackgroundColor:
+ *color = kMenuInvertedSchemeHighlightBackgroundColor;
+ break;
+ default:
+ *color = color_utils::InvertColor(*color);
+ }
+ }
+ return true;
+}
+
+gfx::Size CommonThemeGetPartSize(NativeTheme::Part part,
+ NativeTheme::State state,
+ const NativeTheme::ExtraParams& extra) {
+ gfx::Size size;
+ switch (part) {
+ case NativeTheme::kMenuCheck: {
+ const gfx::ImageSkia* check =
+ ui::ResourceBundle::GetSharedInstance().GetImageNamed(
+ IDR_MENU_CHECK_CHECKED).ToImageSkia();
+ size.SetSize(check->width(), check->height());
+ break;
+ }
+ default:
+ break;
+ }
+
+ return size;
+}
+
+void CommonThemePaintMenuSeparator(
+ SkCanvas* canvas,
+ const gfx::Rect& rect,
+ const NativeTheme::MenuSeparatorExtraParams& extra) {
+ SkColor color;
+ CommonThemeGetSystemColor(NativeTheme::kColorId_MenuSeparatorColor, &color);
+ SkPaint paint;
+ paint.setColor(kMenuSeparatorColor);
+ int position_y = rect.y() + rect.height() / 2;
+ canvas->drawLine(rect.x(), position_y, rect.right(), position_y, paint);
+}
+
+void CommonThemePaintMenuGutter(SkCanvas* canvas, const gfx::Rect& rect) {
+ SkColor color;
+ CommonThemeGetSystemColor(NativeTheme::kColorId_MenuSeparatorColor, &color);
+ SkPaint paint;
+ paint.setColor(kMenuSeparatorColor);
+ int position_x = rect.x() + rect.width() / 2;
+ canvas->drawLine(position_x, rect.y(), position_x, rect.bottom(), paint);
+}
+
+void CommonThemePaintMenuBackground(SkCanvas* canvas, const gfx::Rect& rect) {
+ SkColor color;
+ CommonThemeGetSystemColor(NativeTheme::kColorId_MenuBackgroundColor, &color);
+ SkPaint paint;
+ paint.setColor(color);
+ canvas->drawRect(gfx::RectToSkRect(rect), paint);
+}
+
+void CommonThemePaintMenuItemBackground(SkCanvas* canvas,
+ NativeTheme::State state,
+ const gfx::Rect& rect) {
+ SkColor color;
+ SkPaint paint;
+ switch (state) {
+ case NativeTheme::kNormal:
+ case NativeTheme::kDisabled:
+ CommonThemeGetSystemColor(NativeTheme::kColorId_MenuBackgroundColor,
+ &color);
+ paint.setColor(color);
+ break;
+ case NativeTheme::kHovered:
+ CommonThemeGetSystemColor(
+ NativeTheme::kColorId_FocusedMenuItemBackgroundColor, &color);
+ paint.setColor(color);
+ break;
+ default:
+ NOTREACHED() << "Invalid state " << state;
+ break;
+ }
+ canvas->drawRect(gfx::RectToSkRect(rect), paint);
+}
+
+SkColor AdjustColorForMenuVariations(NativeTheme::ColorId color_id,
+ SkColor color) {
+ if (NativeTheme::GetMenuVariation() != NativeTheme::MENU_VARIATION_CONTRAST)
+ return color;
+
+ switch(color_id) {
+ case NativeTheme::kColorId_FocusedMenuItemBackgroundColor:
+ return kMenuHighlightBackgroundColor2;
+ case NativeTheme::kColorId_SelectedMenuItemForegroundColor:
+ return SK_ColorWHITE;
+ default:
+ return color;
+ }
+}
+
+} // namespace ui
diff --git a/chromium/ui/native_theme/common_theme.h b/chromium/ui/native_theme/common_theme.h
new file mode 100644
index 00000000000..530032f8390
--- /dev/null
+++ b/chromium/ui/native_theme/common_theme.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2012 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.
+
+#ifndef UI_NATIVE_THEME_COMMON_THEME_H_
+#define UI_NATIVE_THEME_COMMON_THEME_H_
+
+#include "ui/native_theme/native_theme.h"
+
+class SkCanvas;
+
+namespace ui {
+
+// Drawing code that is common for all platforms.
+
+// Returns true and |color| if |color_id| is found, or false otherwise.
+bool NATIVE_THEME_EXPORT CommonThemeGetSystemColor(
+ NativeTheme::ColorId color_id,
+ SkColor* color);
+
+gfx::Size NATIVE_THEME_EXPORT CommonThemeGetPartSize(
+ NativeTheme::Part part,
+ NativeTheme::State state,
+ const NativeTheme::ExtraParams& extra);
+
+void NATIVE_THEME_EXPORT CommonThemePaintMenuSeparator(
+ SkCanvas* canvas,
+ const gfx::Rect& rect,
+ const NativeTheme::MenuSeparatorExtraParams& extra);
+
+void NATIVE_THEME_EXPORT CommonThemePaintMenuGutter(SkCanvas* canvas,
+ const gfx::Rect& rect);
+
+void NATIVE_THEME_EXPORT CommonThemePaintMenuBackground(SkCanvas* canvas,
+ const gfx::Rect& rect);
+
+void NATIVE_THEME_EXPORT CommonThemePaintMenuItemBackground(
+ SkCanvas* canvas,
+ NativeTheme::State state,
+ const gfx::Rect& rect);
+
+// Adjust some colors for menu Finch experiments.
+// |color_id| id of a color to be adjusted.
+// |color| color before adjustment.
+// Returns adjusted color.
+SkColor AdjustColorForMenuVariations(NativeTheme::ColorId color_id,
+ SkColor color);
+
+} // namespace ui
+
+#endif // UI_NATIVE_THEME_COMMON_THEME_H_
diff --git a/chromium/ui/native_theme/fallback_theme.cc b/chromium/ui/native_theme/fallback_theme.cc
new file mode 100644
index 00000000000..6eb68643579
--- /dev/null
+++ b/chromium/ui/native_theme/fallback_theme.cc
@@ -0,0 +1,195 @@
+// Copyright (c) 2012 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 "ui/native_theme/fallback_theme.h"
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "ui/gfx/color_utils.h"
+#include "ui/gfx/skia_utils_gtk.h"
+#include "ui/native_theme/common_theme.h"
+
+namespace ui {
+
+FallbackTheme::FallbackTheme() {
+}
+
+FallbackTheme::~FallbackTheme() {
+}
+
+SkColor FallbackTheme::GetSystemColor(ColorId color_id) const {
+ // This implementation returns hardcoded colors.
+
+ static const SkColor kInvalidColorIdColor = SkColorSetRGB(255, 0, 128);
+ // Menu:
+ static const SkColor kMenuBackgroundColor = SK_ColorWHITE;
+ // Windows:
+ static const SkColor kWindowBackgroundColor = SK_ColorWHITE;
+ // Dialogs:
+ static const SkColor kDialogBackgroundColor = SkColorSetRGB(251, 251, 251);
+ // FocusableBorder:
+ static const SkColor kFocusedBorderColor = SkColorSetRGB(0x4D, 0x90, 0xFE);
+ static const SkColor kUnfocusedBorderColor = SkColorSetRGB(0xD9, 0xD9, 0xD9);
+ // Button:
+ static const SkColor kButtonBackgroundColor = SkColorSetRGB(0xDE, 0xDE, 0xDE);
+ static const SkColor kButtonEnabledColor = SkColorSetRGB(0x22, 0x22, 0x22);
+ static const SkColor kButtonDisabledColor = SkColorSetRGB(0x99, 0x99, 0x99);
+ static const SkColor kButtonHighlightColor = SkColorSetRGB(0, 0, 0);
+ static const SkColor kButtonHoverColor = kButtonEnabledColor;
+ // MenuItem:
+ static const SkColor kEnabledMenuItemForegroundColor = kButtonEnabledColor;
+ static const SkColor kDisabledMenuItemForegroundColor = kButtonDisabledColor;
+ static const SkColor kFocusedMenuItemBackgroundColor =
+ SkColorSetRGB(0xF1, 0xF1, 0xF1);
+ static const SkColor kHoverMenuItemBackgroundColor =
+ SkColorSetARGB(204, 255, 255, 255);
+ static const SkColor kMenuSeparatorColor = SkColorSetRGB(0xED, 0xED, 0xED);
+ static const SkColor kEnabledMenuButtonBorderColor =
+ SkColorSetARGB(36, 0, 0, 0);
+ static const SkColor kFocusedMenuButtonBorderColor =
+ SkColorSetARGB(72, 0, 0, 0);
+ static const SkColor kHoverMenuButtonBorderColor =
+ SkColorSetARGB(72, 0, 0, 0);
+ // Label:
+ static const SkColor kLabelEnabledColor = kButtonEnabledColor;
+ static const SkColor kLabelDisabledColor = kButtonDisabledColor;
+ static const SkColor kLabelBackgroundColor = SK_ColorWHITE;
+ // Textfield:
+ static const SkColor kTextfieldDefaultColor = SK_ColorBLACK;
+ static const SkColor kTextfieldDefaultBackground = SK_ColorWHITE;
+ static const SkColor kTextfieldReadOnlyColor = SK_ColorDKGRAY;
+ static const SkColor kTextfieldReadOnlyBackground = SK_ColorWHITE;
+ static const SkColor kTextfieldSelectionBackgroundFocused =
+ SkColorSetARGB(0x54, 0x60, 0xA8, 0xEB);
+ static const SkColor kTextfieldSelectionColor =
+ color_utils::AlphaBlend(SK_ColorBLACK,
+ kTextfieldSelectionBackgroundFocused, 0xdd);
+ // Tree
+ static const SkColor kTreeBackground = SK_ColorWHITE;
+ static const SkColor kTreeTextColor = SK_ColorBLACK;
+ static const SkColor kTreeSelectedTextColor = SK_ColorBLACK;
+ static const SkColor kTreeSelectionBackgroundColor =
+ SkColorSetRGB(0xEE, 0xEE, 0xEE);
+ static const SkColor kTreeArrowColor = SkColorSetRGB(0x7A, 0x7A, 0x7A);
+ // Table
+ static const SkColor kTableBackground = SK_ColorWHITE;
+ static const SkColor kTableTextColor = SK_ColorBLACK;
+ static const SkColor kTableSelectedTextColor = SK_ColorBLACK;
+ static const SkColor kTableSelectionBackgroundColor =
+ SkColorSetRGB(0xEE, 0xEE, 0xEE);
+ static const SkColor kTableGroupingIndicatorColor =
+ SkColorSetRGB(0xCC, 0xCC, 0xCC);
+
+ SkColor color;
+ if (CommonThemeGetSystemColor(color_id, &color))
+ return color;
+
+ switch (color_id) {
+ // Windows
+ case kColorId_WindowBackground:
+ return kWindowBackgroundColor;
+
+ // Dialogs
+ case kColorId_DialogBackground:
+ return kDialogBackgroundColor;
+
+ // FocusableBorder
+ case kColorId_FocusedBorderColor:
+ return kFocusedBorderColor;
+ case kColorId_UnfocusedBorderColor:
+ return kUnfocusedBorderColor;
+
+ // Button
+ case kColorId_ButtonBackgroundColor:
+ return kButtonBackgroundColor;
+ case kColorId_ButtonEnabledColor:
+ return kButtonEnabledColor;
+ case kColorId_ButtonDisabledColor:
+ return kButtonDisabledColor;
+ case kColorId_ButtonHighlightColor:
+ return kButtonHighlightColor;
+ case kColorId_ButtonHoverColor:
+ return kButtonHoverColor;
+
+ // MenuItem
+ case kColorId_EnabledMenuItemForegroundColor:
+ return kEnabledMenuItemForegroundColor;
+ case kColorId_DisabledMenuItemForegroundColor:
+ return kDisabledMenuItemForegroundColor;
+ case kColorId_SelectedMenuItemForegroundColor:
+ return kEnabledMenuItemForegroundColor;
+ case kColorId_FocusedMenuItemBackgroundColor:
+ return kFocusedMenuItemBackgroundColor;
+ case kColorId_HoverMenuItemBackgroundColor:
+ return kHoverMenuItemBackgroundColor;
+ case kColorId_MenuSeparatorColor:
+ return kMenuSeparatorColor;
+ case kColorId_EnabledMenuButtonBorderColor:
+ return kEnabledMenuButtonBorderColor;
+ case kColorId_FocusedMenuButtonBorderColor:
+ return kFocusedMenuButtonBorderColor;
+ case kColorId_HoverMenuButtonBorderColor:
+ return kHoverMenuButtonBorderColor;
+
+ // Label
+ case kColorId_LabelEnabledColor:
+ return kLabelEnabledColor;
+ case kColorId_LabelDisabledColor:
+ return kLabelDisabledColor;
+ case kColorId_LabelBackgroundColor:
+ return kLabelBackgroundColor;
+
+ // Textfield
+ case kColorId_TextfieldDefaultColor:
+ return kTextfieldDefaultColor;
+ case kColorId_TextfieldDefaultBackground:
+ return kTextfieldDefaultBackground;
+ case kColorId_TextfieldReadOnlyColor:
+ return kTextfieldReadOnlyColor;
+ case kColorId_TextfieldReadOnlyBackground:
+ return kTextfieldReadOnlyBackground;
+ case kColorId_TextfieldSelectionColor:
+ return kTextfieldSelectionColor;
+ case kColorId_TextfieldSelectionBackgroundFocused:
+ return kTextfieldSelectionBackgroundFocused;
+
+ // Tree
+ case kColorId_TreeBackground:
+ return kTreeBackground;
+ case kColorId_TreeText:
+ return kTreeTextColor;
+ case kColorId_TreeSelectedText:
+ case kColorId_TreeSelectedTextUnfocused:
+ return kTreeSelectedTextColor;
+ case kColorId_TreeSelectionBackgroundFocused:
+ case kColorId_TreeSelectionBackgroundUnfocused:
+ return kTreeSelectionBackgroundColor;
+ case kColorId_TreeArrow:
+ return kTreeArrowColor;
+
+ // Table
+ case kColorId_TableBackground:
+ return kTableBackground;
+ case kColorId_TableText:
+ return kTableTextColor;
+ case kColorId_TableSelectedText:
+ case kColorId_TableSelectedTextUnfocused:
+ return kTableSelectedTextColor;
+ case kColorId_TableSelectionBackgroundFocused:
+ case kColorId_TableSelectionBackgroundUnfocused:
+ return kTableSelectionBackgroundColor;
+ case kColorId_TableGroupingIndicatorColor:
+ return kTableGroupingIndicatorColor;
+
+ case kColorId_MenuBackgroundColor:
+ return kMenuBackgroundColor;
+ case kColorId_MenuBorderColor:
+ NOTREACHED();
+ break;
+ }
+
+ return kInvalidColorIdColor;
+}
+
+} // namespace ui
diff --git a/chromium/ui/native_theme/fallback_theme.h b/chromium/ui/native_theme/fallback_theme.h
new file mode 100644
index 00000000000..2a699366f50
--- /dev/null
+++ b/chromium/ui/native_theme/fallback_theme.h
@@ -0,0 +1,28 @@
+// Copyright (c) 2012 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.
+
+#ifndef UI_NATIVE_THEME_FALLBACK_THEME_H_
+#define UI_NATIVE_THEME_FALLBACK_THEME_H_
+
+#include "ui/native_theme/native_theme_base.h"
+
+namespace ui {
+
+// This theme can draw UI controls on every platform. This is only used when
+// zooming a web page and the native theme doesn't support scaling.
+class NATIVE_THEME_EXPORT FallbackTheme : public NativeThemeBase {
+ public:
+ FallbackTheme();
+ virtual ~FallbackTheme();
+
+ private:
+ // Overridden from NativeThemeBase:
+ virtual SkColor GetSystemColor(ColorId color_id) const OVERRIDE;
+
+ DISALLOW_COPY_AND_ASSIGN(FallbackTheme);
+};
+
+} // namespace ui
+
+#endif // UI_NATIVE_THEME_FALLBACK_THEME_H_
diff --git a/chromium/ui/native_theme/native_theme.cc b/chromium/ui/native_theme/native_theme.cc
new file mode 100644
index 00000000000..af44a7f9eb2
--- /dev/null
+++ b/chromium/ui/native_theme/native_theme.cc
@@ -0,0 +1,52 @@
+// Copyright (c) 2012 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 "ui/native_theme/native_theme.h"
+
+#include "base/command_line.h"
+#include "base/metrics/field_trial.h"
+
+
+// Constants for the new menu style field trial.
+const char kMenuVariationFieldTrialName[] = "NewMenuStyle";
+const char kMenuVariationFieldTrialGroupNameCompact1[] = "Compact1";
+const char kMenuVariationFieldTrialGroupNameCompact2[] = "Compact2";
+const char kMenuVariationFieldTrialGroupNameHigherContrast[] = "HigherContrast";
+
+namespace ui {
+
+void NativeTheme::SetScrollbarColors(unsigned inactive_color,
+ unsigned active_color,
+ unsigned track_color) {
+ thumb_inactive_color_ = inactive_color;
+ thumb_active_color_ = active_color;
+ track_color_ = track_color;
+}
+
+// NativeTheme::instance() is implemented in the platform specific source files,
+// such as native_theme_win.cc or native_theme_linux.cc
+
+// static
+NativeTheme::MenuVariation NativeTheme::GetMenuVariation() {
+ std::string trial_group_name =
+ base::FieldTrialList::FindFullName(kMenuVariationFieldTrialName);
+ if (trial_group_name == kMenuVariationFieldTrialGroupNameCompact1)
+ return MENU_VARIATION_COMPACT_1;
+ if (trial_group_name == kMenuVariationFieldTrialGroupNameCompact2)
+ return MENU_VARIATION_COMPACT_2;
+ if (trial_group_name == kMenuVariationFieldTrialGroupNameHigherContrast)
+ return MENU_VARIATION_CONTRAST;
+
+ return MENU_VARIATION_NORMAL;
+}
+
+NativeTheme::NativeTheme()
+ : thumb_inactive_color_(0xeaeaea),
+ thumb_active_color_(0xf4f4f4),
+ track_color_(0xd3d3d3) {
+}
+
+NativeTheme::~NativeTheme() {}
+
+} // namespace ui
diff --git a/chromium/ui/native_theme/native_theme.gyp b/chromium/ui/native_theme/native_theme.gyp
new file mode 100644
index 00000000000..671f2863590
--- /dev/null
+++ b/chromium/ui/native_theme/native_theme.gyp
@@ -0,0 +1,45 @@
+# Copyright (c) 2012 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.
+
+{
+ 'variables': {
+ 'chromium_code': 1,
+ },
+ 'targets': [
+ {
+ 'target_name': 'native_theme',
+ 'type': '<(component)',
+ 'dependencies': [
+ '../../base/base.gyp:base',
+ '../../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
+ '../../skia/skia.gyp:skia',
+ '../ui.gyp:ui',
+ '../ui.gyp:ui_resources',
+ ],
+ 'defines': [
+ 'NATIVE_THEME_IMPLEMENTATION',
+ ],
+ 'sources': [
+ 'common_theme.cc',
+ 'common_theme.h',
+ 'fallback_theme.cc',
+ 'fallback_theme.h',
+ 'native_theme.cc',
+ 'native_theme.h',
+ 'native_theme_android.cc',
+ 'native_theme_android.h',
+ 'native_theme_aura.cc',
+ 'native_theme_aura.h',
+ 'native_theme_base.cc',
+ 'native_theme_base.h',
+ 'native_theme_gtk.cc',
+ 'native_theme_gtk.h',
+ 'native_theme_mac.h',
+ 'native_theme_mac.mm',
+ 'native_theme_win.cc',
+ 'native_theme_win.h',
+ ],
+ },
+ ],
+}
diff --git a/chromium/ui/native_theme/native_theme.h b/chromium/ui/native_theme/native_theme.h
new file mode 100644
index 00000000000..30379169e37
--- /dev/null
+++ b/chromium/ui/native_theme/native_theme.h
@@ -0,0 +1,314 @@
+// Copyright (c) 2012 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.
+
+#ifndef UI_NATIVE_THEME_NATIVE_THEME_H_
+#define UI_NATIVE_THEME_NATIVE_THEME_H_
+
+#include "third_party/skia/include/core/SkColor.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/native_theme/native_theme_export.h"
+
+class SkCanvas;
+
+namespace gfx {
+class Rect;
+class Size;
+}
+
+namespace ui {
+
+// This class supports drawing UI controls (like buttons, text fields, lists,
+// comboboxes, etc) that look like the native UI controls of the underlying
+// platform, such as Windows or Linux. It also supplies default colors for
+// dialog box backgrounds, etc., which are obtained from the system theme where
+// possible.
+//
+// The supported control types are listed in the Part enum. These parts can be
+// in any state given by the State enum, where the actual definition of the
+// state is part-specific. The supported colors are listed in the ColorId enum.
+//
+// Some parts require more information than simply the state in order to be
+// drawn correctly, and this information is given to the Paint() method via the
+// ExtraParams union. Each part that requires more information has its own
+// field in the union.
+//
+// NativeTheme also supports getting the default size of a given part with
+// the GetPartSize() method.
+class NATIVE_THEME_EXPORT NativeTheme {
+ public:
+ // The part to be painted / sized.
+ enum Part {
+ kCheckbox,
+ kInnerSpinButton,
+ kMenuList,
+ kMenuCheck,
+ kMenuCheckBackground,
+ kMenuPopupArrow,
+ kMenuPopupBackground,
+ kMenuPopupGutter,
+ kMenuPopupSeparator,
+ kMenuItemBackground,
+ kProgressBar,
+ kPushButton,
+ kRadio,
+
+ // The order of the arrow enums is important, do not change without also
+ // changing the code in platform implementations.
+ kScrollbarDownArrow,
+ kScrollbarLeftArrow,
+ kScrollbarRightArrow,
+ kScrollbarUpArrow,
+
+ kScrollbarHorizontalThumb,
+ kScrollbarVerticalThumb,
+ kScrollbarHorizontalTrack,
+ kScrollbarVerticalTrack,
+ kScrollbarHorizontalGripper,
+ kScrollbarVerticalGripper,
+ kSliderTrack,
+ kSliderThumb,
+ kTabPanelBackground,
+ kTextField,
+ kTrackbarThumb,
+ kTrackbarTrack,
+ kWindowResizeGripper,
+ kMaxPart,
+ };
+
+ // The state of the part.
+ enum State {
+ kDisabled,
+ kHovered,
+ kNormal,
+ kPressed,
+ kMaxState,
+ };
+
+ enum MenuVariation {
+ MENU_VARIATION_NORMAL,
+ MENU_VARIATION_COMPACT_1,
+ MENU_VARIATION_COMPACT_2,
+ MENU_VARIATION_CONTRAST
+ };
+
+ // Each structure below holds extra information needed when painting a given
+ // part.
+
+ struct ButtonExtraParams {
+ bool checked;
+ bool indeterminate; // Whether the button state is indeterminate.
+ bool is_default; // Whether the button is default button.
+ bool is_focused;
+ bool has_border;
+ int classic_state; // Used on Windows when uxtheme is not available.
+ SkColor background_color;
+ };
+
+ struct InnerSpinButtonExtraParams {
+ bool spin_up;
+ bool read_only;
+ int classic_state; // Used on Windows when uxtheme is not available.
+ };
+
+ struct MenuArrowExtraParams {
+ bool pointing_right;
+ // Used for the disabled state to indicate if the item is both disabled and
+ // selected.
+ bool is_selected;
+ };
+
+ struct MenuCheckExtraParams {
+ bool is_radio;
+ // Used for the disabled state to indicate if the item is both disabled and
+ // selected.
+ bool is_selected;
+ };
+
+ struct MenuItemExtraParams {
+ bool is_selected;
+ };
+
+ struct MenuListExtraParams {
+ bool has_border;
+ bool has_border_radius;
+ int arrow_x;
+ int arrow_y;
+ SkColor background_color;
+ int classic_state; // Used on Windows when uxtheme is not available.
+ };
+
+ struct MenuSeparatorExtraParams {
+ bool has_gutter;
+ };
+
+ struct MenuBackgroundExtraParams {
+ int corner_radius;
+ };
+
+ struct ProgressBarExtraParams {
+ double animated_seconds;
+ bool determinate;
+ int value_rect_x;
+ int value_rect_y;
+ int value_rect_width;
+ int value_rect_height;
+ };
+
+ struct ScrollbarArrowExtraParams {
+ bool is_hovering;
+ };
+
+ struct ScrollbarTrackExtraParams {
+ bool is_upper;
+ int track_x;
+ int track_y;
+ int track_width;
+ int track_height;
+ int classic_state; // Used on Windows when uxtheme is not available.
+ };
+
+ struct ScrollbarThumbExtraParams {
+ bool is_hovering;
+ };
+
+ struct SliderExtraParams {
+ bool vertical;
+ bool in_drag;
+ };
+
+ struct TextFieldExtraParams {
+ bool is_text_area;
+ bool is_listbox;
+ SkColor background_color;
+ bool is_read_only;
+ bool is_focused;
+ bool fill_content_area;
+ bool draw_edges;
+ int classic_state; // Used on Windows when uxtheme is not available.
+ };
+
+ struct TrackbarExtraParams {
+ bool vertical;
+ int classic_state; // Used on Windows when uxtheme is not available.
+ };
+
+ union ExtraParams {
+ ButtonExtraParams button;
+ InnerSpinButtonExtraParams inner_spin;
+ MenuArrowExtraParams menu_arrow;
+ MenuCheckExtraParams menu_check;
+ MenuItemExtraParams menu_item;
+ MenuListExtraParams menu_list;
+ MenuSeparatorExtraParams menu_separator;
+ MenuBackgroundExtraParams menu_background;
+ ProgressBarExtraParams progress_bar;
+ ScrollbarArrowExtraParams scrollbar_arrow;
+ ScrollbarTrackExtraParams scrollbar_track;
+ ScrollbarThumbExtraParams scrollbar_thumb;
+ SliderExtraParams slider;
+ TextFieldExtraParams text_field;
+ TrackbarExtraParams trackbar;
+ };
+
+ // Return the size of the part.
+ virtual gfx::Size GetPartSize(Part part,
+ State state,
+ const ExtraParams& extra) const = 0;
+
+ // Paint the part to the canvas.
+ virtual void Paint(SkCanvas* canvas,
+ Part part,
+ State state,
+ const gfx::Rect& rect,
+ const ExtraParams& extra) const = 0;
+
+ // Supports theme specific colors.
+ void SetScrollbarColors(unsigned inactive_color,
+ unsigned active_color,
+ unsigned track_color);
+
+ // Colors for GetSystemColor().
+ enum ColorId {
+ // Windows
+ kColorId_WindowBackground,
+ // Dialogs
+ kColorId_DialogBackground,
+ // FocusableBorder
+ kColorId_FocusedBorderColor,
+ kColorId_UnfocusedBorderColor,
+ // Button
+ kColorId_ButtonBackgroundColor,
+ kColorId_ButtonEnabledColor,
+ kColorId_ButtonDisabledColor,
+ kColorId_ButtonHighlightColor,
+ kColorId_ButtonHoverColor,
+ // MenuItem
+ kColorId_EnabledMenuItemForegroundColor,
+ kColorId_DisabledMenuItemForegroundColor,
+ kColorId_SelectedMenuItemForegroundColor,
+ kColorId_FocusedMenuItemBackgroundColor,
+ kColorId_HoverMenuItemBackgroundColor,
+ kColorId_MenuSeparatorColor,
+ kColorId_MenuBackgroundColor,
+ kColorId_MenuBorderColor,
+ // MenuButton - buttons in wrench menu
+ kColorId_EnabledMenuButtonBorderColor,
+ kColorId_FocusedMenuButtonBorderColor,
+ kColorId_HoverMenuButtonBorderColor,
+ // Label
+ kColorId_LabelEnabledColor,
+ kColorId_LabelDisabledColor,
+ kColorId_LabelBackgroundColor,
+ // Textfield
+ kColorId_TextfieldDefaultColor,
+ kColorId_TextfieldDefaultBackground,
+ kColorId_TextfieldReadOnlyColor,
+ kColorId_TextfieldReadOnlyBackground,
+ kColorId_TextfieldSelectionColor,
+ kColorId_TextfieldSelectionBackgroundFocused,
+ // Tree
+ kColorId_TreeBackground,
+ kColorId_TreeText,
+ kColorId_TreeSelectedText,
+ kColorId_TreeSelectedTextUnfocused,
+ kColorId_TreeSelectionBackgroundFocused,
+ kColorId_TreeSelectionBackgroundUnfocused,
+ kColorId_TreeArrow,
+ // Table
+ kColorId_TableBackground,
+ kColorId_TableText,
+ kColorId_TableSelectedText,
+ kColorId_TableSelectedTextUnfocused,
+ kColorId_TableSelectionBackgroundFocused,
+ kColorId_TableSelectionBackgroundUnfocused,
+ kColorId_TableGroupingIndicatorColor,
+ // TODO(benrg): move other hardcoded colors here.
+ };
+
+ // Return a color from the system theme.
+ virtual SkColor GetSystemColor(ColorId color_id) const = 0;
+
+ // Returns a shared instance of the native theme.
+ // The returned object should not be deleted by the caller. This function
+ // is not thread safe and should only be called from the UI thread.
+ // Each port of NativeTheme should provide its own implementation of this
+ // function, returning the port's subclass.
+ static NativeTheme* instance();
+
+ static MenuVariation GetMenuVariation();
+
+ protected:
+ NativeTheme();
+ virtual ~NativeTheme();
+
+ unsigned int thumb_inactive_color_;
+ unsigned int thumb_active_color_;
+ unsigned int track_color_;
+
+ DISALLOW_COPY_AND_ASSIGN(NativeTheme);
+};
+
+} // namespace ui
+
+#endif // UI_NATIVE_THEME_NATIVE_THEME_H_
diff --git a/chromium/ui/native_theme/native_theme_android.cc b/chromium/ui/native_theme/native_theme_android.cc
new file mode 100644
index 00000000000..884bc7e3561
--- /dev/null
+++ b/chromium/ui/native_theme/native_theme_android.cc
@@ -0,0 +1,34 @@
+// Copyright (c) 2012 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 "ui/native_theme/native_theme_android.h"
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+namespace ui {
+
+// static
+NativeTheme* NativeTheme::instance() {
+ return NativeThemeAndroid::instance();
+}
+
+// static
+NativeThemeAndroid* NativeThemeAndroid::instance() {
+ CR_DEFINE_STATIC_LOCAL(NativeThemeAndroid, s_native_theme, ());
+ return &s_native_theme;
+}
+
+SkColor NativeThemeAndroid::GetSystemColor(ColorId color_id) const {
+ NOTIMPLEMENTED();
+ return SK_ColorBLACK;
+}
+
+NativeThemeAndroid::NativeThemeAndroid() {
+}
+
+NativeThemeAndroid::~NativeThemeAndroid() {
+}
+
+} // namespace ui
diff --git a/chromium/ui/native_theme/native_theme_android.h b/chromium/ui/native_theme/native_theme_android.h
new file mode 100644
index 00000000000..e1d2f781e5c
--- /dev/null
+++ b/chromium/ui/native_theme/native_theme_android.h
@@ -0,0 +1,28 @@
+// Copyright (c) 2012 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.
+
+#ifndef UI_NATIVE_THEME_NATIVE_THEME_ANDROID_H_
+#define UI_NATIVE_THEME_NATIVE_THEME_ANDROID_H_
+
+#include "ui/native_theme/native_theme_base.h"
+
+namespace ui {
+
+// Android implementation of native theme support.
+class NativeThemeAndroid : public NativeThemeBase {
+ public:
+ static NativeThemeAndroid* instance();
+
+ virtual SkColor GetSystemColor(ColorId color_id) const OVERRIDE;
+
+ private:
+ NativeThemeAndroid();
+ virtual ~NativeThemeAndroid();
+
+ DISALLOW_COPY_AND_ASSIGN(NativeThemeAndroid);
+};
+
+} // namespace ui
+
+#endif // UI_NATIVE_THEME_NATIVE_THEME_ANDROID_H_
diff --git a/chromium/ui/native_theme/native_theme_aura.cc b/chromium/ui/native_theme/native_theme_aura.cc
new file mode 100644
index 00000000000..6cbc2554031
--- /dev/null
+++ b/chromium/ui/native_theme/native_theme_aura.cc
@@ -0,0 +1,229 @@
+// Copyright (c) 2012 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 "ui/native_theme/native_theme_aura.h"
+
+#include "base/logging.h"
+#include "grit/ui_resources.h"
+#include "ui/base/layout.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/image/image_skia.h"
+#include "ui/gfx/path.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/size.h"
+#include "ui/gfx/skbitmap_operations.h"
+#include "ui/native_theme/common_theme.h"
+
+namespace {
+
+const SkColor kMenuBackgroundColor = SK_ColorWHITE;
+
+} // namespace
+
+namespace ui {
+
+// static
+NativeTheme* NativeTheme::instance() {
+ return NativeThemeAura::instance();
+}
+
+// static
+NativeThemeAura* NativeThemeAura::instance() {
+ CR_DEFINE_STATIC_LOCAL(NativeThemeAura, s_native_theme, ());
+ return &s_native_theme;
+}
+
+NativeThemeAura::NativeThemeAura() {
+ // We don't draw scrollbar buttons.
+ set_scrollbar_button_length(0);
+}
+
+NativeThemeAura::~NativeThemeAura() {
+}
+
+void NativeThemeAura::PaintMenuPopupBackground(
+ SkCanvas* canvas,
+ const gfx::Size& size,
+ const MenuBackgroundExtraParams& menu_background) const {
+ if (menu_background.corner_radius > 0) {
+ SkPaint paint;
+ paint.setStyle(SkPaint::kFill_Style);
+ paint.setFlags(SkPaint::kAntiAlias_Flag);
+ paint.setColor(kMenuBackgroundColor);
+
+ 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, paint);
+ } else {
+ canvas->drawColor(kMenuBackgroundColor, SkXfermode::kSrc_Mode);
+ }
+}
+
+void NativeThemeAura::PaintMenuItemBackground(
+ SkCanvas* canvas,
+ State state,
+ const gfx::Rect& rect,
+ const MenuListExtraParams& menu_list) const {
+ CommonThemePaintMenuItemBackground(canvas, state, rect);
+}
+
+void NativeThemeAura::PaintScrollbarTrack(
+ SkCanvas* canvas,
+ Part part,
+ State state,
+ const ScrollbarTrackExtraParams& extra_params,
+ const gfx::Rect& rect) const {
+ ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
+ if (part == kScrollbarVerticalTrack) {
+ int center_offset = 0;
+ int center_height = rect.height();
+
+ if (rect.y() == extra_params.track_y) {
+ // TODO(derat): Honor |state| instead of only using the highlighted images
+ // after updating WebKit so we can draw the entire track in one go instead
+ // of as two separate pieces: otherwise, only the portion of the scrollbar
+ // that the mouse is over gets the highlighted state.
+ gfx::ImageSkia* top = rb.GetImageSkiaNamed(
+ IDR_SCROLL_BASE_VERTICAL_TOP_H);
+ DrawTiledImage(canvas, *top,
+ 0, 0, 1.0, 1.0,
+ rect.x(), rect.y(), top->width(), top->height());
+ center_offset += top->height();
+ center_height -= top->height();
+ }
+
+ if (rect.y() + rect.height() ==
+ extra_params.track_y + extra_params.track_height) {
+ gfx::ImageSkia* bottom = rb.GetImageSkiaNamed(
+ IDR_SCROLL_BASE_VERTICAL_BOTTOM_H);
+ DrawTiledImage(canvas, *bottom,
+ 0, 0, 1.0, 1.0,
+ rect.x(), rect.y() + rect.height() - bottom->height(),
+ bottom->width(), bottom->height());
+ center_height -= bottom->height();
+ }
+
+ if (center_height > 0) {
+ gfx::ImageSkia* center = rb.GetImageSkiaNamed(
+ IDR_SCROLL_BASE_VERTICAL_CENTER_H);
+ DrawTiledImage(canvas, *center,
+ 0, 0, 1.0, 1.0,
+ rect.x(), rect.y() + center_offset,
+ center->width(), center_height);
+ }
+ } else {
+ int center_offset = 0;
+ int center_width = rect.width();
+
+ if (rect.x() == extra_params.track_x) {
+ gfx::ImageSkia* left = rb.GetImageSkiaNamed(
+ IDR_SCROLL_BASE_HORIZONTAL_LEFT_H);
+ DrawTiledImage(canvas, *left,
+ 0, 0, 1.0, 1.0,
+ rect.x(), rect.y(), left->width(), left->height());
+ center_offset += left->width();
+ center_width -= left->width();
+ }
+
+ if (rect.x() + rect.width() ==
+ extra_params.track_x + extra_params.track_width) {
+ gfx::ImageSkia* right = rb.GetImageSkiaNamed(
+ IDR_SCROLL_BASE_HORIZONTAL_RIGHT_H);
+ DrawTiledImage(canvas, *right,
+ 0, 0, 1.0, 1.0,
+ rect.x() + rect.width() - right->width(), rect.y(),
+ right->width(), right->height());
+ center_width -= right->width();
+ }
+
+ if (center_width > 0) {
+ gfx::ImageSkia* center = rb.GetImageSkiaNamed(
+ IDR_SCROLL_BASE_HORIZONTAL_CENTER_H);
+ DrawTiledImage(canvas, *center,
+ 0, 0, 1.0, 1.0,
+ rect.x() + center_offset, rect.y(),
+ center_width, center->height());
+ }
+ }
+}
+
+void NativeThemeAura::PaintScrollbarThumb(SkCanvas* canvas,
+ Part part,
+ State state,
+ const gfx::Rect& rect) const {
+ ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
+ if (part == kScrollbarVerticalThumb) {
+ int top_resource_id =
+ state == kHovered ? IDR_SCROLL_THUMB_VERTICAL_TOP_H :
+ state == kPressed ? IDR_SCROLL_THUMB_VERTICAL_TOP_P :
+ IDR_SCROLL_THUMB_VERTICAL_TOP;
+ gfx::ImageSkia* top = rb.GetImageSkiaNamed(top_resource_id);
+ DrawTiledImage(canvas, *top,
+ 0, 0, 1.0, 1.0,
+ rect.x(), rect.y(), top->width(), top->height());
+
+ int bottom_resource_id =
+ state == kHovered ? IDR_SCROLL_THUMB_VERTICAL_BOTTOM_H :
+ state == kPressed ? IDR_SCROLL_THUMB_VERTICAL_BOTTOM_P :
+ IDR_SCROLL_THUMB_VERTICAL_BOTTOM;
+ gfx::ImageSkia* bottom = rb.GetImageSkiaNamed(bottom_resource_id);
+ DrawTiledImage(canvas, *bottom,
+ 0, 0, 1.0, 1.0,
+ rect.x(), rect.y() + rect.height() - bottom->height(),
+ bottom->width(), bottom->height());
+
+ if (rect.height() > top->height() + bottom->height()) {
+ int center_resource_id =
+ state == kHovered ? IDR_SCROLL_THUMB_VERTICAL_CENTER_H :
+ state == kPressed ? IDR_SCROLL_THUMB_VERTICAL_CENTER_P :
+ IDR_SCROLL_THUMB_VERTICAL_CENTER;
+ gfx::ImageSkia* center = rb.GetImageSkiaNamed(center_resource_id);
+ DrawTiledImage(canvas, *center,
+ 0, 0, 1.0, 1.0,
+ rect.x(), rect.y() + top->height(),
+ center->width(),
+ rect.height() - top->height() - bottom->height());
+ }
+ } else {
+ int left_resource_id =
+ state == kHovered ? IDR_SCROLL_THUMB_HORIZONTAL_LEFT_H :
+ state == kPressed ? IDR_SCROLL_THUMB_HORIZONTAL_LEFT_P :
+ IDR_SCROLL_THUMB_HORIZONTAL_LEFT;
+ gfx::ImageSkia* left = rb.GetImageSkiaNamed(left_resource_id);
+ DrawTiledImage(canvas, *left,
+ 0, 0, 1.0, 1.0,
+ rect.x(), rect.y(), left->width(), left->height());
+
+ int right_resource_id =
+ state == kHovered ? IDR_SCROLL_THUMB_HORIZONTAL_RIGHT_H :
+ state == kPressed ? IDR_SCROLL_THUMB_HORIZONTAL_RIGHT_P :
+ IDR_SCROLL_THUMB_HORIZONTAL_RIGHT;
+ gfx::ImageSkia* right = rb.GetImageSkiaNamed(right_resource_id);
+ DrawTiledImage(canvas, *right,
+ 0, 0, 1.0, 1.0,
+ rect.x() + rect.width() - right->width(), rect.y(),
+ right->width(), right->height());
+
+ if (rect.width() > left->width() + right->width()) {
+ int center_resource_id =
+ state == kHovered ? IDR_SCROLL_THUMB_HORIZONTAL_CENTER_H :
+ state == kPressed ? IDR_SCROLL_THUMB_HORIZONTAL_CENTER_P :
+ IDR_SCROLL_THUMB_HORIZONTAL_CENTER;
+ gfx::ImageSkia* center = rb.GetImageSkiaNamed(center_resource_id);
+ DrawTiledImage(canvas, *center,
+ 0, 0, 1.0, 1.0,
+ rect.x() + left->width(), rect.y(),
+ rect.width() - left->width() - right->width(),
+ center->height());
+ }
+ }
+}
+
+} // namespace ui
diff --git a/chromium/ui/native_theme/native_theme_aura.h b/chromium/ui/native_theme/native_theme_aura.h
new file mode 100644
index 00000000000..b3417b353ce
--- /dev/null
+++ b/chromium/ui/native_theme/native_theme_aura.h
@@ -0,0 +1,49 @@
+// Copyright (c) 2012 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.
+
+#ifndef UI_NATIVE_THEME_NATIVE_THEME_AURA_H_
+#define UI_NATIVE_THEME_NATIVE_THEME_AURA_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "ui/native_theme/fallback_theme.h"
+
+namespace ui {
+
+// Aura implementation of native theme support.
+class NATIVE_THEME_EXPORT NativeThemeAura : public FallbackTheme {
+ public:
+ static NativeThemeAura* instance();
+
+ private:
+ NativeThemeAura();
+ virtual ~NativeThemeAura();
+
+ // Overridden from NativeThemeBase:
+ virtual void PaintMenuPopupBackground(
+ SkCanvas* canvas,
+ const gfx::Size& size,
+ const MenuBackgroundExtraParams& menu_background) const OVERRIDE;
+ virtual void PaintMenuItemBackground(
+ SkCanvas* canvas,
+ State state,
+ const gfx::Rect& rect,
+ const MenuListExtraParams& menu_list) const OVERRIDE;
+ virtual void PaintScrollbarTrack(
+ SkCanvas* canvas,
+ Part part,
+ State state,
+ const ScrollbarTrackExtraParams& extra_params,
+ const gfx::Rect& rect) const OVERRIDE;
+ virtual void PaintScrollbarThumb(SkCanvas* canvas,
+ Part part,
+ State state,
+ const gfx::Rect& rect) const OVERRIDE;
+
+ DISALLOW_COPY_AND_ASSIGN(NativeThemeAura);
+};
+
+} // namespace ui
+
+#endif // UI_NATIVE_THEME_NATIVE_THEME_AURA_H_
diff --git a/chromium/ui/native_theme/native_theme_base.cc b/chromium/ui/native_theme/native_theme_base.cc
new file mode 100644
index 00000000000..2f94f54cd7e
--- /dev/null
+++ b/chromium/ui/native_theme/native_theme_base.cc
@@ -0,0 +1,1114 @@
+// Copyright (c) 2012 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 "ui/native_theme/native_theme_base.h"
+
+#include <limits>
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "grit/ui_resources.h"
+#include "third_party/skia/include/effects/SkGradientShader.h"
+#include "ui/base/layout.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/base/ui_base_switches.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/color_utils.h"
+#include "ui/gfx/image/image_skia.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/size.h"
+#include "ui/gfx/skia_util.h"
+
+namespace {
+
+// These are the default dimensions of radio buttons and checkboxes.
+const int kCheckboxAndRadioWidth = 13;
+const int kCheckboxAndRadioHeight = 13;
+
+// These sizes match the sizes in Chromium Win.
+const int kSliderThumbWidth = 11;
+const int kSliderThumbHeight = 21;
+
+const SkColor kSliderTrackBackgroundColor =
+ SkColorSetRGB(0xe3, 0xdd, 0xd8);
+const SkColor kSliderThumbLightGrey = SkColorSetRGB(0xf4, 0xf2, 0xef);
+const SkColor kSliderThumbDarkGrey = SkColorSetRGB(0xea, 0xe5, 0xe0);
+const SkColor kSliderThumbBorderDarkGrey =
+ SkColorSetRGB(0x9d, 0x96, 0x8e);
+
+const SkColor kMenuPopupBackgroundColor = SkColorSetRGB(210, 225, 246);
+
+const unsigned int kDefaultScrollbarWidth = 15;
+const unsigned int kDefaultScrollbarButtonLength = 14;
+
+const SkColor kCheckboxTinyColor = SK_ColorGRAY;
+const SkColor kCheckboxShadowColor = SkColorSetARGB(0x15, 0, 0, 0);
+const SkColor kCheckboxShadowHoveredColor = SkColorSetARGB(0x1F, 0, 0, 0);
+const SkColor kCheckboxShadowDisabledColor = SkColorSetARGB(0, 0, 0, 0);
+const SkColor kCheckboxGradientColors[] = {
+ SkColorSetRGB(0xed, 0xed, 0xed),
+ SkColorSetRGB(0xde, 0xde, 0xde) };
+const SkColor kCheckboxGradientPressedColors[] = {
+ SkColorSetRGB(0xe7, 0xe7, 0xe7),
+ SkColorSetRGB(0xd7, 0xd7, 0xd7) };
+const SkColor kCheckboxGradientHoveredColors[] = {
+ SkColorSetRGB(0xf0, 0xf0, 0xf0),
+ SkColorSetRGB(0xe0, 0xe0, 0xe0) };
+const SkColor kCheckboxGradientDisabledColors[] = {
+ SkColorSetARGB(0x80, 0xed, 0xed, 0xed),
+ SkColorSetARGB(0x80, 0xde, 0xde, 0xde) };
+const SkColor kCheckboxBorderColor = SkColorSetARGB(0x40, 0, 0, 0);
+const SkColor kCheckboxBorderHoveredColor = SkColorSetARGB(0x4D, 0, 0, 0);
+const SkColor kCheckboxBorderDisabledColor = SkColorSetARGB(0x20, 0, 0, 0);
+const SkColor kCheckboxStrokeColor = SkColorSetARGB(0xB3, 0, 0, 0);
+const SkColor kCheckboxStrokeDisabledColor = SkColorSetARGB(0x59, 0, 0, 0);
+const SkColor kRadioDotColor = SkColorSetRGB(0x66, 0x66, 0x66);
+const SkColor kRadioDotDisabledColor = SkColorSetARGB(0x80, 0x66, 0x66, 0x66);
+
+const SkColor kInputInvalidColor = SkColorSetRGB(0xde, 0x49, 0x32);
+
+// Get lightness adjusted color.
+SkColor BrightenColor(const color_utils::HSL& hsl, SkAlpha alpha,
+ double lightness_amount) {
+ color_utils::HSL adjusted = hsl;
+ adjusted.l += lightness_amount;
+ if (adjusted.l > 1.0)
+ adjusted.l = 1.0;
+ if (adjusted.l < 0.0)
+ adjusted.l = 0.0;
+
+ return color_utils::HSLToSkColor(adjusted, alpha);
+}
+
+} // namespace
+
+namespace ui {
+
+gfx::Size NativeThemeBase::GetPartSize(Part part,
+ State state,
+ const ExtraParams& extra) const {
+ switch (part) {
+ // Please keep these in the order of NativeTheme::Part.
+ case kCheckbox:
+ return gfx::Size(kCheckboxAndRadioWidth, kCheckboxAndRadioHeight);
+ case kInnerSpinButton:
+ return gfx::Size(scrollbar_width_, 0);
+ case kMenuList:
+ return gfx::Size(); // No default size.
+ case kMenuCheck:
+ case kMenuCheckBackground:
+ case kMenuPopupArrow:
+ NOTIMPLEMENTED();
+ break;
+ case kMenuPopupBackground:
+ return gfx::Size(); // No default size.
+ case kMenuPopupGutter:
+ case kMenuPopupSeparator:
+ NOTIMPLEMENTED();
+ break;
+ case kMenuItemBackground:
+ case kProgressBar:
+ case kPushButton:
+ return gfx::Size(); // No default size.
+ case kRadio:
+ return gfx::Size(kCheckboxAndRadioWidth, kCheckboxAndRadioHeight);
+ case kScrollbarDownArrow:
+ case kScrollbarUpArrow:
+ return gfx::Size(scrollbar_width_, scrollbar_button_length_);
+ case kScrollbarLeftArrow:
+ case kScrollbarRightArrow:
+ return gfx::Size(scrollbar_button_length_, scrollbar_width_);
+ case kScrollbarHorizontalThumb:
+ // This matches Firefox on Linux.
+ return gfx::Size(2 * scrollbar_width_, scrollbar_width_);
+ case kScrollbarVerticalThumb:
+ // This matches Firefox on Linux.
+ return gfx::Size(scrollbar_width_, 2 * scrollbar_width_);
+ case kScrollbarHorizontalTrack:
+ return gfx::Size(0, scrollbar_width_);
+ case kScrollbarVerticalTrack:
+ return gfx::Size(scrollbar_width_, 0);
+ case kScrollbarHorizontalGripper:
+ case kScrollbarVerticalGripper:
+ NOTIMPLEMENTED();
+ break;
+ case kSliderTrack:
+ return gfx::Size(); // No default size.
+ case kSliderThumb:
+ // These sizes match the sizes in Chromium Win.
+ return gfx::Size(kSliderThumbWidth, kSliderThumbHeight);
+ case kTabPanelBackground:
+ NOTIMPLEMENTED();
+ break;
+ case kTextField:
+ return gfx::Size(); // No default size.
+ case kTrackbarThumb:
+ case kTrackbarTrack:
+ case kWindowResizeGripper:
+ NOTIMPLEMENTED();
+ break;
+ default:
+ NOTREACHED() << "Unknown theme part: " << part;
+ break;
+ }
+ return gfx::Size();
+}
+
+void NativeThemeBase::Paint(SkCanvas* canvas,
+ Part part,
+ State state,
+ const gfx::Rect& rect,
+ const ExtraParams& extra) const {
+ if (rect.IsEmpty())
+ return;
+
+ switch (part) {
+ // Please keep these in the order of NativeTheme::Part.
+ case kCheckbox:
+ PaintCheckbox(canvas, state, rect, extra.button);
+ break;
+ case kInnerSpinButton:
+ PaintInnerSpinButton(canvas, state, rect, extra.inner_spin);
+ break;
+ case kMenuList:
+ PaintMenuList(canvas, state, rect, extra.menu_list);
+ break;
+ case kMenuCheck:
+ case kMenuCheckBackground:
+ case kMenuPopupArrow:
+ NOTIMPLEMENTED();
+ break;
+ case kMenuPopupBackground:
+ PaintMenuPopupBackground(canvas, rect.size(), extra.menu_background);
+ break;
+ case kMenuPopupGutter:
+ case kMenuPopupSeparator:
+ NOTIMPLEMENTED();
+ break;
+ case kMenuItemBackground:
+ PaintMenuItemBackground(canvas, state, rect, extra.menu_list);
+ break;
+ case kProgressBar:
+ PaintProgressBar(canvas, state, rect, extra.progress_bar);
+ break;
+ case kPushButton:
+ PaintButton(canvas, state, rect, extra.button);
+ break;
+ case kRadio:
+ PaintRadio(canvas, state, rect, extra.button);
+ break;
+ case kScrollbarDownArrow:
+ case kScrollbarUpArrow:
+ case kScrollbarLeftArrow:
+ case kScrollbarRightArrow:
+ PaintArrowButton(canvas, rect, part, state);
+ break;
+ case kScrollbarHorizontalThumb:
+ case kScrollbarVerticalThumb:
+ PaintScrollbarThumb(canvas, part, state, rect);
+ break;
+ case kScrollbarHorizontalTrack:
+ case kScrollbarVerticalTrack:
+ PaintScrollbarTrack(canvas, part, state, extra.scrollbar_track, rect);
+ break;
+ case kScrollbarHorizontalGripper:
+ case kScrollbarVerticalGripper:
+ // Invoked by views scrollbar code, don't care about for non-win
+ // implementations, so no NOTIMPLEMENTED.
+ break;
+ case kSliderTrack:
+ PaintSliderTrack(canvas, state, rect, extra.slider);
+ break;
+ case kSliderThumb:
+ PaintSliderThumb(canvas, state, rect, extra.slider);
+ break;
+ case kTabPanelBackground:
+ NOTIMPLEMENTED();
+ break;
+ case kTextField:
+ PaintTextField(canvas, state, rect, extra.text_field);
+ break;
+ case kTrackbarThumb:
+ case kTrackbarTrack:
+ case kWindowResizeGripper:
+ NOTIMPLEMENTED();
+ break;
+ default:
+ NOTREACHED() << "Unknown theme part: " << part;
+ break;
+ }
+}
+
+NativeThemeBase::NativeThemeBase()
+ : scrollbar_width_(kDefaultScrollbarWidth),
+ scrollbar_button_length_(kDefaultScrollbarButtonLength) {
+}
+
+NativeThemeBase::~NativeThemeBase() {
+}
+
+void NativeThemeBase::PaintArrowButton(
+ SkCanvas* canvas,
+ const gfx::Rect& rect, Part direction, State state) const {
+ int widthMiddle, lengthMiddle;
+ SkPaint paint;
+ if (direction == kScrollbarUpArrow || direction == kScrollbarDownArrow) {
+ widthMiddle = rect.width() / 2 + 1;
+ lengthMiddle = rect.height() / 2 + 1;
+ } else {
+ lengthMiddle = rect.width() / 2 + 1;
+ widthMiddle = rect.height() / 2 + 1;
+ }
+
+ // Calculate button color.
+ SkScalar trackHSV[3];
+ SkColorToHSV(track_color_, trackHSV);
+ SkColor buttonColor = SaturateAndBrighten(trackHSV, 0, 0.2f);
+ SkColor backgroundColor = buttonColor;
+ if (state == kPressed) {
+ SkScalar buttonHSV[3];
+ SkColorToHSV(buttonColor, buttonHSV);
+ buttonColor = SaturateAndBrighten(buttonHSV, 0, -0.1f);
+ } else if (state == kHovered) {
+ SkScalar buttonHSV[3];
+ SkColorToHSV(buttonColor, buttonHSV);
+ buttonColor = SaturateAndBrighten(buttonHSV, 0, 0.05f);
+ }
+
+ SkIRect skrect;
+ skrect.set(rect.x(), rect.y(), rect.x() + rect.width(), rect.y()
+ + rect.height());
+ // Paint the background (the area visible behind the rounded corners).
+ paint.setColor(backgroundColor);
+ canvas->drawIRect(skrect, paint);
+
+ // Paint the button's outline and fill the middle
+ SkPath outline;
+ switch (direction) {
+ case kScrollbarUpArrow:
+ outline.moveTo(rect.x() + 0.5, rect.y() + rect.height() + 0.5);
+ outline.rLineTo(0, -(rect.height() - 2));
+ outline.rLineTo(2, -2);
+ outline.rLineTo(rect.width() - 5, 0);
+ outline.rLineTo(2, 2);
+ outline.rLineTo(0, rect.height() - 2);
+ break;
+ case kScrollbarDownArrow:
+ outline.moveTo(rect.x() + 0.5, rect.y() - 0.5);
+ outline.rLineTo(0, rect.height() - 2);
+ outline.rLineTo(2, 2);
+ outline.rLineTo(rect.width() - 5, 0);
+ outline.rLineTo(2, -2);
+ outline.rLineTo(0, -(rect.height() - 2));
+ break;
+ case kScrollbarRightArrow:
+ outline.moveTo(rect.x() - 0.5, rect.y() + 0.5);
+ outline.rLineTo(rect.width() - 2, 0);
+ outline.rLineTo(2, 2);
+ outline.rLineTo(0, rect.height() - 5);
+ outline.rLineTo(-2, 2);
+ outline.rLineTo(-(rect.width() - 2), 0);
+ break;
+ case kScrollbarLeftArrow:
+ outline.moveTo(rect.x() + rect.width() + 0.5, rect.y() + 0.5);
+ outline.rLineTo(-(rect.width() - 2), 0);
+ outline.rLineTo(-2, 2);
+ outline.rLineTo(0, rect.height() - 5);
+ outline.rLineTo(2, 2);
+ outline.rLineTo(rect.width() - 2, 0);
+ break;
+ default:
+ break;
+ }
+ outline.close();
+
+ paint.setStyle(SkPaint::kFill_Style);
+ paint.setColor(buttonColor);
+ canvas->drawPath(outline, paint);
+
+ paint.setAntiAlias(true);
+ paint.setStyle(SkPaint::kStroke_Style);
+ SkScalar thumbHSV[3];
+ SkColorToHSV(thumb_inactive_color_, thumbHSV);
+ paint.setColor(OutlineColor(trackHSV, thumbHSV));
+ canvas->drawPath(outline, paint);
+
+ // If the button is disabled or read-only, the arrow is drawn with the
+ // outline color.
+ if (state != kDisabled)
+ paint.setColor(SK_ColorBLACK);
+
+ paint.setAntiAlias(false);
+ paint.setStyle(SkPaint::kFill_Style);
+
+ SkPath path;
+ // The constants in this block of code are hand-tailored to produce good
+ // looking arrows without anti-aliasing.
+ switch (direction) {
+ case kScrollbarUpArrow:
+ path.moveTo(rect.x() + widthMiddle - 4, rect.y() + lengthMiddle + 2);
+ path.rLineTo(7, 0);
+ path.rLineTo(-4, -4);
+ break;
+ case kScrollbarDownArrow:
+ path.moveTo(rect.x() + widthMiddle - 4, rect.y() + lengthMiddle - 3);
+ path.rLineTo(7, 0);
+ path.rLineTo(-4, 4);
+ break;
+ case kScrollbarRightArrow:
+ path.moveTo(rect.x() + lengthMiddle - 3, rect.y() + widthMiddle - 4);
+ path.rLineTo(0, 7);
+ path.rLineTo(4, -4);
+ break;
+ case kScrollbarLeftArrow:
+ path.moveTo(rect.x() + lengthMiddle + 1, rect.y() + widthMiddle - 5);
+ path.rLineTo(0, 9);
+ path.rLineTo(-4, -4);
+ break;
+ default:
+ break;
+ }
+ path.close();
+
+ canvas->drawPath(path, paint);
+}
+
+void NativeThemeBase::PaintScrollbarTrack(SkCanvas* canvas,
+ Part part,
+ State state,
+ const ScrollbarTrackExtraParams& extra_params,
+ const gfx::Rect& rect) const {
+ SkPaint paint;
+ SkIRect skrect;
+
+ skrect.set(rect.x(), rect.y(), rect.right(), rect.bottom());
+ SkScalar track_hsv[3];
+ SkColorToHSV(track_color_, track_hsv);
+ paint.setColor(SaturateAndBrighten(track_hsv, 0, 0));
+ canvas->drawIRect(skrect, paint);
+
+ SkScalar thumb_hsv[3];
+ SkColorToHSV(thumb_inactive_color_, thumb_hsv);
+
+ paint.setColor(OutlineColor(track_hsv, thumb_hsv));
+ DrawBox(canvas, rect, paint);
+}
+
+void NativeThemeBase::PaintScrollbarThumb(SkCanvas* canvas,
+ Part part,
+ State state,
+ const gfx::Rect& rect) const {
+ const bool hovered = state == kHovered;
+ const int midx = rect.x() + rect.width() / 2;
+ const int midy = rect.y() + rect.height() / 2;
+ const bool vertical = part == kScrollbarVerticalThumb;
+
+ SkScalar thumb[3];
+ SkColorToHSV(hovered ? thumb_active_color_ : thumb_inactive_color_, thumb);
+
+ SkPaint paint;
+ paint.setColor(SaturateAndBrighten(thumb, 0, 0.02f));
+
+ SkIRect skrect;
+ if (vertical)
+ skrect.set(rect.x(), rect.y(), midx + 1, rect.y() + rect.height());
+ else
+ skrect.set(rect.x(), rect.y(), rect.x() + rect.width(), midy + 1);
+
+ canvas->drawIRect(skrect, paint);
+
+ paint.setColor(SaturateAndBrighten(thumb, 0, -0.02f));
+
+ if (vertical) {
+ skrect.set(
+ midx + 1, rect.y(), rect.x() + rect.width(), rect.y() + rect.height());
+ } else {
+ skrect.set(
+ rect.x(), midy + 1, rect.x() + rect.width(), rect.y() + rect.height());
+ }
+
+ canvas->drawIRect(skrect, paint);
+
+ SkScalar track[3];
+ SkColorToHSV(track_color_, track);
+ paint.setColor(OutlineColor(track, thumb));
+ DrawBox(canvas, rect, paint);
+
+ if (rect.height() > 10 && rect.width() > 10) {
+ const int grippy_half_width = 2;
+ const int inter_grippy_offset = 3;
+ if (vertical) {
+ DrawHorizLine(canvas,
+ midx - grippy_half_width,
+ midx + grippy_half_width,
+ midy - inter_grippy_offset,
+ paint);
+ DrawHorizLine(canvas,
+ midx - grippy_half_width,
+ midx + grippy_half_width,
+ midy,
+ paint);
+ DrawHorizLine(canvas,
+ midx - grippy_half_width,
+ midx + grippy_half_width,
+ midy + inter_grippy_offset,
+ paint);
+ } else {
+ DrawVertLine(canvas,
+ midx - inter_grippy_offset,
+ midy - grippy_half_width,
+ midy + grippy_half_width,
+ paint);
+ DrawVertLine(canvas,
+ midx,
+ midy - grippy_half_width,
+ midy + grippy_half_width,
+ paint);
+ DrawVertLine(canvas,
+ midx + inter_grippy_offset,
+ midy - grippy_half_width,
+ midy + grippy_half_width,
+ paint);
+ }
+ }
+}
+
+void NativeThemeBase::PaintCheckbox(SkCanvas* canvas,
+ State state,
+ const gfx::Rect& rect,
+ const ButtonExtraParams& button) const {
+ SkRect skrect = PaintCheckboxRadioCommon(canvas, state, rect,
+ SkIntToScalar(2));
+ if (!skrect.isEmpty()) {
+ // Draw the checkmark / dash.
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setStyle(SkPaint::kStroke_Style);
+ if (state == kDisabled)
+ paint.setColor(kCheckboxStrokeDisabledColor);
+ else
+ paint.setColor(kCheckboxStrokeColor);
+ if (button.indeterminate) {
+ SkPath dash;
+ dash.moveTo(skrect.x() + skrect.width() * 0.16,
+ (skrect.y() + skrect.bottom()) / 2);
+ dash.rLineTo(skrect.width() * 0.68, 0);
+ paint.setStrokeWidth(SkFloatToScalar(skrect.height() * 0.2));
+ canvas->drawPath(dash, paint);
+ } else if (button.checked) {
+ SkPath check;
+ check.moveTo(skrect.x() + skrect.width() * 0.2,
+ skrect.y() + skrect.height() * 0.5);
+ check.rLineTo(skrect.width() * 0.2, skrect.height() * 0.2);
+ paint.setStrokeWidth(SkFloatToScalar(skrect.height() * 0.23));
+ check.lineTo(skrect.right() - skrect.width() * 0.2,
+ skrect.y() + skrect.height() * 0.2);
+ canvas->drawPath(check, paint);
+ }
+ }
+}
+
+// Draws the common elements of checkboxes and radio buttons.
+// Returns the rectangle within which any additional decorations should be
+// drawn, or empty if none.
+SkRect NativeThemeBase::PaintCheckboxRadioCommon(
+ SkCanvas* canvas,
+ State state,
+ const gfx::Rect& rect,
+ const SkScalar borderRadius) const {
+
+ SkRect skrect = gfx::RectToSkRect(rect);
+
+ // Use the largest square that fits inside the provided rectangle.
+ // No other browser seems to support non-square widget, so accidentally
+ // having non-square sizes is common (eg. amazon and webkit dev tools).
+ if (skrect.width() != skrect.height()) {
+ SkScalar size = SkMinScalar(skrect.width(), skrect.height());
+ skrect.inset((skrect.width() - size) / 2, (skrect.height() - size) / 2);
+ }
+
+ // If the rectangle is too small then paint only a rectangle. We don't want
+ // to have to worry about '- 1' and '+ 1' calculations below having overflow
+ // or underflow.
+ if (skrect.width() <= 2) {
+ SkPaint paint;
+ paint.setColor(kCheckboxTinyColor);
+ paint.setStyle(SkPaint::kFill_Style);
+ canvas->drawRect(skrect, paint);
+ // Too small to draw anything more.
+ return SkRect::MakeEmpty();
+ }
+
+ // Make room for the drop shadow.
+ skrect.iset(skrect.x(), skrect.y(), skrect.right() - 1, skrect.bottom() - 1);
+
+ // Draw the drop shadow below the widget.
+ if (state != kPressed) {
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ SkRect shadowRect = skrect;
+ shadowRect.offset(0, 1);
+ if (state == kDisabled)
+ paint.setColor(kCheckboxShadowDisabledColor);
+ else if (state == kHovered)
+ paint.setColor(kCheckboxShadowHoveredColor);
+ else
+ paint.setColor(kCheckboxShadowColor);
+ paint.setStyle(SkPaint::kFill_Style);
+ canvas->drawRoundRect(shadowRect, borderRadius, borderRadius, paint);
+ }
+
+ // Draw the gradient-filled rectangle
+ SkPoint gradient_bounds[3];
+ gradient_bounds[0].set(skrect.x(), skrect.y());
+ gradient_bounds[1].set(skrect.x(), skrect.y() + skrect.height() * 0.38);
+ gradient_bounds[2].set(skrect.x(), skrect.bottom());
+ const SkColor* startEndColors;
+ if (state == kPressed)
+ startEndColors = kCheckboxGradientPressedColors;
+ else if (state == kHovered)
+ startEndColors = kCheckboxGradientHoveredColors;
+ else if (state == kDisabled)
+ startEndColors = kCheckboxGradientDisabledColors;
+ else /* kNormal */
+ startEndColors = kCheckboxGradientColors;
+ SkColor colors[3] = {startEndColors[0], startEndColors[0], startEndColors[1]};
+ skia::RefPtr<SkShader> shader = skia::AdoptRef(
+ SkGradientShader::CreateLinear(
+ gradient_bounds, colors, NULL, 3, SkShader::kClamp_TileMode, NULL));
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setShader(shader.get());
+ paint.setStyle(SkPaint::kFill_Style);
+ canvas->drawRoundRect(skrect, borderRadius, borderRadius, paint);
+ paint.setShader(NULL);
+
+ // Draw the border.
+ if (state == kHovered)
+ paint.setColor(kCheckboxBorderHoveredColor);
+ else if (state == kDisabled)
+ paint.setColor(kCheckboxBorderDisabledColor);
+ else
+ paint.setColor(kCheckboxBorderColor);
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setStrokeWidth(SkIntToScalar(1));
+ skrect.inset(SkFloatToScalar(.5f), SkFloatToScalar(.5f));
+ canvas->drawRoundRect(skrect, borderRadius, borderRadius, paint);
+
+ // Return the rectangle excluding the drop shadow for drawing any additional
+ // decorations.
+ return skrect;
+}
+
+void NativeThemeBase::PaintRadio(SkCanvas* canvas,
+ State state,
+ const gfx::Rect& rect,
+ const ButtonExtraParams& button) const {
+
+ // Most of a radio button is the same as a checkbox, except the the rounded
+ // square is a circle (i.e. border radius >= 100%).
+ const SkScalar radius = SkFloatToScalar(
+ static_cast<float>(std::max(rect.width(), rect.height())) / 2);
+ SkRect skrect = PaintCheckboxRadioCommon(canvas, state, rect, radius);
+ if (!skrect.isEmpty() && button.checked) {
+ // Draw the dot.
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setStyle(SkPaint::kFill_Style);
+ if (state == kDisabled)
+ paint.setColor(kRadioDotDisabledColor);
+ else
+ paint.setColor(kRadioDotColor);
+ skrect.inset(skrect.width() * 0.25, skrect.height() * 0.25);
+ // Use drawRoundedRect instead of drawOval to be completely consistent
+ // with the border in PaintCheckboxRadioNewCommon.
+ canvas->drawRoundRect(skrect, radius, radius, paint);
+ }
+}
+
+void NativeThemeBase::PaintButton(SkCanvas* canvas,
+ State state,
+ const gfx::Rect& rect,
+ const ButtonExtraParams& button) const {
+ SkPaint paint;
+ const int kRight = rect.right();
+ const int kBottom = rect.bottom();
+ SkRect skrect = SkRect::MakeLTRB(rect.x(), rect.y(), kRight, kBottom);
+ SkColor base_color = button.background_color;
+
+ color_utils::HSL base_hsl;
+ color_utils::SkColorToHSL(base_color, &base_hsl);
+
+ // Our standard gradient is from 0xdd to 0xf8. This is the amount of
+ // increased luminance between those values.
+ SkColor light_color(BrightenColor(base_hsl, SkColorGetA(base_color), 0.105));
+
+ // If the button is too small, fallback to drawing a single, solid color
+ if (rect.width() < 5 || rect.height() < 5) {
+ paint.setColor(base_color);
+ canvas->drawRect(skrect, paint);
+ return;
+ }
+
+ paint.setColor(SK_ColorBLACK);
+ const int kLightEnd = state == kPressed ? 1 : 0;
+ const int kDarkEnd = !kLightEnd;
+ SkPoint gradient_bounds[2];
+ gradient_bounds[kLightEnd].iset(rect.x(), rect.y());
+ gradient_bounds[kDarkEnd].iset(rect.x(), kBottom - 1);
+ SkColor colors[2];
+ colors[0] = light_color;
+ colors[1] = base_color;
+
+ skia::RefPtr<SkShader> shader = skia::AdoptRef(
+ SkGradientShader::CreateLinear(
+ gradient_bounds, colors, NULL, 2, SkShader::kClamp_TileMode, NULL));
+ paint.setStyle(SkPaint::kFill_Style);
+ paint.setAntiAlias(true);
+ paint.setShader(shader.get());
+
+ canvas->drawRoundRect(skrect, SkIntToScalar(1), SkIntToScalar(1), paint);
+ paint.setShader(NULL);
+
+ if (button.has_border) {
+ int border_alpha = state == kHovered ? 0x80 : 0x55;
+ if (button.is_focused) {
+ border_alpha = 0xff;
+ paint.setColor(GetSystemColor(kColorId_FocusedBorderColor));
+ }
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setStrokeWidth(SkIntToScalar(1));
+ paint.setAlpha(border_alpha);
+ skrect.inset(SkFloatToScalar(.5f), SkFloatToScalar(.5f));
+ canvas->drawRoundRect(skrect, SkIntToScalar(1), SkIntToScalar(1), paint);
+ }
+}
+
+void NativeThemeBase::PaintTextField(SkCanvas* canvas,
+ State state,
+ const gfx::Rect& rect,
+ const TextFieldExtraParams& text) const {
+ // The following drawing code simulates the user-agent css border for
+ // text area and text input so that we do not break layout tests. Once we
+ // have decided the desired looks, we should update the code here and
+ // the layout test expectations.
+ SkRect bounds;
+ bounds.set(rect.x(), rect.y(), rect.right() - 1, rect.bottom() - 1);
+
+ SkPaint fill_paint;
+ fill_paint.setStyle(SkPaint::kFill_Style);
+ fill_paint.setColor(text.background_color);
+ canvas->drawRect(bounds, fill_paint);
+
+ if (text.is_text_area) {
+ // Draw text area border: 1px solid black
+ SkPaint stroke_paint;
+ fill_paint.setStyle(SkPaint::kStroke_Style);
+ fill_paint.setColor(SK_ColorBLACK);
+ canvas->drawRect(bounds, fill_paint);
+ } else {
+ // Draw text input and listbox inset border
+ // Text Input: 2px inset #eee
+ // Listbox: 1px inset #808080
+ const SkColor kLightColor = text.is_listbox ?
+ SkColorSetRGB(0x80, 0x80, 0x80) : SkColorSetRGB(0xee, 0xee, 0xee);
+ const SkColor kDarkColor = text.is_listbox ?
+ SkColorSetRGB(0x2c, 0x2c, 0x2c) : SkColorSetRGB(0x9a, 0x9a, 0x9a);
+ const int kBorderWidth = text.is_listbox ? 1 : 2;
+
+ SkPaint dark_paint;
+ dark_paint.setAntiAlias(true);
+ dark_paint.setStyle(SkPaint::kFill_Style);
+ dark_paint.setColor(kDarkColor);
+
+ SkPaint light_paint;
+ light_paint.setAntiAlias(true);
+ light_paint.setStyle(SkPaint::kFill_Style);
+ light_paint.setColor(kLightColor);
+
+ int left = rect.x();
+ int top = rect.y();
+ int right = rect.right();
+ int bottom = rect.bottom();
+
+ SkPath path;
+ path.incReserve(4);
+
+ // Top
+ path.moveTo(SkIntToScalar(left), SkIntToScalar(top));
+ path.lineTo(SkIntToScalar(left + kBorderWidth),
+ SkIntToScalar(top + kBorderWidth));
+ path.lineTo(SkIntToScalar(right - kBorderWidth),
+ SkIntToScalar(top + kBorderWidth));
+ path.lineTo(SkIntToScalar(right), SkIntToScalar(top));
+ canvas->drawPath(path, dark_paint);
+
+ // Bottom
+ path.reset();
+ path.moveTo(SkIntToScalar(left + kBorderWidth),
+ SkIntToScalar(bottom - kBorderWidth));
+ path.lineTo(SkIntToScalar(left), SkIntToScalar(bottom));
+ path.lineTo(SkIntToScalar(right), SkIntToScalar(bottom));
+ path.lineTo(SkIntToScalar(right - kBorderWidth),
+ SkIntToScalar(bottom - kBorderWidth));
+ canvas->drawPath(path, light_paint);
+
+ // Left
+ path.reset();
+ path.moveTo(SkIntToScalar(left), SkIntToScalar(top));
+ path.lineTo(SkIntToScalar(left), SkIntToScalar(bottom));
+ path.lineTo(SkIntToScalar(left + kBorderWidth),
+ SkIntToScalar(bottom - kBorderWidth));
+ path.lineTo(SkIntToScalar(left + kBorderWidth),
+ SkIntToScalar(top + kBorderWidth));
+ canvas->drawPath(path, dark_paint);
+
+ // Right
+ path.reset();
+ path.moveTo(SkIntToScalar(right - kBorderWidth),
+ SkIntToScalar(top + kBorderWidth));
+ path.lineTo(SkIntToScalar(right - kBorderWidth), SkIntToScalar(bottom));
+ path.lineTo(SkIntToScalar(right), SkIntToScalar(bottom));
+ path.lineTo(SkIntToScalar(right), SkIntToScalar(top));
+ canvas->drawPath(path, light_paint);
+ }
+}
+
+void NativeThemeBase::PaintMenuList(
+ SkCanvas* canvas,
+ State state,
+ const gfx::Rect& rect,
+ const MenuListExtraParams& menu_list) const {
+ // If a border radius is specified, we let the WebCore paint the background
+ // and the border of the control.
+ if (!menu_list.has_border_radius) {
+ ButtonExtraParams button = { 0 };
+ button.background_color = menu_list.background_color;
+ button.has_border = menu_list.has_border;
+ PaintButton(canvas, state, rect, button);
+ }
+
+ SkPaint paint;
+ paint.setColor(SK_ColorBLACK);
+ paint.setAntiAlias(true);
+ paint.setStyle(SkPaint::kFill_Style);
+
+ SkPath path;
+ path.moveTo(menu_list.arrow_x, menu_list.arrow_y - 3);
+ path.rLineTo(6, 0);
+ path.rLineTo(-3, 6);
+ path.close();
+ canvas->drawPath(path, paint);
+}
+
+void NativeThemeBase::PaintMenuPopupBackground(
+ SkCanvas* canvas,
+ const gfx::Size& size,
+ const MenuBackgroundExtraParams& menu_background) const {
+ canvas->drawColor(kMenuPopupBackgroundColor, SkXfermode::kSrc_Mode);
+}
+
+void NativeThemeBase::PaintMenuItemBackground(
+ SkCanvas* canvas,
+ State state,
+ const gfx::Rect& rect,
+ const MenuListExtraParams& menu_list) const {
+ // By default don't draw anything over the normal background.
+}
+
+void NativeThemeBase::PaintSliderTrack(SkCanvas* canvas,
+ State state,
+ const gfx::Rect& rect,
+ const SliderExtraParams& slider) const {
+ const int kMidX = rect.x() + rect.width() / 2;
+ const int kMidY = rect.y() + rect.height() / 2;
+
+ SkPaint paint;
+ paint.setColor(kSliderTrackBackgroundColor);
+
+ SkRect skrect;
+ if (slider.vertical) {
+ skrect.set(std::max(rect.x(), kMidX - 2),
+ rect.y(),
+ std::min(rect.right(), kMidX + 2),
+ rect.bottom());
+ } else {
+ skrect.set(rect.x(),
+ std::max(rect.y(), kMidY - 2),
+ rect.right(),
+ std::min(rect.bottom(), kMidY + 2));
+ }
+ canvas->drawRect(skrect, paint);
+}
+
+void NativeThemeBase::PaintSliderThumb(SkCanvas* canvas,
+ State state,
+ const gfx::Rect& rect,
+ const SliderExtraParams& slider) const {
+ const bool hovered = (state == kHovered) || slider.in_drag;
+ const int kMidX = rect.x() + rect.width() / 2;
+ const int kMidY = rect.y() + rect.height() / 2;
+
+ SkPaint paint;
+ paint.setColor(hovered ? SK_ColorWHITE : kSliderThumbLightGrey);
+
+ SkIRect skrect;
+ if (slider.vertical)
+ skrect.set(rect.x(), rect.y(), kMidX + 1, rect.bottom());
+ else
+ skrect.set(rect.x(), rect.y(), rect.right(), kMidY + 1);
+
+ canvas->drawIRect(skrect, paint);
+
+ paint.setColor(hovered ? kSliderThumbLightGrey : kSliderThumbDarkGrey);
+
+ if (slider.vertical)
+ skrect.set(kMidX + 1, rect.y(), rect.right(), rect.bottom());
+ else
+ skrect.set(rect.x(), kMidY + 1, rect.right(), rect.bottom());
+
+ canvas->drawIRect(skrect, paint);
+
+ paint.setColor(kSliderThumbBorderDarkGrey);
+ DrawBox(canvas, rect, paint);
+
+ if (rect.height() > 10 && rect.width() > 10) {
+ DrawHorizLine(canvas, kMidX - 2, kMidX + 2, kMidY, paint);
+ DrawHorizLine(canvas, kMidX - 2, kMidX + 2, kMidY - 3, paint);
+ DrawHorizLine(canvas, kMidX - 2, kMidX + 2, kMidY + 3, paint);
+ }
+}
+
+void NativeThemeBase::PaintInnerSpinButton(SkCanvas* canvas,
+ State state,
+ const gfx::Rect& rect,
+ const InnerSpinButtonExtraParams& spin_button) const {
+ if (spin_button.read_only)
+ state = kDisabled;
+
+ State north_state = state;
+ State south_state = state;
+ if (spin_button.spin_up)
+ south_state = south_state != kDisabled ? kNormal : kDisabled;
+ else
+ north_state = north_state != kDisabled ? kNormal : kDisabled;
+
+ gfx::Rect half = rect;
+ half.set_height(rect.height() / 2);
+ PaintArrowButton(canvas, half, kScrollbarUpArrow, north_state);
+
+ half.set_y(rect.y() + rect.height() / 2);
+ PaintArrowButton(canvas, half, kScrollbarDownArrow, south_state);
+}
+
+void NativeThemeBase::PaintProgressBar(SkCanvas* canvas,
+ State state,
+ const gfx::Rect& rect,
+ const ProgressBarExtraParams& progress_bar) const {
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ gfx::ImageSkia* bar_image = rb.GetImageSkiaNamed(IDR_PROGRESS_BAR);
+ gfx::ImageSkia* left_border_image = rb.GetImageSkiaNamed(
+ IDR_PROGRESS_BORDER_LEFT);
+ gfx::ImageSkia* right_border_image = rb.GetImageSkiaNamed(
+ IDR_PROGRESS_BORDER_RIGHT);
+
+ DCHECK(bar_image->width() > 0);
+ DCHECK(rect.width() > 0);
+
+ float tile_scale_y = static_cast<float>(rect.height()) / bar_image->height();
+
+ int dest_left_border_width = left_border_image->width();
+ int dest_right_border_width = right_border_image->width();
+
+ // Since an implicit float -> int conversion will truncate, we want to make
+ // sure that if a border is desired, it gets at least one pixel.
+ if (dest_left_border_width > 0) {
+ dest_left_border_width = dest_left_border_width * tile_scale_y;
+ dest_left_border_width = std::max(dest_left_border_width, 1);
+ }
+ if (dest_right_border_width > 0) {
+ dest_right_border_width = dest_right_border_width * tile_scale_y;
+ dest_right_border_width = std::max(dest_right_border_width, 1);
+ }
+
+ // Since the width of the progress bar may not be evenly divisible by the
+ // tile size, in order to make it look right we may need to draw some of the
+ // with a width of 1 pixel smaller than the rest of the tiles.
+ int new_tile_width = static_cast<int>(bar_image->width() * tile_scale_y);
+ new_tile_width = std::max(new_tile_width, 1);
+
+ float tile_scale_x = static_cast<float>(new_tile_width) / bar_image->width();
+ if (rect.width() % new_tile_width == 0) {
+ DrawTiledImage(canvas, *bar_image, 0, 0, tile_scale_x, tile_scale_y,
+ rect.x(), rect.y(),
+ rect.width(), rect.height());
+ } else {
+ int num_tiles = 1 + rect.width() / new_tile_width;
+ int overshoot = num_tiles * new_tile_width - rect.width();
+ // Since |overshoot| represents the number of tiles that were too big, draw
+ // |overshoot| tiles with their width reduced by 1.
+ int num_big_tiles = num_tiles - overshoot;
+ int num_small_tiles = overshoot;
+ int small_width = new_tile_width - 1;
+ float small_scale_x = static_cast<float>(small_width) / bar_image->width();
+ float big_scale_x = tile_scale_x;
+
+ gfx::Rect big_rect = rect;
+ gfx::Rect small_rect = rect;
+ big_rect.Inset(0, 0, num_small_tiles*small_width, 0);
+ small_rect.Inset(num_big_tiles*new_tile_width, 0, 0, 0);
+
+ DrawTiledImage(canvas, *bar_image, 0, 0, big_scale_x, tile_scale_y,
+ big_rect.x(), big_rect.y(), big_rect.width(), big_rect.height());
+ DrawTiledImage(canvas, *bar_image, 0, 0, small_scale_x, tile_scale_y,
+ small_rect.x(), small_rect.y(), small_rect.width(), small_rect.height());
+ }
+ if (progress_bar.value_rect_width) {
+ gfx::ImageSkia* value_image = rb.GetImageSkiaNamed(IDR_PROGRESS_VALUE);
+
+ new_tile_width = static_cast<int>(value_image->width() * tile_scale_y);
+ tile_scale_x = static_cast<float>(new_tile_width) /
+ value_image->width();
+
+ DrawTiledImage(canvas, *value_image, 0, 0, tile_scale_x, tile_scale_y,
+ progress_bar.value_rect_x,
+ progress_bar.value_rect_y,
+ progress_bar.value_rect_width,
+ progress_bar.value_rect_height);
+ }
+
+ DrawImageInt(canvas, *left_border_image, 0, 0, left_border_image->width(),
+ left_border_image->height(), rect.x(), rect.y(), dest_left_border_width,
+ rect.height());
+
+ int dest_x = rect.right() - dest_right_border_width;
+ DrawImageInt(canvas, *right_border_image, 0, 0, right_border_image->width(),
+ right_border_image->height(), dest_x, rect.y(),
+ dest_right_border_width, rect.height());
+}
+
+bool NativeThemeBase::IntersectsClipRectInt(SkCanvas* canvas,
+ int x, int y, int w, int h) const {
+ SkRect clip;
+ return canvas->getClipBounds(&clip) &&
+ clip.intersect(SkIntToScalar(x), SkIntToScalar(y), SkIntToScalar(x + w),
+ SkIntToScalar(y + h));
+}
+
+void NativeThemeBase::DrawImageInt(
+ SkCanvas* sk_canvas, const gfx::ImageSkia& image,
+ int src_x, int src_y, int src_w, int src_h,
+ int dest_x, int dest_y, int dest_w, int dest_h) const {
+ // TODO(pkotwicz): Do something better and don't infer device
+ // scale factor from canvas scale.
+ SkMatrix m = sk_canvas->getTotalMatrix();
+ ui::ScaleFactor device_scale_factor = ui::GetScaleFactorFromScale(
+ SkScalarAbs(m.getScaleX()));
+ scoped_ptr<gfx::Canvas> canvas(gfx::Canvas::CreateCanvasWithoutScaling(
+ sk_canvas, device_scale_factor));
+ canvas->DrawImageInt(image, src_x, src_y, src_w, src_h,
+ dest_x, dest_y, dest_w, dest_h, true);
+}
+
+void NativeThemeBase::DrawTiledImage(SkCanvas* sk_canvas,
+ const gfx::ImageSkia& image,
+ int src_x, int src_y, float tile_scale_x, float tile_scale_y,
+ int dest_x, int dest_y, int w, int h) const {
+ // TODO(pkotwicz): Do something better and don't infer device
+ // scale factor from canvas scale.
+ SkMatrix m = sk_canvas->getTotalMatrix();
+ ui::ScaleFactor device_scale_factor = ui::GetScaleFactorFromScale(
+ SkScalarAbs(m.getScaleX()));
+ scoped_ptr<gfx::Canvas> canvas(gfx::Canvas::CreateCanvasWithoutScaling(
+ sk_canvas, device_scale_factor));
+ canvas->TileImageInt(image, src_x, src_y, tile_scale_x,
+ tile_scale_y, dest_x, dest_y, w, h);
+}
+
+SkColor NativeThemeBase::SaturateAndBrighten(SkScalar* hsv,
+ SkScalar saturate_amount,
+ SkScalar brighten_amount) const {
+ SkScalar color[3];
+ color[0] = hsv[0];
+ color[1] = Clamp(hsv[1] + saturate_amount, 0.0, 1.0);
+ color[2] = Clamp(hsv[2] + brighten_amount, 0.0, 1.0);
+ return SkHSVToColor(color);
+}
+
+void NativeThemeBase::DrawVertLine(SkCanvas* canvas,
+ int x,
+ int y1,
+ int y2,
+ const SkPaint& paint) const {
+ SkIRect skrect;
+ skrect.set(x, y1, x + 1, y2 + 1);
+ canvas->drawIRect(skrect, paint);
+}
+
+void NativeThemeBase::DrawHorizLine(SkCanvas* canvas,
+ int x1,
+ int x2,
+ int y,
+ const SkPaint& paint) const {
+ SkIRect skrect;
+ skrect.set(x1, y, x2 + 1, y + 1);
+ canvas->drawIRect(skrect, paint);
+}
+
+void NativeThemeBase::DrawBox(SkCanvas* canvas,
+ const gfx::Rect& rect,
+ const SkPaint& paint) const {
+ const int right = rect.x() + rect.width() - 1;
+ const int bottom = rect.y() + rect.height() - 1;
+ DrawHorizLine(canvas, rect.x(), right, rect.y(), paint);
+ DrawVertLine(canvas, right, rect.y(), bottom, paint);
+ DrawHorizLine(canvas, rect.x(), right, bottom, paint);
+ DrawVertLine(canvas, rect.x(), rect.y(), bottom, paint);
+}
+
+SkScalar NativeThemeBase::Clamp(SkScalar value,
+ SkScalar min,
+ SkScalar max) const {
+ return std::min(std::max(value, min), max);
+}
+
+SkColor NativeThemeBase::OutlineColor(SkScalar* hsv1, SkScalar* hsv2) const {
+ // GTK Theme engines have way too much control over the layout of
+ // the scrollbar. We might be able to more closely approximate its
+ // look-and-feel, if we sent whole images instead of just colors
+ // from the browser to the renderer. But even then, some themes
+ // would just break.
+ //
+ // So, instead, we don't even try to 100% replicate the look of
+ // the native scrollbar. We render our own version, but we make
+ // sure to pick colors that blend in nicely with the system GTK
+ // theme. In most cases, we can just sample a couple of pixels
+ // from the system scrollbar and use those colors to draw our
+ // scrollbar.
+ //
+ // This works fine for the track color and the overall thumb
+ // color. But it fails spectacularly for the outline color used
+ // around the thumb piece. Not all themes have a clearly defined
+ // outline. For some of them it is partially transparent, and for
+ // others the thickness is very unpredictable.
+ //
+ // So, instead of trying to approximate the system theme, we
+ // instead try to compute a reasonable looking choice based on the
+ // known color of the track and the thumb piece. This is difficult
+ // when trying to deal both with high- and low-contrast themes,
+ // and both with positive and inverted themes.
+ //
+ // The following code has been tested to look OK with all of the
+ // default GTK themes.
+ SkScalar min_diff = Clamp((hsv1[1] + hsv2[1]) * 1.2f, 0.28f, 0.5f);
+ SkScalar diff = Clamp(fabs(hsv1[2] - hsv2[2]) / 2, min_diff, 0.5f);
+
+ if (hsv1[2] + hsv2[2] > 1.0)
+ diff = -diff;
+
+ return SaturateAndBrighten(hsv2, -0.2f, diff);
+}
+
+} // namespace ui
diff --git a/chromium/ui/native_theme/native_theme_base.h b/chromium/ui/native_theme/native_theme_base.h
new file mode 100644
index 00000000000..9e314c333d6
--- /dev/null
+++ b/chromium/ui/native_theme/native_theme_base.h
@@ -0,0 +1,180 @@
+// Copyright (c) 2012 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.
+
+#ifndef UI_NATIVE_THEME_NATIVE_THEME_BASE_H_
+#define UI_NATIVE_THEME_NATIVE_THEME_BASE_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "skia/ext/platform_canvas.h"
+#include "ui/native_theme/native_theme.h"
+
+namespace gfx {
+class ImageSkia;
+class Rect;
+class Size;
+}
+
+namespace ui {
+
+// Theme support for non-Windows toolkits.
+class NATIVE_THEME_EXPORT NativeThemeBase : public NativeTheme {
+ public:
+ // NativeTheme implementation:
+ virtual gfx::Size GetPartSize(Part part,
+ State state,
+ const ExtraParams& extra) const OVERRIDE;
+ virtual void Paint(SkCanvas* canvas,
+ Part part,
+ State state,
+ const gfx::Rect& rect,
+ const ExtraParams& extra) const OVERRIDE;
+
+ protected:
+ NativeThemeBase();
+ virtual ~NativeThemeBase();
+
+ // Draw the arrow. Used by scrollbar and inner spin button.
+ virtual void PaintArrowButton(
+ SkCanvas* gc,
+ const gfx::Rect& rect,
+ Part direction,
+ State state) const;
+ // Paint the scrollbar track. Done before the thumb so that it can contain
+ // alpha.
+ virtual void PaintScrollbarTrack(
+ SkCanvas* canvas,
+ Part part,
+ State state,
+ const ScrollbarTrackExtraParams& extra_params,
+ const gfx::Rect& rect) const;
+ // Draw the scrollbar thumb over the track.
+ virtual void PaintScrollbarThumb(
+ SkCanvas* canvas,
+ Part part,
+ State state,
+ const gfx::Rect& rect) const;
+
+ virtual void PaintCheckbox(
+ SkCanvas* canvas,
+ State state,
+ const gfx::Rect& rect,
+ const ButtonExtraParams& button) const;
+
+ virtual void PaintRadio(
+ SkCanvas* canvas,
+ State state,
+ const gfx::Rect& rect,
+ const ButtonExtraParams& button) const;
+
+ virtual void PaintButton(
+ SkCanvas* canvas,
+ State state,
+ const gfx::Rect& rect,
+ const ButtonExtraParams& button) const;
+
+ virtual void PaintTextField(
+ SkCanvas* canvas,
+ State state,
+ const gfx::Rect& rect,
+ const TextFieldExtraParams& text) const;
+
+ virtual void PaintMenuList(
+ SkCanvas* canvas,
+ State state,
+ const gfx::Rect& rect,
+ const MenuListExtraParams& menu_list) const;
+
+ virtual void PaintMenuPopupBackground(
+ SkCanvas* canvas,
+ const gfx::Size& size,
+ const MenuBackgroundExtraParams& menu_background) const;
+
+ virtual void PaintMenuItemBackground(
+ SkCanvas* canvas,
+ State state,
+ const gfx::Rect& rect,
+ const MenuListExtraParams& menu_list) const;
+
+ virtual void PaintSliderTrack(
+ SkCanvas* canvas,
+ State state,
+ const gfx::Rect& rect,
+ const SliderExtraParams& slider) const;
+
+ virtual void PaintSliderThumb(
+ SkCanvas* canvas,
+ State state,
+ const gfx::Rect& rect,
+ const SliderExtraParams& slider) const;
+
+ virtual void PaintInnerSpinButton(
+ SkCanvas* canvas,
+ State state,
+ const gfx::Rect& rect,
+ const InnerSpinButtonExtraParams& spin_button) const;
+
+ virtual void PaintProgressBar(
+ SkCanvas* canvas,
+ State state,
+ const gfx::Rect& rect,
+ const ProgressBarExtraParams& progress_bar) const;
+
+ protected:
+ void set_scrollbar_button_length(unsigned int length) {
+ scrollbar_button_length_ = length;
+ }
+
+ bool IntersectsClipRectInt(SkCanvas* canvas,
+ int x, int y, int w, int h) const;
+
+ void DrawImageInt(SkCanvas* canvas, const gfx::ImageSkia& image,
+ int src_x, int src_y, int src_w, int src_h,
+ int dest_x, int dest_y, int dest_w, int dest_h) const;
+
+ void DrawTiledImage(SkCanvas* canvas,
+ const gfx::ImageSkia& image,
+ int src_x, int src_y,
+ float tile_scale_x, float tile_scale_y,
+ int dest_x, int dest_y, int w, int h) const;
+
+ SkColor SaturateAndBrighten(SkScalar* hsv,
+ SkScalar saturate_amount,
+ SkScalar brighten_amount) const;
+ private:
+ void DrawVertLine(SkCanvas* canvas,
+ int x,
+ int y1,
+ int y2,
+ const SkPaint& paint) const;
+ void DrawHorizLine(SkCanvas* canvas,
+ int x1,
+ int x2,
+ int y,
+ const SkPaint& paint) const;
+ void DrawBox(SkCanvas* canvas,
+ const gfx::Rect& rect,
+ const SkPaint& paint) const;
+ SkScalar Clamp(SkScalar value,
+ SkScalar min,
+ SkScalar max) const;
+ SkColor OutlineColor(SkScalar* hsv1, SkScalar* hsv2) const;
+
+ // Paint the common parts of the checkboxes and radio buttons.
+ // borderRadius specifies how rounded the corners should be.
+ SkRect PaintCheckboxRadioCommon(
+ SkCanvas* canvas,
+ State state,
+ const gfx::Rect& rect,
+ const SkScalar borderRadius) const;
+
+ unsigned int scrollbar_width_;
+ unsigned int scrollbar_button_length_;
+
+ DISALLOW_COPY_AND_ASSIGN(NativeThemeBase);
+};
+
+} // namespace ui
+
+#endif // UI_NATIVE_THEME_NATIVE_THEME_BASE_H_
diff --git a/chromium/ui/native_theme/native_theme_export.h b/chromium/ui/native_theme/native_theme_export.h
new file mode 100644
index 00000000000..85641e5f845
--- /dev/null
+++ b/chromium/ui/native_theme/native_theme_export.h
@@ -0,0 +1,36 @@
+// Copyright 2012 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.
+
+#ifndef UI_NATIVE_THEME_NATIVE_THEME_EXPORT_H_
+#define UI_NATIVE_THEME_NATIVE_THEME_EXPORT_H_
+
+// Defines NATIVE_THEME_EXPORT so that functionality implemented by the
+// native_theme library can be exported to consumers.
+
+#if defined(COMPONENT_BUILD)
+#if defined(WIN32)
+
+#if defined(NATIVE_THEME_IMPLEMENTATION)
+#define NATIVE_THEME_EXPORT __declspec(dllexport)
+#else
+#define NATIVE_THEME_EXPORT __declspec(dllimport)
+#endif // defined(NATIVE_THEME_IMPLEMENTATION)
+
+#else // !defined(WIN32)
+
+#if defined(NATIVE_THEME_IMPLEMENTATION)
+#define NATIVE_THEME_EXPORT __attribute__((visibility("default")))
+#else
+#define NATIVE_THEME_EXPORT
+#endif
+
+#endif // defined(WIN32)
+
+#else // !defined(COMPONENT_BUILD)
+
+#define NATIVE_THEME_EXPORT
+
+#endif // defined(COMPONENT_BUILD)
+
+#endif // UI_NATIVE_THEME_NATIVE_THEME_EXPORT_H_
diff --git a/chromium/ui/native_theme/native_theme_gtk.cc b/chromium/ui/native_theme/native_theme_gtk.cc
new file mode 100644
index 00000000000..e74976678d8
--- /dev/null
+++ b/chromium/ui/native_theme/native_theme_gtk.cc
@@ -0,0 +1,108 @@
+// Copyright (c) 2012 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 "ui/native_theme/native_theme_gtk.h"
+
+#include <gtk/gtk.h>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "ui/gfx/skia_utils_gtk.h"
+
+namespace {
+
+const SkColor kInvalidColorIdColor = SkColorSetRGB(255, 0, 128);
+
+// Theme colors returned by GetSystemColor().
+
+// FocusableBorder:
+const SkColor kFocusedBorderColor = SkColorSetRGB(0x4D, 0x90, 0xFE);
+const SkColor kUnfocusedBorderColor = SkColorSetRGB(0xD9, 0xD9, 0xD9);
+
+// MenuItem
+const SkColor kFocusedMenuItemBackgroundColor = SkColorSetARGB(13, 0, 0, 0);
+const SkColor kHoverMenuItemBackgroundColor = SkColorSetRGB(204, 204, 204);
+
+// MenuButton
+const SkColor kEnabledMenuButtonBorderColor = SkColorSetARGB(36, 0, 0, 0);
+const SkColor kFocusedMenuButtonBorderColor = SkColorSetARGB(72, 0, 0, 0);
+const SkColor kHoverMenuButtonBorderColor = SkColorSetARGB(72, 0, 0, 0);
+
+// Button:
+const SkColor kButtonBackgroundColor = SkColorSetRGB(0xde, 0xde, 0xde);
+const SkColor kButtonEnabledColor = SkColorSetRGB(6, 45, 117);
+const SkColor kButtonDisabledColor = SkColorSetRGB(161, 161, 146);
+const SkColor kButtonHighlightColor = SkColorSetARGB(200, 255, 255, 255);
+const SkColor kButtonHoverColor = kButtonEnabledColor;
+
+} // namespace
+
+namespace ui {
+
+// static
+NativeTheme* NativeTheme::instance() {
+ return NativeThemeGtk::instance();
+}
+
+// static
+NativeThemeGtk* NativeThemeGtk::instance() {
+ CR_DEFINE_STATIC_LOCAL(NativeThemeGtk, s_native_theme, ());
+ return &s_native_theme;
+}
+
+SkColor NativeThemeGtk::GetSystemColor(ColorId color_id) const {
+ switch (color_id) {
+ case kColorId_DialogBackground:
+ // TODO(benrg): This code used to call gtk_widget_get_style() on the
+ // widget being styled. After refactoring, that widget is not available
+ // and we have to call gtk_widget_get_default_style(). Unfortunately,
+ // it turns out that this breaks everything (chromium bug 105609,
+ // chromium-os bug 23461). Need to figure out the right thing and do it.
+ return gfx::GdkColorToSkColor(
+ gtk_widget_get_default_style()->bg[GTK_STATE_NORMAL]);
+
+ // FocusableBorder:
+ case kColorId_FocusedBorderColor:
+ return kFocusedBorderColor;
+ case kColorId_UnfocusedBorderColor:
+ return kUnfocusedBorderColor;
+
+ // MenuItem
+ case kColorId_FocusedMenuItemBackgroundColor:
+ return kFocusedMenuItemBackgroundColor;
+ case kColorId_HoverMenuItemBackgroundColor:
+ return kHoverMenuItemBackgroundColor;
+ case kColorId_EnabledMenuButtonBorderColor:
+ return kEnabledMenuButtonBorderColor;
+ case kColorId_FocusedMenuButtonBorderColor:
+ return kFocusedMenuButtonBorderColor;
+ case kColorId_HoverMenuButtonBorderColor:
+ return kHoverMenuButtonBorderColor;
+
+ // Button:
+ case kColorId_ButtonBackgroundColor:
+ return kButtonBackgroundColor;
+ case kColorId_ButtonEnabledColor:
+ return kButtonEnabledColor;
+ case kColorId_ButtonDisabledColor:
+ return kButtonDisabledColor;
+ case kColorId_ButtonHighlightColor:
+ return kButtonHighlightColor;
+ case kColorId_ButtonHoverColor:
+ return kButtonHoverColor;
+
+ default:
+ NOTREACHED() << "Invalid color_id: " << color_id;
+ break;
+ }
+ return kInvalidColorIdColor;
+}
+
+NativeThemeGtk::NativeThemeGtk() {
+}
+
+NativeThemeGtk::~NativeThemeGtk() {
+}
+
+} // namespace ui
diff --git a/chromium/ui/native_theme/native_theme_gtk.h b/chromium/ui/native_theme/native_theme_gtk.h
new file mode 100644
index 00000000000..05f5a8cd7b4
--- /dev/null
+++ b/chromium/ui/native_theme/native_theme_gtk.h
@@ -0,0 +1,28 @@
+// Copyright (c) 2012 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.
+
+#ifndef UI_NATIVE_THEME_NATIVE_THEME_GTK_H_
+#define UI_NATIVE_THEME_NATIVE_THEME_GTK_H_
+
+#include "ui/native_theme/native_theme_base.h"
+
+namespace ui {
+
+// GTK implementation of native theme support.
+class NativeThemeGtk : public NativeThemeBase {
+ public:
+ static NativeThemeGtk* instance();
+
+ virtual SkColor GetSystemColor(ColorId color_id) const OVERRIDE;
+
+ private:
+ NativeThemeGtk();
+ virtual ~NativeThemeGtk();
+
+ DISALLOW_COPY_AND_ASSIGN(NativeThemeGtk);
+};
+
+} // namespace ui
+
+#endif // UI_NATIVE_THEME_NATIVE_THEME_GTK_H_
diff --git a/chromium/ui/native_theme/native_theme_mac.h b/chromium/ui/native_theme/native_theme_mac.h
new file mode 100644
index 00000000000..20378426db5
--- /dev/null
+++ b/chromium/ui/native_theme/native_theme_mac.h
@@ -0,0 +1,28 @@
+// 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.
+
+#ifndef UI_NATIVE_THEME_NATIVE_THEME_MAC_H_
+#define UI_NATIVE_THEME_NATIVE_THEME_MAC_H_
+
+#include "ui/native_theme/native_theme_base.h"
+
+namespace ui {
+
+// Mac implementation of native theme support.
+class NativeThemeMac : public NativeThemeBase {
+ public:
+ static NativeThemeMac* instance();
+
+ virtual SkColor GetSystemColor(ColorId color_id) const OVERRIDE;
+
+ private:
+ NativeThemeMac();
+ virtual ~NativeThemeMac();
+
+ DISALLOW_COPY_AND_ASSIGN(NativeThemeMac);
+};
+
+} // namespace ui
+
+#endif // UI_NATIVE_THEME_NATIVE_THEME_MAC_H_
diff --git a/chromium/ui/native_theme/native_theme_mac.mm b/chromium/ui/native_theme/native_theme_mac.mm
new file mode 100644
index 00000000000..c3152a8d145
--- /dev/null
+++ b/chromium/ui/native_theme/native_theme_mac.mm
@@ -0,0 +1,51 @@
+// 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 "ui/native_theme/native_theme_mac.h"
+
+#include "base/basictypes.h"
+#include "ui/native_theme/common_theme.h"
+
+namespace {
+
+const SkColor kInvalidColorIdColor = SkColorSetRGB(255, 0, 128);
+const SkColor kDialogBackgroundColor = SkColorSetRGB(251, 251, 251);
+
+} // namespace
+
+namespace ui {
+
+// static
+NativeTheme* NativeTheme::instance() {
+ return NativeThemeMac::instance();
+}
+
+// static
+NativeThemeMac* NativeThemeMac::instance() {
+ CR_DEFINE_STATIC_LOCAL(NativeThemeMac, s_native_theme, ());
+ return &s_native_theme;
+}
+
+SkColor NativeThemeMac::GetSystemColor(ColorId color_id) const {
+ SkColor color;
+ if (CommonThemeGetSystemColor(color_id, &color))
+ return color;
+
+ switch (color_id) {
+ case kColorId_DialogBackground:
+ return kDialogBackgroundColor;
+ default:
+ NOTREACHED() << "Invalid color_id: " << color_id;
+ }
+
+ return kInvalidColorIdColor;
+}
+
+NativeThemeMac::NativeThemeMac() {
+}
+
+NativeThemeMac::~NativeThemeMac() {
+}
+
+} // namespace ui
diff --git a/chromium/ui/native_theme/native_theme_win.cc b/chromium/ui/native_theme/native_theme_win.cc
new file mode 100644
index 00000000000..1839de32263
--- /dev/null
+++ b/chromium/ui/native_theme/native_theme_win.cc
@@ -0,0 +1,2098 @@
+// Copyright (c) 2012 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 "ui/native_theme/native_theme_win.h"
+
+#include <windows.h>
+#include <uxtheme.h>
+#include <vsstyle.h>
+#include <vssym32.h>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/memory/scoped_handle.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/win/scoped_gdi_object.h"
+#include "base/win/scoped_hdc.h"
+#include "base/win/scoped_select_object.h"
+#include "base/win/windows_version.h"
+#include "skia/ext/bitmap_platform_device.h"
+#include "skia/ext/platform_canvas.h"
+#include "skia/ext/skia_utils_win.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkColorPriv.h"
+#include "third_party/skia/include/core/SkShader.h"
+#include "ui/base/win/dpi.h"
+#include "ui/gfx/color_utils.h"
+#include "ui/gfx/gdi_util.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/rect_conversions.h"
+#include "ui/gfx/sys_color_change_listener.h"
+#include "ui/native_theme/common_theme.h"
+
+// This was removed from Winvers.h but is still used.
+#if !defined(COLOR_MENUHIGHLIGHT)
+#define COLOR_MENUHIGHLIGHT 29
+#endif
+
+namespace {
+
+// TODO: Obtain the correct colors using GetSysColor.
+// Theme colors returned by GetSystemColor().
+const SkColor kInvalidColorIdColor = SkColorSetRGB(255, 0, 128);
+// Dialogs:
+const SkColor kDialogBackgroundColor = SkColorSetRGB(251, 251, 251);
+// FocusableBorder:
+const SkColor kFocusedBorderColor = SkColorSetRGB(0x4d, 0x90, 0xfe);
+const SkColor kUnfocusedBorderColor = SkColorSetRGB(0xd9, 0xd9, 0xd9);
+// Button:
+const SkColor kButtonBackgroundColor = SkColorSetRGB(0xde, 0xde, 0xde);
+const SkColor kButtonHighlightColor = SkColorSetARGB(200, 255, 255, 255);
+const SkColor kButtonHoverColor = SkColorSetRGB(6, 45, 117);
+// MenuItem:
+const SkColor kEnabledMenuItemForegroundColor = SkColorSetRGB(6, 45, 117);
+const SkColor kDisabledMenuItemForegroundColor = SkColorSetRGB(161, 161, 146);
+const SkColor kFocusedMenuItemBackgroundColor = SkColorSetRGB(246, 249, 253);
+const SkColor kMenuSeparatorColor = SkColorSetARGB(50, 0, 0, 0);
+// Table:
+const SkColor kTreeSelectionBackgroundUnfocused = SkColorSetRGB(240, 240, 240);
+
+// Windows system color IDs cached and updated by the native theme.
+const int kSystemColors[] = {
+ COLOR_3DFACE,
+ COLOR_BTNTEXT,
+ COLOR_GRAYTEXT,
+ COLOR_HIGHLIGHT,
+ COLOR_HIGHLIGHTTEXT,
+ COLOR_SCROLLBAR,
+ COLOR_WINDOW,
+ COLOR_WINDOWTEXT,
+ COLOR_BTNFACE,
+ COLOR_MENUHIGHLIGHT,
+};
+
+void SetCheckerboardShader(SkPaint* paint, const RECT& align_rect) {
+ // Create a 2x2 checkerboard pattern using the 3D face and highlight colors.
+ const SkColor face = color_utils::GetSysSkColor(COLOR_3DFACE);
+ const SkColor highlight = color_utils::GetSysSkColor(COLOR_3DHILIGHT);
+ SkColor buffer[] = { face, highlight, highlight, face };
+ // Confusing bit: we first create a temporary bitmap with our desired pattern,
+ // then copy it to another bitmap. The temporary bitmap doesn't take
+ // ownership of the pixel data, and so will point to garbage when this
+ // function returns. The copy will copy the pixel data into a place owned by
+ // the bitmap, which is in turn owned by the shader, etc., so it will live
+ // until we're done using it.
+ SkBitmap temp_bitmap;
+ temp_bitmap.setConfig(SkBitmap::kARGB_8888_Config, 2, 2);
+ temp_bitmap.setPixels(buffer);
+ SkBitmap bitmap;
+ temp_bitmap.copyTo(&bitmap, temp_bitmap.config());
+ skia::RefPtr<SkShader> shader = skia::AdoptRef(
+ SkShader::CreateBitmapShader(
+ bitmap, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode));
+
+ // Align the pattern with the upper corner of |align_rect|.
+ SkMatrix matrix;
+ matrix.setTranslate(SkIntToScalar(align_rect.left),
+ SkIntToScalar(align_rect.top));
+ shader->setLocalMatrix(matrix);
+ paint->setShader(shader.get());
+}
+
+// <-a->
+// [ ***** ]
+// ____ | |
+// <-a-> <------b----->
+// a: object_width
+// b: frame_width
+// *: animating object
+//
+// - the animation goes from "[" to "]" repeatedly.
+// - the animation offset is at first "|"
+//
+int ComputeAnimationProgress(int frame_width,
+ int object_width,
+ int pixels_per_second,
+ double animated_seconds) {
+ int animation_width = frame_width + object_width;
+ double interval = static_cast<double>(animation_width) / pixels_per_second;
+ double ratio = fmod(animated_seconds, interval) / interval;
+ return static_cast<int>(animation_width * ratio) - object_width;
+}
+
+RECT InsetRect(const RECT* rect, int size) {
+ gfx::Rect result(*rect);
+ result.Inset(size, size);
+ return result.ToRECT();
+}
+
+// Returns true if using a high contrast theme.
+bool UsingHighContrastTheme() {
+ HIGHCONTRAST result;
+ result.cbSize = sizeof(HIGHCONTRAST);
+ return SystemParametersInfo(SPI_GETHIGHCONTRAST, result.cbSize, &result, 0) &&
+ (result.dwFlags & HCF_HIGHCONTRASTON) == HCF_HIGHCONTRASTON;
+}
+
+} // namespace
+
+namespace ui {
+
+bool NativeThemeWin::IsThemingActive() const {
+ if (is_theme_active_)
+ return !!is_theme_active_();
+ return false;
+}
+
+HRESULT NativeThemeWin::GetThemeColor(ThemeName theme,
+ int part_id,
+ int state_id,
+ int prop_id,
+ SkColor* color) const {
+ HANDLE handle = GetThemeHandle(theme);
+ if (handle && get_theme_color_) {
+ COLORREF color_ref;
+ if (get_theme_color_(handle, part_id, state_id, prop_id, &color_ref) ==
+ S_OK) {
+ *color = skia::COLORREFToSkColor(color_ref);
+ return S_OK;
+ }
+ }
+ return E_NOTIMPL;
+}
+
+SkColor NativeThemeWin::GetThemeColorWithDefault(ThemeName theme,
+ int part_id,
+ int state_id,
+ int prop_id,
+ int default_sys_color) const {
+ SkColor color;
+ if (GetThemeColor(theme, part_id, state_id, prop_id, &color) != S_OK)
+ color = color_utils::GetSysSkColor(default_sys_color);
+ return color;
+}
+
+gfx::Size NativeThemeWin::GetThemeBorderSize(ThemeName theme) const {
+ // For simplicity use the wildcard state==0, part==0, since it works
+ // for the cases we currently depend on.
+ int border;
+ if (GetThemeInt(theme, 0, 0, TMT_BORDERSIZE, &border) == S_OK)
+ return gfx::Size(border, border);
+ else
+ return gfx::Size(GetSystemMetrics(SM_CXEDGE), GetSystemMetrics(SM_CYEDGE));
+}
+
+void NativeThemeWin::DisableTheming() const {
+ if (!set_theme_properties_)
+ return;
+ set_theme_properties_(0);
+}
+
+void NativeThemeWin::CloseHandles() const {
+ if (!close_theme_)
+ return;
+
+ for (int i = 0; i < LAST; ++i) {
+ if (theme_handles_[i]) {
+ close_theme_(theme_handles_[i]);
+ theme_handles_[i] = NULL;
+ }
+ }
+}
+
+bool NativeThemeWin::IsClassicTheme(ThemeName name) const {
+ if (!theme_dll_)
+ return true;
+
+ return !GetThemeHandle(name);
+}
+
+// TODO(sky): seems like we should default to NativeThemeWin, but that currently
+// breaks a couple of tests (FocusTraversalTest.NormalTraversal in
+// views_unittests).
+#if !defined(USE_AURA)
+// static
+NativeTheme* NativeTheme::instance() {
+ return NativeThemeWin::instance();
+}
+#endif
+
+// static
+NativeThemeWin* NativeThemeWin::instance() {
+ CR_DEFINE_STATIC_LOCAL(NativeThemeWin, s_native_theme, ());
+ return &s_native_theme;
+}
+
+gfx::Size NativeThemeWin::GetPartSize(Part part,
+ State state,
+ const ExtraParams& extra) const {
+ gfx::Size part_size = CommonThemeGetPartSize(part, state, extra);
+ if (!part_size.IsEmpty())
+ return part_size;
+
+ // The GetThemePartSize call below returns the default size without
+ // accounting for user customization (crbug/218291).
+ SIZE size;
+ switch (part) {
+ case kScrollbarDownArrow:
+ case kScrollbarLeftArrow:
+ case kScrollbarRightArrow:
+ case kScrollbarUpArrow:
+ case kScrollbarHorizontalThumb:
+ case kScrollbarVerticalThumb:
+ case kScrollbarHorizontalTrack:
+ case kScrollbarVerticalTrack:
+ size.cx = size.cy = ui::win::GetSystemMetricsInDIP(SM_CXVSCROLL);
+ return gfx::Size(size.cx, size.cy);
+ }
+
+ int part_id = GetWindowsPart(part, state, extra);
+ int state_id = GetWindowsState(part, state, extra);
+
+ HDC hdc = GetDC(NULL);
+ HRESULT hr = GetThemePartSize(GetThemeName(part), hdc, part_id, state_id,
+ NULL, TS_TRUE, &size);
+ ReleaseDC(NULL, hdc);
+
+ if (FAILED(hr)) {
+ // TODO(rogerta): For now, we need to support radio buttons and checkboxes
+ // when theming is not enabled. Support for other parts can be added
+ // if/when needed.
+ switch (part) {
+ case kCheckbox:
+ case kRadio:
+ // TODO(rogerta): I was not able to find any API to get the default
+ // size of these controls, so determined these values empirically.
+ size.cx = 13;
+ size.cy = 13;
+ break;
+ default:
+ size.cx = 0;
+ size.cy = 0;
+ break;
+ }
+ }
+
+ return gfx::Size(size.cx, size.cy);
+}
+
+void NativeThemeWin::Paint(SkCanvas* canvas,
+ Part part,
+ State state,
+ const gfx::Rect& rect,
+ const ExtraParams& extra) const {
+ if (rect.IsEmpty())
+ return;
+
+ switch (part) {
+ case kMenuPopupGutter:
+ CommonThemePaintMenuGutter(canvas, rect);
+ return;
+ case kMenuPopupSeparator:
+ CommonThemePaintMenuSeparator(canvas, rect, extra.menu_separator);
+ return;
+ case kMenuPopupBackground:
+ CommonThemePaintMenuBackground(canvas, rect);
+ return;
+ case kMenuItemBackground:
+ CommonThemePaintMenuItemBackground(canvas, state, rect);
+ return;
+ }
+
+ bool needs_paint_indirect = false;
+ if (!skia::SupportsPlatformPaint(canvas)) {
+ // This block will only get hit with --enable-accelerated-drawing flag.
+ needs_paint_indirect = true;
+ } else {
+ // Scrollbar components on Windows Classic theme (on all Windows versions)
+ // have particularly problematic alpha values, so always draw them
+ // indirectly. In addition, scrollbar thumbs and grippers for the Windows XP
+ // theme (available only on Windows XP) also need their alpha values
+ // fixed.
+ switch (part) {
+ case kScrollbarDownArrow:
+ case kScrollbarUpArrow:
+ case kScrollbarLeftArrow:
+ case kScrollbarRightArrow:
+ if (!GetThemeHandle(SCROLLBAR))
+ needs_paint_indirect = true;
+ break;
+ case kScrollbarHorizontalThumb:
+ case kScrollbarVerticalThumb:
+ case kScrollbarHorizontalGripper:
+ case kScrollbarVerticalGripper:
+ if (!GetThemeHandle(SCROLLBAR) ||
+ base::win::GetVersion() == base::win::VERSION_XP)
+ needs_paint_indirect = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (needs_paint_indirect)
+ PaintIndirect(canvas, part, state, rect, extra);
+ else
+ PaintDirect(canvas, part, state, rect, extra);
+}
+
+NativeThemeWin::NativeThemeWin()
+ : theme_dll_(LoadLibrary(L"uxtheme.dll")),
+ draw_theme_(NULL),
+ draw_theme_ex_(NULL),
+ get_theme_color_(NULL),
+ get_theme_content_rect_(NULL),
+ get_theme_part_size_(NULL),
+ open_theme_(NULL),
+ close_theme_(NULL),
+ set_theme_properties_(NULL),
+ is_theme_active_(NULL),
+ get_theme_int_(NULL),
+ color_change_listener_(this) {
+ if (theme_dll_) {
+ draw_theme_ = reinterpret_cast<DrawThemeBackgroundPtr>(
+ GetProcAddress(theme_dll_, "DrawThemeBackground"));
+ draw_theme_ex_ = reinterpret_cast<DrawThemeBackgroundExPtr>(
+ GetProcAddress(theme_dll_, "DrawThemeBackgroundEx"));
+ get_theme_color_ = reinterpret_cast<GetThemeColorPtr>(
+ GetProcAddress(theme_dll_, "GetThemeColor"));
+ get_theme_content_rect_ = reinterpret_cast<GetThemeContentRectPtr>(
+ GetProcAddress(theme_dll_, "GetThemeBackgroundContentRect"));
+ get_theme_part_size_ = reinterpret_cast<GetThemePartSizePtr>(
+ GetProcAddress(theme_dll_, "GetThemePartSize"));
+ open_theme_ = reinterpret_cast<OpenThemeDataPtr>(
+ GetProcAddress(theme_dll_, "OpenThemeData"));
+ close_theme_ = reinterpret_cast<CloseThemeDataPtr>(
+ GetProcAddress(theme_dll_, "CloseThemeData"));
+ set_theme_properties_ = reinterpret_cast<SetThemeAppPropertiesPtr>(
+ GetProcAddress(theme_dll_, "SetThemeAppProperties"));
+ is_theme_active_ = reinterpret_cast<IsThemeActivePtr>(
+ GetProcAddress(theme_dll_, "IsThemeActive"));
+ get_theme_int_ = reinterpret_cast<GetThemeIntPtr>(
+ GetProcAddress(theme_dll_, "GetThemeInt"));
+ }
+ memset(theme_handles_, 0, sizeof(theme_handles_));
+
+ // Initialize the cached system colors.
+ UpdateSystemColors();
+}
+
+NativeThemeWin::~NativeThemeWin() {
+ if (theme_dll_) {
+ // todo (cpu): fix this soon. Making a call to CloseHandles() here breaks
+ // certain tests and the reliability bots.
+ // CloseHandles();
+ FreeLibrary(theme_dll_);
+ }
+}
+
+void NativeThemeWin::OnSysColorChange() {
+ UpdateSystemColors();
+}
+
+void NativeThemeWin::UpdateSystemColors() {
+ for (int i = 0; i < arraysize(kSystemColors); ++i) {
+ system_colors_[kSystemColors[i]] =
+ color_utils::GetSysSkColor(kSystemColors[i]);
+ }
+}
+
+void NativeThemeWin::PaintDirect(SkCanvas* canvas,
+ Part part,
+ State state,
+ const gfx::Rect& rect,
+ const ExtraParams& extra) const {
+ skia::ScopedPlatformPaint scoped_platform_paint(canvas);
+ HDC hdc = scoped_platform_paint.GetPlatformSurface();
+
+ switch (part) {
+ case kCheckbox:
+ PaintCheckbox(hdc, part, state, rect, extra.button);
+ break;
+ case kRadio:
+ PaintRadioButton(hdc, part, state, rect, extra.button);
+ break;
+ case kPushButton:
+ PaintPushButton(hdc, part, state, rect, extra.button);
+ break;
+ case kMenuPopupArrow:
+ PaintMenuArrow(hdc, state, rect, extra.menu_arrow);
+ break;
+ case kMenuPopupGutter:
+ PaintMenuGutter(hdc, rect);
+ break;
+ case kMenuPopupSeparator:
+ PaintMenuSeparator(hdc, rect, extra.menu_separator);
+ break;
+ case kMenuPopupBackground:
+ PaintMenuBackground(hdc, rect);
+ break;
+ case kMenuCheck:
+ PaintMenuCheck(hdc, state, rect, extra.menu_check);
+ break;
+ case kMenuCheckBackground:
+ PaintMenuCheckBackground(hdc, state, rect);
+ break;
+ case kMenuItemBackground:
+ PaintMenuItemBackground(hdc, state, rect, extra.menu_item);
+ break;
+ case kMenuList:
+ PaintMenuList(hdc, state, rect, extra.menu_list);
+ break;
+ case kScrollbarDownArrow:
+ case kScrollbarUpArrow:
+ case kScrollbarLeftArrow:
+ case kScrollbarRightArrow:
+ PaintScrollbarArrow(hdc, part, state, rect, extra.scrollbar_arrow);
+ break;
+ case kScrollbarHorizontalTrack:
+ case kScrollbarVerticalTrack:
+ PaintScrollbarTrack(canvas, hdc, part, state, rect,
+ extra.scrollbar_track);
+ break;
+ case kScrollbarHorizontalThumb:
+ case kScrollbarVerticalThumb:
+ case kScrollbarHorizontalGripper:
+ case kScrollbarVerticalGripper:
+ PaintScrollbarThumb(hdc, part, state, rect, extra.scrollbar_thumb);
+ break;
+ case kInnerSpinButton:
+ PaintSpinButton(hdc, part, state, rect, extra.inner_spin);
+ break;
+ case kTrackbarThumb:
+ case kTrackbarTrack:
+ PaintTrackbar(canvas, hdc, part, state, rect, extra.trackbar);
+ break;
+ case kProgressBar:
+ PaintProgressBar(hdc, rect, extra.progress_bar);
+ break;
+ case kWindowResizeGripper:
+ PaintWindowResizeGripper(hdc, rect);
+ break;
+ case kTabPanelBackground:
+ PaintTabPanelBackground(hdc, rect);
+ break;
+ case kTextField:
+ PaintTextField(hdc, part, state, rect, extra.text_field);
+ break;
+
+ case kSliderTrack:
+ case kSliderThumb:
+ default:
+ // While transitioning NativeThemeWin to the single Paint() entry point,
+ // unsupported parts will DCHECK here.
+ NOTREACHED();
+ }
+}
+
+SkColor NativeThemeWin::GetSystemColor(ColorId color_id) const {
+ SkColor color;
+ if (CommonThemeGetSystemColor(color_id, &color))
+ return color;
+
+ switch (color_id) {
+ // Windows
+ case kColorId_WindowBackground:
+ return system_colors_[COLOR_WINDOW];
+
+ // Dialogs
+ case kColorId_DialogBackground:
+ if (gfx::IsInvertedColorScheme())
+ return color_utils::InvertColor(kDialogBackgroundColor);
+ return kDialogBackgroundColor;
+
+ // FocusableBorder
+ case kColorId_FocusedBorderColor:
+ return kFocusedBorderColor;
+ case kColorId_UnfocusedBorderColor:
+ return kUnfocusedBorderColor;
+
+ // Button
+ case kColorId_ButtonBackgroundColor:
+ return kButtonBackgroundColor;
+ case kColorId_ButtonEnabledColor:
+ return system_colors_[COLOR_BTNTEXT];
+ case kColorId_ButtonDisabledColor:
+ return system_colors_[COLOR_GRAYTEXT];
+ case kColorId_ButtonHighlightColor:
+ return kButtonHighlightColor;
+ case kColorId_ButtonHoverColor:
+ return kButtonHoverColor;
+
+ // MenuItem
+ case kColorId_EnabledMenuItemForegroundColor:
+ return kEnabledMenuItemForegroundColor;
+ case kColorId_DisabledMenuItemForegroundColor:
+ return kDisabledMenuItemForegroundColor;
+ case kColorId_FocusedMenuItemBackgroundColor:
+ return kFocusedMenuItemBackgroundColor;
+ case kColorId_MenuSeparatorColor:
+ return kMenuSeparatorColor;
+
+ // Label
+ case kColorId_LabelEnabledColor:
+ return system_colors_[COLOR_BTNTEXT];
+ case kColorId_LabelDisabledColor:
+ return system_colors_[COLOR_GRAYTEXT];
+ case kColorId_LabelBackgroundColor:
+ return system_colors_[COLOR_WINDOW];
+
+ // Textfield
+ case kColorId_TextfieldDefaultColor:
+ return system_colors_[COLOR_WINDOWTEXT];
+ case kColorId_TextfieldDefaultBackground:
+ return system_colors_[COLOR_WINDOW];
+ case kColorId_TextfieldReadOnlyColor:
+ return system_colors_[COLOR_GRAYTEXT];
+ case kColorId_TextfieldReadOnlyBackground:
+ return system_colors_[COLOR_3DFACE];
+ case kColorId_TextfieldSelectionColor:
+ return system_colors_[COLOR_HIGHLIGHTTEXT];
+ case kColorId_TextfieldSelectionBackgroundFocused:
+ return system_colors_[COLOR_HIGHLIGHT];
+
+ // Tree
+ // NOTE: these aren't right for all themes, but as close as I could get.
+ case kColorId_TreeBackground:
+ return system_colors_[COLOR_WINDOW];
+ case kColorId_TreeText:
+ return system_colors_[COLOR_WINDOWTEXT];
+ case kColorId_TreeSelectedText:
+ return system_colors_[COLOR_HIGHLIGHTTEXT];
+ case kColorId_TreeSelectedTextUnfocused:
+ return system_colors_[COLOR_BTNTEXT];
+ case kColorId_TreeSelectionBackgroundFocused:
+ return system_colors_[COLOR_HIGHLIGHT];
+ case kColorId_TreeSelectionBackgroundUnfocused:
+ return system_colors_[UsingHighContrastTheme() ?
+ COLOR_MENUHIGHLIGHT : COLOR_BTNFACE];
+ case kColorId_TreeArrow:
+ return system_colors_[COLOR_WINDOWTEXT];
+
+ // Table
+ case kColorId_TableBackground:
+ return system_colors_[COLOR_WINDOW];
+ case kColorId_TableText:
+ return system_colors_[COLOR_WINDOWTEXT];
+ case kColorId_TableSelectedText:
+ return system_colors_[COLOR_HIGHLIGHTTEXT];
+ case kColorId_TableSelectedTextUnfocused:
+ return system_colors_[COLOR_BTNTEXT];
+ case kColorId_TableSelectionBackgroundFocused:
+ return system_colors_[COLOR_HIGHLIGHT];
+ case kColorId_TableSelectionBackgroundUnfocused:
+ return system_colors_[UsingHighContrastTheme() ?
+ COLOR_MENUHIGHLIGHT : COLOR_BTNFACE];
+ case kColorId_TableGroupingIndicatorColor:
+ return system_colors_[COLOR_GRAYTEXT];
+
+ default:
+ NOTREACHED();
+ break;
+ }
+ return kInvalidColorIdColor;
+}
+
+void NativeThemeWin::PaintIndirect(SkCanvas* canvas,
+ Part part,
+ State state,
+ const gfx::Rect& rect,
+ const ExtraParams& extra) const {
+ // TODO(asvitkine): This path is pretty inefficient - for each paint operation
+ // it creates a new offscreen bitmap Skia canvas. This can
+ // be sped up by doing it only once per part/state and
+ // keeping a cache of the resulting bitmaps.
+
+ // Create an offscreen canvas that is backed by an HDC.
+ skia::RefPtr<skia::BitmapPlatformDevice> device = skia::AdoptRef(
+ skia::BitmapPlatformDevice::Create(
+ rect.width(), rect.height(), false, NULL));
+ DCHECK(device);
+ SkCanvas offscreen_canvas(device.get());
+ DCHECK(skia::SupportsPlatformPaint(&offscreen_canvas));
+
+ // Some of the Windows theme drawing operations do not write correct alpha
+ // values for fully-opaque pixels; instead the pixels get alpha 0. This is
+ // especially a problem on Windows XP or when using the Classic theme.
+ //
+ // To work-around this, mark all pixels with a placeholder value, to detect
+ // which pixels get touched by the paint operation. After paint, set any
+ // pixels that have alpha 0 to opaque and placeholders to fully-transparent.
+ const SkColor placeholder = SkColorSetARGB(1, 0, 0, 0);
+ offscreen_canvas.clear(placeholder);
+
+ // Offset destination rects to have origin (0,0).
+ gfx::Rect adjusted_rect(rect.size());
+ ExtraParams adjusted_extra(extra);
+ switch (part) {
+ case kProgressBar:
+ adjusted_extra.progress_bar.value_rect_x = 0;
+ adjusted_extra.progress_bar.value_rect_y = 0;
+ break;
+ case kScrollbarHorizontalTrack:
+ case kScrollbarVerticalTrack:
+ adjusted_extra.scrollbar_track.track_x = 0;
+ adjusted_extra.scrollbar_track.track_y = 0;
+ break;
+ default: break;
+ }
+ // Draw the theme controls using existing HDC-drawing code.
+ PaintDirect(&offscreen_canvas,
+ part,
+ state,
+ adjusted_rect,
+ adjusted_extra);
+
+ // Copy the pixels to a bitmap that has ref-counted pixel storage, which is
+ // necessary to have when drawing to a SkPicture.
+ const SkBitmap& hdc_bitmap =
+ offscreen_canvas.getDevice()->accessBitmap(false);
+ SkBitmap bitmap;
+ hdc_bitmap.copyTo(&bitmap, SkBitmap::kARGB_8888_Config);
+
+ // Post-process the pixels to fix up the alpha values (see big comment above).
+ const SkPMColor placeholder_value = SkPreMultiplyColor(placeholder);
+ const int pixel_count = rect.width() * rect.height();
+ SkPMColor* pixels = bitmap.getAddr32(0, 0);
+ for (int i = 0; i < pixel_count; i++) {
+ if (pixels[i] == placeholder_value) {
+ // Pixel wasn't touched - make it fully transparent.
+ pixels[i] = SkPackARGB32(0, 0, 0, 0);
+ } else if (SkGetPackedA32(pixels[i]) == 0) {
+ // Pixel was touched but has incorrect alpha of 0, make it fully opaque.
+ pixels[i] = SkPackARGB32(0xFF,
+ SkGetPackedR32(pixels[i]),
+ SkGetPackedG32(pixels[i]),
+ SkGetPackedB32(pixels[i]));
+ }
+ }
+
+ // Draw the offscreen bitmap to the destination canvas.
+ canvas->drawBitmap(bitmap, rect.x(), rect.y());
+}
+
+HRESULT NativeThemeWin::GetThemePartSize(ThemeName theme_name,
+ HDC hdc,
+ int part_id,
+ int state_id,
+ RECT* rect,
+ int ts,
+ SIZE* size) const {
+ HANDLE handle = GetThemeHandle(theme_name);
+ if (handle && get_theme_part_size_)
+ return get_theme_part_size_(handle, hdc, part_id, state_id, rect, ts, size);
+
+ return E_NOTIMPL;
+}
+
+HRESULT NativeThemeWin::PaintButton(HDC hdc,
+ State state,
+ const ButtonExtraParams& extra,
+ int part_id,
+ int state_id,
+ RECT* rect) const {
+ HANDLE handle = GetThemeHandle(BUTTON);
+ if (handle && draw_theme_)
+ return draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
+
+ // Adjust classic_state based on part, state, and extras.
+ int classic_state = extra.classic_state;
+ switch (part_id) {
+ case BP_CHECKBOX:
+ classic_state |= DFCS_BUTTONCHECK;
+ break;
+ case BP_RADIOBUTTON:
+ classic_state |= DFCS_BUTTONRADIO;
+ break;
+ case BP_PUSHBUTTON:
+ classic_state |= DFCS_BUTTONPUSH;
+ break;
+ default:
+ NOTREACHED() << "Unknown part_id: " << part_id;
+ break;
+ }
+
+ switch (state) {
+ case kDisabled:
+ classic_state |= DFCS_INACTIVE;
+ break;
+ case kPressed:
+ classic_state |= DFCS_PUSHED;
+ break;
+ case kNormal:
+ case kHovered:
+ break;
+ default:
+ NOTREACHED() << "Unknown state: " << state;
+ break;
+ }
+
+ if (extra.checked)
+ classic_state |= DFCS_CHECKED;
+
+ // Draw it manually.
+ // All pressed states have both low bits set, and no other states do.
+ const bool focused = ((state_id & ETS_FOCUSED) == ETS_FOCUSED);
+ const bool pressed = ((state_id & PBS_PRESSED) == PBS_PRESSED);
+ if ((BP_PUSHBUTTON == part_id) && (pressed || focused)) {
+ // BP_PUSHBUTTON has a focus rect drawn around the outer edge, and the
+ // button itself is shrunk by 1 pixel.
+ HBRUSH brush = GetSysColorBrush(COLOR_3DDKSHADOW);
+ if (brush) {
+ FrameRect(hdc, rect, brush);
+ InflateRect(rect, -1, -1);
+ }
+ }
+ DrawFrameControl(hdc, rect, DFC_BUTTON, classic_state);
+
+ // Draw the focus rectangle (the dotted line box) only on buttons. For radio
+ // and checkboxes, we let webkit draw the focus rectangle (orange glow).
+ if ((BP_PUSHBUTTON == part_id) && focused) {
+ // The focus rect is inside the button. The exact number of pixels depends
+ // on whether we're in classic mode or using uxtheme.
+ if (handle && get_theme_content_rect_) {
+ get_theme_content_rect_(handle, hdc, part_id, state_id, rect, rect);
+ } else {
+ InflateRect(rect, -GetSystemMetrics(SM_CXEDGE),
+ -GetSystemMetrics(SM_CYEDGE));
+ }
+ DrawFocusRect(hdc, rect);
+ }
+
+ // Classic theme doesn't support indeterminate checkboxes. We draw
+ // a recangle inside a checkbox like IE10 does.
+ if (part_id == BP_CHECKBOX && extra.indeterminate) {
+ RECT inner_rect = *rect;
+ // "4 / 13" is same as IE10 in classic theme.
+ int padding = (inner_rect.right - inner_rect.left) * 4 / 13;
+ InflateRect(&inner_rect, -padding, -padding);
+ int color_index = state == kDisabled ? COLOR_GRAYTEXT : COLOR_WINDOWTEXT;
+ FillRect(hdc, &inner_rect, GetSysColorBrush(color_index));
+ }
+
+ return S_OK;
+}
+
+HRESULT NativeThemeWin::PaintMenuSeparator(
+ HDC hdc,
+ const gfx::Rect& rect,
+ const MenuSeparatorExtraParams& extra) const {
+ RECT rect_win = rect.ToRECT();
+
+ HANDLE handle = GetThemeHandle(MENU);
+ if (handle && draw_theme_) {
+ // Delta is needed for non-classic to move separator up slightly.
+ --rect_win.top;
+ --rect_win.bottom;
+ return draw_theme_(handle, hdc, MENU_POPUPSEPARATOR, MPI_NORMAL, &rect_win,
+ NULL);
+ }
+
+ DrawEdge(hdc, &rect_win, EDGE_ETCHED, BF_TOP);
+ return S_OK;
+}
+
+HRESULT NativeThemeWin::PaintMenuGutter(HDC hdc,
+ const gfx::Rect& rect) const {
+ RECT rect_win = rect.ToRECT();
+ HANDLE handle = GetThemeHandle(MENU);
+ if (handle && draw_theme_)
+ return draw_theme_(handle, hdc, MENU_POPUPGUTTER, MPI_NORMAL, &rect_win,
+ NULL);
+ return E_NOTIMPL;
+}
+
+HRESULT NativeThemeWin::PaintMenuArrow(HDC hdc,
+ State state,
+ const gfx::Rect& rect,
+ const MenuArrowExtraParams& extra)
+ const {
+ int state_id = MSM_NORMAL;
+ if (state == kDisabled)
+ state_id = MSM_DISABLED;
+
+ HANDLE handle = GetThemeHandle(MENU);
+ RECT rect_win = rect.ToRECT();
+ if (handle && draw_theme_) {
+ if (extra.pointing_right) {
+ return draw_theme_(handle, hdc, MENU_POPUPSUBMENU, state_id, &rect_win,
+ NULL);
+ } else {
+ // There is no way to tell the uxtheme API to draw a left pointing arrow;
+ // it doesn't have a flag equivalent to DFCS_MENUARROWRIGHT. But they
+ // are needed for RTL locales on Vista. So use a memory DC and mirror
+ // the region with GDI's StretchBlt.
+ gfx::Rect r(rect);
+ base::win::ScopedCreateDC mem_dc(CreateCompatibleDC(hdc));
+ base::win::ScopedBitmap mem_bitmap(CreateCompatibleBitmap(hdc, r.width(),
+ r.height()));
+ base::win::ScopedSelectObject select_bitmap(mem_dc, mem_bitmap);
+ // Copy and horizontally mirror the background from hdc into mem_dc. Use
+ // a negative-width source rect, starting at the rightmost pixel.
+ StretchBlt(mem_dc, 0, 0, r.width(), r.height(),
+ hdc, r.right()-1, r.y(), -r.width(), r.height(), SRCCOPY);
+ // Draw the arrow.
+ RECT theme_rect = {0, 0, r.width(), r.height()};
+ HRESULT result = draw_theme_(handle, mem_dc, MENU_POPUPSUBMENU,
+ state_id, &theme_rect, NULL);
+ // Copy and mirror the result back into mem_dc.
+ StretchBlt(hdc, r.x(), r.y(), r.width(), r.height(),
+ mem_dc, r.width()-1, 0, -r.width(), r.height(), SRCCOPY);
+ return result;
+ }
+ }
+
+ // For some reason, Windows uses the name DFCS_MENUARROWRIGHT to indicate a
+ // left pointing arrow. This makes the following 'if' statement slightly
+ // counterintuitive.
+ UINT pfc_state;
+ if (extra.pointing_right)
+ pfc_state = DFCS_MENUARROW;
+ else
+ pfc_state = DFCS_MENUARROWRIGHT;
+ return PaintFrameControl(hdc, rect, DFC_MENU, pfc_state, extra.is_selected,
+ state);
+}
+
+HRESULT NativeThemeWin::PaintMenuBackground(HDC hdc,
+ const gfx::Rect& rect) const {
+ HANDLE handle = GetThemeHandle(MENU);
+ RECT rect_win = rect.ToRECT();
+ if (handle && draw_theme_) {
+ HRESULT result = draw_theme_(handle, hdc, MENU_POPUPBACKGROUND, 0,
+ &rect_win, NULL);
+ FrameRect(hdc, &rect_win, GetSysColorBrush(COLOR_3DSHADOW));
+ return result;
+ }
+
+ FillRect(hdc, &rect_win, GetSysColorBrush(COLOR_MENU));
+ DrawEdge(hdc, &rect_win, EDGE_RAISED, BF_RECT);
+ return S_OK;
+}
+
+HRESULT NativeThemeWin::PaintMenuCheck(
+ HDC hdc,
+ State state,
+ const gfx::Rect& rect,
+ const MenuCheckExtraParams& extra) const {
+ HANDLE handle = GetThemeHandle(MENU);
+ int state_id;
+ if (extra.is_radio) {
+ state_id = state == kDisabled ? MC_BULLETDISABLED : MC_BULLETNORMAL;
+ } else {
+ state_id = state == kDisabled ? MC_CHECKMARKDISABLED : MC_CHECKMARKNORMAL;
+ }
+
+ RECT rect_win = rect.ToRECT();
+ if (handle && draw_theme_)
+ return draw_theme_(handle, hdc, MENU_POPUPCHECK, state_id, &rect_win, NULL);
+
+ return PaintFrameControl(hdc, rect, DFC_MENU,
+ extra.is_radio ? DFCS_MENUBULLET : DFCS_MENUCHECK,
+ extra.is_selected, state);
+}
+
+HRESULT NativeThemeWin::PaintMenuCheckBackground(HDC hdc,
+ State state,
+ const gfx::Rect& rect) const {
+ HANDLE handle = GetThemeHandle(MENU);
+ int state_id = state == kDisabled ? MCB_DISABLED : MCB_NORMAL;
+ RECT rect_win = rect.ToRECT();
+ if (handle && draw_theme_)
+ return draw_theme_(handle, hdc, MENU_POPUPCHECKBACKGROUND, state_id,
+ &rect_win, NULL);
+ // Nothing to do for background.
+ return S_OK;
+}
+
+HRESULT NativeThemeWin::PaintMenuItemBackground(
+ HDC hdc,
+ State state,
+ const gfx::Rect& rect,
+ const MenuItemExtraParams& extra) const {
+ HANDLE handle = GetThemeHandle(MENU);
+ RECT rect_win = rect.ToRECT();
+ int state_id;
+ switch (state) {
+ case kNormal:
+ state_id = MPI_NORMAL;
+ break;
+ case kDisabled:
+ state_id = extra.is_selected ? MPI_DISABLEDHOT : MPI_DISABLED;
+ break;
+ case kHovered:
+ state_id = MPI_HOT;
+ break;
+ default:
+ NOTREACHED() << "Invalid state " << state;
+ break;
+ }
+
+ if (handle && draw_theme_)
+ return draw_theme_(handle, hdc, MENU_POPUPITEM, state_id, &rect_win, NULL);
+
+ if (extra.is_selected)
+ FillRect(hdc, &rect_win, GetSysColorBrush(COLOR_HIGHLIGHT));
+ return S_OK;
+}
+
+HRESULT NativeThemeWin::PaintPushButton(HDC hdc,
+ Part part,
+ State state,
+ const gfx::Rect& rect,
+ const ButtonExtraParams& extra) const {
+ int state_id;
+ switch (state) {
+ case kDisabled:
+ state_id = PBS_DISABLED;
+ break;
+ case kHovered:
+ state_id = PBS_HOT;
+ break;
+ case kNormal:
+ state_id = extra.is_default ? PBS_DEFAULTED : PBS_NORMAL;
+ break;
+ case kPressed:
+ state_id = PBS_PRESSED;
+ break;
+ default:
+ NOTREACHED() << "Invalid state: " << state;
+ break;
+ }
+
+ RECT rect_win = rect.ToRECT();
+ return PaintButton(hdc, state, extra, BP_PUSHBUTTON, state_id, &rect_win);
+}
+
+HRESULT NativeThemeWin::PaintRadioButton(HDC hdc,
+ Part part,
+ State state,
+ const gfx::Rect& rect,
+ const ButtonExtraParams& extra) const {
+ int state_id;
+ switch (state) {
+ case kDisabled:
+ state_id = extra.checked ? RBS_CHECKEDDISABLED : RBS_UNCHECKEDDISABLED;
+ break;
+ case kHovered:
+ state_id = extra.checked ? RBS_CHECKEDHOT : RBS_UNCHECKEDHOT;
+ break;
+ case kNormal:
+ state_id = extra.checked ? RBS_CHECKEDNORMAL : RBS_UNCHECKEDNORMAL;
+ break;
+ case kPressed:
+ state_id = extra.checked ? RBS_CHECKEDPRESSED : RBS_UNCHECKEDPRESSED;
+ break;
+ default:
+ NOTREACHED() << "Invalid state: " << state;
+ break;
+ }
+
+ RECT rect_win = rect.ToRECT();
+ return PaintButton(hdc, state, extra, BP_RADIOBUTTON, state_id, &rect_win);
+}
+
+HRESULT NativeThemeWin::PaintCheckbox(HDC hdc,
+ Part part,
+ State state,
+ const gfx::Rect& rect,
+ const ButtonExtraParams& extra) const {
+ int state_id;
+ switch (state) {
+ case kDisabled:
+ state_id = extra.checked ? CBS_CHECKEDDISABLED :
+ extra.indeterminate ? CBS_MIXEDDISABLED :
+ CBS_UNCHECKEDDISABLED;
+ break;
+ case kHovered:
+ state_id = extra.checked ? CBS_CHECKEDHOT :
+ extra.indeterminate ? CBS_MIXEDHOT :
+ CBS_UNCHECKEDHOT;
+ break;
+ case kNormal:
+ state_id = extra.checked ? CBS_CHECKEDNORMAL :
+ extra.indeterminate ? CBS_MIXEDNORMAL :
+ CBS_UNCHECKEDNORMAL;
+ break;
+ case kPressed:
+ state_id = extra.checked ? CBS_CHECKEDPRESSED :
+ extra.indeterminate ? CBS_MIXEDPRESSED :
+ CBS_UNCHECKEDPRESSED;
+ break;
+ default:
+ NOTREACHED() << "Invalid state: " << state;
+ break;
+ }
+
+ RECT rect_win = rect.ToRECT();
+ return PaintButton(hdc, state, extra, BP_CHECKBOX, state_id, &rect_win);
+}
+
+HRESULT NativeThemeWin::PaintMenuList(HDC hdc,
+ State state,
+ const gfx::Rect& rect,
+ const MenuListExtraParams& extra) const {
+ HANDLE handle = GetThemeHandle(MENULIST);
+ RECT rect_win = rect.ToRECT();
+ int state_id;
+ switch (state) {
+ case kNormal:
+ state_id = CBXS_NORMAL;
+ break;
+ case kDisabled:
+ state_id = CBXS_DISABLED;
+ break;
+ case kHovered:
+ state_id = CBXS_HOT;
+ break;
+ case kPressed:
+ state_id = CBXS_PRESSED;
+ break;
+ default:
+ NOTREACHED() << "Invalid state " << state;
+ break;
+ }
+
+ if (handle && draw_theme_)
+ return draw_theme_(handle, hdc, CP_DROPDOWNBUTTON, state_id, &rect_win,
+ NULL);
+
+ // Draw it manually.
+ DrawFrameControl(hdc, &rect_win, DFC_SCROLL,
+ DFCS_SCROLLCOMBOBOX | extra.classic_state);
+ return S_OK;
+}
+
+HRESULT NativeThemeWin::PaintScrollbarArrow(
+ HDC hdc,
+ Part part,
+ State state,
+ const gfx::Rect& rect,
+ const ScrollbarArrowExtraParams& extra) const {
+ static const int state_id_matrix[4][kMaxState] = {
+ ABS_DOWNDISABLED, ABS_DOWNHOT, ABS_DOWNNORMAL, ABS_DOWNPRESSED,
+ ABS_LEFTDISABLED, ABS_LEFTHOT, ABS_LEFTNORMAL, ABS_LEFTPRESSED,
+ ABS_RIGHTDISABLED, ABS_RIGHTHOT, ABS_RIGHTNORMAL, ABS_RIGHTPRESSED,
+ ABS_UPDISABLED, ABS_UPHOT, ABS_UPNORMAL, ABS_UPPRESSED
+ };
+ HANDLE handle = GetThemeHandle(SCROLLBAR);
+ RECT rect_win = rect.ToRECT();
+ if (handle && draw_theme_) {
+ int index = part - kScrollbarDownArrow;
+ DCHECK(index >=0 && index < 4);
+ int state_id = state_id_matrix[index][state];
+
+ // Hovering means that the cursor is over the scroolbar, but not over the
+ // specific arrow itself. We don't want to show it "hot" mode, but only
+ // in "hover" mode.
+ if (state == kHovered && extra.is_hovering) {
+ switch (part) {
+ case kScrollbarDownArrow:
+ state_id = ABS_DOWNHOVER;
+ break;
+ case kScrollbarLeftArrow:
+ state_id = ABS_LEFTHOVER;
+ break;
+ case kScrollbarRightArrow:
+ state_id = ABS_RIGHTHOVER;
+ break;
+ case kScrollbarUpArrow:
+ state_id = ABS_UPHOVER;
+ break;
+ default:
+ NOTREACHED() << "Invalid part: " << part;
+ break;
+ }
+ }
+ return PaintScaledTheme(handle, hdc, SBP_ARROWBTN, state_id, rect);
+ }
+
+ int classic_state = DFCS_SCROLLDOWN;
+ switch (part) {
+ case kScrollbarDownArrow:
+ classic_state = DFCS_SCROLLDOWN;
+ break;
+ case kScrollbarLeftArrow:
+ classic_state = DFCS_SCROLLLEFT;
+ break;
+ case kScrollbarRightArrow:
+ classic_state = DFCS_SCROLLRIGHT;
+ break;
+ case kScrollbarUpArrow:
+ classic_state = DFCS_SCROLLUP;
+ break;
+ default:
+ NOTREACHED() << "Invalid part: " << part;
+ break;
+ }
+ switch (state) {
+ case kDisabled:
+ classic_state |= DFCS_INACTIVE;
+ break;
+ case kHovered:
+ classic_state |= DFCS_HOT;
+ break;
+ case kNormal:
+ break;
+ case kPressed:
+ classic_state |= DFCS_PUSHED;
+ break;
+ default:
+ NOTREACHED() << "Invalid state: " << state;
+ break;
+ }
+ DrawFrameControl(hdc, &rect_win, DFC_SCROLL, classic_state);
+ return S_OK;
+}
+
+HRESULT NativeThemeWin::PaintScrollbarThumb(
+ HDC hdc,
+ Part part,
+ State state,
+ const gfx::Rect& rect,
+ const ScrollbarThumbExtraParams& extra) const {
+ HANDLE handle = GetThemeHandle(SCROLLBAR);
+ RECT rect_win = rect.ToRECT();
+ int part_id;
+ int state_id;
+
+ switch (part) {
+ case NativeTheme::kScrollbarHorizontalThumb:
+ part_id = SBP_THUMBBTNHORZ;
+ break;
+ case NativeTheme::kScrollbarVerticalThumb:
+ part_id = SBP_THUMBBTNVERT;
+ break;
+ case NativeTheme::kScrollbarHorizontalGripper:
+ part_id = SBP_GRIPPERHORZ;
+ break;
+ case NativeTheme::kScrollbarVerticalGripper:
+ part_id = SBP_GRIPPERVERT;
+ break;
+ default:
+ NOTREACHED() << "Invalid part: " << part;
+ break;
+ }
+
+ switch (state) {
+ case kDisabled:
+ state_id = SCRBS_DISABLED;
+ break;
+ case kHovered:
+ state_id = extra.is_hovering ? SCRBS_HOVER : SCRBS_HOT;
+ break;
+ case kNormal:
+ state_id = SCRBS_NORMAL;
+ break;
+ case kPressed:
+ state_id = SCRBS_PRESSED;
+ break;
+ default:
+ NOTREACHED() << "Invalid state: " << state;
+ break;
+ }
+
+ if (handle && draw_theme_)
+ return PaintScaledTheme(handle, hdc, part_id, state_id, rect);
+
+ // Draw it manually.
+ if ((part_id == SBP_THUMBBTNHORZ) || (part_id == SBP_THUMBBTNVERT))
+ DrawEdge(hdc, &rect_win, EDGE_RAISED, BF_RECT | BF_MIDDLE);
+ // Classic mode doesn't have a gripper.
+ return S_OK;
+}
+
+HRESULT NativeThemeWin::PaintScrollbarTrack(
+ SkCanvas* canvas,
+ HDC hdc,
+ Part part,
+ State state,
+ const gfx::Rect& rect,
+ const ScrollbarTrackExtraParams& extra) const {
+ HANDLE handle = GetThemeHandle(SCROLLBAR);
+ RECT rect_win = rect.ToRECT();
+ int part_id;
+ int state_id;
+
+ switch (part) {
+ case NativeTheme::kScrollbarHorizontalTrack:
+ part_id = extra.is_upper ? SBP_UPPERTRACKHORZ : SBP_LOWERTRACKHORZ;
+ break;
+ case NativeTheme::kScrollbarVerticalTrack:
+ part_id = extra.is_upper ? SBP_UPPERTRACKVERT : SBP_LOWERTRACKVERT;
+ break;
+ default:
+ NOTREACHED() << "Invalid part: " << part;
+ break;
+ }
+
+ switch (state) {
+ case kDisabled:
+ state_id = SCRBS_DISABLED;
+ break;
+ case kHovered:
+ state_id = SCRBS_HOVER;
+ break;
+ case kNormal:
+ state_id = SCRBS_NORMAL;
+ break;
+ case kPressed:
+ state_id = SCRBS_PRESSED;
+ break;
+ default:
+ NOTREACHED() << "Invalid state: " << state;
+ break;
+ }
+
+ if (handle && draw_theme_)
+ return draw_theme_(handle, hdc, part_id, state_id, &rect_win, NULL);
+
+ // Draw it manually.
+ if ((system_colors_[COLOR_SCROLLBAR] != system_colors_[COLOR_3DFACE]) &&
+ (system_colors_[COLOR_SCROLLBAR] != system_colors_[COLOR_WINDOW])) {
+ FillRect(hdc, &rect_win, reinterpret_cast<HBRUSH>(COLOR_SCROLLBAR + 1));
+ } else {
+ SkPaint paint;
+ RECT align_rect = gfx::Rect(extra.track_x, extra.track_y, extra.track_width,
+ extra.track_height).ToRECT();
+ SetCheckerboardShader(&paint, align_rect);
+ canvas->drawIRect(skia::RECTToSkIRect(rect_win), paint);
+ }
+ if (extra.classic_state & DFCS_PUSHED)
+ InvertRect(hdc, &rect_win);
+ return S_OK;
+}
+
+HRESULT NativeThemeWin::PaintSpinButton(
+ HDC hdc,
+ Part part,
+ State state,
+ const gfx::Rect& rect,
+ const InnerSpinButtonExtraParams& extra) const {
+ HANDLE handle = GetThemeHandle(SPIN);
+ RECT rect_win = rect.ToRECT();
+ int part_id = extra.spin_up ? SPNP_UP : SPNP_DOWN;
+ int state_id;
+ switch (state) {
+ case kDisabled:
+ state_id = extra.spin_up ? UPS_DISABLED : DNS_DISABLED;
+ break;
+ case kHovered:
+ state_id = extra.spin_up ? UPS_HOT : DNS_HOT;
+ break;
+ case kNormal:
+ state_id = extra.spin_up ? UPS_NORMAL : DNS_NORMAL;
+ break;
+ case kPressed:
+ state_id = extra.spin_up ? UPS_PRESSED : DNS_PRESSED;
+ break;
+ default:
+ NOTREACHED() << "Invalid state " << state;
+ break;
+ }
+
+ if (handle && draw_theme_)
+ return draw_theme_(handle, hdc, part_id, state_id, &rect_win, NULL);
+ DrawFrameControl(hdc, &rect_win, DFC_SCROLL, extra.classic_state);
+ return S_OK;
+}
+
+HRESULT NativeThemeWin::PaintTrackbar(
+ SkCanvas* canvas,
+ HDC hdc,
+ Part part,
+ State state,
+ const gfx::Rect& rect,
+ const TrackbarExtraParams& extra) const {
+ int part_id = part == kTrackbarTrack ? TKP_TRACK : TKP_THUMBBOTTOM;
+ if (extra.vertical)
+ part_id = part == kTrackbarTrack ? TKP_TRACKVERT : TKP_THUMBVERT;
+
+ int state_id = 0;
+ switch (state) {
+ case kDisabled:
+ state_id = TUS_DISABLED;
+ break;
+ case kHovered:
+ state_id = TUS_HOT;
+ break;
+ case kNormal:
+ state_id = TUS_NORMAL;
+ break;
+ case kPressed:
+ state_id = TUS_PRESSED;
+ break;
+ default:
+ NOTREACHED() << "Invalid state " << state;
+ break;
+ }
+
+ // Make the channel be 4 px thick in the center of the supplied rect. (4 px
+ // matches what XP does in various menus; GetThemePartSize() doesn't seem to
+ // return good values here.)
+ RECT rect_win = rect.ToRECT();
+ RECT channel_rect = rect.ToRECT();
+ const int channel_thickness = 4;
+ if (part_id == TKP_TRACK) {
+ channel_rect.top +=
+ ((channel_rect.bottom - channel_rect.top - channel_thickness) / 2);
+ channel_rect.bottom = channel_rect.top + channel_thickness;
+ } else if (part_id == TKP_TRACKVERT) {
+ channel_rect.left +=
+ ((channel_rect.right - channel_rect.left - channel_thickness) / 2);
+ channel_rect.right = channel_rect.left + channel_thickness;
+ } // else this isn't actually a channel, so |channel_rect| == |rect|.
+
+ HANDLE handle = GetThemeHandle(TRACKBAR);
+ if (handle && draw_theme_)
+ return draw_theme_(handle, hdc, part_id, state_id, &channel_rect, NULL);
+
+ // Classic mode, draw it manually.
+ if ((part_id == TKP_TRACK) || (part_id == TKP_TRACKVERT)) {
+ DrawEdge(hdc, &channel_rect, EDGE_SUNKEN, BF_RECT);
+ } else if (part_id == TKP_THUMBVERT) {
+ DrawEdge(hdc, &rect_win, EDGE_RAISED, BF_RECT | BF_SOFT | BF_MIDDLE);
+ } else {
+ // Split rect into top and bottom pieces.
+ RECT top_section = rect.ToRECT();
+ RECT bottom_section = rect.ToRECT();
+ top_section.bottom -= ((bottom_section.right - bottom_section.left) / 2);
+ bottom_section.top = top_section.bottom;
+ DrawEdge(hdc, &top_section, EDGE_RAISED,
+ BF_LEFT | BF_TOP | BF_RIGHT | BF_SOFT | BF_MIDDLE | BF_ADJUST);
+
+ // Split triangular piece into two diagonals.
+ RECT& left_half = bottom_section;
+ RECT right_half = bottom_section;
+ right_half.left += ((bottom_section.right - bottom_section.left) / 2);
+ left_half.right = right_half.left;
+ DrawEdge(hdc, &left_half, EDGE_RAISED,
+ BF_DIAGONAL_ENDTOPLEFT | BF_SOFT | BF_MIDDLE | BF_ADJUST);
+ DrawEdge(hdc, &right_half, EDGE_RAISED,
+ BF_DIAGONAL_ENDBOTTOMLEFT | BF_SOFT | BF_MIDDLE | BF_ADJUST);
+
+ // If the button is pressed, draw hatching.
+ if (extra.classic_state & DFCS_PUSHED) {
+ SkPaint paint;
+ SetCheckerboardShader(&paint, rect_win);
+
+ // Fill all three pieces with the pattern.
+ canvas->drawIRect(skia::RECTToSkIRect(top_section), paint);
+
+ SkScalar left_triangle_top = SkIntToScalar(left_half.top);
+ SkScalar left_triangle_right = SkIntToScalar(left_half.right);
+ SkPath left_triangle;
+ left_triangle.moveTo(SkIntToScalar(left_half.left), left_triangle_top);
+ left_triangle.lineTo(left_triangle_right, left_triangle_top);
+ left_triangle.lineTo(left_triangle_right,
+ SkIntToScalar(left_half.bottom));
+ left_triangle.close();
+ canvas->drawPath(left_triangle, paint);
+
+ SkScalar right_triangle_left = SkIntToScalar(right_half.left);
+ SkScalar right_triangle_top = SkIntToScalar(right_half.top);
+ SkPath right_triangle;
+ right_triangle.moveTo(right_triangle_left, right_triangle_top);
+ right_triangle.lineTo(SkIntToScalar(right_half.right),
+ right_triangle_top);
+ right_triangle.lineTo(right_triangle_left,
+ SkIntToScalar(right_half.bottom));
+ right_triangle.close();
+ canvas->drawPath(right_triangle, paint);
+ }
+ }
+ return S_OK;
+}
+
+HRESULT NativeThemeWin::PaintProgressBar(
+ HDC hdc,
+ const gfx::Rect& rect,
+ const ProgressBarExtraParams& extra) const {
+ // There is no documentation about the animation speed, frame-rate, nor
+ // size of moving overlay of the indeterminate progress bar.
+ // So we just observed real-world programs and guessed following parameters.
+ const int kDeteminateOverlayPixelsPerSecond = 300;
+ const int kDeteminateOverlayWidth = 120;
+ const int kIndeterminateOverlayPixelsPerSecond = 175;
+ const int kVistaIndeterminateOverlayWidth = 120;
+ const int kXPIndeterminateOverlayWidth = 55;
+ // The thickness of the bar frame inside |value_rect|
+ const int kXPBarPadding = 3;
+
+ RECT bar_rect = rect.ToRECT();
+ RECT value_rect = gfx::Rect(extra.value_rect_x,
+ extra.value_rect_y,
+ extra.value_rect_width,
+ extra.value_rect_height).ToRECT();
+
+ bool pre_vista = base::win::GetVersion() < base::win::VERSION_VISTA;
+ HANDLE handle = GetThemeHandle(PROGRESS);
+ if (handle && draw_theme_ && draw_theme_ex_) {
+ draw_theme_(handle, hdc, PP_BAR, 0, &bar_rect, NULL);
+
+ int bar_width = bar_rect.right - bar_rect.left;
+ if (extra.determinate) {
+ // TODO(morrita): this RTL guess can be wrong.
+ // We should pass the direction from WebKit side.
+ bool is_rtl = (bar_rect.right == value_rect.right &&
+ bar_rect.left != value_rect.left);
+ // We should care the direction here because PP_CNUNK painting
+ // is asymmetric.
+ DTBGOPTS value_draw_options;
+ value_draw_options.dwSize = sizeof(DTBGOPTS);
+ value_draw_options.dwFlags = is_rtl ? DTBG_MIRRORDC : 0;
+ value_draw_options.rcClip = bar_rect;
+
+ if (pre_vista) {
+ // On XP, progress bar is chunk-style and has no glossy effect.
+ // We need to shrink destination rect to fit the part inside the bar
+ // with an appropriate margin.
+ RECT shrunk_value_rect = InsetRect(&value_rect, kXPBarPadding);
+ draw_theme_ex_(handle, hdc, PP_CHUNK, 0,
+ &shrunk_value_rect, &value_draw_options);
+ } else {
+ // On Vista or later, the progress bar part has a
+ // single-block value part. It also has glossy effect.
+ // And the value part has exactly same height as the bar part
+ // so we don't need to shrink the rect.
+ draw_theme_ex_(handle, hdc, PP_FILL, 0,
+ &value_rect, &value_draw_options);
+
+ int dx = ComputeAnimationProgress(bar_width,
+ kDeteminateOverlayWidth,
+ kDeteminateOverlayPixelsPerSecond,
+ extra.animated_seconds);
+ RECT overlay_rect = value_rect;
+ overlay_rect.left += dx;
+ overlay_rect.right = overlay_rect.left + kDeteminateOverlayWidth;
+ draw_theme_(handle, hdc, PP_MOVEOVERLAY, 0, &overlay_rect, &value_rect);
+ }
+ } else {
+ // A glossy overlay for indeterminate progress bar has small pause
+ // after each animation. We emulate this by adding an invisible margin
+ // the animation has to traverse.
+ int width_with_margin = bar_width + kIndeterminateOverlayPixelsPerSecond;
+ int overlay_width = pre_vista ?
+ kXPIndeterminateOverlayWidth : kVistaIndeterminateOverlayWidth;
+ int dx = ComputeAnimationProgress(width_with_margin,
+ overlay_width,
+ kIndeterminateOverlayPixelsPerSecond,
+ extra.animated_seconds);
+ RECT overlay_rect = bar_rect;
+ overlay_rect.left += dx;
+ overlay_rect.right = overlay_rect.left + overlay_width;
+ if (pre_vista) {
+ RECT shrunk_rect = InsetRect(&overlay_rect, kXPBarPadding);
+ RECT shrunk_bar_rect = InsetRect(&bar_rect, kXPBarPadding);
+ draw_theme_(handle, hdc, PP_CHUNK, 0, &shrunk_rect, &shrunk_bar_rect);
+ } else {
+ draw_theme_(handle, hdc, PP_MOVEOVERLAY, 0, &overlay_rect, &bar_rect);
+ }
+ }
+
+ return S_OK;
+ }
+
+ HBRUSH bg_brush = GetSysColorBrush(COLOR_BTNFACE);
+ HBRUSH fg_brush = GetSysColorBrush(COLOR_BTNSHADOW);
+ FillRect(hdc, &bar_rect, bg_brush);
+ FillRect(hdc, &value_rect, fg_brush);
+ DrawEdge(hdc, &bar_rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
+ return S_OK;
+}
+
+HRESULT NativeThemeWin::PaintWindowResizeGripper(HDC hdc,
+ const gfx::Rect& rect) const {
+ HANDLE handle = GetThemeHandle(STATUS);
+ RECT rect_win = rect.ToRECT();
+ if (handle && draw_theme_) {
+ // Paint the status bar gripper. There doesn't seem to be a
+ // standard gripper in Windows for the space between
+ // scrollbars. This is pretty close, but it's supposed to be
+ // painted over a status bar.
+ return draw_theme_(handle, hdc, SP_GRIPPER, 0, &rect_win, NULL);
+ }
+
+ // Draw a windows classic scrollbar gripper.
+ DrawFrameControl(hdc, &rect_win, DFC_SCROLL, DFCS_SCROLLSIZEGRIP);
+ return S_OK;
+}
+
+HRESULT NativeThemeWin::PaintTabPanelBackground(HDC hdc,
+ const gfx::Rect& rect) const {
+ HANDLE handle = GetThemeHandle(TAB);
+ RECT rect_win = rect.ToRECT();
+ if (handle && draw_theme_)
+ return draw_theme_(handle, hdc, TABP_BODY, 0, &rect_win, NULL);
+
+ // Classic just renders a flat color background.
+ FillRect(hdc, &rect_win, reinterpret_cast<HBRUSH>(COLOR_3DFACE + 1));
+ return S_OK;
+}
+
+HRESULT NativeThemeWin::PaintTextField(
+ HDC hdc,
+ Part part,
+ State state,
+ const gfx::Rect& rect,
+ const TextFieldExtraParams& extra) const {
+ int part_id = EP_EDITTEXT;
+ int state_id = ETS_NORMAL;
+ switch (state) {
+ case kNormal:
+ if (extra.is_read_only) {
+ state_id = ETS_READONLY;
+ } else if (extra.is_focused) {
+ state_id = ETS_FOCUSED;
+ } else {
+ state_id = ETS_NORMAL;
+ }
+ break;
+ case kHovered:
+ state_id = ETS_HOT;
+ break;
+ case kPressed:
+ state_id = ETS_SELECTED;
+ break;
+ case kDisabled:
+ state_id = ETS_DISABLED;
+ break;
+ default:
+ NOTREACHED() << "Invalid state: " << state;
+ break;
+ }
+
+ RECT rect_win = rect.ToRECT();
+ return PaintTextField(hdc, part_id, state_id, extra.classic_state,
+ &rect_win,
+ skia::SkColorToCOLORREF(extra.background_color),
+ extra.fill_content_area, extra.draw_edges);
+}
+
+HRESULT NativeThemeWin::PaintTextField(HDC hdc,
+ int part_id,
+ int state_id,
+ int classic_state,
+ RECT* rect,
+ COLORREF color,
+ bool fill_content_area,
+ bool draw_edges) const {
+ // TODO(ojan): http://b/1210017 Figure out how to give the ability to
+ // exclude individual edges from being drawn.
+
+ HANDLE handle = GetThemeHandle(TEXTFIELD);
+ // TODO(mpcomplete): can we detect if the color is specified by the user,
+ // and if not, just use the system color?
+ // CreateSolidBrush() accepts a RGB value but alpha must be 0.
+ HBRUSH bg_brush = CreateSolidBrush(color);
+ HRESULT hr;
+ // DrawThemeBackgroundEx was introduced in XP SP2, so that it's possible
+ // draw_theme_ex_ is NULL and draw_theme_ is non-null.
+ if (handle && (draw_theme_ex_ || (draw_theme_ && draw_edges))) {
+ if (draw_theme_ex_) {
+ static const DTBGOPTS omit_border_options = {
+ sizeof(DTBGOPTS),
+ DTBG_OMITBORDER,
+ { 0, 0, 0, 0 }
+ };
+ const DTBGOPTS* draw_opts = draw_edges ? NULL : &omit_border_options;
+ hr = draw_theme_ex_(handle, hdc, part_id, state_id, rect, draw_opts);
+ } else {
+ hr = draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
+ }
+
+ // TODO(maruel): Need to be fixed if get_theme_content_rect_ is NULL.
+ if (fill_content_area && get_theme_content_rect_) {
+ RECT content_rect;
+ hr = get_theme_content_rect_(handle, hdc, part_id, state_id, rect,
+ &content_rect);
+ FillRect(hdc, &content_rect, bg_brush);
+ }
+ } else {
+ // Draw it manually.
+ if (draw_edges)
+ DrawEdge(hdc, rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
+
+ if (fill_content_area) {
+ FillRect(hdc, rect, (classic_state & DFCS_INACTIVE) ?
+ reinterpret_cast<HBRUSH>(COLOR_BTNFACE + 1) : bg_brush);
+ }
+ hr = S_OK;
+ }
+ DeleteObject(bg_brush);
+ return hr;
+}
+
+HRESULT NativeThemeWin::PaintScaledTheme(HANDLE theme,
+ HDC hdc,
+ int part_id,
+ int state_id,
+ const gfx::Rect& rect) const {
+ // Correct the scaling and positioning of sub-components such as scrollbar
+ // arrows and thumb grippers in the event that the world transform applies
+ // scaling (e.g. in high-DPI mode).
+ XFORM save_transform;
+ if (GetWorldTransform(hdc, &save_transform)) {
+ float scale = save_transform.eM11;
+ if (scale != 1 && save_transform.eM12 == 0) {
+ ModifyWorldTransform(hdc, NULL, MWT_IDENTITY);
+ gfx::Rect scaled_rect = gfx::ToEnclosedRect(
+ gfx::ScaleRect(rect, scale));
+ RECT bounds = gfx::Rect(scaled_rect.x() + save_transform.eDx,
+ scaled_rect.y() + save_transform.eDy,
+ scaled_rect.width(),
+ scaled_rect.height()).ToRECT();
+ HRESULT result = draw_theme_(theme, hdc, part_id, state_id, &bounds,
+ NULL);
+ SetWorldTransform(hdc, &save_transform);
+ return result;
+ }
+ }
+ RECT bounds = rect.ToRECT();
+ return draw_theme_(theme, hdc, part_id, state_id, &bounds, NULL);
+}
+
+// static
+NativeThemeWin::ThemeName NativeThemeWin::GetThemeName(Part part) {
+ ThemeName name;
+ switch (part) {
+ case kCheckbox:
+ case kRadio:
+ case kPushButton:
+ name = BUTTON;
+ break;
+ case kInnerSpinButton:
+ name = SPIN;
+ break;
+ case kMenuCheck:
+ case kMenuPopupGutter:
+ case kMenuList:
+ case kMenuPopupArrow:
+ case kMenuPopupSeparator:
+ name = MENU;
+ break;
+ case kProgressBar:
+ name = PROGRESS;
+ break;
+ case kScrollbarDownArrow:
+ case kScrollbarLeftArrow:
+ case kScrollbarRightArrow:
+ case kScrollbarUpArrow:
+ case kScrollbarHorizontalThumb:
+ case kScrollbarVerticalThumb:
+ case kScrollbarHorizontalTrack:
+ case kScrollbarVerticalTrack:
+ name = SCROLLBAR;
+ break;
+ case kSliderTrack:
+ case kSliderThumb:
+ name = TRACKBAR;
+ break;
+ case kTextField:
+ name = TEXTFIELD;
+ break;
+ case kWindowResizeGripper:
+ name = STATUS;
+ break;
+ default:
+ NOTREACHED() << "Invalid part: " << part;
+ break;
+ }
+ return name;
+}
+
+// static
+int NativeThemeWin::GetWindowsPart(Part part,
+ State state,
+ const ExtraParams& extra) {
+ int part_id;
+ switch (part) {
+ case kCheckbox:
+ part_id = BP_CHECKBOX;
+ break;
+ case kMenuCheck:
+ part_id = MENU_POPUPCHECK;
+ break;
+ case kMenuPopupArrow:
+ part_id = MENU_POPUPSUBMENU;
+ break;
+ case kMenuPopupGutter:
+ part_id = MENU_POPUPGUTTER;
+ break;
+ case kMenuPopupSeparator:
+ part_id = MENU_POPUPSEPARATOR;
+ break;
+ case kPushButton:
+ part_id = BP_PUSHBUTTON;
+ break;
+ case kRadio:
+ part_id = BP_RADIOBUTTON;
+ break;
+ case kWindowResizeGripper:
+ part_id = SP_GRIPPER;
+ break;
+ case kScrollbarDownArrow:
+ case kScrollbarLeftArrow:
+ case kScrollbarRightArrow:
+ case kScrollbarUpArrow:
+ part_id = SBP_ARROWBTN;
+ break;
+ case kScrollbarHorizontalThumb:
+ part_id = SBP_THUMBBTNHORZ;
+ break;
+ case kScrollbarVerticalThumb:
+ part_id = SBP_THUMBBTNVERT;
+ break;
+ default:
+ NOTREACHED() << "Invalid part: " << part;
+ break;
+ }
+ return part_id;
+}
+
+int NativeThemeWin::GetWindowsState(Part part,
+ State state,
+ const ExtraParams& extra) {
+ int state_id;
+ switch (part) {
+ case kCheckbox:
+ switch (state) {
+ case kNormal:
+ state_id = CBS_UNCHECKEDNORMAL;
+ break;
+ case kHovered:
+ state_id = CBS_UNCHECKEDHOT;
+ break;
+ case kPressed:
+ state_id = CBS_UNCHECKEDPRESSED;
+ break;
+ case kDisabled:
+ state_id = CBS_UNCHECKEDDISABLED;
+ break;
+ default:
+ NOTREACHED() << "Invalid state: " << state;
+ break;
+ }
+ break;
+ case kMenuCheck:
+ switch (state) {
+ case kNormal:
+ case kHovered:
+ case kPressed:
+ state_id = extra.menu_check.is_radio ? MC_BULLETNORMAL
+ : MC_CHECKMARKNORMAL;
+ break;
+ case kDisabled:
+ state_id = extra.menu_check.is_radio ? MC_BULLETDISABLED
+ : MC_CHECKMARKDISABLED;
+ break;
+ default:
+ NOTREACHED() << "Invalid state: " << state;
+ break;
+ }
+ break;
+ case kMenuPopupArrow:
+ case kMenuPopupGutter:
+ case kMenuPopupSeparator:
+ switch (state) {
+ case kNormal:
+ state_id = MBI_NORMAL;
+ break;
+ case kHovered:
+ state_id = MBI_HOT;
+ break;
+ case kPressed:
+ state_id = MBI_PUSHED;
+ break;
+ case kDisabled:
+ state_id = MBI_DISABLED;
+ break;
+ default:
+ NOTREACHED() << "Invalid state: " << state;
+ break;
+ }
+ break;
+ case kPushButton:
+ switch (state) {
+ case kNormal:
+ state_id = PBS_NORMAL;
+ break;
+ case kHovered:
+ state_id = PBS_HOT;
+ break;
+ case kPressed:
+ state_id = PBS_PRESSED;
+ break;
+ case kDisabled:
+ state_id = PBS_DISABLED;
+ break;
+ default:
+ NOTREACHED() << "Invalid state: " << state;
+ break;
+ }
+ break;
+ case kRadio:
+ switch (state) {
+ case kNormal:
+ state_id = RBS_UNCHECKEDNORMAL;
+ break;
+ case kHovered:
+ state_id = RBS_UNCHECKEDHOT;
+ break;
+ case kPressed:
+ state_id = RBS_UNCHECKEDPRESSED;
+ break;
+ case kDisabled:
+ state_id = RBS_UNCHECKEDDISABLED;
+ break;
+ default:
+ NOTREACHED() << "Invalid state: " << state;
+ break;
+ }
+ break;
+ case kWindowResizeGripper:
+ switch (state) {
+ case kNormal:
+ case kHovered:
+ case kPressed:
+ case kDisabled:
+ state_id = 1; // gripper has no windows state
+ break;
+ default:
+ NOTREACHED() << "Invalid state: " << state;
+ break;
+ }
+ break;
+ case kScrollbarDownArrow:
+ switch (state) {
+ case kNormal:
+ state_id = ABS_DOWNNORMAL;
+ break;
+ case kHovered:
+ // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
+ state_id = base::win::GetVersion() < base::win::VERSION_VISTA ?
+ ABS_DOWNHOT : ABS_DOWNHOVER;
+ break;
+ case kPressed:
+ state_id = ABS_DOWNPRESSED;
+ break;
+ case kDisabled:
+ state_id = ABS_DOWNDISABLED;
+ break;
+ default:
+ NOTREACHED() << "Invalid state: " << state;
+ break;
+ }
+ break;
+ case kScrollbarLeftArrow:
+ switch (state) {
+ case kNormal:
+ state_id = ABS_LEFTNORMAL;
+ break;
+ case kHovered:
+ // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
+ state_id = base::win::GetVersion() < base::win::VERSION_VISTA ?
+ ABS_LEFTHOT : ABS_LEFTHOVER;
+ break;
+ case kPressed:
+ state_id = ABS_LEFTPRESSED;
+ break;
+ case kDisabled:
+ state_id = ABS_LEFTDISABLED;
+ break;
+ default:
+ NOTREACHED() << "Invalid state: " << state;
+ break;
+ }
+ break;
+ case kScrollbarRightArrow:
+ switch (state) {
+ case kNormal:
+ state_id = ABS_RIGHTNORMAL;
+ break;
+ case kHovered:
+ // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
+ state_id = base::win::GetVersion() < base::win::VERSION_VISTA ?
+ ABS_RIGHTHOT : ABS_RIGHTHOVER;
+ break;
+ case kPressed:
+ state_id = ABS_RIGHTPRESSED;
+ break;
+ case kDisabled:
+ state_id = ABS_RIGHTDISABLED;
+ break;
+ default:
+ NOTREACHED() << "Invalid state: " << state;
+ break;
+ }
+ break;
+ case kScrollbarUpArrow:
+ switch (state) {
+ case kNormal:
+ state_id = ABS_UPNORMAL;
+ break;
+ case kHovered:
+ // Mimic ScrollbarThemeChromiumWin.cpp in WebKit.
+ state_id = base::win::GetVersion() < base::win::VERSION_VISTA ?
+ ABS_UPHOT : ABS_UPHOVER;
+ break;
+ case kPressed:
+ state_id = ABS_UPPRESSED;
+ break;
+ case kDisabled:
+ state_id = ABS_UPDISABLED;
+ break;
+ default:
+ NOTREACHED() << "Invalid state: " << state;
+ break;
+ }
+ break;
+ case kScrollbarHorizontalThumb:
+ case kScrollbarVerticalThumb:
+ switch (state) {
+ case kNormal:
+ state_id = SCRBS_NORMAL;
+ break;
+ case kHovered:
+ // Mimic WebKit's behaviour in ScrollbarThemeChromiumWin.cpp.
+ state_id = base::win::GetVersion() < base::win::VERSION_VISTA ?
+ SCRBS_HOT : SCRBS_HOVER;
+ break;
+ case kPressed:
+ state_id = SCRBS_PRESSED;
+ break;
+ case kDisabled:
+ state_id = SCRBS_DISABLED;
+ break;
+ default:
+ NOTREACHED() << "Invalid state: " << state;
+ break;
+ }
+ break;
+ default:
+ NOTREACHED() << "Invalid part: " << part;
+ break;
+ }
+ return state_id;
+}
+
+HRESULT NativeThemeWin::GetThemeInt(ThemeName theme,
+ int part_id,
+ int state_id,
+ int prop_id,
+ int *value) const {
+ HANDLE handle = GetThemeHandle(theme);
+ if (handle && get_theme_int_)
+ return get_theme_int_(handle, part_id, state_id, prop_id, value);
+ return E_NOTIMPL;
+}
+
+HRESULT NativeThemeWin::PaintFrameControl(HDC hdc,
+ const gfx::Rect& rect,
+ UINT type,
+ UINT state,
+ bool is_selected,
+ State control_state) const {
+ const int width = rect.width();
+ const int height = rect.height();
+
+ // DrawFrameControl for menu arrow/check wants a monochrome bitmap.
+ base::win::ScopedBitmap mask_bitmap(CreateBitmap(width, height, 1, 1, NULL));
+
+ if (mask_bitmap == NULL)
+ return E_OUTOFMEMORY;
+
+ base::win::ScopedCreateDC bitmap_dc(CreateCompatibleDC(NULL));
+ base::win::ScopedSelectObject select_bitmap(bitmap_dc, mask_bitmap);
+ RECT local_rect = { 0, 0, width, height };
+ DrawFrameControl(bitmap_dc, &local_rect, type, state);
+
+ // We're going to use BitBlt with a b&w mask. This results in using the dest
+ // dc's text color for the black bits in the mask, and the dest dc's
+ // background color for the white bits in the mask. DrawFrameControl draws the
+ // check in black, and the background in white.
+ int bg_color_key;
+ int text_color_key;
+ switch (control_state) {
+ case NativeTheme::kHovered:
+ bg_color_key = COLOR_HIGHLIGHT;
+ text_color_key = COLOR_HIGHLIGHTTEXT;
+ break;
+ case NativeTheme::kNormal:
+ bg_color_key = COLOR_MENU;
+ text_color_key = COLOR_MENUTEXT;
+ break;
+ case NativeTheme::kDisabled:
+ bg_color_key = is_selected ? COLOR_HIGHLIGHT : COLOR_MENU;
+ text_color_key = COLOR_GRAYTEXT;
+ break;
+ default:
+ NOTREACHED();
+ bg_color_key = COLOR_MENU;
+ text_color_key = COLOR_MENUTEXT;
+ break;
+ }
+ COLORREF old_bg_color = SetBkColor(hdc, GetSysColor(bg_color_key));
+ COLORREF old_text_color = SetTextColor(hdc, GetSysColor(text_color_key));
+ BitBlt(hdc, rect.x(), rect.y(), width, height, bitmap_dc, 0, 0, SRCCOPY);
+ SetBkColor(hdc, old_bg_color);
+ SetTextColor(hdc, old_text_color);
+
+ return S_OK;
+}
+
+HANDLE NativeThemeWin::GetThemeHandle(ThemeName theme_name) const {
+ if (!open_theme_ || theme_name < 0 || theme_name >= LAST)
+ return 0;
+
+ if (theme_handles_[theme_name])
+ return theme_handles_[theme_name];
+
+ // Not found, try to load it.
+ HANDLE handle = 0;
+ switch (theme_name) {
+ case BUTTON:
+ handle = open_theme_(NULL, L"Button");
+ break;
+ case LIST:
+ handle = open_theme_(NULL, L"Listview");
+ break;
+ case MENU:
+ handle = open_theme_(NULL, L"Menu");
+ break;
+ case MENULIST:
+ handle = open_theme_(NULL, L"Combobox");
+ break;
+ case SCROLLBAR:
+ handle = open_theme_(NULL, L"Scrollbar");
+ break;
+ case STATUS:
+ handle = open_theme_(NULL, L"Status");
+ break;
+ case TAB:
+ handle = open_theme_(NULL, L"Tab");
+ break;
+ case TEXTFIELD:
+ handle = open_theme_(NULL, L"Edit");
+ break;
+ case TRACKBAR:
+ handle = open_theme_(NULL, L"Trackbar");
+ break;
+ case WINDOW:
+ handle = open_theme_(NULL, L"Window");
+ break;
+ case PROGRESS:
+ handle = open_theme_(NULL, L"Progress");
+ break;
+ case SPIN:
+ handle = open_theme_(NULL, L"Spin");
+ break;
+ default:
+ NOTREACHED();
+ }
+ theme_handles_[theme_name] = handle;
+ return handle;
+}
+
+} // namespace ui
diff --git a/chromium/ui/native_theme/native_theme_win.h b/chromium/ui/native_theme/native_theme_win.h
new file mode 100644
index 00000000000..5542f20d0a3
--- /dev/null
+++ b/chromium/ui/native_theme/native_theme_win.h
@@ -0,0 +1,364 @@
+// Copyright (c) 2012 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.
+
+#ifndef UI_NATIVE_THEME_NATIVE_THEME_WIN_H_
+#define UI_NATIVE_THEME_NATIVE_THEME_WIN_H_
+
+// A wrapper class for working with custom XP/Vista themes provided in
+// uxtheme.dll. This is a singleton class that can be grabbed using
+// NativeThemeWin::instance().
+// For more information on visual style parts and states, see:
+// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/commctls/userex/topics/partsandstates.asp
+
+#include <map>
+
+#include <windows.h>
+#include <uxtheme.h>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "ui/gfx/size.h"
+#include "ui/gfx/sys_color_change_listener.h"
+#include "ui/native_theme/native_theme.h"
+
+class SkCanvas;
+
+namespace ui {
+
+// Windows implementation of native theme class.
+//
+// At the moment, this class in in transition from an older API that consists
+// of several PaintXXX methods to an API, inherited from the NativeTheme base
+// class, that consists of a single Paint() method with a argument to indicate
+// what kind of part to paint.
+class NATIVE_THEME_EXPORT NativeThemeWin : public NativeTheme,
+ public gfx::SysColorChangeListener {
+ public:
+ enum ThemeName {
+ BUTTON,
+ LIST,
+ MENU,
+ MENULIST,
+ SCROLLBAR,
+ STATUS,
+ TAB,
+ TEXTFIELD,
+ TRACKBAR,
+ WINDOW,
+ PROGRESS,
+ SPIN,
+ LAST
+ };
+
+ bool IsThemingActive() const;
+
+ HRESULT GetThemeColor(ThemeName theme,
+ int part_id,
+ int state_id,
+ int prop_id,
+ SkColor* color) const;
+
+ // Get the theme color if theming is enabled. If theming is unsupported
+ // for this part, use Win32's GetSysColor to find the color specified
+ // by default_sys_color.
+ SkColor GetThemeColorWithDefault(ThemeName theme,
+ int part_id,
+ int state_id,
+ int prop_id,
+ int default_sys_color) const;
+
+ // Get the thickness of the border associated with the specified theme,
+ // defaulting to GetSystemMetrics edge size if themes are disabled.
+ // In Classic Windows, borders are typically 2px; on XP+, they are 1px.
+ gfx::Size GetThemeBorderSize(ThemeName theme) const;
+
+ // Disables all theming for top-level windows in the entire process, from
+ // when this method is called until the process exits. All the other
+ // methods in this class will continue to work, but their output will ignore
+ // the user's theme. This is meant for use when running tests that require
+ // consistent visual results.
+ void DisableTheming() const;
+
+ // Closes cached theme handles so we can unload the DLL or update our UI
+ // for a theme change.
+ void CloseHandles() const;
+
+ // Returns true if classic theme is in use.
+ bool IsClassicTheme(ThemeName name) const;
+
+ // Gets our singleton instance.
+ static NativeThemeWin* instance();
+
+ HRESULT PaintTextField(HDC hdc,
+ int part_id,
+ int state_id,
+ int classic_state,
+ RECT* rect,
+ COLORREF color,
+ bool fill_content_area,
+ bool draw_edges) const;
+
+ // NativeTheme implementation:
+ virtual gfx::Size GetPartSize(Part part,
+ State state,
+ const ExtraParams& extra) const OVERRIDE;
+ virtual void Paint(SkCanvas* canvas,
+ Part part,
+ State state,
+ const gfx::Rect& rect,
+ const ExtraParams& extra) const OVERRIDE;
+ virtual SkColor GetSystemColor(ColorId color_id) const OVERRIDE;
+
+ private:
+ NativeThemeWin();
+ ~NativeThemeWin();
+
+ // gfx::SysColorChangeListener implementation:
+ virtual void OnSysColorChange() OVERRIDE;
+
+ // Update the locally cached set of system colors.
+ void UpdateSystemColors();
+
+ // Paint directly to canvas' HDC.
+ void PaintDirect(SkCanvas* canvas,
+ Part part,
+ State state,
+ const gfx::Rect& rect,
+ const ExtraParams& extra) const;
+
+ // Create a temporary HDC, paint to that, clean up the alpha values in the
+ // temporary HDC, and then blit the result to canvas. This is to work around
+ // the fact that Windows XP and some classic themes give bogus alpha values.
+ void PaintIndirect(SkCanvas* canvas,
+ Part part,
+ State state,
+ const gfx::Rect& rect,
+ const ExtraParams& extra) const;
+
+ HRESULT GetThemePartSize(ThemeName themeName,
+ HDC hdc,
+ int part_id,
+ int state_id,
+ RECT* rect,
+ int ts,
+ SIZE* size) const;
+
+ HRESULT PaintButton(HDC hdc,
+ State state,
+ const ButtonExtraParams& extra,
+ int part_id,
+ int state_id,
+ RECT* rect) const;
+
+ HRESULT PaintMenuSeparator(HDC hdc,
+ const gfx::Rect& rect,
+ const MenuSeparatorExtraParams& extra) const;
+
+ HRESULT PaintMenuGutter(HDC hdc, const gfx::Rect& rect) const;
+
+ // |arrow_direction| determines whether the arrow is pointing to the left or
+ // to the right. In RTL locales, sub-menus open from right to left and
+ // therefore the menu arrow should point to the left and not to the right.
+ HRESULT PaintMenuArrow(HDC hdc,
+ State state,
+ const gfx::Rect& rect,
+ const MenuArrowExtraParams& extra) const;
+
+ HRESULT PaintMenuBackground(HDC hdc, const gfx::Rect& rect) const;
+
+ HRESULT PaintMenuCheck(HDC hdc,
+ State state,
+ const gfx::Rect& rect,
+ const MenuCheckExtraParams& extra) const;
+
+ HRESULT PaintMenuCheckBackground(HDC hdc,
+ State state,
+ const gfx::Rect& rect) const;
+
+ HRESULT PaintMenuItemBackground(HDC hdc,
+ State state,
+ const gfx::Rect& rect,
+ const MenuItemExtraParams& extra) const;
+
+ HRESULT PaintPushButton(HDC hdc,
+ Part part,
+ State state,
+ const gfx::Rect& rect,
+ const ButtonExtraParams& extra) const;
+
+ HRESULT PaintRadioButton(HDC hdc,
+ Part part,
+ State state,
+ const gfx::Rect& rect,
+ const ButtonExtraParams& extra) const;
+
+ HRESULT PaintCheckbox(HDC hdc,
+ Part part,
+ State state,
+ const gfx::Rect& rect,
+ const ButtonExtraParams& extra) const;
+
+ HRESULT PaintMenuList(HDC hdc,
+ State state,
+ const gfx::Rect& rect,
+ const MenuListExtraParams& extra) const;
+
+ // Paints a scrollbar arrow. |classic_state| should have the appropriate
+ // classic part number ORed in already.
+ HRESULT PaintScrollbarArrow(HDC hdc,
+ Part part,
+ State state,
+ const gfx::Rect& rect,
+ const ScrollbarArrowExtraParams& extra) const;
+
+ HRESULT PaintScrollbarThumb(HDC hdc,
+ Part part,
+ State state,
+ const gfx::Rect& rect,
+ const ScrollbarThumbExtraParams& extra) const;
+
+ // This method is deprecated and will be removed in the near future.
+ // Paints a scrollbar track section. |align_rect| is only used in classic
+ // mode, and makes sure the checkerboard pattern in |target_rect| is aligned
+ // with one presumed to be in |align_rect|.
+ HRESULT PaintScrollbarTrack(SkCanvas* canvas,
+ HDC hdc,
+ Part part,
+ State state,
+ const gfx::Rect& rect,
+ const ScrollbarTrackExtraParams& extra) const;
+
+ HRESULT PaintSpinButton(HDC hdc,
+ Part part,
+ State state,
+ const gfx::Rect& rect,
+ const InnerSpinButtonExtraParams& extra) const;
+
+ HRESULT PaintTrackbar(SkCanvas* canvas,
+ HDC hdc,
+ Part part,
+ State state,
+ const gfx::Rect& rect,
+ const TrackbarExtraParams& extra) const;
+
+ HRESULT PaintProgressBar(HDC hdc,
+ const gfx::Rect& rect,
+ const ProgressBarExtraParams& extra) const;
+
+ HRESULT PaintWindowResizeGripper(HDC hdc, const gfx::Rect& rect) const;
+
+ HRESULT PaintTabPanelBackground(HDC hdc, const gfx::Rect& rect) const;
+
+ HRESULT PaintTextField(HDC hdc,
+ Part part,
+ State state,
+ const gfx::Rect& rect,
+ const TextFieldExtraParams& extra) const;
+
+ // Paints a theme part, with support for scene scaling in high-DPI mode.
+ // |theme| is the theme handle. |hdc| is the handle for the device context.
+ // |part_id| is the identifier for the part (e.g. thumb gripper). |state_id|
+ // is the identifier for the rendering state of the part (e.g. hover). |rect|
+ // is the bounds for rendering, expressed in logical coordinates.
+ HRESULT PaintScaledTheme(HANDLE theme,
+ HDC hdc,
+ int part_id,
+ int state_id,
+ const gfx::Rect& rect) const;
+
+ // Get the windows theme name/part/state. These three helper functions are
+ // used only by GetPartSize(), as each of the corresponding PaintXXX()
+ // methods do further validation of the part and state that is required for
+ // getting the size.
+ static ThemeName GetThemeName(Part part);
+ static int GetWindowsPart(Part part, State state, const ExtraParams& extra);
+ static int GetWindowsState(Part part, State state, const ExtraParams& extra);
+
+ HRESULT GetThemeInt(ThemeName theme,
+ int part_id,
+ int state_id,
+ int prop_id,
+ int *result) const;
+
+ HRESULT PaintFrameControl(HDC hdc,
+ const gfx::Rect& rect,
+ UINT type,
+ UINT state,
+ bool is_selected,
+ State control_state) const;
+
+ // Returns a handle to the theme data.
+ HANDLE GetThemeHandle(ThemeName theme_name) const;
+
+ typedef HRESULT (WINAPI* DrawThemeBackgroundPtr)(HANDLE theme,
+ HDC hdc,
+ int part_id,
+ int state_id,
+ const RECT* rect,
+ const RECT* clip_rect);
+ typedef HRESULT (WINAPI* DrawThemeBackgroundExPtr)(HANDLE theme,
+ HDC hdc,
+ int part_id,
+ int state_id,
+ const RECT* rect,
+ const DTBGOPTS* opts);
+ typedef HRESULT (WINAPI* GetThemeColorPtr)(HANDLE hTheme,
+ int part_id,
+ int state_id,
+ int prop_id,
+ COLORREF* color);
+ typedef HRESULT (WINAPI* GetThemeContentRectPtr)(HANDLE hTheme,
+ HDC hdc,
+ int part_id,
+ int state_id,
+ const RECT* rect,
+ RECT* content_rect);
+ typedef HRESULT (WINAPI* GetThemePartSizePtr)(HANDLE hTheme,
+ HDC hdc,
+ int part_id,
+ int state_id,
+ RECT* rect,
+ int ts,
+ SIZE* size);
+ typedef HANDLE (WINAPI* OpenThemeDataPtr)(HWND window,
+ LPCWSTR class_list);
+ typedef HRESULT (WINAPI* CloseThemeDataPtr)(HANDLE theme);
+
+ typedef void (WINAPI* SetThemeAppPropertiesPtr) (DWORD flags);
+ typedef BOOL (WINAPI* IsThemeActivePtr)();
+ typedef HRESULT (WINAPI* GetThemeIntPtr)(HANDLE hTheme,
+ int part_id,
+ int state_id,
+ int prop_id,
+ int *value);
+
+ // Function pointers into uxtheme.dll.
+ DrawThemeBackgroundPtr draw_theme_;
+ DrawThemeBackgroundExPtr draw_theme_ex_;
+ GetThemeColorPtr get_theme_color_;
+ GetThemeContentRectPtr get_theme_content_rect_;
+ GetThemePartSizePtr get_theme_part_size_;
+ OpenThemeDataPtr open_theme_;
+ CloseThemeDataPtr close_theme_;
+ SetThemeAppPropertiesPtr set_theme_properties_;
+ IsThemeActivePtr is_theme_active_;
+ GetThemeIntPtr get_theme_int_;
+
+ // Handle to uxtheme.dll.
+ HMODULE theme_dll_;
+
+ // A cache of open theme handles.
+ mutable HANDLE theme_handles_[LAST];
+
+ // The system color change listener and the updated cache of system colors.
+ gfx::ScopedSysColorChangeListener color_change_listener_;
+ mutable std::map<int, SkColor> system_colors_;
+
+ DISALLOW_COPY_AND_ASSIGN(NativeThemeWin);
+};
+
+} // namespace ui
+
+#endif // UI_NATIVE_THEME_NATIVE_THEME_WIN_H_
diff --git a/chromium/ui/native_theme/native_theme_win_unittest.cc b/chromium/ui/native_theme/native_theme_win_unittest.cc
new file mode 100644
index 00000000000..a2bd6aaee98
--- /dev/null
+++ b/chromium/ui/native_theme/native_theme_win_unittest.cc
@@ -0,0 +1,15 @@
+// Copyright (c) 2012 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 "ui/native_theme/native_theme_win.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ui {
+
+TEST(NativeThemeTest, Init) {
+ ASSERT_TRUE(NativeThemeWin::instance() != NULL);
+}
+
+} // namespace ui