diff options
Diffstat (limited to 'chromium/ash/wm/frame_painter.cc')
-rw-r--r-- | chromium/ash/wm/frame_painter.cc | 935 |
1 files changed, 0 insertions, 935 deletions
diff --git a/chromium/ash/wm/frame_painter.cc b/chromium/ash/wm/frame_painter.cc deleted file mode 100644 index adf9dd7b7a2..00000000000 --- a/chromium/ash/wm/frame_painter.cc +++ /dev/null @@ -1,935 +0,0 @@ -// 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 "ash/wm/frame_painter.h" - -#include <vector> - -#include "ash/ash_constants.h" -#include "ash/root_window_controller.h" -#include "ash/shell.h" -#include "ash/shell_window_ids.h" -#include "ash/wm/property_util.h" -#include "ash/wm/window_properties.h" -#include "ash/wm/window_util.h" -#include "base/logging.h" // DCHECK -#include "grit/ash_resources.h" -#include "third_party/skia/include/core/SkCanvas.h" -#include "third_party/skia/include/core/SkColor.h" -#include "third_party/skia/include/core/SkPaint.h" -#include "third_party/skia/include/core/SkPath.h" -#include "ui/aura/client/aura_constants.h" -#include "ui/aura/env.h" -#include "ui/aura/root_window.h" -#include "ui/aura/window.h" -#include "ui/base/animation/slide_animation.h" -#include "ui/base/hit_test.h" -#include "ui/base/layout.h" -#include "ui/base/resource/resource_bundle.h" -#include "ui/base/theme_provider.h" -#include "ui/gfx/canvas.h" -#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" - -using aura::RootWindow; -using aura::Window; -using views::Widget; - -namespace { -// TODO(jamescook): Border is specified to be a single pixel overlapping -// the web content and may need to be built into the shadow layers instead. -const int kBorderThickness = 0; -// Space between left edge of window and popup window icon. -const int kIconOffsetX = 9; -// Height and width of window icon. -const int kIconSize = 16; -// Space between the title text and the caption buttons. -const int kTitleLogoSpacing = 5; -// Space between window icon and title text. -const int kTitleIconOffsetX = 5; -// Space between window edge and title text, when there is no icon. -const int kTitleNoIconOffsetX = 8; -// Color for the non-maximized window title text. -const SkColor kNonMaximizedWindowTitleTextColor = SkColorSetRGB(40, 40, 40); -// Color for the maximized window title text. -const SkColor kMaximizedWindowTitleTextColor = SK_ColorWHITE; -// Size of header/content separator line below the header image. -const int kHeaderContentSeparatorSize = 1; -// Color of header bottom edge line. -const SkColor kHeaderContentSeparatorColor = SkColorSetRGB(128, 128, 128); -// Space between close button and right edge of window. -const int kCloseButtonOffsetX = 0; -// Space between close button and top edge of window. -const int kCloseButtonOffsetY = 0; -// The size and close buttons are designed to slightly overlap in order -// to do fancy hover highlighting. -const int kSizeButtonOffsetX = -1; -// In the pre-Ash era the web content area had a frame along the left edge, so -// user-generated theme images for the new tab page assume they are shifted -// right relative to the header. Now that we have removed the left edge frame -// we need to copy the theme image for the window header from a few pixels -// inset to preserve alignment with the NTP image, or else we'll break a bunch -// of existing themes. We do something similar on OS X for the same reason. -const int kThemeFrameImageInsetX = 5; -// Duration of crossfade animation for activating and deactivating frame. -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 |image| -// starting |image_inset_x| pixels from the left of the image. -void TileRoundRect(gfx::Canvas* canvas, - const gfx::ImageSkia& image, - const SkPaint& paint, - const gfx::Rect& bounds, - int top_left_corner_radius, - int top_right_corner_radius, - int image_inset_x) { - SkRect rect = gfx::RectToSkRect(bounds); - const SkScalar kTopLeftRadius = SkIntToScalar(top_left_corner_radius); - const SkScalar kTopRightRadius = SkIntToScalar(top_right_corner_radius); - SkScalar radii[8] = { - kTopLeftRadius, kTopLeftRadius, // top-left - kTopRightRadius, kTopRightRadius, // top-right - 0, 0, // bottom-right - 0, 0}; // bottom-left - SkPath path; - path.addRoundRect(rect, radii, SkPath::kCW_Direction); - 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) { - for (Window* window = child; window; window = window->parent()) { - // We must use TargetVisibility() because windows animate in and out and - // IsVisible() also tracks the layer visibility state. - if (!window->TargetVisibility()) - return false; - } - return true; -} - -// Returns true if |window| is a "normal" window for purposes of solo window -// computations. Returns false for windows that are: -// * Not drawn (for example, DragDropTracker uses one for mouse capture) -// * Modal alerts (it looks odd for headers to change when an alert opens) -// * Constrained windows (ditto) -bool IsSoloWindowHeaderCandidate(aura::Window* window) { - return window && - window->type() == aura::client::WINDOW_TYPE_NORMAL && - window->layer() && - window->layer()->type() != ui::LAYER_NOT_DRAWN && - window->GetProperty(aura::client::kModalKey) == ui::MODAL_TYPE_NONE && - !window->GetProperty(ash::kConstrainedWindowKey); -} - -// Returns a list of windows in |root_window|| that potentially could have -// a transparent solo-window header. -std::vector<Window*> GetWindowsForSoloHeaderUpdate(RootWindow* root_window) { - std::vector<Window*> windows; - // During shutdown there may not be a workspace controller. In that case - // we don't care about updating any windows. - // Avoid memory allocations for typical window counts. - windows.reserve(16); - // Collect windows from the desktop. - Window* desktop = ash::Shell::GetContainer( - root_window, ash::internal::kShellWindowId_DefaultContainer); - windows.insert(windows.end(), - desktop->children().begin(), - desktop->children().end()); - // Collect "always on top" windows. - Window* top_container = - ash::Shell::GetContainer( - root_window, ash::internal::kShellWindowId_AlwaysOnTopContainer); - windows.insert(windows.end(), - top_container->children().begin(), - top_container->children().end()); - return windows; -} -} // namespace - -namespace ash { - -// static -int FramePainter::kActiveWindowOpacity = 255; // 1.0 -int FramePainter::kInactiveWindowOpacity = 255; // 1.0 -int FramePainter::kSoloWindowOpacity = 77; // 0.3 - -/////////////////////////////////////////////////////////////////////////////// -// FramePainter, public: - -FramePainter::FramePainter() - : frame_(NULL), - window_icon_(NULL), - size_button_(NULL), - close_button_(NULL), - window_(NULL), - button_separator_(NULL), - top_left_corner_(NULL), - top_edge_(NULL), - top_right_corner_(NULL), - 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), - size_button_behavior_(SIZE_BUTTON_MAXIMIZES) {} - -FramePainter::~FramePainter() { - // Sometimes we are destroyed before the window closes, so ensure we clean up. - if (window_) { - window_->RemoveObserver(this); - } -} - -void FramePainter::Init(views::Widget* frame, - views::View* window_icon, - views::ImageButton* size_button, - views::ImageButton* close_button, - SizeButtonBehavior behavior) { - DCHECK(frame); - // window_icon may be NULL. - DCHECK(size_button); - DCHECK(close_button); - frame_ = frame; - window_icon_ = window_icon; - size_button_ = size_button; - close_button_ = close_button; - size_button_behavior_ = behavior; - - // Window frame image parts. - ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); - button_separator_ = - rb.GetImageNamed(IDR_AURA_WINDOW_BUTTON_SEPARATOR).ToImageSkia(); - top_left_corner_ = - rb.GetImageNamed(IDR_AURA_WINDOW_HEADER_SHADE_TOP_LEFT).ToImageSkia(); - top_edge_ = - rb.GetImageNamed(IDR_AURA_WINDOW_HEADER_SHADE_TOP).ToImageSkia(); - top_right_corner_ = - rb.GetImageNamed(IDR_AURA_WINDOW_HEADER_SHADE_TOP_RIGHT).ToImageSkia(); - header_left_edge_ = - rb.GetImageNamed(IDR_AURA_WINDOW_HEADER_SHADE_LEFT).ToImageSkia(); - header_right_edge_ = - rb.GetImageNamed(IDR_AURA_WINDOW_HEADER_SHADE_RIGHT).ToImageSkia(); - - window_ = frame->GetNativeWindow(); - gfx::Insets mouse_insets = gfx::Insets(-kResizeOutsideBoundsSize, - -kResizeOutsideBoundsSize, - -kResizeOutsideBoundsSize, - -kResizeOutsideBoundsSize); - gfx::Insets touch_insets = mouse_insets.Scale( - kResizeOutsideBoundsScaleForTouch); - // Ensure we get resize cursors for a few pixels outside our bounds. - window_->SetHitTestBoundsOverrideOuter(mouse_insets, touch_insets); - // Ensure we get resize cursors just inside our bounds as well. - window_->set_hit_test_bounds_override_inner(mouse_insets); - - // Watch for maximize/restore/fullscreen state changes. Observer removes - // itself in OnWindowDestroying() below, or in the destructor if we go away - // before the window. - window_->AddObserver(this); - - // Solo-window header updates are handled by the workspace controller when - // this window is added to the desktop. -} - -// static -void FramePainter::UpdateSoloWindowHeader(RootWindow* root_window) { - // Use a separate function here so callers outside of FramePainter don't need - // to know about "ignorable_window". - UpdateSoloWindowInRoot(root_window, NULL /* ignorable_window */); -} - -gfx::Rect FramePainter::GetBoundsForClientView( - int top_height, - const gfx::Rect& window_bounds) const { - return gfx::Rect( - kBorderThickness, - top_height, - std::max(0, window_bounds.width() - (2 * kBorderThickness)), - std::max(0, window_bounds.height() - top_height - kBorderThickness)); -} - -gfx::Rect FramePainter::GetWindowBoundsForClientBounds( - int top_height, - const gfx::Rect& client_bounds) const { - return gfx::Rect(std::max(0, client_bounds.x() - kBorderThickness), - std::max(0, client_bounds.y() - top_height), - client_bounds.width() + (2 * kBorderThickness), - client_bounds.height() + top_height + kBorderThickness); -} - -int FramePainter::NonClientHitTest(views::NonClientFrameView* view, - const gfx::Point& point) { - gfx::Rect expanded_bounds = view->bounds(); - int outside_bounds = kResizeOutsideBoundsSize; - - if (aura::Env::GetInstance()->is_touch_down()) - outside_bounds *= kResizeOutsideBoundsScaleForTouch; - expanded_bounds.Inset(-outside_bounds, -outside_bounds); - - if (!expanded_bounds.Contains(point)) - return HTNOWHERE; - - // No avatar button. - - // Check the frame first, as we allow a small area overlapping the contents - // to be used for resize handles. - bool can_ever_resize = frame_->widget_delegate() ? - frame_->widget_delegate()->CanResize() : - false; - // Don't allow overlapping resize handles when the window is maximized or - // fullscreen, as it can't be resized in those states. - int resize_border = - frame_->IsMaximized() || frame_->IsFullscreen() ? 0 : - kResizeInsideBoundsSize; - int frame_component = view->GetHTComponentForFrame(point, - resize_border, - resize_border, - kResizeAreaCornerSize, - kResizeAreaCornerSize, - can_ever_resize); - if (frame_component != HTNOWHERE) - return frame_component; - - int client_component = frame_->client_view()->NonClientHitTest(point); - if (client_component != HTNOWHERE) - return client_component; - - // Then see if the point is within any of the window controls. - if (close_button_->visible() && - close_button_->GetMirroredBounds().Contains(point)) - return HTCLOSE; - if (size_button_->visible() && - size_button_->GetMirroredBounds().Contains(point)) - return HTMAXBUTTON; - - // Caption is a safe default. - return HTCAPTION; -} - -gfx::Size FramePainter::GetMinimumSize(views::NonClientFrameView* view) { - gfx::Size min_size = frame_->client_view()->GetMinimumSize(); - // Ensure we can display the top of the caption area. - gfx::Rect client_bounds = view->GetBoundsForClientView(); - min_size.Enlarge(0, client_bounds.y()); - // Ensure we have enough space for the window icon and buttons. We allow - // the title string to collapse to zero width. - int title_width = GetTitleOffsetX() + - size_button_->width() + kSizeButtonOffsetX + - close_button_->width() + kCloseButtonOffsetX; - if (title_width > min_size.width()) - min_size.set_width(title_width); - return min_size; -} - -gfx::Size FramePainter::GetMaximumSize(views::NonClientFrameView* view) { - return frame_->client_view()->GetMaximumSize(); -} - -int FramePainter::GetRightInset() const { - gfx::Size close_size = close_button_->GetPreferredSize(); - gfx::Size size_button_size = size_button_->GetPreferredSize(); - int inset = close_size.width() + kCloseButtonOffsetX + - size_button_size.width() + kSizeButtonOffsetX; - return inset; -} - -int FramePainter::GetThemeBackgroundXInset() const { - return kThemeFrameImageInsetX; -} - -bool FramePainter::ShouldUseMinimalHeaderStyle(Themed header_themed) const { - // Use the minimalistic header style whenever |frame_| is maximized or - // fullscreen EXCEPT: - // - If the user has installed a theme with custom images for the header. - // - For windows which are not tracked by the workspace code (which are used - // for tab dragging). - return ((frame_->IsMaximized() || frame_->IsFullscreen()) && - header_themed == THEMED_NO && - GetTrackedByWorkspace(frame_->GetNativeWindow())); -} - -void FramePainter::PaintHeader(views::NonClientFrameView* view, - gfx::Canvas* canvas, - HeaderMode header_mode, - int 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 - // and gives a slower frame rate. - // TODO(sky): expose a better way to determine this rather than assuming - // the parent is a workspace. - bool parent_animating = parent && - (parent->layer()->GetAnimator()->IsAnimatingProperty( - ui::LayerAnimationElement::OPACITY) || - parent->layer()->GetAnimator()->IsAnimatingProperty( - ui::LayerAnimationElement::VISIBILITY)); - 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(); - } else { - crossfade_animation_.reset(); - } - } - - int opacity = - 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()); - - int corner_radius = GetHeaderCornerRadius(); - SkPaint paint; - - if (crossfade_animation_.get() && crossfade_animation_->is_animating()) { - gfx::ImageSkia* crossfade_theme_frame = - theme_provider->GetImageSkiaNamed(crossfade_theme_frame_id_); - 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; - - // Draw the old header background, clipping the corners to be rounded. - paint.setAlpha(old_alpha); - paint.setXfermodeMode(SkXfermode::kPlus_Mode); - PaintFrameImagesInRoundRect(canvas, - crossfade_theme_frame, - crossfade_theme_frame_overlay, - paint, - header_frame_bounds_, - corner_radius, - GetThemeBackgroundXInset()); - - paint.setAlpha(new_alpha); - } - } else { - paint.setAlpha(opacity); - } - - // Draw the header background, clipping the corners to be rounded. - PaintFrameImagesInRoundRect(canvas, - theme_frame, - theme_frame_overlay, - paint, - header_frame_bounds_, - corner_radius, - GetThemeBackgroundXInset()); - - previous_theme_frame_id_ = theme_frame_id; - previous_theme_frame_overlay_id_ = theme_frame_overlay_id; - previous_opacity_ = opacity; - - // 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(), - button_separator_->width(), close_button_->height()); - canvas->DrawImageInt(*button_separator_, - view->GetMirroredXForRect(divider), - close_button_->y()); - - // We don't need the extra lightness in the edges when we're at the top edge - // of the screen or when the header's corners are not rounded. - // - // TODO(sky): this isn't quite right. What we really want is a method that - // returns bounds ignoring transforms on certain windows (such as workspaces) - // and is relative to the root. - if (frame_->GetNativeWindow()->bounds().y() == 0 || corner_radius == 0) - return; - - // Draw the top corners and edge. - int top_left_height = top_left_corner_->height(); - canvas->DrawImageInt(*top_left_corner_, - 0, 0, top_left_corner_->width(), top_left_height, - 0, 0, top_left_corner_->width(), top_left_height, - false); - canvas->TileImageInt(*top_edge_, - top_left_corner_->width(), - 0, - view->width() - top_left_corner_->width() - top_right_corner_->width(), - top_edge_->height()); - int top_right_height = top_right_corner_->height(); - canvas->DrawImageInt(*top_right_corner_, - 0, 0, - top_right_corner_->width(), top_right_height, - view->width() - top_right_corner_->width(), 0, - top_right_corner_->width(), top_right_height, - false); - - // Header left edge. - int header_left_height = theme_frame->height() - top_left_height; - canvas->TileImageInt(*header_left_edge_, - 0, top_left_height, - header_left_edge_->width(), header_left_height); - - // Header right edge. - int header_right_height = theme_frame->height() - top_right_height; - canvas->TileImageInt(*header_right_edge_, - view->width() - header_right_edge_->width(), - top_right_height, - header_right_edge_->width(), - header_right_height); - - // We don't draw edges around the content area. Web content goes flush - // to the edge of the window. -} - -void FramePainter::PaintHeaderContentSeparator(views::NonClientFrameView* view, - gfx::Canvas* canvas) { - // Paint the line just above the content area. - gfx::Rect client_bounds = view->GetBoundsForClientView(); - canvas->FillRect(gfx::Rect(client_bounds.x(), - client_bounds.y() - kHeaderContentSeparatorSize, - client_bounds.width(), - kHeaderContentSeparatorSize), - kHeaderContentSeparatorColor); -} - -int FramePainter::HeaderContentSeparatorSize() const { - return kHeaderContentSeparatorSize; -} - -void FramePainter::PaintTitleBar(views::NonClientFrameView* view, - gfx::Canvas* canvas, - const gfx::Font& title_font) { - // The window icon is painted by its own views::View. - views::WidgetDelegate* delegate = frame_->widget_delegate(); - if (delegate && delegate->ShouldShowWindowTitle()) { - gfx::Rect title_bounds = GetTitleBounds(title_font); - SkColor title_color = frame_->IsMaximized() ? - kMaximizedWindowTitleTextColor : kNonMaximizedWindowTitleTextColor; - canvas->DrawStringInt(delegate->GetWindowTitle(), - title_font, - title_color, - view->GetMirroredXForRect(title_bounds), - title_bounds.y(), - title_bounds.width(), - title_bounds.height(), - gfx::Canvas::NO_SUBPIXEL_RENDERING); - } -} - -void FramePainter::LayoutHeader(views::NonClientFrameView* view, - bool shorter_layout) { - // The new assets only make sense if the window is actually maximized or - // fullscreen. - if (shorter_layout && - (frame_->IsMaximized() || frame_->IsFullscreen()) && - GetTrackedByWorkspace(frame_->GetNativeWindow())) { - SetButtonImages(close_button_, - IDR_AURA_WINDOW_MAXIMIZED_CLOSE2, - IDR_AURA_WINDOW_MAXIMIZED_CLOSE2_H, - IDR_AURA_WINDOW_MAXIMIZED_CLOSE2_P); - // The chat window cannot be restored but only minimized. - if (size_button_behavior_ == SIZE_BUTTON_MINIMIZES) { - SetButtonImages(size_button_, - IDR_AURA_WINDOW_MINIMIZE_SHORT, - IDR_AURA_WINDOW_MINIMIZE_SHORT_H, - IDR_AURA_WINDOW_MINIMIZE_SHORT_P); - } else { - SetButtonImages(size_button_, - IDR_AURA_WINDOW_MAXIMIZED_RESTORE2, - IDR_AURA_WINDOW_MAXIMIZED_RESTORE2_H, - IDR_AURA_WINDOW_MAXIMIZED_RESTORE2_P); - } - } else if (shorter_layout) { - SetButtonImages(close_button_, - IDR_AURA_WINDOW_MAXIMIZED_CLOSE, - IDR_AURA_WINDOW_MAXIMIZED_CLOSE_H, - IDR_AURA_WINDOW_MAXIMIZED_CLOSE_P); - // The chat window cannot be restored but only minimized. - if (size_button_behavior_ == SIZE_BUTTON_MINIMIZES) { - SetButtonImages(size_button_, - IDR_AURA_WINDOW_MINIMIZE_SHORT, - IDR_AURA_WINDOW_MINIMIZE_SHORT_H, - IDR_AURA_WINDOW_MINIMIZE_SHORT_P); - } else { - SetButtonImages(size_button_, - IDR_AURA_WINDOW_MAXIMIZED_RESTORE, - IDR_AURA_WINDOW_MAXIMIZED_RESTORE_H, - IDR_AURA_WINDOW_MAXIMIZED_RESTORE_P); - } - } else { - SetButtonImages(close_button_, - IDR_AURA_WINDOW_CLOSE, - IDR_AURA_WINDOW_CLOSE_H, - IDR_AURA_WINDOW_CLOSE_P); - SetButtonImages(size_button_, - IDR_AURA_WINDOW_MAXIMIZE, - IDR_AURA_WINDOW_MAXIMIZE_H, - IDR_AURA_WINDOW_MAXIMIZE_P); - } - - gfx::Size close_size = close_button_->GetPreferredSize(); - close_button_->SetBounds( - view->width() - close_size.width() - kCloseButtonOffsetX, - kCloseButtonOffsetY, - close_size.width(), - close_size.height()); - - gfx::Size size_button_size = size_button_->GetPreferredSize(); - size_button_->SetBounds( - close_button_->x() - size_button_size.width() - kSizeButtonOffsetX, - close_button_->y(), - size_button_size.width(), - size_button_size.height()); - - if (window_icon_) { - // Vertically center the window icon with respect to the close button. - int icon_offset_y = GetCloseButtonCenterY() - window_icon_->height() / 2; - window_icon_->SetBounds(kIconOffsetX, icon_offset_y, kIconSize, kIconSize); - } -} - -void FramePainter::SchedulePaintForTitle(const gfx::Font& title_font) { - frame_->non_client_view()->SchedulePaintInRect(GetTitleBounds(title_font)); -} - -void FramePainter::OnThemeChanged() { - // We do not cache the images for |previous_theme_frame_id_| and - // |previous_theme_frame_overlay_id_|. Changing the theme changes the images - // returned from ui::ThemeProvider for |previous_theme_frame_id_| - // and |previous_theme_frame_overlay_id_|. Reset the image ids to prevent - // starting a crossfade animation with these images. - previous_theme_frame_id_ = 0; - previous_theme_frame_overlay_id_ = 0; - - if (crossfade_animation_.get() && crossfade_animation_->is_animating()) { - crossfade_animation_.reset(); - frame_->non_client_view()->SchedulePaintInRect(header_frame_bounds_); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// aura::WindowObserver overrides: - -void FramePainter::OnWindowPropertyChanged(aura::Window* window, - const void* key, - intptr_t old) { - // When 'kWindowTrackedByWorkspaceKey' changes, we are going to paint the - // header differently. Schedule a paint to ensure everything is updated - // correctly. - if (key == internal::kWindowTrackedByWorkspaceKey && - GetTrackedByWorkspace(window)) { - frame_->non_client_view()->SchedulePaint(); - } - - if (key != aura::client::kShowStateKey) - return; - - // Maximized and fullscreen windows don't want resize handles overlapping the - // content area, because when the user moves the cursor to the right screen - // edge we want them to be able to hit the scroll bar. - if (ash::wm::IsWindowMaximized(window) || - ash::wm::IsWindowFullscreen(window)) { - window->set_hit_test_bounds_override_inner(gfx::Insets()); - } else { - window->set_hit_test_bounds_override_inner( - gfx::Insets(kResizeInsideBoundsSize, kResizeInsideBoundsSize, - kResizeInsideBoundsSize, kResizeInsideBoundsSize)); - } -} - -void FramePainter::OnWindowVisibilityChanged(aura::Window* window, - bool visible) { - // OnWindowVisibilityChanged can be called for the child windows of |window_|. - if (window != window_) - return; - - // Window visibility change may trigger the change of window solo-ness in a - // different window. - UpdateSoloWindowInRoot(window_->GetRootWindow(), visible ? NULL : window_); -} - -void FramePainter::OnWindowDestroying(aura::Window* destroying) { - DCHECK_EQ(window_, destroying); - - // Must be removed here and not in the destructor, as the aura::Window is - // already destroyed when our destructor runs. - window_->RemoveObserver(this); - - // If we have two or more windows open and we close this one, we might trigger - // the solo window appearance for another window. - UpdateSoloWindowInRoot(window_->GetRootWindow(), window_); - - window_ = NULL; -} - -void FramePainter::OnWindowBoundsChanged(aura::Window* window, - const gfx::Rect& old_bounds, - const gfx::Rect& new_bounds) { - // TODO(sky): this isn't quite right. What we really want is a method that - // returns bounds ignoring transforms on certain windows (such as workspaces). - if ((!frame_->IsMaximized() && !frame_->IsFullscreen()) && - ((old_bounds.y() == 0 && new_bounds.y() != 0) || - (old_bounds.y() != 0 && new_bounds.y() == 0))) { - SchedulePaintForHeader(); - } -} - -void FramePainter::OnWindowAddedToRootWindow(aura::Window* window) { - // Needs to trigger the window appearance change if the window moves across - // root windows and a solo window is already in the new root. - UpdateSoloWindowInRoot(window->GetRootWindow(), NULL /* ignore_window */); -} - -void FramePainter::OnWindowRemovingFromRootWindow(aura::Window* window) { - // Needs to trigger the window appearance change if the window moves across - // root windows and only one window is left in the previous root. Because - // |window| is not yet moved, |window| has to be ignored. - UpdateSoloWindowInRoot(window->GetRootWindow(), window); -} - -/////////////////////////////////////////////////////////////////////////////// -// ui::AnimationDelegate overrides: - -void FramePainter::AnimationProgressed(const ui::Animation* animation) { - frame_->non_client_view()->SchedulePaintInRect(header_frame_bounds_); -} - -/////////////////////////////////////////////////////////////////////////////// -// FramePainter, private: - -void FramePainter::SetButtonImages(views::ImageButton* button, - int normal_image_id, - int hot_image_id, - int pushed_image_id) { - ui::ThemeProvider* theme_provider = frame_->GetThemeProvider(); - button->SetImage(views::CustomButton::STATE_NORMAL, - theme_provider->GetImageSkiaNamed(normal_image_id)); - button->SetImage(views::CustomButton::STATE_HOVERED, - theme_provider->GetImageSkiaNamed(hot_image_id)); - button->SetImage(views::CustomButton::STATE_PRESSED, - theme_provider->GetImageSkiaNamed(pushed_image_id)); -} - -void FramePainter::SetToggledButtonImages(views::ToggleImageButton* button, - int normal_image_id, - int hot_image_id, - int pushed_image_id) { - ui::ThemeProvider* theme_provider = frame_->GetThemeProvider(); - button->SetToggledImage(views::CustomButton::STATE_NORMAL, - theme_provider->GetImageSkiaNamed(normal_image_id)); - button->SetToggledImage(views::CustomButton::STATE_HOVERED, - theme_provider->GetImageSkiaNamed(hot_image_id)); - button->SetToggledImage(views::CustomButton::STATE_PRESSED, - theme_provider->GetImageSkiaNamed(pushed_image_id)); -} - -int FramePainter::GetTitleOffsetX() const { - return window_icon_ ? - window_icon_->bounds().right() + kTitleIconOffsetX : - kTitleNoIconOffsetX; -} - -int FramePainter::GetCloseButtonCenterY() const { - return close_button_->y() + close_button_->height() / 2; -} - -int FramePainter::GetHeaderCornerRadius() const { - // Use square corners for maximized and fullscreen windows when they are - // tracked by the workspace code. (Windows which are not tracked by the - // workspace code are used for tab dragging.) - bool square_corners = ((frame_->IsMaximized() || frame_->IsFullscreen())) && - GetTrackedByWorkspace(frame_->GetNativeWindow()); - const int kCornerRadius = 2; - return square_corners ? 0 : kCornerRadius; -} - -int FramePainter::GetHeaderOpacity( - HeaderMode header_mode, - int theme_frame_id, - int theme_frame_overlay_id) const { - // User-provided themes are painted fully opaque. - 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)) - return kFullyOpaque; - - // Single browser window is very transparent. - if (UseSoloWindowHeader()) - return kSoloWindowOpacity; - - // Otherwise, change transparency based on window activation status. - if (header_mode == ACTIVE) - return kActiveWindowOpacity; - return kInactiveWindowOpacity; -} - -bool FramePainter::UseSoloWindowHeader() const { - // Don't use transparent headers for panels, pop-ups, etc. - if (!IsSoloWindowHeaderCandidate(window_)) - return false; - aura::RootWindow* root = window_->GetRootWindow(); - if (!root || root->GetProperty(internal::kIgnoreSoloWindowFramePainterPolicy)) - return false; - // Don't recompute every time, as it would require many window property - // lookups. - return root->GetProperty(internal::kSoloWindowHeaderKey); -} - -// static -bool FramePainter::UseSoloWindowHeaderInRoot(RootWindow* root_window, - Window* ignore_window) { - int visible_window_count = 0; - std::vector<Window*> windows = GetWindowsForSoloHeaderUpdate(root_window); - for (std::vector<Window*>::const_iterator it = windows.begin(); - it != windows.end(); - ++it) { - Window* window = *it; - // Various sorts of windows "don't count" for this computation. - if (ignore_window == window || - !IsSoloWindowHeaderCandidate(window) || - !IsVisibleToRoot(window)) - continue; - if (wm::IsWindowMaximized(window)) - return false; - ++visible_window_count; - if (visible_window_count > 1) - return false; - } - // Count must be tested because all windows might be "don't count" windows - // in the loop above. - return visible_window_count == 1; -} - -// static -void FramePainter::UpdateSoloWindowInRoot(RootWindow* root, - Window* ignore_window) { -#if defined(OS_WIN) - // Non-Ash Windows doesn't do solo-window counting for transparency effects, - // as the desktop background and window frames are managed by the OS. - if (!ash::Shell::HasInstance()) - return; -#endif - if (!root) - return; - bool old_solo_header = root->GetProperty(internal::kSoloWindowHeaderKey); - bool new_solo_header = UseSoloWindowHeaderInRoot(root, ignore_window); - if (old_solo_header == new_solo_header) - return; - root->SetProperty(internal::kSoloWindowHeaderKey, new_solo_header); - // Invalidate all the window frames in the desktop. There should only be - // a few. - std::vector<Window*> windows = GetWindowsForSoloHeaderUpdate(root); - for (std::vector<Window*>::const_iterator it = windows.begin(); - it != windows.end(); - ++it) { - Widget* widget = Widget::GetWidgetForNativeWindow(*it); - if (widget && widget->non_client_view()) - widget->non_client_view()->SchedulePaint(); - } -} - -void FramePainter::SchedulePaintForHeader() { - int top_left_height = top_left_corner_->height(); - int top_right_height = top_right_corner_->height(); - frame_->non_client_view()->SchedulePaintInRect( - gfx::Rect(0, 0, frame_->non_client_view()->width(), - std::max(top_left_height, top_right_height))); -} - -gfx::Rect FramePainter::GetTitleBounds(const gfx::Font& title_font) { - int title_x = GetTitleOffsetX(); - // Center the text with respect to the close button. This way it adapts to - // the caption height and aligns exactly with the window icon. Don't use - // |window_icon_| for this computation as it may be NULL. - int title_y = GetCloseButtonCenterY() - title_font.GetHeight() / 2; - return gfx::Rect( - title_x, - std::max(0, title_y), - std::max(0, size_button_->x() - kTitleLogoSpacing - title_x), - title_font.GetHeight()); -} - -} // namespace ash |