blob: 8807eb83c753c4ee00a8757337fa87e34dfbe808 [file] [log] [blame]
// Copyright 2018 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/frame/frame_header.h"
#include "ash/frame/caption_buttons/caption_button_model.h"
#include "ash/frame/caption_buttons/frame_caption_button_container_view.h"
#include "ash/frame/frame_header_util.h"
#include "ash/public/cpp/ash_layout_constants.h"
#include "ash/public/cpp/vector_icons/vector_icons.h"
#include "ash/public/cpp/window_properties.h"
#include "ash/resources/vector_icons/vector_icons.h"
#include "base/logging.h" // DCHECK
#include "ui/gfx/canvas.h"
#include "ui/gfx/color_utils.h"
#include "ui/gfx/font_list.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/scoped_canvas.h"
#include "ui/views/view.h"
#include "ui/views/widget/native_widget_aura.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_delegate.h"
namespace ash {
namespace {
// Duration of crossfade animation for activating and deactivating frame.
const int kActivationCrossfadeDurationMs = 200;
} // namespace
///////////////////////////////////////////////////////////////////////////////
// FrameHeader, public:
FrameHeader::~FrameHeader() = default;
int FrameHeader::GetMinimumHeaderWidth() const {
// Ensure we have enough space for the window icon and buttons. We allow
// the title string to collapse to zero width.
return GetTitleBounds().x() +
caption_button_container_->GetMinimumSize().width();
}
void FrameHeader::PaintHeader(gfx::Canvas* canvas, Mode mode) {
Mode old_mode = mode_;
mode_ = mode;
if (mode_ != old_mode) {
UpdateCaptionButtonColors();
if (!initial_paint_ &&
FrameHeaderUtil::CanAnimateActivation(target_widget_)) {
activation_animation_.SetSlideDuration(kActivationCrossfadeDurationMs);
if (mode_ == MODE_ACTIVE)
activation_animation_.Show();
else
activation_animation_.Hide();
} else {
if (mode_ == MODE_ACTIVE)
activation_animation_.Reset(1);
else
activation_animation_.Reset(0);
}
initial_paint_ = false;
}
DoPaintHeader(canvas);
}
void FrameHeader::LayoutHeader() {
LayoutHeaderInternal();
// Default to the header height; owning code may override via
// SetHeaderHeightForPainting().
painted_height_ = GetHeaderHeight();
}
int FrameHeader::GetHeaderHeight() const {
return caption_button_container_->height();
}
int FrameHeader::GetHeaderHeightForPainting() const {
return painted_height_;
}
void FrameHeader::SetHeaderHeightForPainting(int height) {
painted_height_ = height;
}
void FrameHeader::SchedulePaintForTitle() {
view_->SchedulePaintInRect(view_->GetMirroredRect(GetTitleBounds()));
}
void FrameHeader::SetPaintAsActive(bool paint_as_active) {
caption_button_container_->SetPaintAsActive(paint_as_active);
if (back_button_)
back_button_->set_paint_as_active(paint_as_active);
UpdateCaptionButtonColors();
}
void FrameHeader::SetWidthInPixels(int width_in_pixels) {
NOTREACHED() << "Frame does not support drawing width in pixels";
}
void FrameHeader::OnShowStateChanged(ui::WindowShowState show_state) {
if (show_state == ui::SHOW_STATE_MINIMIZED)
return;
LayoutHeaderInternal();
}
void FrameHeader::SetLeftHeaderView(views::View* left_header_view) {
left_header_view_ = left_header_view;
}
void FrameHeader::SetBackButton(FrameCaptionButton* back_button) {
back_button_ = back_button;
if (back_button_) {
back_button_->SetColorMode(GetButtonColorMode());
back_button_->SetBackgroundColor(GetCurrentFrameColor());
back_button_->SetImage(CAPTION_BUTTON_ICON_BACK,
FrameCaptionButton::ANIMATE_NO,
kWindowControlBackIcon);
}
}
FrameCaptionButton* FrameHeader::GetBackButton() const {
return back_button_;
}
void FrameHeader::SetFrameColors(SkColor active_frame_color,
SkColor inactive_frame_color) {
DoSetFrameColors(active_frame_color, inactive_frame_color);
}
void FrameHeader::SetFrameTextOverride(
const base::string16& frame_text_override) {
frame_text_override_ = frame_text_override;
SchedulePaintForTitle();
}
///////////////////////////////////////////////////////////////////////////////
// gfx::AnimationDelegate overrides:
void FrameHeader::AnimationProgressed(const gfx::Animation* animation) {
view_->SchedulePaintInRect(GetPaintedBounds());
}
///////////////////////////////////////////////////////////////////////////////
// FrameHeader, protected:
FrameHeader::FrameHeader(views::Widget* target_widget, views::View* view)
: target_widget_(target_widget), view_(view) {
DCHECK(target_widget);
DCHECK(view);
}
gfx::Rect FrameHeader::GetPaintedBounds() const {
return gfx::Rect(view_->width(), painted_height_);
}
void FrameHeader::UpdateCaptionButtonColors() {
auto button_color_mode = GetButtonColorMode();
caption_button_container_->SetColorMode(button_color_mode);
caption_button_container_->SetBackgroundColor(GetCurrentFrameColor());
if (back_button_) {
back_button_->SetColorMode(button_color_mode);
back_button_->SetBackgroundColor(GetCurrentFrameColor());
}
}
void FrameHeader::PaintTitleBar(gfx::Canvas* canvas) {
base::string16 text = frame_text_override_;
views::WidgetDelegate* target_widget_delegate =
target_widget_->widget_delegate();
if (text.empty() && target_widget_delegate &&
target_widget_delegate->ShouldShowWindowTitle()) {
text = target_widget_delegate->GetWindowTitle();
}
if (!text.empty()) {
canvas->DrawStringRectWithFlags(
text, views::NativeWidgetAura::GetWindowTitleFontList(),
GetTitleColor(), view_->GetMirroredRect(GetTitleBounds()),
gfx::Canvas::NO_SUBPIXEL_RENDERING);
}
}
void FrameHeader::SetCaptionButtonContainer(
FrameCaptionButtonContainerView* caption_button_container) {
caption_button_container_ = caption_button_container;
caption_button_container_->SetButtonImage(CAPTION_BUTTON_ICON_MINIMIZE,
kWindowControlMinimizeIcon);
caption_button_container_->SetButtonImage(CAPTION_BUTTON_ICON_MENU,
kWindowControlMenuIcon);
caption_button_container_->SetButtonImage(CAPTION_BUTTON_ICON_CLOSE,
kWindowControlCloseIcon);
caption_button_container_->SetButtonImage(CAPTION_BUTTON_ICON_LEFT_SNAPPED,
kWindowControlLeftSnappedIcon);
caption_button_container_->SetButtonImage(CAPTION_BUTTON_ICON_RIGHT_SNAPPED,
kWindowControlRightSnappedIcon);
}
///////////////////////////////////////////////////////////////////////////////
// FrameHeader, private:
void FrameHeader::LayoutHeaderInternal() {
bool use_zoom_icons = caption_button_container()->model()->InZoomMode();
const gfx::VectorIcon& restore_icon =
use_zoom_icons ? kWindowControlDezoomIcon : kWindowControlRestoreIcon;
const gfx::VectorIcon& maximize_icon =
use_zoom_icons ? kWindowControlZoomIcon : kWindowControlMaximizeIcon;
const gfx::VectorIcon& icon =
target_widget_->IsMaximized() || target_widget_->IsFullscreen()
? restore_icon
: maximize_icon;
caption_button_container()->SetButtonImage(
CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE, icon);
caption_button_container()->SetButtonSize(
GetAshLayoutSize(GetButtonLayoutSize()));
const gfx::Size caption_button_container_size =
caption_button_container()->GetPreferredSize();
caption_button_container()->SetBounds(
view_->width() - caption_button_container_size.width(), 0,
caption_button_container_size.width(),
caption_button_container_size.height());
caption_button_container()->Layout();
// Update clients on the bounds of the caption button only if the bounds have
// changed.
const gfx::Rect* old_bounds_value =
target_widget()->GetNativeWindow()->GetProperty(
ash::kCaptionButtonBoundsKey);
// The bounds are relative to the top right, rather than the normal top left.
gfx::Rect new_bounds_value =
caption_button_container()->bounds() - gfx::Vector2d(view_->width(), 0);
if (!old_bounds_value || new_bounds_value != *old_bounds_value) {
target_widget()->GetNativeWindow()->SetProperty(
ash::kCaptionButtonBoundsKey, new gfx::Rect(new_bounds_value));
}
int origin = 0;
if (back_button_) {
gfx::Size size = back_button_->GetPreferredSize();
back_button_->SetBounds(0, 0, size.width(),
caption_button_container_size.height());
origin = back_button_->bounds().right();
}
if (left_header_view_) {
// Vertically center the left header view (typically the window icon) with
// respect to the caption button container.
const gfx::Size icon_size(left_header_view_->GetPreferredSize());
const int icon_offset_y = (GetHeaderHeight() - icon_size.height()) / 2;
left_header_view_->SetBounds(FrameHeaderUtil::GetLeftViewXInset() + origin,
icon_offset_y, icon_size.width(),
icon_size.height());
}
}
gfx::Rect FrameHeader::GetTitleBounds() const {
views::View* left_view = left_header_view_ ? left_header_view_ : back_button_;
return FrameHeaderUtil::GetAvailableTitleBounds(
left_view, caption_button_container_, GetHeaderHeight());
}
FrameCaptionButton::ColorMode FrameHeader::GetButtonColorMode() {
return target_widget()->GetNativeWindow()->GetProperty(
ash::kFrameIsThemedByHostedAppKey)
? FrameCaptionButton::ColorMode::kThemed
: FrameCaptionButton::ColorMode::kDefault;
}
} // namespace ash