diff options
Diffstat (limited to 'chromium/ash/wm/caption_buttons/frame_caption_button_container_view.cc')
-rw-r--r-- | chromium/ash/wm/caption_buttons/frame_caption_button_container_view.cc | 276 |
1 files changed, 276 insertions, 0 deletions
diff --git a/chromium/ash/wm/caption_buttons/frame_caption_button_container_view.cc b/chromium/ash/wm/caption_buttons/frame_caption_button_container_view.cc new file mode 100644 index 00000000000..1ac3a2625ae --- /dev/null +++ b/chromium/ash/wm/caption_buttons/frame_caption_button_container_view.cc @@ -0,0 +1,276 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ash/wm/caption_buttons/frame_caption_button_container_view.h" + +#include "ash/ash_switches.h" +#include "ash/metrics/user_metrics_recorder.h" +#include "ash/shell.h" +#include "ash/wm/caption_buttons/alternate_frame_size_button.h" +#include "ash/wm/caption_buttons/frame_caption_button.h" +#include "ash/wm/caption_buttons/frame_maximize_button.h" +#include "grit/ash_resources.h" +#include "grit/ui_strings.h" // Accessibility names +#include "ui/base/hit_test.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/compositor/scoped_animation_duration_scale_mode.h" +#include "ui/gfx/canvas.h" +#include "ui/gfx/insets.h" +#include "ui/gfx/point.h" +#include "ui/views/controls/button/image_button.h" +#include "ui/views/widget/widget.h" +#include "ui/views/widget/widget_delegate.h" + +namespace ash { + +namespace { + +// The distance between buttons. +const int kDistanceBetweenButtons = -1; + +// Converts |point| from |src| to |dst| and hittests against |dst|. +bool ConvertPointToViewAndHitTest(const views::View* src, + const views::View* dst, + const gfx::Point& point) { + gfx::Point converted(point); + views::View::ConvertPointToTarget(src, dst, &converted); + return dst->HitTestPoint(converted); +} + +} // namespace + +// static +const char FrameCaptionButtonContainerView::kViewClassName[] = + "FrameCaptionButtonContainerView"; + +FrameCaptionButtonContainerView::FrameCaptionButtonContainerView( + views::Widget* frame, + MinimizeAllowed minimize_allowed) + : frame_(frame), + header_style_(HEADER_STYLE_SHORT), + minimize_button_(NULL), + size_button_(NULL), + close_button_(NULL) { + bool alternate_style = switches::UseAlternateFrameCaptionButtonStyle(); + + // Insert the buttons left to right. + minimize_button_ = new FrameCaptionButton(this, CAPTION_BUTTON_ICON_MINIMIZE); + minimize_button_->SetAccessibleName( + l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MINIMIZE)); + // Hide |minimize_button_| when using the non-alternate button style because + // |size_button_| is capable of minimizing in this case. + minimize_button_->SetVisible( + minimize_allowed == MINIMIZE_ALLOWED && + (alternate_style || !frame_->widget_delegate()->CanMaximize())); + AddChildView(minimize_button_); + + if (alternate_style) + size_button_ = new AlternateFrameSizeButton(this, frame, this); + else + size_button_ = new FrameMaximizeButton(this, frame); + size_button_->SetAccessibleName( + l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MAXIMIZE)); + size_button_->SetVisible(frame_->widget_delegate()->CanMaximize()); + AddChildView(size_button_); + + close_button_ = new FrameCaptionButton(this, CAPTION_BUTTON_ICON_CLOSE); + close_button_->SetAccessibleName( + l10n_util::GetStringUTF16(IDS_APP_ACCNAME_CLOSE)); + AddChildView(close_button_); + + button_separator_ = ui::ResourceBundle::GetSharedInstance().GetImageNamed( + IDR_AURA_WINDOW_BUTTON_SEPARATOR).AsImageSkia(); +} + +FrameCaptionButtonContainerView::~FrameCaptionButtonContainerView() { +} + +FrameMaximizeButton* +FrameCaptionButtonContainerView::GetOldStyleSizeButton() { + return switches::UseAlternateFrameCaptionButtonStyle() ? + NULL : static_cast<FrameMaximizeButton*>(size_button_); +} + +void FrameCaptionButtonContainerView::ResetWindowControls() { + SetButtonsToNormal(ANIMATE_NO); +} + +int FrameCaptionButtonContainerView::NonClientHitTest( + const gfx::Point& point) const { + if (close_button_->visible() && + ConvertPointToViewAndHitTest(this, close_button_, point)) { + return HTCLOSE; + } else if (size_button_->visible() && + ConvertPointToViewAndHitTest(this, size_button_, point)) { + return HTMAXBUTTON; + } else if (minimize_button_->visible() && + ConvertPointToViewAndHitTest(this, minimize_button_, point)) { + return HTMINBUTTON; + } + return HTNOWHERE; +} + +gfx::Size FrameCaptionButtonContainerView::GetPreferredSize() { + int width = 0; + bool first_visible = true; + for (int i = 0; i < child_count(); ++i) { + views::View* child = child_at(i); + if (!child->visible()) + continue; + + width += child_at(i)->GetPreferredSize().width(); + if (!first_visible) + width += kDistanceBetweenButtons; + first_visible = false; + } + return gfx::Size(width, close_button_->GetPreferredSize().height()); +} + +void FrameCaptionButtonContainerView::Layout() { + FrameCaptionButton::Style style = FrameCaptionButton::STYLE_SHORT_RESTORED; + if (header_style_ == HEADER_STYLE_SHORT) { + if (frame_->IsMaximized() || frame_->IsFullscreen()) + style = FrameCaptionButton::STYLE_SHORT_MAXIMIZED_OR_FULLSCREEN; + // Else: FrameCaptionButton::STYLE_SHORT_RESTORED; + } else { + style = FrameCaptionButton::STYLE_TALL_RESTORED; + } + + minimize_button_->SetStyle(style); + size_button_->SetStyle(style); + close_button_->SetStyle(style); + + int x = 0; + for (int i = 0; i < child_count(); ++i) { + views::View* child = child_at(i); + if (!child->visible()) + continue; + + gfx::Size size = child->GetPreferredSize(); + child->SetBounds(x, 0, size.width(), size.height()); + x += size.width() + kDistanceBetweenButtons; + } +} + +const char* FrameCaptionButtonContainerView::GetClassName() const { + return kViewClassName; +} + +void FrameCaptionButtonContainerView::OnPaint(gfx::Canvas* canvas) { + views::View::OnPaint(canvas); + + // The alternate button style does not paint the button separator. + if (!switches::UseAlternateFrameCaptionButtonStyle()) { + // We should have at most two visible buttons. The button separator is + // always painted underneath the close button regardless of whether a + // button other than the close button is visible. + gfx::Rect divider(close_button_->bounds().origin(), + button_separator_.size()); + canvas->DrawImageInt(button_separator_, + GetMirroredXForRect(divider), + divider.y()); + } +} + +void FrameCaptionButtonContainerView::ButtonPressed(views::Button* sender, + const ui::Event& event) { + // When shift-clicking, slow down animations for visual debugging. + // We used to do this via an event filter that looked for the shift key being + // pressed but this interfered with several normal keyboard shortcuts. + scoped_ptr<ui::ScopedAnimationDurationScaleMode> slow_duration_mode; + if (event.IsShiftDown()) { + slow_duration_mode.reset(new ui::ScopedAnimationDurationScaleMode( + ui::ScopedAnimationDurationScaleMode::SLOW_DURATION)); + } + + // Abort any animations of the button icons. + SetButtonsToNormal(ANIMATE_NO); + + ash::UserMetricsAction action = + ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_MINIMIZE; + if (sender == minimize_button_) { + frame_->Minimize(); + } else if (sender == size_button_) { + if (frame_->IsFullscreen()) { // Can be clicked in immersive fullscreen. + frame_->SetFullscreen(false); + action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_EXIT_FULLSCREEN; + } else if (frame_->IsMaximized()) { + frame_->Restore(); + action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_RESTORE; + } else { + frame_->Maximize(); + action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_MAXIMIZE; + } + } else if(sender == close_button_) { + frame_->Close(); + action = ash::UMA_WINDOW_CLOSE_BUTTON_CLICK; + } else { + return; + } + ash::Shell::GetInstance()->metrics()->RecordUserMetricsAction(action); +} + +bool FrameCaptionButtonContainerView::IsMinimizeButtonVisible() const { + return minimize_button_->visible(); +} + +void FrameCaptionButtonContainerView::SetButtonsToNormal(Animate animate) { + SetButtonIcons(CAPTION_BUTTON_ICON_MINIMIZE, CAPTION_BUTTON_ICON_CLOSE, + animate); + minimize_button_->SetState(views::Button::STATE_NORMAL); + size_button_->SetState(views::Button::STATE_NORMAL); + close_button_->SetState(views::Button::STATE_NORMAL); +} + +void FrameCaptionButtonContainerView::SetButtonIcons( + CaptionButtonIcon minimize_button_icon, + CaptionButtonIcon close_button_icon, + Animate animate) { + FrameCaptionButton::Animate fcb_animate = (animate == ANIMATE_YES) ? + FrameCaptionButton::ANIMATE_YES : FrameCaptionButton::ANIMATE_NO; + minimize_button_->SetIcon(minimize_button_icon, fcb_animate); + close_button_->SetIcon(close_button_icon, fcb_animate); +} + +const FrameCaptionButton* +FrameCaptionButtonContainerView::PressButtonAt( + const gfx::Point& position_in_screen, + const gfx::Insets& pressed_hittest_outer_insets) const { + DCHECK(switches::UseAlternateFrameCaptionButtonStyle()); + gfx::Point position(position_in_screen); + views::View::ConvertPointFromScreen(this, &position); + + FrameCaptionButton* buttons[] = { + close_button_, size_button_, minimize_button_ + }; + FrameCaptionButton* pressed_button = NULL; + for (size_t i = 0; i < arraysize(buttons); ++i) { + FrameCaptionButton* button = buttons[i]; + if (!button->visible()) + continue; + + if (button->state() == views::Button::STATE_PRESSED) { + gfx::Rect expanded_bounds = button->bounds(); + expanded_bounds.Inset(pressed_hittest_outer_insets); + if (expanded_bounds.Contains(position)) { + pressed_button = button; + // Do not break in order to give preference to buttons which are + // closer to |position_in_screen| than the currently pressed button. + // TODO(pkotwicz): Make the caption buttons not overlap. + } + } else if (ConvertPointToViewAndHitTest(this, button, position)) { + pressed_button = button; + break; + } + } + + for (size_t i = 0; i < arraysize(buttons); ++i) { + buttons[i]->SetState(buttons[i] == pressed_button ? + views::Button::STATE_PRESSED : views::Button::STATE_NORMAL); + } + return pressed_button; +} + +} // namespace ash |