diff options
-rw-r--r-- | ash/wm/custom_frame_view_ash.cc | 2 | ||||
-rw-r--r-- | ash/wm/frame_painter.cc | 148 | ||||
-rw-r--r-- | ash/wm/frame_painter.h | 15 | ||||
-rw-r--r-- | ash/wm/frame_painter_unittest.cc | 21 | ||||
-rw-r--r-- | ash/wm/panels/panel_frame_view.cc | 2 | ||||
-rw-r--r-- | chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc | 19 | ||||
-rw-r--r-- | chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h | 10 |
7 files changed, 141 insertions, 76 deletions
diff --git a/ash/wm/custom_frame_view_ash.cc b/ash/wm/custom_frame_view_ash.cc index 3fb5b24..8c66eb8 100644 --- a/ash/wm/custom_frame_view_ash.cc +++ b/ash/wm/custom_frame_view_ash.cc @@ -150,7 +150,7 @@ void CustomFrameViewAsh::OnPaint(gfx::Canvas* canvas) { canvas, paint_as_active ? FramePainter::ACTIVE : FramePainter::INACTIVE, theme_image_id, - NULL); + 0); frame_painter_->PaintTitleBar(this, canvas, GetTitleFont()); frame_painter_->PaintHeaderContentSeparator(this, canvas); canvas->Restore(); diff --git a/ash/wm/frame_painter.cc b/ash/wm/frame_painter.cc index f94bf12..5aeb714 100644 --- a/ash/wm/frame_painter.cc +++ b/ash/wm/frame_painter.cc @@ -33,6 +33,7 @@ #include "ui/gfx/font.h" #include "ui/gfx/image/image.h" #include "ui/gfx/screen.h" +#include "ui/gfx/skia_util.h" #include "ui/views/controls/button/image_button.h" #include "ui/views/widget/widget.h" #include "ui/views/widget/widget_delegate.h" @@ -82,23 +83,21 @@ const int kActivationCrossfadeDurationMs = 200; // Alpha/opacity value for fully-opaque headers. const int kFullyOpaque = 255; -// Tiles an image into an area, rounding the top corners. Samples the |bitmap| -// starting |bitmap_offset_x| pixels from the left of the image. +// Tiles an image into an area, rounding the top corners. Samples |image| +// starting |image_inset_x| pixels from the left of the image. void TileRoundRect(gfx::Canvas* canvas, - int x, int y, int w, int h, - const SkPaint& paint, const gfx::ImageSkia& image, - int corner_radius, + const SkPaint& paint, + const gfx::Rect& bounds, + int top_left_corner_radius, + int top_right_corner_radius, int image_inset_x) { - // To get the shader to sample the image |inset_y| pixels in but tile across - // the whole image, we adjust the target rectangle for the shader to the right - // and translate the canvas left to compensate. - SkRect rect; - rect.iset(x, y, x + w, y + h); - const SkScalar kRadius = SkIntToScalar(corner_radius); + SkRect rect = gfx::RectToSkRect(bounds); + const SkScalar kTopLeftRadius = SkIntToScalar(top_left_corner_radius); + const SkScalar kTopRightRadius = SkIntToScalar(top_right_corner_radius); SkScalar radii[8] = { - kRadius, kRadius, // top-left - kRadius, kRadius, // top-right + kTopLeftRadius, kTopLeftRadius, // top-left + kTopRightRadius, kTopRightRadius, // top-right 0, 0, // bottom-right 0, 0}; // bottom-left SkPath path; @@ -106,6 +105,53 @@ void TileRoundRect(gfx::Canvas* canvas, canvas->DrawImageInPath(image, -image_inset_x, 0, path, paint); } +// Tiles |frame_image| and |frame_overlay_image| into an area, rounding the top +// corners. +void PaintFrameImagesInRoundRect(gfx::Canvas* canvas, + const gfx::ImageSkia* frame_image, + const gfx::ImageSkia* frame_overlay_image, + const SkPaint& paint, + const gfx::Rect& bounds, + int corner_radius, + int image_inset_x) { + SkXfermode::Mode normal_mode; + SkXfermode::AsMode(NULL, &normal_mode); + + // If |paint| is using an unusual SkXfermode::Mode (this is the case while + // crossfading), we must create a new canvas to overlay |frame_image| and + // |frame_overlay_image| using |normal_mode| and then paint the result + // using the unusual mode. We try to avoid this because creating a new + // browser-width canvas is expensive. + bool fast_path = (!frame_overlay_image || + SkXfermode::IsMode(paint.getXfermode(), normal_mode)); + if (fast_path) { + TileRoundRect(canvas, *frame_image, paint, bounds, corner_radius, + corner_radius, image_inset_x); + + if (frame_overlay_image) { + // Adjust |bounds| such that |frame_overlay_image| is not tiled. + gfx::Rect overlay_bounds = bounds; + overlay_bounds.Intersect( + gfx::Rect(bounds.origin(), frame_overlay_image->size())); + int top_left_corner_radius = corner_radius; + int top_right_corner_radius = corner_radius; + if (overlay_bounds.width() < bounds.width() - corner_radius) + top_right_corner_radius = 0; + TileRoundRect(canvas, *frame_overlay_image, paint, overlay_bounds, + top_left_corner_radius, top_right_corner_radius, 0); + } + } else { + gfx::Canvas temporary_canvas(bounds.size(), canvas->scale_factor(), false); + temporary_canvas.TileImageInt(*frame_image, + image_inset_x, 0, + 0, 0, + bounds.width(), bounds.height()); + temporary_canvas.DrawImageInt(*frame_overlay_image, 0, 0); + TileRoundRect(canvas, gfx::ImageSkia(temporary_canvas.ExtractImageRep()), + paint, bounds, corner_radius, corner_radius, 0); + } +} + // Returns true if |child| and all ancestors are visible. Useful to ensure that // a window is individually visible and is not part of a hidden workspace. bool IsVisibleToRoot(Window* child) { @@ -179,8 +225,10 @@ FramePainter::FramePainter() header_left_edge_(NULL), header_right_edge_(NULL), previous_theme_frame_id_(0), + previous_theme_frame_overlay_id_(0), previous_opacity_(0), crossfade_theme_frame_id_(0), + crossfade_theme_frame_overlay_id_(0), crossfade_opacity_(0), crossfade_animation_(NULL), size_button_behavior_(SIZE_BUTTON_MAXIMIZES) { @@ -373,9 +421,11 @@ void FramePainter::PaintHeader(views::NonClientFrameView* view, gfx::Canvas* canvas, HeaderMode header_mode, int theme_frame_id, - const gfx::ImageSkia* theme_frame_overlay) { - if (previous_theme_frame_id_ != 0 && - previous_theme_frame_id_ != theme_frame_id) { + int theme_frame_overlay_id) { + bool initial_paint = (previous_theme_frame_id_ == 0); + if (!initial_paint && + (previous_theme_frame_id_ != theme_frame_id || + previous_theme_frame_overlay_id_ != theme_frame_overlay_id)) { aura::Window* parent = frame_->GetNativeWindow()->parent(); // Don't animate the header if the parent (a workspace) is already // animating. Doing so results in continually painting during the animation @@ -390,6 +440,7 @@ void FramePainter::PaintHeader(views::NonClientFrameView* view, if (!parent_animating) { crossfade_animation_.reset(new ui::SlideAnimation(this)); crossfade_theme_frame_id_ = previous_theme_frame_id_; + crossfade_theme_frame_overlay_id_ = previous_theme_frame_overlay_id_; crossfade_opacity_ = previous_opacity_; crossfade_animation_->SetSlideDuration(kActivationCrossfadeDurationMs); crossfade_animation_->Show(); @@ -399,10 +450,15 @@ void FramePainter::PaintHeader(views::NonClientFrameView* view, } int opacity = - GetHeaderOpacity(header_mode, theme_frame_id, theme_frame_overlay); + GetHeaderOpacity(header_mode, theme_frame_id, theme_frame_overlay_id); ui::ThemeProvider* theme_provider = frame_->GetThemeProvider(); gfx::ImageSkia* theme_frame = theme_provider->GetImageSkiaNamed( theme_frame_id); + gfx::ImageSkia* theme_frame_overlay = NULL; + if (theme_frame_overlay_id != 0) { + theme_frame_overlay = theme_provider->GetImageSkiaNamed( + theme_frame_overlay_id); + } header_frame_bounds_ = gfx::Rect(0, 0, view->width(), theme_frame->height()); const int kCornerRadius = 2; @@ -411,7 +467,19 @@ void FramePainter::PaintHeader(views::NonClientFrameView* view, if (crossfade_animation_.get() && crossfade_animation_->is_animating()) { gfx::ImageSkia* crossfade_theme_frame = theme_provider->GetImageSkiaNamed(crossfade_theme_frame_id_); - if (crossfade_theme_frame) { + gfx::ImageSkia* crossfade_theme_frame_overlay = NULL; + if (crossfade_theme_frame_overlay_id_ != 0) { + crossfade_theme_frame_overlay = theme_provider->GetImageSkiaNamed( + crossfade_theme_frame_overlay_id_); + } + if (!crossfade_theme_frame || + (crossfade_theme_frame_overlay_id_ != 0 && + !crossfade_theme_frame_overlay)) { + // Reset the animation. This case occurs when the user switches the theme + // that they are using. + crossfade_animation_.reset(); + paint.setAlpha(opacity); + } else { double current_value = crossfade_animation_->GetCurrentValue(); int old_alpha = (1 - current_value) * crossfade_opacity_; int new_alpha = current_value * opacity; @@ -419,37 +487,33 @@ void FramePainter::PaintHeader(views::NonClientFrameView* view, // Draw the old header background, clipping the corners to be rounded. paint.setAlpha(old_alpha); paint.setXfermodeMode(SkXfermode::kPlus_Mode); - TileRoundRect(canvas, - 0, 0, view->width(), theme_frame->height(), - paint, - *crossfade_theme_frame, - kCornerRadius, - GetThemeBackgroundXInset()); + PaintFrameImagesInRoundRect(canvas, + crossfade_theme_frame, + crossfade_theme_frame_overlay, + paint, + header_frame_bounds_, + kCornerRadius, + GetThemeBackgroundXInset()); paint.setAlpha(new_alpha); - } else { - crossfade_animation_.reset(); - paint.setAlpha(opacity); } } else { paint.setAlpha(opacity); } // Draw the header background, clipping the corners to be rounded. - TileRoundRect(canvas, - 0, 0, view->width(), theme_frame->height(), - paint, - *theme_frame, - kCornerRadius, - GetThemeBackgroundXInset()); + PaintFrameImagesInRoundRect(canvas, + theme_frame, + theme_frame_overlay, + paint, + header_frame_bounds_, + kCornerRadius, + GetThemeBackgroundXInset()); previous_theme_frame_id_ = theme_frame_id; + previous_theme_frame_overlay_id_ = theme_frame_overlay_id; previous_opacity_ = opacity; - // Draw the theme frame overlay, if available. - if (theme_frame_overlay) - canvas->DrawImageInt(*theme_frame_overlay, 0, 0); - // Separator between the maximize and close buttons. It overlaps the left // edge of the close button. gfx::Rect divider(close_button_->x(), close_button_->y(), @@ -759,12 +823,14 @@ int FramePainter::GetTitleOffsetX() const { int FramePainter::GetHeaderOpacity( HeaderMode header_mode, int theme_frame_id, - const gfx::ImageSkia* theme_frame_overlay) const { + int theme_frame_overlay_id) const { // User-provided themes are painted fully opaque. - if (frame_->GetThemeProvider()->HasCustomImage(theme_frame_id)) - return kFullyOpaque; - if (theme_frame_overlay) + ui::ThemeProvider* theme_provider = frame_->GetThemeProvider(); + if (theme_provider->HasCustomImage(theme_frame_id) || + (theme_frame_overlay_id != 0 && + theme_provider->HasCustomImage(theme_frame_overlay_id))) { return kFullyOpaque; + } // The header is fully opaque when using the minimalistic header style. if (ShouldUseMinimalHeaderStyle(THEMED_NO)) diff --git a/ash/wm/frame_painter.h b/ash/wm/frame_painter.h index a46aa17..e288166 100644 --- a/ash/wm/frame_painter.h +++ b/ash/wm/frame_painter.h @@ -100,11 +100,12 @@ class ASH_EXPORT FramePainter : public aura::WindowObserver, bool ShouldUseMinimalHeaderStyle(Themed header_themed) const; // Paints the frame header. + // |theme_frame_overlay_id| is 0 if no overlay image should be used. void PaintHeader(views::NonClientFrameView* view, gfx::Canvas* canvas, HeaderMode header_mode, int theme_frame_id, - const gfx::ImageSkia* theme_frame_overlay); + int theme_frame_overlay_id); // Paints the header/content separator line. Exists as a separate function // because some windows with complex headers (e.g. browsers with tab strips) @@ -172,9 +173,13 @@ class ASH_EXPORT FramePainter : public aura::WindowObserver, int GetTitleOffsetX() const; // Returns the opacity value used to paint the header. + // |theme_frame_overlay_id| is 0 if no overlay image is used. int GetHeaderOpacity(HeaderMode header_mode, int theme_frame_id, - const gfx::ImageSkia* theme_frame_overlay) const; + int theme_frame_overlay_id) const; + + // Adjust frame operations for left / right maximized modes. + int AdjustFrameHitCodeForMaximizedModes(int hit_code); // Returns true if the user is cycling through workspaces. bool IsCyclingThroughWorkspaces() const; @@ -221,12 +226,14 @@ class ASH_EXPORT FramePainter : public aura::WindowObserver, const gfx::ImageSkia* header_left_edge_; const gfx::ImageSkia* header_right_edge_; - // Image id and opacity last used for painting header. + // Image ids and opacity last used for painting header. int previous_theme_frame_id_; + int previous_theme_frame_overlay_id_; int previous_opacity_; - // Image id and opacity we are crossfading from. + // Image ids and opacity we are crossfading from. int crossfade_theme_frame_id_; + int crossfade_theme_frame_overlay_id_; int crossfade_opacity_; gfx::Rect header_frame_bounds_; diff --git a/ash/wm/frame_painter_unittest.cc b/ash/wm/frame_painter_unittest.cc index 8108320..3c9d55c 100644 --- a/ash/wm/frame_painter_unittest.cc +++ b/ash/wm/frame_painter_unittest.cc @@ -594,7 +594,7 @@ TEST_F(FramePainterTest, GetHeaderOpacity) { EXPECT_EQ(FramePainter::kSoloWindowOpacity, p1.GetHeaderOpacity(FramePainter::ACTIVE, IDR_AURA_WINDOW_HEADER_BASE_ACTIVE, - NULL)); + 0)); // Create a second widget and painter. scoped_ptr<Widget> w2(CreateTestWidget()); @@ -608,33 +608,20 @@ TEST_F(FramePainterTest, GetHeaderOpacity) { EXPECT_EQ(FramePainter::kActiveWindowOpacity, p2.GetHeaderOpacity(FramePainter::ACTIVE, IDR_AURA_WINDOW_HEADER_BASE_ACTIVE, - NULL)); + 0)); // Inactive window has inactive window opacity. EXPECT_EQ(FramePainter::kInactiveWindowOpacity, p2.GetHeaderOpacity(FramePainter::INACTIVE, IDR_AURA_WINDOW_HEADER_BASE_INACTIVE, - NULL)); - - // Custom overlay image is drawn completely opaque. - gfx::ImageSkia custom_overlay; - EXPECT_EQ(255, - p1.GetHeaderOpacity(FramePainter::ACTIVE, - IDR_AURA_WINDOW_HEADER_BASE_ACTIVE, - &custom_overlay)); + 0)); // Regular maximized windows are fully opaque. ash::wm::MaximizeWindow(w1->GetNativeWindow()); EXPECT_EQ(255, p1.GetHeaderOpacity(FramePainter::ACTIVE, IDR_AURA_WINDOW_HEADER_BASE_ACTIVE, - NULL)); - - // Windows with custom overlays are fully opaque when maximized. - EXPECT_EQ(255, - p1.GetHeaderOpacity(FramePainter::ACTIVE, - IDR_AURA_WINDOW_HEADER_BASE_ACTIVE, - &custom_overlay)); + 0)); } // Test that the minimal header style is used in the proper situations. diff --git a/ash/wm/panels/panel_frame_view.cc b/ash/wm/panels/panel_frame_view.cc index 59bab64..36b6950 100644 --- a/ash/wm/panels/panel_frame_view.cc +++ b/ash/wm/panels/panel_frame_view.cc @@ -118,7 +118,7 @@ void PanelFrameView::OnPaint(gfx::Canvas* canvas) { canvas, paint_as_active ? FramePainter::ACTIVE : FramePainter::INACTIVE, theme_frame_id, - NULL); + 0); frame_painter_->PaintTitleBar(this, canvas, title_font_); frame_painter_->PaintHeaderContentSeparator(this, canvas); } diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc index 36fbdf2..d658b84 100644 --- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc +++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc @@ -221,11 +221,13 @@ void BrowserNonClientFrameViewAsh::OnPaint(gfx::Canvas* canvas) { // The primary header image changes based on window activation state and // theme, so we look it up for each paint. int theme_frame_image_id = GetThemeFrameImageId(); - const gfx::ImageSkia* theme_frame_overlay_image = GetThemeFrameOverlayImage(); + int theme_frame_overlay_image_id = GetThemeFrameOverlayImageId(); + ui::ThemeProvider* theme_provider = GetThemeProvider(); ash::FramePainter::Themed header_themed = ash::FramePainter::THEMED_NO; - if (GetThemeProvider()->HasCustomImage(theme_frame_image_id) || - theme_frame_overlay_image) { + if (theme_provider->HasCustomImage(theme_frame_image_id) || + (theme_frame_overlay_image_id != 0 && + theme_provider->HasCustomImage(theme_frame_overlay_image_id))) { header_themed = ash::FramePainter::THEMED_YES; } @@ -238,7 +240,7 @@ void BrowserNonClientFrameViewAsh::OnPaint(gfx::Canvas* canvas) { ShouldPaintAsActive() ? ash::FramePainter::ACTIVE : ash::FramePainter::INACTIVE, theme_frame_image_id, - theme_frame_overlay_image); + theme_frame_overlay_image_id); if (browser_view()->ShouldShowWindowTitle()) frame_painter_->PaintTitleBar(this, canvas, BrowserFrame::GetTitleFont()); if (browser_view()->IsToolbarVisible()) @@ -530,14 +532,13 @@ int BrowserNonClientFrameViewAsh::GetThemeFrameImageId() const { IDR_AURA_WINDOW_HEADER_BASE_INACTIVE; } -const gfx::ImageSkia* -BrowserNonClientFrameViewAsh::GetThemeFrameOverlayImage() const { +int BrowserNonClientFrameViewAsh::GetThemeFrameOverlayImageId() const { ui::ThemeProvider* tp = GetThemeProvider(); if (tp->HasCustomImage(IDR_THEME_FRAME_OVERLAY) && browser_view()->IsBrowserTypeNormal() && !browser_view()->IsOffTheRecord()) { - return tp->GetImageSkiaNamed(ShouldPaintAsActive() ? - IDR_THEME_FRAME_OVERLAY : IDR_THEME_FRAME_OVERLAY_INACTIVE); + return ShouldPaintAsActive() ? + IDR_THEME_FRAME_OVERLAY : IDR_THEME_FRAME_OVERLAY_INACTIVE; } - return NULL; + return 0; } diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h index 140a7c7..eea274d 100644 --- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h +++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h @@ -89,10 +89,14 @@ class BrowserNonClientFrameViewAsh // above the content area. void PaintContentEdge(gfx::Canvas* canvas); - // Returns the correct image id for the frame header based on activation - // state and incognito mode. + // Returns the id of the header frame image based on the browser type, + // activation state and incognito mode. int GetThemeFrameImageId() const; - const gfx::ImageSkia* GetThemeFrameOverlayImage() const; + + // Returns the id of the header frame overlay image based on the activation + // state and incognito mode. + // Returns 0 if no overlay image should be used. + int GetThemeFrameOverlayImageId() const; // Window controls. The |size_button_| either toggles maximized or toggles // minimized. The exact behavior is determined by |size_button_minimizes_|. |