From 86281adbe4f9e4c2eae038cf15706bf0d105483e Mon Sep 17 00:00:00 2001 From: "brettw@chromium.org" Date: Wed, 7 Oct 2009 20:07:17 +0000 Subject: Move classes depending on Skia out of base/gfx and into app/gfx. Rename native_theme to native_theme_win since its Windows-specific. BUG=none TEST=none Review URL: http://codereview.chromium.org/259047 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@28300 0039d316-1c4b-4281-b951-d872f2087c98 --- app/app.gyp | 16 + app/gfx/blit.cc | 101 +++++ app/gfx/blit.h | 47 +++ app/gfx/gdi_util.cc | 79 ++++ app/gfx/gdi_util.h | 36 ++ app/gfx/gtk_util.cc | 86 +++++ app/gfx/gtk_util.h | 67 ++++ app/gfx/native_theme_win.cc | 710 +++++++++++++++++++++++++++++++++++ app/gfx/native_theme_win.h | 296 +++++++++++++++ app/gfx/native_theme_win_unittest.cc | 11 + app/resource_bundle_linux.cc | 2 +- app/win_util.cc | 2 +- 12 files changed, 1451 insertions(+), 2 deletions(-) create mode 100644 app/gfx/blit.cc create mode 100644 app/gfx/blit.h create mode 100644 app/gfx/gdi_util.cc create mode 100644 app/gfx/gdi_util.h create mode 100644 app/gfx/gtk_util.cc create mode 100644 app/gfx/gtk_util.h create mode 100644 app/gfx/native_theme_win.cc create mode 100644 app/gfx/native_theme_win.h create mode 100644 app/gfx/native_theme_win_unittest.cc (limited to 'app') diff --git a/app/app.gyp b/app/app.gyp index 1ae1335..c35ba0e 100644 --- a/app/app.gyp +++ b/app/app.gyp @@ -81,6 +81,8 @@ 'drag_drop_types_gtk.cc', 'drag_drop_types_win.cc', 'drag_drop_types.h', + 'gfx/blit.cc', + 'gfx/blit.h', 'gfx/canvas.cc', 'gfx/canvas.h', 'gfx/canvas_linux.cc', @@ -98,9 +100,15 @@ 'gfx/color_utils.cc', 'gfx/color_utils.h', 'gfx/favicon_size.h', + 'gfx/gdi_util.cc', + 'gfx/gdi_util.h', + 'gfx/gtk_util.cc', + 'gfx/gtk_util.h', 'gfx/icon_util.cc', 'gfx/icon_util.h', 'gfx/insets.h', + 'gfx/native_theme_win.cc', + 'gfx/native_theme_win.h', 'gfx/path_gtk.cc', 'gfx/path_win.cc', 'gfx/path.h', @@ -190,13 +198,19 @@ ['OS!="win"', { 'sources!': [ 'drag_drop_types.h', + 'gfx/gdi_util.cc', + 'gfx/gdi_util.h', 'gfx/icon_util.cc', 'gfx/icon_util.h', + 'gfx/native_theme_win.cc', + 'gfx/native_theme_win.h', 'os_exchange_data.cc', ], }], ['OS!="linux"', { 'sources!': [ + 'gfx/gtk_util.cc', + 'gfx/gtk_util.h', 'gtk_dnd_util.cc', 'gtk_dnd_util.h', ], @@ -228,6 +242,7 @@ 'gfx/color_utils_unittest.cc', 'gfx/font_unittest.cc', 'gfx/icon_util_unittest.cc', + 'gfx/native_theme_win_unittest.cc', 'gfx/skbitmap_operations_unittest.cc', 'gfx/text_elider_unittest.cc', 'l10n_util_mac_unittest.mm', @@ -254,6 +269,7 @@ ['OS!="win"', { 'sources!': [ 'gfx/icon_util_unittest.cc', + 'gfx/native_theme_win_unittest.cc', 'os_exchange_data_win_unittest.cc', 'win_util_unittest.cc', ], diff --git a/app/gfx/blit.cc b/app/gfx/blit.cc new file mode 100644 index 0000000..359e142 --- /dev/null +++ b/app/gfx/blit.cc @@ -0,0 +1,101 @@ +// Copyright (c) 2009 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 "app/gfx/blit.h" + +#if defined(OS_LINUX) +#include +#endif + +#include "base/gfx/point.h" +#include "base/gfx/rect.h" +#if defined(OS_MACOSX) +#include "base/scoped_cftyperef.h" +#endif +#include "skia/ext/platform_canvas.h" +#include "skia/ext/platform_device.h" + +namespace gfx { + +void BlitContextToContext(NativeDrawingContext dst_context, + const Rect& dst_rect, + NativeDrawingContext src_context, + const Point& src_origin) { +#if defined(OS_WIN) + BitBlt(dst_context, dst_rect.x(), dst_rect.y(), + dst_rect.width(), dst_rect.height(), + src_context, src_origin.x(), src_origin.y(), SRCCOPY); +#elif defined(OS_MACOSX) + // Only translations and/or vertical flips in the source context are + // supported; more complex source context transforms will be ignored. + + // If there is a translation on the source context, we need to account for + // it ourselves since CGBitmapContextCreateImage will bypass it. + Rect src_rect(src_origin, dst_rect.size()); + CGAffineTransform transform = CGContextGetCTM(src_context); + bool flipped = fabs(transform.d + 1) < 0.0001; + CGFloat delta_y = flipped ? CGBitmapContextGetHeight(src_context) - + transform.ty + : transform.ty; + src_rect.Offset(transform.tx, delta_y); + + scoped_cftyperef + src_image(CGBitmapContextCreateImage(src_context)); + scoped_cftyperef src_sub_image( + CGImageCreateWithImageInRect(src_image, src_rect.ToCGRect())); + CGContextDrawImage(dst_context, dst_rect.ToCGRect(), src_sub_image); +#elif defined(OS_LINUX) + // Only translations in the source context are supported; more complex + // source context transforms will be ignored. + cairo_save(dst_context); + double surface_x = src_origin.x(); + double surface_y = src_origin.y(); + cairo_user_to_device(src_context, &surface_x, &surface_y); + cairo_set_source_surface(dst_context, cairo_get_target(src_context), + dst_rect.x()-surface_x, dst_rect.y()-surface_y); + cairo_rectangle(dst_context, dst_rect.x(), dst_rect.y(), + dst_rect.width(), dst_rect.height()); + cairo_clip(dst_context); + cairo_paint(dst_context); + cairo_restore(dst_context); +#endif +} + +static NativeDrawingContext GetContextFromCanvas( + skia::PlatformCanvas *canvas) { + skia::PlatformDevice& device = canvas->getTopPlatformDevice(); +#if defined(OS_WIN) + return device.getBitmapDC(); +#elif defined(OS_MACOSX) + return device.GetBitmapContext(); +#elif defined(OS_LINUX) + return device.beginPlatformPaint(); +#endif +} + +void BlitContextToCanvas(skia::PlatformCanvas *dst_canvas, + const Rect& dst_rect, + NativeDrawingContext src_context, + const Point& src_origin) { + BlitContextToContext(GetContextFromCanvas(dst_canvas), dst_rect, + src_context, src_origin); +} + +void BlitCanvasToContext(NativeDrawingContext dst_context, + const Rect& dst_rect, + skia::PlatformCanvas *src_canvas, + const Point& src_origin) { + BlitContextToContext(dst_context, dst_rect, + GetContextFromCanvas(src_canvas), src_origin); +} + +void BlitCanvasToCanvas(skia::PlatformCanvas *dst_canvas, + const Rect& dst_rect, + skia::PlatformCanvas *src_canvas, + const Point& src_origin) { + BlitContextToContext(GetContextFromCanvas(dst_canvas), dst_rect, + GetContextFromCanvas(src_canvas), src_origin); +} + +} // namespace gfx diff --git a/app/gfx/blit.h b/app/gfx/blit.h new file mode 100644 index 0000000..f2460ca --- /dev/null +++ b/app/gfx/blit.h @@ -0,0 +1,47 @@ +// Copyright (c) 2009 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 APP_GFX_BLIT_H_ +#define APP_GFX_BLIT_H_ + +#include "base/gfx/native_widget_types.h" +#include "base/gfx/point.h" +#include "base/gfx/rect.h" + +namespace skia { +class PlatformCanvas; +} // namespace skia + +namespace gfx { + +class Point; +class Rect; + +// Blits a rectangle from the source context into the destination context. +void BlitContextToContext(NativeDrawingContext dst_context, + const Rect& dst_rect, + NativeDrawingContext src_context, + const Point& src_origin); + +// Blits a rectangle from the source context into the destination canvas. +void BlitContextToCanvas(skia::PlatformCanvas *dst_canvas, + const Rect& dst_rect, + NativeDrawingContext src_context, + const Point& src_origin); + +// Blits a rectangle from the source canvas into the destination context. +void BlitCanvasToContext(NativeDrawingContext dst_context, + const Rect& dst_rect, + skia::PlatformCanvas *src_canvas, + const Point& src_origin); + +// Blits a rectangle from the source canvas into the destination canvas. +void BlitCanvasToCanvas(skia::PlatformCanvas *dst_canvas, + const Rect& dst_rect, + skia::PlatformCanvas *src_canvas, + const Point& src_origin); + +} // namespace gfx + +#endif // APP_GFX_BLIT_H_ diff --git a/app/gfx/gdi_util.cc b/app/gfx/gdi_util.cc new file mode 100644 index 0000000..2711280 --- /dev/null +++ b/app/gfx/gdi_util.cc @@ -0,0 +1,79 @@ +// Copyright (c) 2009 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 "app/gfx/gdi_util.h" + +namespace gfx { + +void CreateBitmapHeader(int width, int height, BITMAPINFOHEADER* hdr) { + CreateBitmapHeaderWithColorDepth(width, height, 32, hdr); +} + +void CreateBitmapHeaderWithColorDepth(int width, int height, int color_depth, + BITMAPINFOHEADER* hdr) { + // These values are shared with gfx::PlatformDevice + hdr->biSize = sizeof(BITMAPINFOHEADER); + hdr->biWidth = width; + hdr->biHeight = -height; // minus means top-down bitmap + hdr->biPlanes = 1; + hdr->biBitCount = color_depth; + hdr->biCompression = BI_RGB; // no compression + hdr->biSizeImage = 0; + hdr->biXPelsPerMeter = 1; + hdr->biYPelsPerMeter = 1; + hdr->biClrUsed = 0; + hdr->biClrImportant = 0; +} + + +void CreateBitmapV4Header(int width, int height, BITMAPV4HEADER* hdr) { + // Because bmp v4 header is just an extension, we just create a v3 header and + // copy the bits over to the v4 header. + BITMAPINFOHEADER header_v3; + CreateBitmapHeader(width, height, &header_v3); + memset(hdr, 0, sizeof(BITMAPV4HEADER)); + memcpy(hdr, &header_v3, sizeof(BITMAPINFOHEADER)); + + // Correct the size of the header and fill in the mask values. + hdr->bV4Size = sizeof(BITMAPV4HEADER); + hdr->bV4RedMask = 0x00ff0000; + hdr->bV4GreenMask = 0x0000ff00; + hdr->bV4BlueMask = 0x000000ff; + hdr->bV4AlphaMask = 0xff000000; +} + +// Creates a monochrome bitmap header. +void CreateMonochromeBitmapHeader(int width, + int height, + BITMAPINFOHEADER* hdr) { + hdr->biSize = sizeof(BITMAPINFOHEADER); + hdr->biWidth = width; + hdr->biHeight = -height; + hdr->biPlanes = 1; + hdr->biBitCount = 1; + hdr->biCompression = BI_RGB; + hdr->biSizeImage = 0; + hdr->biXPelsPerMeter = 1; + hdr->biYPelsPerMeter = 1; + hdr->biClrUsed = 0; + hdr->biClrImportant = 0; +} + +void SubtractRectanglesFromRegion(HRGN hrgn, + const std::vector& cutouts) { + if (cutouts.size()) { + HRGN cutout = ::CreateRectRgn(0, 0, 0, 0); + for (size_t i = 0; i < cutouts.size(); i++) { + ::SetRectRgn(cutout, + cutouts[i].x(), + cutouts[i].y(), + cutouts[i].right(), + cutouts[i].bottom()); + ::CombineRgn(hrgn, hrgn, cutout, RGN_DIFF); + } + ::DeleteObject(cutout); + } +} + +} // namespace gfx diff --git a/app/gfx/gdi_util.h b/app/gfx/gdi_util.h new file mode 100644 index 0000000..c458d9f --- /dev/null +++ b/app/gfx/gdi_util.h @@ -0,0 +1,36 @@ +// Copyright (c) 2009 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 APP_GFX_GDI_UTIL_H_ +#define APP_GFX_GDI_UTIL_H_ + +#include +#include +#include "base/gfx/rect.h" + +namespace gfx { + +// Creates a BITMAPINFOHEADER structure given the bitmap's size. +void CreateBitmapHeader(int width, int height, BITMAPINFOHEADER* hdr); + +// Creates a BITMAPINFOHEADER structure given the bitmap's size and +// color depth in bits per pixel. +void CreateBitmapHeaderWithColorDepth(int width, int height, int color_depth, + BITMAPINFOHEADER* hdr); + +// Creates a BITMAPV4HEADER structure given the bitmap's size. You probably +// only need to use BMP V4 if you need transparency (alpha channel). This +// function sets the AlphaMask to 0xff000000. +void CreateBitmapV4Header(int width, int height, BITMAPV4HEADER* hdr); + +// Creates a monochrome bitmap header. +void CreateMonochromeBitmapHeader(int width, int height, BITMAPINFOHEADER* hdr); + +// Modify the given hrgn by subtracting the given rectangles. +void SubtractRectanglesFromRegion(HRGN hrgn, + const std::vector& cutouts); + +} // namespace gfx + +#endif // APP_GFX_GDI_UTIL_H_ diff --git a/app/gfx/gtk_util.cc b/app/gfx/gtk_util.cc new file mode 100644 index 0000000..03d0504 --- /dev/null +++ b/app/gfx/gtk_util.cc @@ -0,0 +1,86 @@ +// Copyright (c) 2009 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 "app/gfx/gtk_util.h" + +#include +#include +#include + +#include "base/basictypes.h" +#include "base/gfx/rect.h" +#include "base/linux_util.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "third_party/skia/include/core/SkUnPreMultiply.h" + +namespace { + +void FreePixels(guchar* pixels, gpointer data) { + free(data); +} + +} // namespace + +namespace gfx { + +const GdkColor kGdkWhite = GDK_COLOR_RGB(0xff, 0xff, 0xff); +const GdkColor kGdkBlack = GDK_COLOR_RGB(0x00, 0x00, 0x00); +const GdkColor kGdkGreen = GDK_COLOR_RGB(0x00, 0xff, 0x00); + +GdkPixbuf* GdkPixbufFromSkBitmap(const SkBitmap* bitmap) { + bitmap->lockPixels(); + int width = bitmap->width(); + int height = bitmap->height(); + int stride = bitmap->rowBytes(); + + // SkBitmaps are premultiplied, we need to unpremultiply them. + const int kBytesPerPixel = 4; + uint8* divided = static_cast(malloc(height * stride)); + + for (int y = 0, i = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + uint32 pixel = bitmap->getAddr32(0, y)[x]; + + int alpha = SkColorGetA(pixel); + if (alpha != 0 && alpha != 255) { + SkColor unmultiplied = SkUnPreMultiply::PMColorToColor(pixel); + divided[i + 0] = SkColorGetR(unmultiplied); + divided[i + 1] = SkColorGetG(unmultiplied); + divided[i + 2] = SkColorGetB(unmultiplied); + divided[i + 3] = alpha; + } else { + divided[i + 0] = SkColorGetR(pixel); + divided[i + 1] = SkColorGetG(pixel); + divided[i + 2] = SkColorGetB(pixel); + divided[i + 3] = alpha; + } + i += kBytesPerPixel; + } + } + + // This pixbuf takes ownership of our malloc()ed data and will + // free it for us when it is destroyed. + GdkPixbuf* pixbuf = gdk_pixbuf_new_from_data( + divided, + GDK_COLORSPACE_RGB, // The only colorspace gtk supports. + true, // There is an alpha channel. + 8, + width, height, stride, &FreePixels, divided); + + bitmap->unlockPixels(); + return pixbuf; +} + +void SubtractRectanglesFromRegion(GdkRegion* region, + const std::vector& cutouts) { + for (size_t i = 0; i < cutouts.size(); ++i) { + GdkRectangle rect = cutouts[i].ToGdkRectangle(); + GdkRegion* rect_region = gdk_region_rectangle(&rect); + gdk_region_subtract(region, rect_region); + // TODO(deanm): It would be nice to be able to reuse the GdkRegion here. + gdk_region_destroy(rect_region); + } +} + +} // namespace gfx diff --git a/app/gfx/gtk_util.h b/app/gfx/gtk_util.h new file mode 100644 index 0000000..523f7bf --- /dev/null +++ b/app/gfx/gtk_util.h @@ -0,0 +1,67 @@ +// Copyright (c) 2009 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 BASE_GFX_GTK_UTIL_H_ +#define BASE_GFX_GTK_UTIL_H_ + +#include +#include + +#include + +#include "base/scoped_ptr.h" + +typedef struct _GdkColor GdkColor; +typedef struct _GdkPixbuf GdkPixbuf; +typedef struct _GdkRegion GdkRegion; + +class SkBitmap; + +const int kSkiaToGDKMultiplier = 257; + +// Define a macro for creating GdkColors from RGB values. This is a macro to +// allow static construction of literals, etc. Use this like: +// GdkColor white = GDK_COLOR_RGB(0xff, 0xff, 0xff); +#define GDK_COLOR_RGB(r, g, b) {0, r * kSkiaToGDKMultiplier, \ + g * kSkiaToGDKMultiplier, b * kSkiaToGDKMultiplier} + +namespace gfx { + +class Rect; + +extern const GdkColor kGdkWhite; +extern const GdkColor kGdkBlack; +extern const GdkColor kGdkGreen; + +// Convert and copy a SkBitmap to a GdkPixbuf. NOTE: this uses BGRAToRGBA, so +// it is an expensive operation. +GdkPixbuf* GdkPixbufFromSkBitmap(const SkBitmap* bitmap); + +// Modify the given region by subtracting the given rectangles. +void SubtractRectanglesFromRegion(GdkRegion* region, + const std::vector& cutouts); + +} // namespace gfx + +namespace { +// A helper class that will g_object_unref |p| when it goes out of scope. +// This never adds a ref, it only unrefs. +template +struct GObjectUnrefer { + void operator()(Type* ptr) const { + if (ptr) + g_object_unref(ptr); + } +}; +} // namespace + +// It's not legal C++ to have a templatized typedefs, so we wrap it in a +// struct. When using this, you need to include ::Type. E.g., +// ScopedGObject::Type loader(gdk_pixbuf_loader_new()); +template +struct ScopedGObject { + typedef scoped_ptr_malloc > Type; +}; + +#endif // BASE_GFX_GTK_UTIL_H_ diff --git a/app/gfx/native_theme_win.cc b/app/gfx/native_theme_win.cc new file mode 100644 index 0000000..594042b --- /dev/null +++ b/app/gfx/native_theme_win.cc @@ -0,0 +1,710 @@ +// Copyright (c) 2009 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 "app/gfx/native_theme_win.h" + +#include +#include +#include +#include + +#include "app/gfx/gdi_util.h" +#include "base/gfx/rect.h" +#include "base/logging.h" +#include "base/scoped_handle.h" +#include "skia/ext/platform_canvas.h" +#include "skia/ext/skia_utils_win.h" +#include "third_party/skia/include/core/SkShader.h" + +namespace { + +void SetCheckerboardShader(SkPaint* paint, const RECT& align_rect) { + // Create a 2x2 checkerboard pattern using the 3D face and highlight colors. + SkColor face = skia::COLORREFToSkColor(GetSysColor(COLOR_3DFACE)); + SkColor highlight = skia::COLORREFToSkColor(GetSysColor(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()); + SkShader* shader = 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)->safeUnref(); +} + +} // namespace + +namespace gfx { + +/* static */ +const NativeTheme* NativeTheme::instance() { + // The global NativeTheme instance. + static const NativeTheme s_native_theme; + return &s_native_theme; +} + +NativeTheme::NativeTheme() + : 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) { + if (theme_dll_) { + draw_theme_ = reinterpret_cast( + GetProcAddress(theme_dll_, "DrawThemeBackground")); + draw_theme_ex_ = reinterpret_cast( + GetProcAddress(theme_dll_, "DrawThemeBackgroundEx")); + get_theme_color_ = reinterpret_cast( + GetProcAddress(theme_dll_, "GetThemeColor")); + get_theme_content_rect_ = reinterpret_cast( + GetProcAddress(theme_dll_, "GetThemeBackgroundContentRect")); + get_theme_part_size_ = reinterpret_cast( + GetProcAddress(theme_dll_, "GetThemePartSize")); + open_theme_ = reinterpret_cast( + GetProcAddress(theme_dll_, "OpenThemeData")); + close_theme_ = reinterpret_cast( + GetProcAddress(theme_dll_, "CloseThemeData")); + set_theme_properties_ = reinterpret_cast( + GetProcAddress(theme_dll_, "SetThemeAppProperties")); + is_theme_active_ = reinterpret_cast( + GetProcAddress(theme_dll_, "IsThemeActive")); + get_theme_int_ = reinterpret_cast( + GetProcAddress(theme_dll_, "GetThemeInt")); + } + memset(theme_handles_, 0, sizeof(theme_handles_)); +} + +NativeTheme::~NativeTheme() { + if (theme_dll_) { + // todo (cpu): fix this soon. + // CloseHandles(); + FreeLibrary(theme_dll_); + } +} + +HRESULT NativeTheme::PaintButton(HDC hdc, + int part_id, + int state_id, + int classic_state, + RECT* rect) const { + HANDLE handle = GetThemeHandle(BUTTON); + if (handle && draw_theme_) + return draw_theme_(handle, hdc, part_id, state_id, rect, NULL); + + // 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); + } + + return S_OK; +} + +HRESULT NativeTheme::PaintDialogBackground(HDC hdc, bool active, + RECT* rect) const { + HANDLE handle = GetThemeHandle(WINDOW); + if (handle && draw_theme_) { + return draw_theme_(handle, hdc, WP_DIALOG, + active ? FS_ACTIVE : FS_INACTIVE, rect, NULL); + } + + // Classic just renders a flat color background. + FillRect(hdc, rect, reinterpret_cast(COLOR_3DFACE + 1)); + return S_OK; +} + +HRESULT NativeTheme::PaintListBackground(HDC hdc, + bool enabled, + RECT* rect) const { + HANDLE handle = GetThemeHandle(LIST); + if (handle && draw_theme_) + return draw_theme_(handle, hdc, 1, TS_NORMAL, rect, NULL); + + // Draw it manually. + HBRUSH bg_brush = GetSysColorBrush(COLOR_WINDOW); + FillRect(hdc, rect, bg_brush); + DrawEdge(hdc, rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST); + return S_OK; +} + +HRESULT NativeTheme::PaintMenuArrow(ThemeName theme, + HDC hdc, + int part_id, + int state_id, + RECT* rect, + MenuArrowDirection arrow_direction, + bool is_highlighted) const { + HANDLE handle = GetThemeHandle(MENU); + if (handle && draw_theme_) { + if (arrow_direction == RIGHT_POINTING_ARROW) { + return draw_theme_(handle, hdc, part_id, state_id, rect, 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. + Rect r(*rect); + ScopedHDC mem_dc(CreateCompatibleDC(hdc)); + ScopedBitmap mem_bitmap(CreateCompatibleBitmap(hdc, r.width(), + r.height())); + HGDIOBJ old_bitmap = SelectObject(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, part_id, + 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); + SelectObject(mem_dc, old_bitmap); + 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 state; + if (arrow_direction == RIGHT_POINTING_ARROW) + state = DFCS_MENUARROW; + else + state = DFCS_MENUARROWRIGHT; + return PaintFrameControl(hdc, rect, DFC_MENU, state, is_highlighted); +} + +HRESULT NativeTheme::PaintMenuBackground(ThemeName theme, + HDC hdc, + int part_id, + int state_id, + RECT* rect) const { + HANDLE handle = GetThemeHandle(MENU); + if (handle && draw_theme_) { + HRESULT result = draw_theme_(handle, hdc, part_id, state_id, rect, NULL); + FrameRect(hdc, rect, GetSysColorBrush(COLOR_3DSHADOW)); + return result; + } + + FillRect(hdc, rect, GetSysColorBrush(COLOR_MENU)); + DrawEdge(hdc, rect, EDGE_RAISED, BF_RECT); + return S_OK; +} + +HRESULT NativeTheme::PaintMenuCheckBackground(ThemeName theme, + HDC hdc, + int part_id, + int state_id, + RECT* rect) const { + HANDLE handle = GetThemeHandle(MENU); + if (handle && draw_theme_) + return draw_theme_(handle, hdc, part_id, state_id, rect, NULL); + // Nothing to do for background. + return S_OK; +} + +HRESULT NativeTheme::PaintMenuCheck(ThemeName theme, + HDC hdc, + int part_id, + int state_id, + RECT* rect, + bool is_highlighted) const { + HANDLE handle = GetThemeHandle(MENU); + if (handle && draw_theme_) { + return draw_theme_(handle, hdc, part_id, state_id, rect, NULL); + } + return PaintFrameControl(hdc, rect, DFC_MENU, DFCS_MENUCHECK, is_highlighted); +} + +HRESULT NativeTheme::PaintMenuGutter(HDC hdc, + int part_id, + int state_id, + RECT* rect) const { + HANDLE handle = GetThemeHandle(MENU); + if (handle && draw_theme_) + return draw_theme_(handle, hdc, part_id, state_id, rect, NULL); + return E_NOTIMPL; +} + +HRESULT NativeTheme::PaintMenuItemBackground(ThemeName theme, + HDC hdc, + int part_id, + int state_id, + bool selected, + RECT* rect) const { + HANDLE handle = GetThemeHandle(MENU); + if (handle && draw_theme_) + return draw_theme_(handle, hdc, part_id, state_id, rect, NULL); + if (selected) + FillRect(hdc, rect, GetSysColorBrush(COLOR_HIGHLIGHT)); + return S_OK; +} + +HRESULT NativeTheme::PaintMenuList(HDC hdc, + int part_id, + int state_id, + int classic_state, + RECT* rect) const { + HANDLE handle = GetThemeHandle(MENULIST); + if (handle && draw_theme_) + return draw_theme_(handle, hdc, part_id, state_id, rect, NULL); + + // Draw it manually. + DrawFrameControl(hdc, rect, DFC_SCROLL, DFCS_SCROLLCOMBOBOX | classic_state); + return S_OK; +} + +HRESULT NativeTheme::PaintMenuSeparator(HDC hdc, + int part_id, + int state_id, + RECT* rect) const { + HANDLE handle = GetThemeHandle(MENU); + if (handle && draw_theme_) + return draw_theme_(handle, hdc, part_id, state_id, rect, NULL); + DrawEdge(hdc, rect, EDGE_ETCHED, BF_TOP); + return S_OK; +} + +HRESULT NativeTheme::PaintScrollbarArrow(HDC hdc, + int state_id, + int classic_state, + RECT* rect) const { + HANDLE handle = GetThemeHandle(SCROLLBAR); + if (handle && draw_theme_) + return draw_theme_(handle, hdc, SBP_ARROWBTN, state_id, rect, NULL); + + // Draw it manually. + DrawFrameControl(hdc, rect, DFC_SCROLL, classic_state); + return S_OK; +} + +HRESULT NativeTheme::PaintScrollbarTrack( + HDC hdc, + int part_id, + int state_id, + int classic_state, + RECT* target_rect, + RECT* align_rect, + skia::PlatformCanvas* canvas) const { + HANDLE handle = GetThemeHandle(SCROLLBAR); + if (handle && draw_theme_) + return draw_theme_(handle, hdc, part_id, state_id, target_rect, NULL); + + // Draw it manually. + const DWORD colorScrollbar = GetSysColor(COLOR_SCROLLBAR); + const DWORD color3DFace = GetSysColor(COLOR_3DFACE); + if ((colorScrollbar != color3DFace) && + (colorScrollbar != GetSysColor(COLOR_WINDOW))) { + FillRect(hdc, target_rect, reinterpret_cast(COLOR_SCROLLBAR + 1)); + } else { + SkPaint paint; + SetCheckerboardShader(&paint, *align_rect); + canvas->drawIRect(skia::RECTToSkIRect(*target_rect), paint); + } + if (classic_state & DFCS_PUSHED) + InvertRect(hdc, target_rect); + return S_OK; +} + +HRESULT NativeTheme::PaintScrollbarThumb(HDC hdc, + int part_id, + int state_id, + int classic_state, + RECT* rect) const { + HANDLE handle = GetThemeHandle(SCROLLBAR); + if (handle && draw_theme_) + return draw_theme_(handle, hdc, part_id, state_id, rect, NULL); + + // Draw it manually. + if ((part_id == SBP_THUMBBTNHORZ) || (part_id == SBP_THUMBBTNVERT)) + DrawEdge(hdc, rect, EDGE_RAISED, BF_RECT | BF_MIDDLE); + // Classic mode doesn't have a gripper. + return S_OK; +} + +HRESULT NativeTheme::PaintStatusGripper(HDC hdc, + int part_id, + int state_id, + int classic_state, + RECT* rect) const { + HANDLE handle = GetThemeHandle(STATUS); + 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, NULL); + } + + // Draw a windows classic scrollbar gripper. + DrawFrameControl(hdc, rect, DFC_SCROLL, DFCS_SCROLLSIZEGRIP); + return S_OK; +} + +HRESULT NativeTheme::PaintTabPanelBackground(HDC hdc, RECT* rect) const { + HANDLE handle = GetThemeHandle(TAB); + if (handle && draw_theme_) + return draw_theme_(handle, hdc, TABP_BODY, 0, rect, NULL); + + // Classic just renders a flat color background. + FillRect(hdc, rect, reinterpret_cast(COLOR_3DFACE + 1)); + return S_OK; +} + +HRESULT NativeTheme::PaintTrackbar(HDC hdc, + int part_id, + int state_id, + int classic_state, + RECT* rect, + skia::PlatformCanvas* canvas) const { + // 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 channel_rect = *rect; + 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, EDGE_RAISED, BF_RECT | BF_SOFT | BF_MIDDLE); + } else { + // Split rect into top and bottom pieces. + RECT top_section = *rect; + RECT bottom_section = *rect; + 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 (classic_state & DFCS_PUSHED) { + SkPaint paint; + SetCheckerboardShader(&paint, *rect); + + // 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 NativeTheme::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 DTBGOPTS omit_border_options = { + sizeof(DTBGOPTS), + DTBG_OMITBORDER, + {0,0,0,0} + }; + 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(COLOR_BTNFACE + 1) : bg_brush); + } + hr = S_OK; + } + DeleteObject(bg_brush); + return hr; +} + +bool NativeTheme::IsThemingActive() const { + if (is_theme_active_) + return !!is_theme_active_(); + return false; +} + +HRESULT NativeTheme::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 NativeTheme::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 NativeTheme::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 = skia::COLORREFToSkColor(GetSysColor(default_sys_color)); + return color; +} + +HRESULT NativeTheme::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; +} + +Size NativeTheme::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 Size(border, border); + else + return Size(GetSystemMetrics(SM_CXEDGE), GetSystemMetrics(SM_CYEDGE)); +} + + +void NativeTheme::DisableTheming() const { + if (!set_theme_properties_) + return; + set_theme_properties_(0); +} + +HRESULT NativeTheme::PaintFrameControl(HDC hdc, + RECT* rect, + UINT type, + UINT state, + bool is_highlighted) const { + const int width = rect->right - rect->left; + const int height = rect->bottom - rect->top; + + // DrawFrameControl for menu arrow/check wants a monochrome bitmap. + ScopedBitmap mask_bitmap(CreateBitmap(width, height, 1, 1, NULL)); + + if (mask_bitmap == NULL) + return E_OUTOFMEMORY; + + ScopedHDC bitmap_dc(CreateCompatibleDC(NULL)); + HGDIOBJ org_bitmap = SelectObject(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. + COLORREF old_bg_color = + SetBkColor(hdc, + GetSysColor(is_highlighted ? COLOR_HIGHLIGHT : COLOR_MENU)); + COLORREF old_text_color = + SetTextColor(hdc, + GetSysColor(is_highlighted ? COLOR_HIGHLIGHTTEXT : + COLOR_MENUTEXT)); + BitBlt(hdc, rect->left, rect->top, width, height, bitmap_dc, 0, 0, SRCCOPY); + SetBkColor(hdc, old_bg_color); + SetTextColor(hdc, old_text_color); + + SelectObject(bitmap_dc, org_bitmap); + + return S_OK; +} + +void NativeTheme::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; + } +} + +HANDLE NativeTheme::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; + default: + NOTREACHED(); + } + theme_handles_[theme_name] = handle; + return handle; +} + +} // namespace gfx diff --git a/app/gfx/native_theme_win.h b/app/gfx/native_theme_win.h new file mode 100644 index 0000000..62415b4 --- /dev/null +++ b/app/gfx/native_theme_win.h @@ -0,0 +1,296 @@ +// Copyright (c) 2009 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. +// +// A wrapper class for working with custom XP/Vista themes provided in +// uxtheme.dll. This is a singleton class that can be grabbed using +// NativeTheme::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 + +#ifndef APP_GFX_NATIVE_THEME_WIN_H_ +#define APP_GFX_NATIVE_THEME_WIN_H_ + +#include +#include +#include "base/basictypes.h" +#include "base/gfx/size.h" +#include "third_party/skia/include/core/SkColor.h" + +namespace skia { +class PlatformCanvas; +} // namespace skia + +namespace gfx { + +// TODO: Define class member enums to replace part_id and state_id parameters +// that are currently defined in . Afterward, classic_state should +// be removed and class users wouldn't need to include anymore. +// This would enable HOT state on non-themed UI (like when RDP'ing) and would +// simplify usage. +// TODO: This class should probably be changed to be platform independent at +// the same time. +class NativeTheme { + public: + enum ThemeName { + BUTTON, + LIST, + MENU, + MENULIST, + SCROLLBAR, + STATUS, + TAB, + TEXTFIELD, + TRACKBAR, + WINDOW, + LAST + }; + + // This enumeration is used within PaintMenuArrow in order to indicate the + // direction the menu arrow should point to. + enum MenuArrowDirection { + LEFT_POINTING_ARROW, + RIGHT_POINTING_ARROW + }; + + 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); + + HRESULT PaintButton(HDC hdc, + int part_id, + int state_id, + int classic_state, + RECT* rect) const; + + HRESULT PaintDialogBackground(HDC dc, bool active, RECT* rect) const; + + HRESULT PaintListBackground(HDC dc, bool enabled, 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(ThemeName theme, + HDC hdc, + int part_id, + int state_id, + RECT* rect, + MenuArrowDirection arrow_direction, + bool is_highlighted) const; + + HRESULT PaintMenuBackground(ThemeName theme, + HDC hdc, + int part_id, + int state_id, + RECT* rect) const; + + HRESULT PaintMenuCheck(ThemeName theme, + HDC hdc, + int part_id, + int state_id, + RECT* rect, + bool is_highlighted) const; + + HRESULT PaintMenuCheckBackground(ThemeName theme, + HDC hdc, + int part_id, + int state_id, + RECT* rect) const; + + HRESULT PaintMenuGutter(HDC hdc, + int part_id, + int state_id, + RECT* rect) const; + + HRESULT PaintMenuItemBackground(ThemeName theme, + HDC hdc, + int part_id, + int state_id, + bool selected, + RECT* rect) const; + + HRESULT PaintMenuList(HDC hdc, + int part_id, + int state_id, + int classic_state, + RECT* rect) const; + + HRESULT PaintMenuSeparator(HDC hdc, + int part_id, + int state_id, + RECT* rect) const; + + // Paints a scrollbar arrow. |classic_state| should have the appropriate + // classic part number ORed in already. + HRESULT PaintScrollbarArrow(HDC hdc, + int state_id, + int classic_state, + RECT* rect) const; + + // 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(HDC hdc, + int part_id, + int state_id, + int classic_state, + RECT* target_rect, + RECT* align_rect, + skia::PlatformCanvas* canvas) const; + + // Paints a scrollbar thumb or gripper. + HRESULT PaintScrollbarThumb(HDC hdc, + int part_id, + int state_id, + int classic_state, + RECT* rect) const; + + HRESULT PaintStatusGripper(HDC hdc, + int part_id, + int state_id, + int classic_state, + RECT* rect) const; + + HRESULT PaintTabPanelBackground(HDC dc, RECT* rect) const; + + 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; + + HRESULT PaintTrackbar(HDC hdc, + int part_id, + int state_id, + int classic_state, + RECT* rect, + skia::PlatformCanvas* canvas) const; + + bool IsThemingActive() const; + + HRESULT GetThemePartSize(ThemeName themeName, + HDC hdc, + int part_id, + int state_id, + RECT* rect, + int ts, + SIZE* size) 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; + + HRESULT GetThemeInt(ThemeName theme, + int part_id, + int state_id, + int prop_id, + int *result) 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. + 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; + + // Gets our singleton instance. + static const NativeTheme* instance(); + + private: + NativeTheme(); + ~NativeTheme(); + + HRESULT PaintFrameControl(HDC hdc, + RECT* rect, + UINT type, + UINT state, + bool is_highlighted) const; + + // Returns a handle to the theme data. + HANDLE GetThemeHandle(ThemeName theme_name) const; + + // 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]; + + DISALLOW_COPY_AND_ASSIGN(NativeTheme); +}; + +} // namespace gfx + +#endif // APP_GFX_NATIVE_THEME_WIN_H_ diff --git a/app/gfx/native_theme_win_unittest.cc b/app/gfx/native_theme_win_unittest.cc new file mode 100644 index 0000000..fea87eab --- /dev/null +++ b/app/gfx/native_theme_win_unittest.cc @@ -0,0 +1,11 @@ +// Copyright (c) 2009 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 "app/gfx/native_theme_win.h" + +#include "testing/gtest/include/gtest/gtest.h" + +TEST(NativeThemeTest, Init) { + ASSERT_TRUE(gfx::NativeTheme::instance() != NULL); +} diff --git a/app/resource_bundle_linux.cc b/app/resource_bundle_linux.cc index 85f00c6..911cc03 100644 --- a/app/resource_bundle_linux.cc +++ b/app/resource_bundle_linux.cc @@ -8,12 +8,12 @@ #include "app/app_paths.h" #include "app/gfx/font.h" +#include "app/gfx/gtk_util.h" #include "app/l10n_util.h" #include "base/base_paths.h" #include "base/data_pack.h" #include "base/file_path.h" #include "base/file_util.h" -#include "base/gfx/gtk_util.h" #include "base/logging.h" #include "base/path_service.h" #include "base/string_piece.h" diff --git a/app/win_util.cc b/app/win_util.cc index 0e985ce..b3b66c2 100644 --- a/app/win_util.cc +++ b/app/win_util.cc @@ -13,12 +13,12 @@ #include #include "app/gfx/codec/png_codec.h" +#include "app/gfx/gdi_util.h" #include "app/l10n_util.h" #include "app/l10n_util_win.h" #include "base/base_switches.h" #include "base/command_line.h" #include "base/file_util.h" -#include "base/gfx/gdi_util.h" #include "base/logging.h" #include "base/native_library.h" #include "base/registry.h" -- cgit v1.1