diff options
author | pkotwicz@chromium.org <pkotwicz@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-24 00:33:20 +0000 |
---|---|---|
committer | pkotwicz@chromium.org <pkotwicz@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-24 00:33:20 +0000 |
commit | 87625ab4b1771132b3908f445ce8e3d84f83774b (patch) | |
tree | 52c1bae27dcfd53aa4f8c44b61198a39e98839a7 | |
parent | 50f53ac54c5c3beaed05bb09f6db7da568a95550 (diff) | |
download | chromium_src-87625ab4b1771132b3908f445ce8e3d84f83774b.zip chromium_src-87625ab4b1771132b3908f445ce8e3d84f83774b.tar.gz chromium_src-87625ab4b1771132b3908f445ce8e3d84f83774b.tar.bz2 |
Make PanelFrameView, CustomFrameViewAsh and BrowserNonClientFrameViewAsh use FrameCaptionButtonContainerView.
TEST=FrameCaptionButtonContainerViewTest.*
BUG=274171
Review URL: https://chromiumcodereview.appspot.com/23202005
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@219391 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | ash/wm/custom_frame_view_ash.cc | 70 | ||||
-rw-r--r-- | ash/wm/custom_frame_view_ash.h | 37 | ||||
-rw-r--r-- | ash/wm/custom_frame_view_ash_unittest.cc | 248 | ||||
-rw-r--r-- | ash/wm/frame_painter.cc | 175 | ||||
-rw-r--r-- | ash/wm/frame_painter.h | 23 | ||||
-rw-r--r-- | ash/wm/frame_painter_unittest.cc | 114 | ||||
-rw-r--r-- | ash/wm/panels/panel_frame_view.cc | 35 | ||||
-rw-r--r-- | ash/wm/panels/panel_frame_view.h | 11 | ||||
-rw-r--r-- | ash/wm/workspace/frame_caption_button_container_view.cc | 147 | ||||
-rw-r--r-- | ash/wm/workspace/frame_caption_button_container_view.h | 49 | ||||
-rw-r--r-- | ash/wm/workspace/frame_caption_button_container_view_unittest.cc | 269 | ||||
-rw-r--r-- | chrome/browser/ui/views/frame/app_non_client_frame_view_ash.cc | 6 | ||||
-rw-r--r-- | chrome/browser/ui/views/frame/app_non_client_frame_view_ash.h | 6 | ||||
-rw-r--r-- | chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc | 89 | ||||
-rw-r--r-- | chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h | 17 | ||||
-rw-r--r-- | chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc | 17 |
16 files changed, 633 insertions, 680 deletions
diff --git a/ash/wm/custom_frame_view_ash.cc b/ash/wm/custom_frame_view_ash.cc index f7090a4..7824464 100644 --- a/ash/wm/custom_frame_view_ash.cc +++ b/ash/wm/custom_frame_view_ash.cc @@ -4,21 +4,15 @@ #include "ash/wm/custom_frame_view_ash.h" -#include "ash/shell_delegate.h" #include "ash/wm/frame_painter.h" -#include "ash/wm/workspace/frame_maximize_button.h" +#include "ash/wm/workspace/frame_caption_button_container_view.h" #include "grit/ash_resources.h" -#include "grit/ui_strings.h" // Accessibility names -#include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" -#include "ui/compositor/layer_animator.h" -#include "ui/compositor/scoped_animation_duration_scale_mode.h" #include "ui/gfx/canvas.h" #include "ui/gfx/font.h" #include "ui/gfx/image/image.h" #include "ui/gfx/rect.h" #include "ui/gfx/size.h" -#include "ui/views/controls/button/image_button.h" #include "ui/views/widget/native_widget_aura.h" #include "ui/views/widget/widget.h" #include "ui/views/widget/widget_delegate.h" @@ -43,8 +37,7 @@ const char CustomFrameViewAsh::kViewClassName[] = "CustomFrameViewAsh"; // CustomFrameViewAsh, public: CustomFrameViewAsh::CustomFrameViewAsh() : frame_(NULL), - maximize_button_(NULL), - close_button_(NULL), + caption_button_container_(NULL), frame_painter_(new ash::FramePainter) { } @@ -54,19 +47,17 @@ CustomFrameViewAsh::~CustomFrameViewAsh() { void CustomFrameViewAsh::Init(views::Widget* frame) { frame_ = frame; - maximize_button_ = new FrameMaximizeButton(this, this); - maximize_button_->SetAccessibleName( - l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MAXIMIZE)); - AddChildView(maximize_button_); - close_button_ = new views::ImageButton(this); - close_button_->SetAccessibleName( - l10n_util::GetStringUTF16(IDS_APP_ACCNAME_CLOSE)); - AddChildView(close_button_); + // Unfortunately, there is no views::WidgetDelegate::CanMinimize(). Assume + // that the window frame can be minimized if it can be maximized. + FrameCaptionButtonContainerView::MinimizeAllowed minimize_allowed = + frame_->widget_delegate()->CanMaximize() ? + FrameCaptionButtonContainerView::MINIMIZE_ALLOWED : + FrameCaptionButtonContainerView::MINIMIZE_DISALLOWED; + caption_button_container_ = new FrameCaptionButtonContainerView(this, frame, + minimize_allowed); + AddChildView(caption_button_container_); - maximize_button_->SetVisible(frame_->widget_delegate()->CanMaximize()); - - frame_painter_->Init(frame_, NULL, maximize_button_, close_button_, - FramePainter::SIZE_BUTTON_MAXIMIZES); + frame_painter_->Init(frame_, NULL, caption_button_container_); } //////////////////////////////////////////////////////////////////////////////// @@ -94,7 +85,7 @@ void CustomFrameViewAsh::GetWindowMask(const gfx::Size& size, } void CustomFrameViewAsh::ResetWindowControls() { - maximize_button_->SetState(views::CustomButton::STATE_NORMAL); + caption_button_container_->ResetWindowControls(); } void CustomFrameViewAsh::UpdateWindowIcon() { @@ -161,39 +152,6 @@ gfx::Size CustomFrameViewAsh::GetMaximumSize() { } //////////////////////////////////////////////////////////////////////////////// -// views::ButtonListener overrides: -void CustomFrameViewAsh::ButtonPressed(views::Button* sender, - const ui::Event& event) { - scoped_ptr<ui::ScopedAnimationDurationScaleMode> slow_duration_mode; - if (event.IsShiftDown()) { - slow_duration_mode.reset(new ui::ScopedAnimationDurationScaleMode( - ui::ScopedAnimationDurationScaleMode::SLOW_DURATION)); - } - - ash::UserMetricsAction action = - ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_MAXIMIZE; - - if (sender == maximize_button_) { - // The maximize button may move out from under the cursor. - ResetWindowControls(); - if (frame_->IsMaximized()) { - action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_RESTORE; - frame_->Restore(); - } else { - frame_->Maximize(); - } - // |this| may be deleted - some windows delete their frames on maximize. - } else if (sender == close_button_) { - action = ash::UMA_WINDOW_CLOSE_BUTTON_CLICK; - frame_->Close(); - } else { - return; - } - - ash::Shell::GetInstance()->delegate()->RecordUserMetricsAction(action); -} - -//////////////////////////////////////////////////////////////////////////////// // CustomFrameViewAsh, private: int CustomFrameViewAsh::NonClientTopBorderHeight() const { @@ -202,7 +160,7 @@ int CustomFrameViewAsh::NonClientTopBorderHeight() const { // Reserve enough space to see the buttons, including any offset from top and // reserving space for the separator line. - return close_button_->bounds().bottom() + + return caption_button_container_->bounds().bottom() + frame_painter_->HeaderContentSeparatorSize(); } diff --git a/ash/wm/custom_frame_view_ash.h b/ash/wm/custom_frame_view_ash.h index 80525c3..57526b4 100644 --- a/ash/wm/custom_frame_view_ash.h +++ b/ash/wm/custom_frame_view_ash.h @@ -12,7 +12,6 @@ namespace ash { class FramePainter; -class FrameMaximizeButton; } namespace gfx { class Font; @@ -24,10 +23,15 @@ class Widget; namespace ash { +class FrameCaptionButtonContainerView; + +namespace test { +class CustomFrameViewAshTest; +} + // A NonClientFrameView used for dialogs and other non-browser windows. // See also views::CustomFrameView and BrowserNonClientFrameViewAsh. -class ASH_EXPORT CustomFrameViewAsh : public views::NonClientFrameView, - public views::ButtonListener { +class ASH_EXPORT CustomFrameViewAsh : public views::NonClientFrameView { public: // Internal class name. static const char kViewClassName[]; @@ -35,25 +39,8 @@ class ASH_EXPORT CustomFrameViewAsh : public views::NonClientFrameView, CustomFrameViewAsh(); virtual ~CustomFrameViewAsh(); - // For testing. - class TestApi { - public: - explicit TestApi(CustomFrameViewAsh* frame) : frame_(frame) { - } - - ash::FrameMaximizeButton* maximize_button() const { - return frame_->maximize_button_; - } - - private: - TestApi(); - CustomFrameViewAsh* frame_; - }; - void Init(views::Widget* frame); - views::ImageButton* close_button() { return close_button_; } - // views::NonClientFrameView overrides: virtual gfx::Rect GetBoundsForClientView() const OVERRIDE; virtual gfx::Rect GetWindowBoundsForClientBounds( @@ -73,19 +60,17 @@ class ASH_EXPORT CustomFrameViewAsh : public views::NonClientFrameView, virtual gfx::Size GetMinimumSize() OVERRIDE; virtual gfx::Size GetMaximumSize() OVERRIDE; - // views::ButtonListener overrides: - virtual void ButtonPressed(views::Button* sender, - const ui::Event& event) OVERRIDE; - private: + friend class test::CustomFrameViewAshTest; + // Height from top of window to top of client area. int NonClientTopBorderHeight() const; // Not owned. views::Widget* frame_; - ash::FrameMaximizeButton* maximize_button_; - views::ImageButton* close_button_; + // View which contains the window controls. + FrameCaptionButtonContainerView* caption_button_container_; scoped_ptr<FramePainter> frame_painter_; diff --git a/ash/wm/custom_frame_view_ash_unittest.cc b/ash/wm/custom_frame_view_ash_unittest.cc index c087046..8036cc6 100644 --- a/ash/wm/custom_frame_view_ash_unittest.cc +++ b/ash/wm/custom_frame_view_ash_unittest.cc @@ -9,6 +9,7 @@ #include "ash/wm/maximize_bubble_controller.h" #include "ash/wm/property_util.h" #include "ash/wm/window_util.h" +#include "ash/wm/workspace/frame_caption_button_container_view.h" #include "ash/wm/workspace/frame_maximize_button.h" #include "ash/wm/workspace/snap_sizer.h" #include "base/command_line.h" @@ -20,16 +21,14 @@ #include "ui/base/events/event_utils.h" #include "ui/base/gestures/gesture_configuration.h" #include "ui/views/controls/button/image_button.h" -#include "ui/views/test/test_views_delegate.h" #include "ui/views/widget/widget.h" #include "ui/views/widget/widget_delegate.h" namespace ash { -namespace internal { +namespace test { namespace { - class CancelCallbackHandler { public: CancelCallbackHandler(int update_events_before_cancel, @@ -60,28 +59,6 @@ class CancelCallbackHandler { DISALLOW_COPY_AND_ASSIGN(CancelCallbackHandler); }; -class ShellViewsDelegate : public views::TestViewsDelegate { - public: - ShellViewsDelegate() {} - virtual ~ShellViewsDelegate() {} - - // Overridden from views::TestViewsDelegate: - virtual views::NonClientFrameView* CreateDefaultNonClientFrameView( - views::Widget* widget) OVERRIDE { - // Always test CustomFrameViewAsh, which may not be the ash::Shell default. - CustomFrameViewAsh* frame_view = new CustomFrameViewAsh; - frame_view->Init(widget); - return frame_view; - } - virtual bool UseTransparentWindows() const OVERRIDE { - // Ash uses transparent window frames. - return true; - } - - private: - DISALLOW_COPY_AND_ASSIGN(ShellViewsDelegate); -}; - class TestWidgetDelegate : public views::WidgetDelegateView { public: TestWidgetDelegate() {} @@ -97,6 +74,13 @@ class TestWidgetDelegate : public views::WidgetDelegateView { virtual bool CanMaximize() const OVERRIDE { return true; } + virtual views::NonClientFrameView* CreateNonClientFrameView( + views::Widget* widget) OVERRIDE { + // Always test CustomFrameViewAsh, which may not be the ash::Shell default. + CustomFrameViewAsh* frame_view = new CustomFrameViewAsh; + frame_view->Init(widget); + return frame_view; + } private: DISALLOW_COPY_AND_ASSIGN(TestWidgetDelegate); @@ -114,34 +98,43 @@ class CustomFrameViewAshTest : public ash::test::AshTestBase { views::Widget* widget = new views::Widget; params.context = CurrentContext(); params.delegate = new TestWidgetDelegate; + params.bounds = gfx::Rect(10, 10, 100, 100); + params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; widget->Init(params); widget->Show(); return widget; } - CustomFrameViewAsh* GetCustomFrameViewAsh(views::Widget* widget) const { - return static_cast<CustomFrameViewAsh*>(widget->non_client_view()-> - frame_view()); + void CloseWidget() { + if (widget_) + widget_->CloseNow(); + widget_ = NULL; } virtual void SetUp() OVERRIDE { AshTestBase::SetUp(); - if (!views::ViewsDelegate::views_delegate) { - views_delegate_.reset(new ShellViewsDelegate); - views::ViewsDelegate::views_delegate = views_delegate_.get(); - } + + widget_ = CreateWidget(); + CustomFrameViewAsh* frame = static_cast<CustomFrameViewAsh*>( + widget()->non_client_view()->frame_view()); + FrameCaptionButtonContainerView::TestApi test( + frame->caption_button_container_); + maximize_button_ = static_cast<FrameMaximizeButton*>( + test.size_button()); } virtual void TearDown() OVERRIDE { - if (views_delegate_) { - views::ViewsDelegate::views_delegate = NULL; - views_delegate_.reset(); - } + CloseWidget(); AshTestBase::TearDown(); } + views::Widget* widget() { return widget_; } + + FrameMaximizeButton* maximize_button() { return maximize_button_; } + private: - scoped_ptr<views::ViewsDelegate> views_delegate_; + views::Widget* widget_; + FrameMaximizeButton* maximize_button_; DISALLOW_COPY_AND_ASSIGN(CustomFrameViewAshTest); }; @@ -149,11 +142,8 @@ class CustomFrameViewAshTest : public ash::test::AshTestBase { // Tests that clicking on the resize-button toggles between maximize and normal // state. TEST_F(CustomFrameViewAshTest, ResizeButtonToggleMaximize) { - views::Widget* widget = CreateWidget(); - aura::Window* window = widget->GetNativeWindow(); - CustomFrameViewAsh* frame = GetCustomFrameViewAsh(widget); - CustomFrameViewAsh::TestApi test(frame); - views::View* view = test.maximize_button(); + aura::Window* window = widget()->GetNativeWindow(); + views::View* view = maximize_button(); gfx::Point center = view->GetBoundsInScreen().CenterPoint(); aura::test::EventGenerator generator(window->GetRootWindow(), center); @@ -181,8 +171,6 @@ TEST_F(CustomFrameViewAshTest, ResizeButtonToggleMaximize) { generator.GestureTapDownAndUp(view->GetBoundsInScreen().CenterPoint()); EXPECT_FALSE(ash::wm::IsWindowMaximized(window)); - - widget->Close(); } #if defined(OS_WIN) @@ -194,11 +182,8 @@ TEST_F(CustomFrameViewAshTest, ResizeButtonToggleMaximize) { // Tests that click+dragging on the resize-button tiles or minimizes the window. TEST_F(CustomFrameViewAshTest, MAYBE_ResizeButtonDrag) { - views::Widget* widget = CreateWidget(); - aura::Window* window = widget->GetNativeWindow(); - CustomFrameViewAsh* frame = GetCustomFrameViewAsh(widget); - CustomFrameViewAsh::TestApi test(frame); - views::View* view = test.maximize_button(); + aura::Window* window = widget()->GetNativeWindow(); + views::View* view = maximize_button(); gfx::Point center = view->GetBoundsInScreen().CenterPoint(); aura::test::EventGenerator generator(window->GetRootWindow(), center); @@ -302,8 +287,6 @@ TEST_F(CustomFrameViewAshTest, MAYBE_ResizeButtonDrag) { } // Test with gesture events. - - widget->Close(); } #if defined(OS_WIN) @@ -319,16 +302,13 @@ TEST_F(CustomFrameViewAshTest, MAYBE_ResizeButtonDrag) { // trigger dependent on the available drag distance. TEST_F(CustomFrameViewAshTest, MAYBE_TouchDragResizeCloseToCornerDiffersFromMouse) { - views::Widget* widget = CreateWidget(); - aura::Window* window = widget->GetNativeWindow(); - CustomFrameViewAsh* frame = GetCustomFrameViewAsh(widget); - CustomFrameViewAsh::TestApi test(frame); - views::View* view = test.maximize_button(); + aura::Window* window = widget()->GetNativeWindow(); + views::View* view = maximize_button(); - gfx::Rect work_area = widget->GetWorkAreaBoundsInScreen(); + gfx::Rect work_area = widget()->GetWorkAreaBoundsInScreen(); gfx::Rect bounds = window->bounds(); bounds.set_x(work_area.width() - bounds.width()); - widget->SetBounds(bounds); + widget()->SetBounds(bounds); gfx::Point start_point = view->GetBoundsInScreen().CenterPoint(); // We want to move all the way to the right (the few pixels we have). @@ -351,7 +331,7 @@ TEST_F(CustomFrameViewAshTest, EXPECT_NE(bounds.ToString(), touch_result.ToString()); // Set the position back to where it was before and re-try with a mouse. - widget->SetBounds(bounds); + widget()->SetBounds(bounds); generator.MoveMouseTo(start_point); generator.PressLeftButton(); @@ -370,16 +350,13 @@ TEST_F(CustomFrameViewAshTest, EXPECT_GT(mouse_result.width(), touch_result.width()); } - // Test that closing the (browser) window with an opened balloon does not // crash the system. In other words: Make sure that shutting down the frame // destroys the opened balloon in an orderly fashion. TEST_F(CustomFrameViewAshTest, MaximizeButtonExternalShutDown) { - views::Widget* widget = CreateWidget(); - aura::Window* window = widget->GetNativeWindow(); - CustomFrameViewAsh* frame = GetCustomFrameViewAsh(widget); - CustomFrameViewAsh::TestApi test(frame); - ash::FrameMaximizeButton* maximize_button = test.maximize_button(); + aura::Window* window = widget()->GetNativeWindow(); + ash::FrameMaximizeButton* maximize_button = + CustomFrameViewAshTest::maximize_button(); maximize_button->set_bubble_appearance_delay_ms(0); gfx::Point button_pos = maximize_button->GetBoundsInScreen().CenterPoint(); gfx::Point off_pos(button_pos.x() + 100, button_pos.y() + 100); @@ -394,17 +371,15 @@ TEST_F(CustomFrameViewAshTest, MaximizeButtonExternalShutDown) { // Even though the widget is closing the bubble menu should not crash upon // its delayed destruction. - widget->CloseNow(); + CloseWidget(); } // Test that maximizing the browser after hovering in does not crash the system // when the observer gets removed in the bubble destruction process. TEST_F(CustomFrameViewAshTest, MaximizeOnHoverThenClick) { - views::Widget* widget = CreateWidget(); - aura::Window* window = widget->GetNativeWindow(); - CustomFrameViewAsh* frame = GetCustomFrameViewAsh(widget); - CustomFrameViewAsh::TestApi test(frame); - ash::FrameMaximizeButton* maximize_button = test.maximize_button(); + aura::Window* window = widget()->GetNativeWindow(); + ash::FrameMaximizeButton* maximize_button = + CustomFrameViewAshTest::maximize_button(); maximize_button->set_bubble_appearance_delay_ms(0); gfx::Point button_pos = maximize_button->GetBoundsInScreen().CenterPoint(); gfx::Point off_pos(button_pos.x() + 100, button_pos.y() + 100); @@ -425,11 +400,9 @@ TEST_F(CustomFrameViewAshTest, MaximizeOnHoverThenClick) { // pressing and dragging the button itself off the button will also release the // phantom window. TEST_F(CustomFrameViewAshTest, MaximizeLeftButtonDragOut) { - views::Widget* widget = CreateWidget(); - aura::Window* window = widget->GetNativeWindow(); - CustomFrameViewAsh* frame = GetCustomFrameViewAsh(widget); - CustomFrameViewAsh::TestApi test(frame); - ash::FrameMaximizeButton* maximize_button = test.maximize_button(); + aura::Window* window = widget()->GetNativeWindow(); + ash::FrameMaximizeButton* maximize_button = + CustomFrameViewAshTest::maximize_button(); maximize_button->set_bubble_appearance_delay_ms(0); gfx::Point button_pos = maximize_button->GetBoundsInScreen().CenterPoint(); gfx::Point off_pos(button_pos.x() + 100, button_pos.y() + 100); @@ -472,11 +445,9 @@ TEST_F(CustomFrameViewAshTest, MaximizeLeftButtonDragOut) { // Test that clicking a button in the maximizer bubble (in this case the // maximize left button) will do the requested action. TEST_F(CustomFrameViewAshTest, MaximizeLeftByButton) { - views::Widget* widget = CreateWidget(); - aura::Window* window = widget->GetNativeWindow(); - CustomFrameViewAsh* frame = GetCustomFrameViewAsh(widget); - CustomFrameViewAsh::TestApi test(frame); - ash::FrameMaximizeButton* maximize_button = test.maximize_button(); + aura::Window* window = widget()->GetNativeWindow(); + ash::FrameMaximizeButton* maximize_button = + CustomFrameViewAshTest::maximize_button(); maximize_button->set_bubble_appearance_delay_ms(0); gfx::Point button_pos = maximize_button->GetBoundsInScreen().CenterPoint(); gfx::Point off_pos(button_pos.x() + 100, button_pos.y() + 100); @@ -511,11 +482,9 @@ TEST_F(CustomFrameViewAshTest, MaximizeLeftByButton) { // Test that the activation focus does not change when the bubble gets shown. TEST_F(CustomFrameViewAshTest, MaximizeKeepFocus) { - views::Widget* widget = CreateWidget(); - aura::Window* window = widget->GetNativeWindow(); - CustomFrameViewAsh* frame = GetCustomFrameViewAsh(widget); - CustomFrameViewAsh::TestApi test(frame); - ash::FrameMaximizeButton* maximize_button = test.maximize_button(); + aura::Window* window = widget()->GetNativeWindow(); + ash::FrameMaximizeButton* maximize_button = + CustomFrameViewAshTest::maximize_button(); maximize_button->set_bubble_appearance_delay_ms(0); gfx::Point button_pos = maximize_button->GetBoundsInScreen().CenterPoint(); gfx::Point off_pos(button_pos.x() + 100, button_pos.y() + 100); @@ -536,12 +505,10 @@ TEST_F(CustomFrameViewAshTest, MaximizeKeepFocus) { } TEST_F(CustomFrameViewAshTest, MaximizeTap) { - views::Widget* widget = CreateWidget(); - aura::Window* window = widget->GetNativeWindow(); + aura::Window* window = widget()->GetNativeWindow(); aura::RootWindow* root_window = window->GetRootWindow(); - CustomFrameViewAsh* frame = GetCustomFrameViewAsh(widget); - CustomFrameViewAsh::TestApi test(frame); - ash::FrameMaximizeButton* maximize_button = test.maximize_button(); + ash::FrameMaximizeButton* maximize_button = + CustomFrameViewAshTest::maximize_button(); gfx::Point button_pos = maximize_button->GetBoundsInScreen().CenterPoint(); const int touch_default_radius = @@ -568,11 +535,9 @@ TEST_F(CustomFrameViewAshTest, MaximizeTap) { // Test that only the left button will activate the maximize button. TEST_F(CustomFrameViewAshTest, OnlyLeftButtonMaximizes) { - views::Widget* widget = CreateWidget(); - aura::Window* window = widget->GetNativeWindow(); - CustomFrameViewAsh* frame = GetCustomFrameViewAsh(widget); - CustomFrameViewAsh::TestApi test(frame); - ash::FrameMaximizeButton* maximize_button = test.maximize_button(); + aura::Window* window = widget()->GetNativeWindow(); + ash::FrameMaximizeButton* maximize_button = + CustomFrameViewAshTest::maximize_button(); maximize_button->set_bubble_appearance_delay_ms(0); gfx::Point button_pos = maximize_button->GetBoundsInScreen().CenterPoint(); gfx::Point off_pos(button_pos.x() + 100, button_pos.y() + 100); @@ -658,27 +623,24 @@ void ClickMaxButton( // Test that the restore from left/right maximize is properly done. TEST_F(CustomFrameViewAshTest, MaximizeLeftRestore) { - views::Widget* widget = CreateWidget(); - aura::Window* window = widget->GetNativeWindow(); - widget->SetBounds(gfx::Rect(10, 10, 100, 100)); - gfx::Rect initial_bounds = widget->GetWindowBoundsInScreen(); - CustomFrameViewAsh* frame = GetCustomFrameViewAsh(widget); - CustomFrameViewAsh::TestApi test(frame); - ash::FrameMaximizeButton* maximize_button = test.maximize_button(); + aura::Window* window = widget()->GetNativeWindow(); + gfx::Rect initial_bounds = widget()->GetWindowBoundsInScreen(); + ash::FrameMaximizeButton* maximize_button = + CustomFrameViewAshTest::maximize_button(); maximize_button->set_bubble_appearance_delay_ms(0); ClickMaxButton(maximize_button, window, SNAP_LEFT); // The window should not be maximized. EXPECT_FALSE(ash::wm::IsWindowMaximized(window)); // But the bounds should be different. - gfx::Rect new_bounds = widget->GetWindowBoundsInScreen(); + gfx::Rect new_bounds = widget()->GetWindowBoundsInScreen(); EXPECT_EQ(0, new_bounds.x()); EXPECT_EQ(0, new_bounds.y()); // Now click the same button again to see that it restores. ClickMaxButton(maximize_button, window, SNAP_LEFT); // But the bounds should be restored. - new_bounds = widget->GetWindowBoundsInScreen(); + new_bounds = widget()->GetWindowBoundsInScreen(); EXPECT_EQ(new_bounds.x(), initial_bounds.x()); EXPECT_EQ(new_bounds.y(), initial_bounds.x()); EXPECT_EQ(new_bounds.width(), initial_bounds.width()); @@ -689,13 +651,10 @@ TEST_F(CustomFrameViewAshTest, MaximizeLeftRestore) { // Maximize, left/right maximize and then restore should works. TEST_F(CustomFrameViewAshTest, MaximizeMaximizeLeftRestore) { - views::Widget* widget = CreateWidget(); - aura::Window* window = widget->GetNativeWindow(); - widget->SetBounds(gfx::Rect(10, 10, 100, 100)); - gfx::Rect initial_bounds = widget->GetWindowBoundsInScreen(); - CustomFrameViewAsh* frame = GetCustomFrameViewAsh(widget); - CustomFrameViewAsh::TestApi test(frame); - ash::FrameMaximizeButton* maximize_button = test.maximize_button(); + aura::Window* window = widget()->GetNativeWindow(); + gfx::Rect initial_bounds = widget()->GetWindowBoundsInScreen(); + ash::FrameMaximizeButton* maximize_button = + CustomFrameViewAshTest::maximize_button(); maximize_button->set_bubble_appearance_delay_ms(0); ClickMaxButton(maximize_button, window, SNAP_NONE); @@ -703,7 +662,7 @@ TEST_F(CustomFrameViewAshTest, MaximizeMaximizeLeftRestore) { ClickMaxButton(maximize_button, window, SNAP_LEFT); EXPECT_FALSE(ash::wm::IsWindowMaximized(window)); - gfx::Rect new_bounds = widget->GetWindowBoundsInScreen(); + gfx::Rect new_bounds = widget()->GetWindowBoundsInScreen(); EXPECT_EQ(0, new_bounds.x()); EXPECT_EQ(0, new_bounds.y()); @@ -711,7 +670,7 @@ TEST_F(CustomFrameViewAshTest, MaximizeMaximizeLeftRestore) { ClickMaxButton(maximize_button, window, SNAP_LEFT); RunAllPendingInMessageLoop(); // But the bounds should be restored. - new_bounds = widget->GetWindowBoundsInScreen(); + new_bounds = widget()->GetWindowBoundsInScreen(); EXPECT_EQ(new_bounds.x(), initial_bounds.x()); EXPECT_EQ(new_bounds.y(), initial_bounds.x()); EXPECT_EQ(new_bounds.width(), initial_bounds.width()); @@ -722,13 +681,10 @@ TEST_F(CustomFrameViewAshTest, MaximizeMaximizeLeftRestore) { // Left/right maximize, maximize and then restore should work. TEST_F(CustomFrameViewAshTest, MaximizeLeftMaximizeRestore) { - views::Widget* widget = CreateWidget(); - aura::Window* window = widget->GetNativeWindow(); - widget->SetBounds(gfx::Rect(10, 10, 100, 100)); - gfx::Rect initial_bounds = widget->GetWindowBoundsInScreen(); - CustomFrameViewAsh* frame = GetCustomFrameViewAsh(widget); - CustomFrameViewAsh::TestApi test(frame); - ash::FrameMaximizeButton* maximize_button = test.maximize_button(); + aura::Window* window = widget()->GetNativeWindow(); + gfx::Rect initial_bounds = widget()->GetWindowBoundsInScreen(); + ash::FrameMaximizeButton* maximize_button = + CustomFrameViewAshTest::maximize_button(); maximize_button->set_bubble_appearance_delay_ms(0); ClickMaxButton(maximize_button, window, SNAP_LEFT); @@ -739,7 +695,7 @@ TEST_F(CustomFrameViewAshTest, MaximizeLeftMaximizeRestore) { ClickMaxButton(maximize_button, window, SNAP_NONE); EXPECT_FALSE(ash::wm::IsWindowMaximized(window)); - gfx::Rect new_bounds = widget->GetWindowBoundsInScreen(); + gfx::Rect new_bounds = widget()->GetWindowBoundsInScreen(); EXPECT_EQ(new_bounds.x(), initial_bounds.x()); EXPECT_EQ(new_bounds.y(), initial_bounds.x()); EXPECT_EQ(new_bounds.width(), initial_bounds.width()); @@ -751,13 +707,10 @@ TEST_F(CustomFrameViewAshTest, MaximizeLeftMaximizeRestore) { // Starting with a window which has no restore bounds, maximize then left/right // maximize should not be centered but left/right maximized. TEST_F(CustomFrameViewAshTest, MaximizeThenLeftMaximize) { - views::Widget* widget = CreateWidget(); - aura::Window* window = widget->GetNativeWindow(); - widget->SetBounds(gfx::Rect(10, 10, 100, 100)); - gfx::Rect initial_bounds = widget->GetWindowBoundsInScreen(); - CustomFrameViewAsh* frame = GetCustomFrameViewAsh(widget); - CustomFrameViewAsh::TestApi test(frame); - ash::FrameMaximizeButton* maximize_button = test.maximize_button(); + aura::Window* window = widget()->GetNativeWindow(); + gfx::Rect initial_bounds = widget()->GetWindowBoundsInScreen(); + ash::FrameMaximizeButton* maximize_button = + CustomFrameViewAshTest::maximize_button(); maximize_button->set_bubble_appearance_delay_ms(0); // Make sure that there is no restore rectangle. EXPECT_EQ(NULL, GetRestoreBoundsInScreen(window)); @@ -768,7 +721,7 @@ TEST_F(CustomFrameViewAshTest, MaximizeThenLeftMaximize) { ClickMaxButton(maximize_button, window, SNAP_LEFT); EXPECT_FALSE(ash::wm::IsWindowMaximized(window)); - gfx::Rect new_bounds = widget->GetWindowBoundsInScreen(); + gfx::Rect new_bounds = widget()->GetWindowBoundsInScreen(); EXPECT_EQ(new_bounds.x(), 0); EXPECT_EQ(new_bounds.y(), 0); // Make sure that the restore rectangle is the original rectangle. @@ -778,12 +731,9 @@ TEST_F(CustomFrameViewAshTest, MaximizeThenLeftMaximize) { // Test that minimizing the window per keyboard closes the maximize bubble. TEST_F(CustomFrameViewAshTest, MinimizePerKeyClosesBubble) { - views::Widget* widget = CreateWidget(); - aura::Window* window = widget->GetNativeWindow(); - widget->SetBounds(gfx::Rect(10, 10, 100, 100)); - CustomFrameViewAsh* frame = GetCustomFrameViewAsh(widget); - CustomFrameViewAsh::TestApi test(frame); - ash::FrameMaximizeButton* maximize_button = test.maximize_button(); + aura::Window* window = widget()->GetNativeWindow(); + ash::FrameMaximizeButton* maximize_button = + CustomFrameViewAshTest::maximize_button(); gfx::Point button_pos = maximize_button->GetBoundsInScreen().CenterPoint(); gfx::Point off_pos(button_pos.x() + 100, button_pos.y() + 100); @@ -805,12 +755,9 @@ TEST_F(CustomFrameViewAshTest, MinimizePerKeyClosesBubble) { // Tests that dragging down on the maximize button minimizes the window. TEST_F(CustomFrameViewAshTest, MaximizeButtonDragDownMinimizes) { - views::Widget* widget = CreateWidget(); - aura::Window* window = widget->GetNativeWindow(); - widget->SetBounds(gfx::Rect(10, 10, 100, 100)); - CustomFrameViewAsh* frame = GetCustomFrameViewAsh(widget); - CustomFrameViewAsh::TestApi test(frame); - FrameMaximizeButton* maximize_button = test.maximize_button(); + aura::Window* window = widget()->GetNativeWindow(); + ash::FrameMaximizeButton* maximize_button = + CustomFrameViewAshTest::maximize_button(); // Drag down on a maximized window. wm::MaximizeWindow(window); @@ -838,13 +785,10 @@ TEST_F(CustomFrameViewAshTest, MaximizeButtonDragDownMinimizes) { // Tests that dragging Left and pressing ESC does properly abort. TEST_F(CustomFrameViewAshTest, MaximizeButtonDragLeftEscapeExits) { - views::Widget* widget = CreateWidget(); - aura::Window* window = widget->GetNativeWindow(); - gfx::Rect window_position = gfx::Rect(10, 10, 100, 100); - widget->SetBounds(window_position); - CustomFrameViewAsh* frame = GetCustomFrameViewAsh(widget); - CustomFrameViewAsh::TestApi test(frame); - FrameMaximizeButton* maximize_button = test.maximize_button(); + aura::Window* window = widget()->GetNativeWindow(); + gfx::Rect initial_bounds = widget()->GetWindowBoundsInScreen(); + ash::FrameMaximizeButton* maximize_button = + CustomFrameViewAshTest::maximize_button(); gfx::Point button_pos = maximize_button->GetBoundsInScreen().CenterPoint(); gfx::Point off_pos(button_pos.x() - button_pos.x() / 2, button_pos.y()); @@ -861,11 +805,11 @@ TEST_F(CustomFrameViewAshTest, MaximizeButtonDragLeftEscapeExits) { base::Unretained(&cancel_handler))); // Check that there was no size change. - EXPECT_EQ(widget->GetWindowBoundsInScreen().size().ToString(), - window_position.size().ToString()); + EXPECT_EQ(widget()->GetWindowBoundsInScreen().size().ToString(), + initial_bounds.size().ToString()); // Check that there is no phantom window left open. EXPECT_FALSE(maximize_button->phantom_window_open()); } -} // namespace internal +} // namespace test } // namespace ash diff --git a/ash/wm/frame_painter.cc b/ash/wm/frame_painter.cc index 5b7782a..31e8232 100644 --- a/ash/wm/frame_painter.cc +++ b/ash/wm/frame_painter.cc @@ -13,6 +13,7 @@ #include "ash/wm/property_util.h" #include "ash/wm/window_properties.h" #include "ash/wm/window_util.h" +#include "ash/wm/workspace/frame_caption_button_container_view.h" #include "base/logging.h" // DCHECK #include "grit/ash_resources.h" #include "third_party/skia/include/core/SkCanvas.h" @@ -33,7 +34,6 @@ #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" @@ -63,13 +63,6 @@ const SkColor kMaximizedWindowTitleTextColor = SK_ColorWHITE; 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 @@ -215,10 +208,8 @@ int FramePainter::kSoloWindowOpacity = 77; // 0.3 FramePainter::FramePainter() : frame_(NULL), window_icon_(NULL), - size_button_(NULL), - close_button_(NULL), + caption_button_container_(NULL), window_(NULL), - button_separator_(NULL), top_left_corner_(NULL), top_edge_(NULL), top_right_corner_(NULL), @@ -229,8 +220,7 @@ FramePainter::FramePainter() previous_opacity_(0), crossfade_theme_frame_id_(0), crossfade_theme_frame_overlay_id_(0), - crossfade_opacity_(0), - size_button_behavior_(SIZE_BUTTON_MAXIMIZES) {} + crossfade_opacity_(0) {} FramePainter::~FramePainter() { // Sometimes we are destroyed before the window closes, so ensure we clean up. @@ -239,25 +229,19 @@ FramePainter::~FramePainter() { } } -void FramePainter::Init(views::Widget* frame, - views::View* window_icon, - views::ImageButton* size_button, - views::ImageButton* close_button, - SizeButtonBehavior behavior) { +void FramePainter::Init( + views::Widget* frame, + views::View* window_icon, + FrameCaptionButtonContainerView* caption_button_container) { DCHECK(frame); // window_icon may be NULL. - DCHECK(size_button); - DCHECK(close_button); + DCHECK(caption_button_container); frame_ = frame; window_icon_ = window_icon; - size_button_ = size_button; - close_button_ = close_button; - size_button_behavior_ = behavior; + caption_button_container_ = caption_button_container; // 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_ = @@ -353,13 +337,15 @@ int FramePainter::NonClientHitTest(views::NonClientFrameView* view, 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; + if (caption_button_container_->visible()) { + gfx::Point point_in_caption_button_container(point); + views::View::ConvertPointToTarget(view, caption_button_container_, + &point_in_caption_button_container); + client_component = caption_button_container_->NonClientHitTest( + point_in_caption_button_container); + if (client_component != HTNOWHERE) + return client_component; + } // Caption is a safe default. return HTCAPTION; @@ -373,8 +359,7 @@ gfx::Size FramePainter::GetMinimumSize(views::NonClientFrameView* view) { // 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; + caption_button_container_->GetMinimumSize().width(); if (title_width > min_size.width()) min_size.set_width(title_width); return min_size; @@ -385,11 +370,7 @@ gfx::Size FramePainter::GetMaximumSize(views::NonClientFrameView* view) { } 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; + return caption_button_container_->GetPreferredSize().width(); } int FramePainter::GetThemeBackgroundXInset() const { @@ -504,14 +485,6 @@ void FramePainter::PaintHeader(views::NonClientFrameView* view, 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. // @@ -595,72 +568,24 @@ void FramePainter::PaintTitleBar(views::NonClientFrameView* view, 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()); + caption_button_container_->set_header_style(shorter_layout ? + FrameCaptionButtonContainerView::HEADER_STYLE_SHORT : + FrameCaptionButtonContainerView::HEADER_STYLE_TALL); + caption_button_container_->Layout(); + + 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()); if (window_icon_) { - // Vertically center the window icon with respect to the close button. - int icon_offset_y = GetCloseButtonCenterY() - window_icon_->height() / 2; + // Vertically center the window icon with respect to the caption button + // container. + int icon_offset_y = + GetCaptionButtonContainerCenterY() - window_icon_->height() / 2; window_icon_->SetBounds(kIconOffsetX, icon_offset_y, kIconSize, kIconSize); } } @@ -774,27 +699,15 @@ void FramePainter::AnimationProgressed(const ui::Animation* animation) { /////////////////////////////////////////////////////////////////////////////// // 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)); -} - 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::GetCaptionButtonContainerCenterY() const { + return caption_button_container_->y() + + caption_button_container_->height() / 2; } int FramePainter::GetHeaderCornerRadius() const { @@ -908,14 +821,14 @@ void FramePainter::SchedulePaintForHeader() { 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; + // Center the text with respect to the caption button container. This way it + // adapts to the caption button height and aligns exactly with the window + // icon. Don't use |window_icon_| for this computation as it may be NULL. + int title_y = GetCaptionButtonContainerCenterY() - title_font.GetHeight() / 2; return gfx::Rect( title_x, std::max(0, title_y), - std::max(0, size_button_->x() - kTitleLogoSpacing - title_x), + std::max(0, caption_button_container_->x() - kTitleLogoSpacing - title_x), title_font.GetHeight()); } diff --git a/ash/wm/frame_painter.h b/ash/wm/frame_painter.h index b164edf..1f41c8c 100644 --- a/ash/wm/frame_painter.h +++ b/ash/wm/frame_painter.h @@ -29,13 +29,13 @@ namespace ui { class SlideAnimation; } namespace views { -class ImageButton; class NonClientFrameView; class View; class Widget; } namespace ash { +class FrameCaptionButtonContainerView; // Helper class for painting window frames. Exists to share code between // various implementations of views::NonClientFrameView. Canonical source of @@ -70,9 +70,7 @@ class ASH_EXPORT FramePainter : public aura::WindowObserver, // |frame| and buttons are used for layout and are not owned. void Init(views::Widget* frame, views::View* window_icon, - views::ImageButton* size_button, - views::ImageButton* close_button, - SizeButtonBehavior behavior); + FrameCaptionButtonContainerView* caption_button_container); // Updates the solo-window transparent header appearance for all windows // using frame painters in |root_window|. @@ -162,17 +160,12 @@ class ASH_EXPORT FramePainter : public aura::WindowObserver, FRIEND_TEST_ALL_PREFIXES(FramePainterTest, NoCrashShutdownWithAlwaysOnTopWindow); - // Sets the images for a button based on IDs from the |frame_| theme provider. - void SetButtonImages(views::ImageButton* button, - int normal_image_id, - int hot_image_id, - int pushed_image_id); - // Returns the offset between window left edge and title string. int GetTitleOffsetX() const; - // Returns the vertical center of the close button in window coordinates. - int GetCloseButtonCenterY() const; + // Returns the vertical center of the caption button container in window + // coordinates. + int GetCaptionButtonContainerCenterY() const; // Returns the opacity value used to paint the header. // |theme_frame_overlay_id| is 0 if no overlay image is used. @@ -215,12 +208,10 @@ class ASH_EXPORT FramePainter : public aura::WindowObserver, // Not owned views::Widget* frame_; views::View* window_icon_; // May be NULL. - views::ImageButton* size_button_; - views::ImageButton* close_button_; + FrameCaptionButtonContainerView* caption_button_container_; aura::Window* window_; // Window frame header/caption parts. - const gfx::ImageSkia* button_separator_; const gfx::ImageSkia* top_left_corner_; const gfx::ImageSkia* top_edge_; const gfx::ImageSkia* top_right_corner_; @@ -240,8 +231,6 @@ class ASH_EXPORT FramePainter : public aura::WindowObserver, gfx::Rect header_frame_bounds_; scoped_ptr<ui::SlideAnimation> crossfade_animation_; - SizeButtonBehavior size_button_behavior_; - DISALLOW_COPY_AND_ASSIGN(FramePainter); }; diff --git a/ash/wm/frame_painter_unittest.cc b/ash/wm/frame_painter_unittest.cc index 5826be6..a669f8a 100644 --- a/ash/wm/frame_painter_unittest.cc +++ b/ash/wm/frame_painter_unittest.cc @@ -11,6 +11,7 @@ #include "ash/wm/property_util.h" #include "ash/wm/window_properties.h" #include "ash/wm/window_util.h" +#include "ash/wm/workspace/frame_caption_button_container_view.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" #include "grit/ash_resources.h" @@ -19,39 +20,19 @@ #include "ui/aura/root_window.h" #include "ui/aura/window_observer.h" #include "ui/base/hit_test.h" -#include "ui/base/theme_provider.h" #include "ui/gfx/font.h" #include "ui/gfx/screen.h" -#include "ui/views/controls/button/button.h" -#include "ui/views/controls/button/image_button.h" #include "ui/views/widget/widget.h" #include "ui/views/widget/widget_delegate.h" #include "ui/views/widget/widget_observer.h" #include "ui/views/window/non_client_view.h" using ash::FramePainter; -using ui::ThemeProvider; -using views::Button; -using views::ImageButton; using views::NonClientFrameView; -using views::ToggleImageButton; using views::Widget; namespace { -bool ImagesMatch(ImageButton* button, - int normal_image_id, - int hovered_image_id, - int pressed_image_id) { - ThemeProvider* theme = button->GetWidget()->GetThemeProvider(); - gfx::ImageSkia* normal = theme->GetImageSkiaNamed(normal_image_id); - gfx::ImageSkia* hovered = theme->GetImageSkiaNamed(hovered_image_id); - gfx::ImageSkia* pressed = theme->GetImageSkiaNamed(pressed_image_id); - return button->GetImage(Button::STATE_NORMAL).BackedBySameObjectAs(*normal) && - button->GetImage(Button::STATE_HOVERED).BackedBySameObjectAs(*hovered) && - button->GetImage(Button::STATE_PRESSED).BackedBySameObjectAs(*pressed); -} - class ResizableWidgetDelegate : public views::WidgetDelegate { public: ResizableWidgetDelegate(views::Widget* widget) { @@ -135,18 +116,16 @@ class ScopedOpacityConstantModifier { // Creates a new FramePainter with empty buttons. Caller owns the memory. FramePainter* CreateTestPainter(Widget* widget) { FramePainter* painter = new FramePainter(); - ImageButton* size_button = new ImageButton(NULL); - ImageButton* close_button = new ImageButton(NULL); - // Add the buttons to the widget's non-client frame view so they will be - // deleted when the widget is destroyed. NonClientFrameView* frame_view = widget->non_client_view()->frame_view(); - frame_view->AddChildView(size_button); - frame_view->AddChildView(close_button); - painter->Init(widget, - NULL, - size_button, - close_button, - FramePainter::SIZE_BUTTON_MAXIMIZES); + ash::FrameCaptionButtonContainerView* container = + new ash::FrameCaptionButtonContainerView( + frame_view, + widget, + ash::FrameCaptionButtonContainerView::MINIMIZE_ALLOWED); + // Add the container to the widget's non-client frame view so that it will be + // deleted when the widget is destroyed. + frame_view->AddChildView(container); + painter->Init(widget, NULL, container); return painter; } @@ -251,69 +230,6 @@ TEST_F(FramePainterTest, CreateAndDeleteSingleWindow) { EXPECT_TRUE(root->GetProperty(internal::kSoloWindowHeaderKey)); } -TEST_F(FramePainterTest, LayoutHeader) { - scoped_ptr<Widget> widget(CreateTestWidget()); - ImageButton size_button(NULL); - ImageButton close_button(NULL); - NonClientFrameView* frame_view = widget->non_client_view()->frame_view(); - frame_view->AddChildView(&size_button); - frame_view->AddChildView(&close_button); - scoped_ptr<FramePainter> painter(new FramePainter); - painter->Init(widget.get(), - NULL, - &size_button, - &close_button, - FramePainter::SIZE_BUTTON_MAXIMIZES); - widget->Show(); - - // Basic layout. - painter->LayoutHeader(frame_view, false); - EXPECT_TRUE(ImagesMatch(&close_button, - IDR_AURA_WINDOW_CLOSE, - IDR_AURA_WINDOW_CLOSE_H, - IDR_AURA_WINDOW_CLOSE_P)); - EXPECT_TRUE(ImagesMatch(&size_button, - IDR_AURA_WINDOW_MAXIMIZE, - IDR_AURA_WINDOW_MAXIMIZE_H, - IDR_AURA_WINDOW_MAXIMIZE_P)); - - // Shorter layout. - painter->LayoutHeader(frame_view, true); - EXPECT_TRUE(ImagesMatch(&close_button, - IDR_AURA_WINDOW_MAXIMIZED_CLOSE, - IDR_AURA_WINDOW_MAXIMIZED_CLOSE_H, - IDR_AURA_WINDOW_MAXIMIZED_CLOSE_P)); - EXPECT_TRUE(ImagesMatch(&size_button, - IDR_AURA_WINDOW_MAXIMIZED_RESTORE, - IDR_AURA_WINDOW_MAXIMIZED_RESTORE_H, - IDR_AURA_WINDOW_MAXIMIZED_RESTORE_P)); - - // Maximized shorter layout. - widget->Maximize(); - painter->LayoutHeader(frame_view, true); - EXPECT_TRUE(ImagesMatch(&close_button, - IDR_AURA_WINDOW_MAXIMIZED_CLOSE2, - IDR_AURA_WINDOW_MAXIMIZED_CLOSE2_H, - IDR_AURA_WINDOW_MAXIMIZED_CLOSE2_P)); - EXPECT_TRUE(ImagesMatch(&size_button, - IDR_AURA_WINDOW_MAXIMIZED_RESTORE2, - IDR_AURA_WINDOW_MAXIMIZED_RESTORE2_H, - IDR_AURA_WINDOW_MAXIMIZED_RESTORE2_P)); - - // Fullscreen can show the buttons during an immersive reveal, so it should - // use the same images as maximized. - widget->SetFullscreen(true); - painter->LayoutHeader(frame_view, true); - EXPECT_TRUE(ImagesMatch(&close_button, - IDR_AURA_WINDOW_MAXIMIZED_CLOSE2, - IDR_AURA_WINDOW_MAXIMIZED_CLOSE2_H, - IDR_AURA_WINDOW_MAXIMIZED_CLOSE2_P)); - EXPECT_TRUE(ImagesMatch(&size_button, - IDR_AURA_WINDOW_MAXIMIZED_RESTORE2, - IDR_AURA_WINDOW_MAXIMIZED_RESTORE2_H, - IDR_AURA_WINDOW_MAXIMIZED_RESTORE2_P)); -} - TEST_F(FramePainterTest, UseSoloWindowHeader) { // Create a widget and a painter for it. scoped_ptr<Widget> w1(CreateTestWidget()); @@ -660,15 +576,11 @@ TEST_F(FramePainterTest, MinimalHeaderStyle) { TEST_F(FramePainterTest, TitleIconAlignment) { scoped_ptr<Widget> w(CreateTestWidget()); FramePainter p; - ImageButton size(NULL); - ImageButton close(NULL); + ash::FrameCaptionButtonContainerView container(NULL, w.get(), + ash::FrameCaptionButtonContainerView::MINIMIZE_ALLOWED); views::View window_icon; window_icon.SetBounds(0, 0, 16, 16); - p.Init(w.get(), - &window_icon, - &size, - &close, - FramePainter::SIZE_BUTTON_MAXIMIZES); + p.Init(w.get(), &window_icon, &container); w->SetBounds(gfx::Rect(0, 0, 500, 500)); w->Show(); diff --git a/ash/wm/panels/panel_frame_view.cc b/ash/wm/panels/panel_frame_view.cc index f394dc0..db137fa 100644 --- a/ash/wm/panels/panel_frame_view.cc +++ b/ash/wm/panels/panel_frame_view.cc @@ -5,8 +5,8 @@ #include "ash/wm/panels/panel_frame_view.h" #include "ash/wm/frame_painter.h" +#include "ash/wm/workspace/frame_caption_button_container_view.h" #include "grit/ash_resources.h" -#include "grit/ui_strings.h" // Accessibility names #include "third_party/skia/include/core/SkPaint.h" #include "ui/base/animation/throb_animation.h" #include "ui/base/cursor/cursor.h" @@ -16,7 +16,6 @@ #include "ui/gfx/canvas.h" #include "ui/gfx/font.h" #include "ui/gfx/image/image.h" -#include "ui/views/controls/button/image_button.h" #include "ui/views/controls/image_view.h" #include "ui/views/widget/native_widget_aura.h" #include "ui/views/widget/widget.h" @@ -25,12 +24,11 @@ namespace ash { // static -const char PanelFrameView::kViewClassName[] = "ash/wm/panels/PanelFrameView"; +const char PanelFrameView::kViewClassName[] = "PanelFrameView"; PanelFrameView::PanelFrameView(views::Widget* frame, FrameType frame_type) : frame_(frame), - close_button_(NULL), - minimize_button_(NULL), + caption_button_container_(NULL), window_icon_(NULL), title_font_(gfx::Font(views::NativeWidgetAura::GetWindowTitleFont())) { if (frame_type != FRAME_NONE) @@ -47,23 +45,16 @@ const char* PanelFrameView::GetClassName() const { void PanelFrameView::InitFramePainter() { frame_painter_.reset(new FramePainter); - close_button_ = new views::ImageButton(this); - close_button_->SetAccessibleName( - l10n_util::GetStringUTF16(IDS_APP_ACCNAME_CLOSE)); - AddChildView(close_button_); - - minimize_button_ = new views::ImageButton(this); - minimize_button_->SetAccessibleName( - l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MINIMIZE)); - AddChildView(minimize_button_); + caption_button_container_ = new FrameCaptionButtonContainerView(this, frame_, + FrameCaptionButtonContainerView::MINIMIZE_ALLOWED); + AddChildView(caption_button_container_); if (frame_->widget_delegate()->ShouldShowWindowIcon()) { window_icon_ = new views::ImageView(); AddChildView(window_icon_); } - frame_painter_->Init(frame_, window_icon_, minimize_button_, close_button_, - FramePainter::SIZE_BUTTON_MINIMIZES); + frame_painter_->Init(frame_, window_icon_, caption_button_container_); } gfx::Size PanelFrameView::GetMinimumSize() { @@ -133,7 +124,7 @@ gfx::Rect PanelFrameView::GetBoundsForClientView() const { if (!frame_painter_) return bounds(); return frame_painter_->GetBoundsForClientView( - close_button_->bounds().bottom(), + caption_button_container_->bounds().bottom(), bounds()); } @@ -142,15 +133,7 @@ gfx::Rect PanelFrameView::GetWindowBoundsForClientBounds( if (!frame_painter_) return client_bounds; return frame_painter_->GetWindowBoundsForClientBounds( - close_button_->bounds().bottom(), client_bounds); -} - -void PanelFrameView::ButtonPressed(views::Button* sender, - const ui::Event& event) { - if (sender == close_button_) - GetWidget()->Close(); - if (sender == minimize_button_) - GetWidget()->Minimize(); + caption_button_container_->bounds().bottom(), client_bounds); } } // namespace ash diff --git a/ash/wm/panels/panel_frame_view.h b/ash/wm/panels/panel_frame_view.h index 4a0a6f2..cccfd18 100644 --- a/ash/wm/panels/panel_frame_view.h +++ b/ash/wm/panels/panel_frame_view.h @@ -19,10 +19,10 @@ class ImageView; namespace ash { +class FrameCaptionButtonContainerView; class FramePainter; -class ASH_EXPORT PanelFrameView : public views::NonClientFrameView, - public views::ButtonListener { +class ASH_EXPORT PanelFrameView : public views::NonClientFrameView { public: // Internal class name. static const char kViewClassName[]; @@ -57,16 +57,11 @@ class ASH_EXPORT PanelFrameView : public views::NonClientFrameView, virtual void Layout() OVERRIDE; virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; - // Overridden from views::ButtonListener: - virtual void ButtonPressed(views::Button* sender, - const ui::Event& event) OVERRIDE; - // Child View class describing the panel's title bar behavior // and buttons, owned by the view hierarchy scoped_ptr<FramePainter> frame_painter_; views::Widget* frame_; - views::ImageButton* close_button_; - views::ImageButton* minimize_button_; + FrameCaptionButtonContainerView* caption_button_container_; views::ImageView* window_icon_; gfx::Rect client_view_bounds_; const gfx::Font title_font_; diff --git a/ash/wm/workspace/frame_caption_button_container_view.cc b/ash/wm/workspace/frame_caption_button_container_view.cc index 7f1d020e..962f847 100644 --- a/ash/wm/workspace/frame_caption_button_container_view.cc +++ b/ash/wm/workspace/frame_caption_button_container_view.cc @@ -6,14 +6,18 @@ #include "ash/shell.h" #include "ash/shell_delegate.h" +#include "ash/wm/property_util.h" #include "ash/wm/workspace/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/views/controls/button/image_button.h" #include "ui/views/widget/widget.h" +#include "ui/views/widget/widget_delegate.h" #include "ui/views/window/non_client_view.h" namespace ash { @@ -31,29 +35,61 @@ const char FrameCaptionButtonContainerView::kViewClassName[] = FrameCaptionButtonContainerView::FrameCaptionButtonContainerView( views::NonClientFrameView* frame_view, - views::Widget* frame) + views::Widget* frame, + MinimizeAllowed minimize_allowed) : frame_(frame), + header_style_(HEADER_STYLE_SHORT), + minimize_button_(new views::ImageButton(this)), size_button_(new FrameMaximizeButton(this, frame_view)), close_button_(new views::ImageButton(this)) { // Insert the buttons left to right. + minimize_button_->SetAccessibleName( + l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MINIMIZE)); + // Hide |minimize_button_| when |size_button_| is visible because + // |size_button_| is capable of minimizing. + // TODO(pkotwicz): We should probably show the minimize button when in + // "always maximized" mode. + minimize_button_->SetVisible(minimize_allowed == MINIMIZE_ALLOWED && + !frame_->widget_delegate()->CanMaximize()); + AddChildView(minimize_button_); + size_button_->SetAccessibleName( l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MAXIMIZE)); - if (ash::Shell::IsForcedMaximizeMode()) - size_button_->SetVisible(false); + size_button_->SetVisible(frame_->widget_delegate()->CanMaximize() && + !ash::Shell::IsForcedMaximizeMode()); AddChildView(size_button_); 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() { } void FrameCaptionButtonContainerView::ResetWindowControls() { + minimize_button_->SetState(views::CustomButton::STATE_NORMAL); size_button_->SetState(views::CustomButton::STATE_NORMAL); } +int FrameCaptionButtonContainerView::NonClientHitTest( + const gfx::Point& point) const { + if (close_button_->visible() && + close_button_->GetMirroredBounds().Contains(point)) { + return HTCLOSE; + } else if (size_button_->visible() && + size_button_->GetMirroredBounds().Contains(point)) { + return HTMAXBUTTON; + } else if (minimize_button_->visible() && + minimize_button_->GetMirroredBounds().Contains(point)) { + return HTMINBUTTON; + } + return HTNOWHERE; +} + gfx::Size FrameCaptionButtonContainerView::GetPreferredSize() { int width = 0; bool first_visible = true; @@ -74,14 +110,53 @@ gfx::Size FrameCaptionButtonContainerView::GetPreferredSize() { } void FrameCaptionButtonContainerView::Layout() { - SetButtonImages(size_button_, - IDR_AURA_WINDOW_FULLSCREEN_RESTORE, - IDR_AURA_WINDOW_FULLSCREEN_RESTORE_H, - IDR_AURA_WINDOW_FULLSCREEN_RESTORE_P); - SetButtonImages(close_button_, - IDR_AURA_WINDOW_FULLSCREEN_CLOSE, - IDR_AURA_WINDOW_FULLSCREEN_CLOSE_H, - IDR_AURA_WINDOW_FULLSCREEN_CLOSE_P); + SetButtonImages(minimize_button_, + IDR_AURA_WINDOW_MINIMIZE_SHORT, + IDR_AURA_WINDOW_MINIMIZE_SHORT_H, + IDR_AURA_WINDOW_MINIMIZE_SHORT_P); + + if (header_style_ == HEADER_STYLE_MAXIMIZED_HOSTED_APP) { + SetButtonImages(size_button_, + IDR_AURA_WINDOW_FULLSCREEN_RESTORE, + IDR_AURA_WINDOW_FULLSCREEN_RESTORE_H, + IDR_AURA_WINDOW_FULLSCREEN_RESTORE_P); + SetButtonImages(close_button_, + IDR_AURA_WINDOW_FULLSCREEN_CLOSE, + IDR_AURA_WINDOW_FULLSCREEN_CLOSE_H, + IDR_AURA_WINDOW_FULLSCREEN_CLOSE_P); + } else if (header_style_ == HEADER_STYLE_SHORT) { + // The new assets only make sense if the window is maximized or fullscreen + // because we usually use a black header in this case. + if ((frame_->IsMaximized() || frame_->IsFullscreen()) && + GetTrackedByWorkspace(frame_->GetNativeWindow())) { + SetButtonImages(size_button_, + IDR_AURA_WINDOW_MAXIMIZED_RESTORE2, + IDR_AURA_WINDOW_MAXIMIZED_RESTORE2_H, + IDR_AURA_WINDOW_MAXIMIZED_RESTORE2_P); + SetButtonImages(close_button_, + IDR_AURA_WINDOW_MAXIMIZED_CLOSE2, + IDR_AURA_WINDOW_MAXIMIZED_CLOSE2_H, + IDR_AURA_WINDOW_MAXIMIZED_CLOSE2_P); + } else { + SetButtonImages(size_button_, + IDR_AURA_WINDOW_MAXIMIZED_RESTORE, + IDR_AURA_WINDOW_MAXIMIZED_RESTORE_H, + IDR_AURA_WINDOW_MAXIMIZED_RESTORE_P); + SetButtonImages(close_button_, + IDR_AURA_WINDOW_MAXIMIZED_CLOSE, + IDR_AURA_WINDOW_MAXIMIZED_CLOSE_H, + IDR_AURA_WINDOW_MAXIMIZED_CLOSE_P); + } + } else { + SetButtonImages(size_button_, + IDR_AURA_WINDOW_MAXIMIZE, + IDR_AURA_WINDOW_MAXIMIZE_H, + IDR_AURA_WINDOW_MAXIMIZE_P); + SetButtonImages(close_button_, + IDR_AURA_WINDOW_CLOSE, + IDR_AURA_WINDOW_CLOSE_H, + IDR_AURA_WINDOW_CLOSE_P); + } gfx::Insets insets(GetInsets()); int x = insets.left(); @@ -101,6 +176,22 @@ const char* FrameCaptionButtonContainerView::GetClassName() const { return kViewClassName; } +void FrameCaptionButtonContainerView::OnPaint(gfx::Canvas* canvas) { + views::View::OnPaint(canvas); + + // AppNonClientFrameViewAsh does not paint the button separator. + if (header_style_ != HEADER_STYLE_MAXIMIZED_HOSTED_APP) { + // 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::SetButtonImages( views::ImageButton* button, int normal_image_id, @@ -117,15 +208,37 @@ void FrameCaptionButtonContainerView::SetButtonImages( 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)); + } + ash::UserMetricsAction action = - ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_RESTORE; - if (sender == size_button_) { - // The maximize button may move out from under the cursor. + ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_MINIMIZE; + if (sender == minimize_button_) { + // The minimize button may move out from under the cursor. ResetWindowControls(); - frame_->Restore(); - } else if (sender == close_button_) { - action = ash::UMA_WINDOW_CLOSE_BUTTON_CLICK; + frame_->Minimize(); + } else if (sender == size_button_) { + // The size button may move out from under the cursor. + ResetWindowControls(); + 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; } diff --git a/ash/wm/workspace/frame_caption_button_container_view.h b/ash/wm/workspace/frame_caption_button_container_view.h index 0fe256b..b1bb377 100644 --- a/ash/wm/workspace/frame_caption_button_container_view.h +++ b/ash/wm/workspace/frame_caption_button_container_view.h @@ -6,6 +6,7 @@ #define ASH_WM_WORKSPACE_FRAME_CAPTION_BUTTON_CONTAINER_VIEW_H_ #include "ash/ash_export.h" +#include "ui/gfx/image/image_skia.h" #include "ui/views/controls/button/button.h" #include "ui/views/view.h" @@ -25,12 +26,34 @@ class ASH_EXPORT FrameCaptionButtonContainerView public: static const char kViewClassName[]; + // Whether the frame can be minimized (either via the maximize/restore button + // or via a dedicated button). + enum MinimizeAllowed { + MINIMIZE_ALLOWED, + MINIMIZE_DISALLOWED + }; + enum HeaderStyle { + // Dialogs, panels, packaged apps, tabbed maximized/fullscreen browser + // windows. + HEADER_STYLE_SHORT, + + // Restored tabbed browser windows, popups for browser windows, restored + // hosted app windows, popups for hosted app windows. + HEADER_STYLE_TALL, + + // AppNonClientFrameViewAsh. + HEADER_STYLE_MAXIMIZED_HOSTED_APP + }; + // |frame_view| and |frame| are the NonClientFrameView and the views::Widget // that the caption buttons act on. + // |minimize_allowed| indicates whether the frame can be minimized (either via + // the maximize/restore button or via a dedicated button). // TODO(pkotwicz): Remove the |frame_view| parameter once FrameMaximizeButton // is refactored to take in a views::Widget instead. FrameCaptionButtonContainerView(views::NonClientFrameView* frame_view, - views::Widget* frame); + views::Widget* frame, + MinimizeAllowed minimize_allowed); virtual ~FrameCaptionButtonContainerView(); // For testing. @@ -40,6 +63,10 @@ class ASH_EXPORT FrameCaptionButtonContainerView : container_view_(container_view) { } + views::ImageButton* minimize_button() const { + return container_view_->minimize_button_; + } + views::ImageButton* size_button() const { return container_view_->size_button_; } @@ -57,10 +84,21 @@ class ASH_EXPORT FrameCaptionButtonContainerView // Tell the window controls to reset themselves to the normal state. void ResetWindowControls(); + // Determines the window HT* code for the caption button at |point|. Returns + // HTNOWHERE if |point| is not over any of the caption buttons. |point| must + // be in the coordinates of the FrameCaptionButtonContainerView. + int NonClientHitTest(const gfx::Point& point) const; + + // Sets the header style. + void set_header_style(HeaderStyle header_style) { + header_style_ = header_style; + } + // views::View overrides: virtual gfx::Size GetPreferredSize() OVERRIDE; virtual void Layout() OVERRIDE; virtual const char* GetClassName() const OVERRIDE; + virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; private: // Sets the images for a button based on the given ids. @@ -76,7 +114,14 @@ class ASH_EXPORT FrameCaptionButtonContainerView // The widget that the buttons act on. views::Widget* frame_; - // The buttons. + // The close button separator. + gfx::ImageSkia button_separator_; + + HeaderStyle header_style_; + + // The buttons. At most one of |minimize_button_| and |size_button_| is + // visible. + views::ImageButton* minimize_button_; views::ImageButton* size_button_; views::ImageButton* close_button_; diff --git a/ash/wm/workspace/frame_caption_button_container_view_unittest.cc b/ash/wm/workspace/frame_caption_button_container_view_unittest.cc index 69bb97b..2f520cf 100644 --- a/ash/wm/workspace/frame_caption_button_container_view_unittest.cc +++ b/ash/wm/workspace/frame_caption_button_container_view_unittest.cc @@ -5,70 +5,255 @@ #include "ash/wm/workspace/frame_caption_button_container_view.h" #include "ash/ash_switches.h" +#include "ash/test/ash_test_base.h" #include "base/command_line.h" -#include "testing/gtest/include/gtest/gtest.h" +#include "grit/ash_resources.h" +#include "ui/aura/root_window.h" +#include "ui/base/resource/resource_bundle.h" #include "ui/views/border.h" #include "ui/views/controls/button/image_button.h" +#include "ui/views/widget/widget.h" +#include "ui/views/widget/widget_delegate.h" namespace ash { -typedef testing::Test FrameCaptionButtonContainerViewTest; - -TEST_F(FrameCaptionButtonContainerViewTest, Sanity) { - // 1) Test the layout and the caption button visibility in the default case. - // Both the size button and the close button should be visible. - FrameCaptionButtonContainerView c1(NULL, NULL); - c1.Layout(); - FrameCaptionButtonContainerView::TestApi t1(&c1); - views::ImageButton* size_button = t1.size_button(); - views::ImageButton* close_button = t1.close_button(); - EXPECT_TRUE(size_button->visible()); - EXPECT_TRUE(close_button->visible()); - - // The size button should be left of the close button. (in non-RTL) - EXPECT_LT(size_button->x(), close_button->x()); - - // The container's bounds should be flush with the caption buttons. - EXPECT_EQ(0, size_button->x()); - EXPECT_EQ(c1.GetPreferredSize().width(), close_button->bounds().right()); - EXPECT_EQ(c1.GetPreferredSize().height(), close_button->bounds().height()); - - // 2) Test the layout and the caption button visibility when the - // "force-maximize-mode" experiment is turned on. Only the close button - // should be visible. - CommandLine::ForCurrentProcess()->AppendSwitch(switches::kForcedMaximizeMode); +namespace { + +// Returns true if the images for |button|'s states match the passed in ids. +bool ImagesMatch(views::ImageButton* button, + int normal_image_id, + int hovered_image_id, + int pressed_image_id) { + ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); + gfx::ImageSkia* normal = rb.GetImageSkiaNamed(normal_image_id); + gfx::ImageSkia* hovered = rb.GetImageSkiaNamed(hovered_image_id); + gfx::ImageSkia* pressed = rb.GetImageSkiaNamed(pressed_image_id); + using views::Button; + return button->GetImage(Button::STATE_NORMAL).BackedBySameObjectAs(*normal) && + button->GetImage(Button::STATE_HOVERED).BackedBySameObjectAs(*hovered) && + button->GetImage(Button::STATE_PRESSED).BackedBySameObjectAs(*pressed); +} + +// Returns true if |leftmost| and |rightmost| are flush with |container|'s +// edges. +bool CheckButtonsAtEdges(FrameCaptionButtonContainerView* container, + const views::ImageButton& leftmost, + const views::ImageButton& rightmost) { + gfx::Rect container_size(container->GetPreferredSize()); + if (leftmost.y() == rightmost.y() && + leftmost.height() == rightmost.height() && + leftmost.x() == 0 && + leftmost.y() == 0 && + leftmost.height() == container_size.height() && + rightmost.bounds().right() == container_size.width()) { + return true; + } + + LOG(ERROR) << "Buttons " << leftmost.bounds().ToString() << " " + << rightmost.bounds().ToString() << " not at edges of " + << gfx::Rect(container_size).ToString(); + return false; +} + +class TestWidgetDelegate : public views::WidgetDelegateView { + public: + TestWidgetDelegate(bool can_maximize) : can_maximize_(can_maximize) { + } + virtual ~TestWidgetDelegate() { + } + + virtual bool CanMaximize() const OVERRIDE { + return can_maximize_; + } + + private: + bool can_maximize_; + + DISALLOW_COPY_AND_ASSIGN(TestWidgetDelegate); +}; + +} // namespace + +class FrameCaptionButtonContainerViewTest : public ash::test::AshTestBase { + public: + enum MaximizeAllowed { + MAXIMIZE_ALLOWED, + MAXIMIZE_DISALLOWED + }; + + FrameCaptionButtonContainerViewTest() { + } - FrameCaptionButtonContainerView c2(NULL, NULL); - c2.Layout(); - FrameCaptionButtonContainerView::TestApi t2(&c2); - size_button = t2.size_button(); - close_button = t2.close_button(); + virtual ~FrameCaptionButtonContainerViewTest() { + } - EXPECT_FALSE(size_button->visible()); - EXPECT_TRUE(close_button->visible()); - EXPECT_EQ(c2.GetPreferredSize().width(), close_button->width()); - EXPECT_EQ(c2.GetPreferredSize().height(), close_button->height()); + // Creates a widget which allows maximizing based on |maximize_allowed|. + // The caller takes ownership of the returned widget. + views::Widget* CreateTestWidget( + MaximizeAllowed maximize_allowed) WARN_UNUSED_RESULT { + views::Widget* widget = new views::Widget; + views::Widget::InitParams params; + params.delegate = new TestWidgetDelegate( + maximize_allowed == MAXIMIZE_ALLOWED); + params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.context = CurrentContext(); + widget->Init(params); + return widget; + } + + private: + DISALLOW_COPY_AND_ASSIGN(FrameCaptionButtonContainerViewTest); +}; + +// Test how the allowed actions affect which caption buttons are visible. +TEST_F(FrameCaptionButtonContainerViewTest, ButtonVisibility) { + // The minimize button should be hidden when both minimizing and maximizing + // are allowed because the size button can do both. + scoped_ptr<views::Widget> widget_can_maximize( + CreateTestWidget(MAXIMIZE_ALLOWED)); + FrameCaptionButtonContainerView container1(NULL, widget_can_maximize.get(), + FrameCaptionButtonContainerView::MINIMIZE_ALLOWED); + container1.Layout(); + FrameCaptionButtonContainerView::TestApi t1(&container1); + EXPECT_FALSE(t1.minimize_button()->visible()); + EXPECT_TRUE(t1.size_button()->visible()); + EXPECT_TRUE(t1.close_button()->visible()); + EXPECT_TRUE(CheckButtonsAtEdges( + &container1, *t1.size_button(), *t1.close_button())); + + // The minimize button should be visible when minimizing is allowed but + // maximizing is disallowed. + scoped_ptr<views::Widget> widget_cannot_maximize( + CreateTestWidget(MAXIMIZE_DISALLOWED)); + FrameCaptionButtonContainerView container2(NULL, widget_cannot_maximize.get(), + FrameCaptionButtonContainerView::MINIMIZE_ALLOWED); + container2.Layout(); + FrameCaptionButtonContainerView::TestApi t2(&container2); + EXPECT_TRUE(t2.minimize_button()->visible()); + EXPECT_FALSE(t2.size_button()->visible()); + EXPECT_TRUE(t2.close_button()->visible()); + EXPECT_TRUE(CheckButtonsAtEdges( + &container2, *t2.minimize_button(), *t2.close_button())); + + // Neither the minimize button nor the size button should be visible when + // neither minimizing nor maximizing are allowed. + FrameCaptionButtonContainerView container3(NULL, widget_cannot_maximize.get(), + FrameCaptionButtonContainerView::MINIMIZE_DISALLOWED); + container3.Layout(); + FrameCaptionButtonContainerView::TestApi t3(&container3); + EXPECT_FALSE(t3.minimize_button()->visible()); + EXPECT_FALSE(t3.size_button()->visible()); + EXPECT_TRUE(t3.close_button()->visible()); + EXPECT_TRUE(CheckButtonsAtEdges( + &container3, *t3.close_button(), *t3.close_button())); + + // Neither the minimize button nor the size button should be visible when the + // "force-maximize-mode" experiment is turned on. + CommandLine::ForCurrentProcess()->AppendSwitch(switches::kForcedMaximizeMode); + FrameCaptionButtonContainerView container4(NULL, widget_can_maximize.get(), + FrameCaptionButtonContainerView::MINIMIZE_ALLOWED); + container4.Layout(); + FrameCaptionButtonContainerView::TestApi t4(&container4); + EXPECT_FALSE(t4.minimize_button()->visible()); + EXPECT_FALSE(t4.size_button()->visible()); + EXPECT_TRUE(t4.close_button()->visible()); + EXPECT_TRUE(CheckButtonsAtEdges( + &container4, *t4.close_button(), *t4.close_button())); } // Test the layout when a border is set on the container. -TEST_F(FrameCaptionButtonContainerViewTest, Border) { +TEST_F(FrameCaptionButtonContainerViewTest, LayoutBorder) { const int kTopInset = 1; const int kLeftInset = 2; const int kBottomInset = 3; const int kRightInset = 4; - FrameCaptionButtonContainerView c(NULL, NULL); - c.set_border(views::Border::CreateEmptyBorder( + scoped_ptr<views::Widget> widget(CreateTestWidget(MAXIMIZE_ALLOWED)); + FrameCaptionButtonContainerView container(NULL, widget.get(), + FrameCaptionButtonContainerView::MINIMIZE_ALLOWED); + container.set_border(views::Border::CreateEmptyBorder( kTopInset, kLeftInset, kBottomInset, kRightInset)); - c.Layout(); - FrameCaptionButtonContainerView::TestApi t(&c); + container.Layout(); + FrameCaptionButtonContainerView::TestApi t(&container); EXPECT_EQ(kLeftInset, t.size_button()->x()); EXPECT_EQ(kTopInset, t.close_button()->y()); - EXPECT_EQ(c.GetPreferredSize().height(), + EXPECT_EQ(container.GetPreferredSize().height(), t.close_button()->bounds().bottom() + kBottomInset); - EXPECT_EQ(c.GetPreferredSize().width(), + EXPECT_EQ(container.GetPreferredSize().width(), t.close_button()->bounds().right() + kRightInset); } +// Test how the header style affects which images are used for the buttons. +TEST_F(FrameCaptionButtonContainerViewTest, HeaderStyle) { + scoped_ptr<views::Widget> widget(CreateTestWidget(MAXIMIZE_ALLOWED)); + FrameCaptionButtonContainerView container(NULL, widget.get(), + FrameCaptionButtonContainerView::MINIMIZE_ALLOWED); + FrameCaptionButtonContainerView::TestApi t(&container); + + // Tall header style. + container.set_header_style( + FrameCaptionButtonContainerView::HEADER_STYLE_TALL); + container.Layout(); + EXPECT_TRUE(ImagesMatch(t.size_button(), + IDR_AURA_WINDOW_MAXIMIZE, + IDR_AURA_WINDOW_MAXIMIZE_H, + IDR_AURA_WINDOW_MAXIMIZE_P)); + EXPECT_TRUE(ImagesMatch(t.close_button(), + IDR_AURA_WINDOW_CLOSE, + IDR_AURA_WINDOW_CLOSE_H, + IDR_AURA_WINDOW_CLOSE_P)); + + // Short header style. + container.set_header_style( + FrameCaptionButtonContainerView::HEADER_STYLE_SHORT); + container.Layout(); + EXPECT_TRUE(ImagesMatch(t.size_button(), + IDR_AURA_WINDOW_MAXIMIZED_RESTORE, + IDR_AURA_WINDOW_MAXIMIZED_RESTORE_H, + IDR_AURA_WINDOW_MAXIMIZED_RESTORE_P)); + EXPECT_TRUE(ImagesMatch(t.close_button(), + IDR_AURA_WINDOW_MAXIMIZED_CLOSE, + IDR_AURA_WINDOW_MAXIMIZED_CLOSE_H, + IDR_AURA_WINDOW_MAXIMIZED_CLOSE_P)); + + // Maximized short header style. + widget->Maximize(); + container.Layout(); + EXPECT_TRUE(ImagesMatch(t.size_button(), + IDR_AURA_WINDOW_MAXIMIZED_RESTORE2, + IDR_AURA_WINDOW_MAXIMIZED_RESTORE2_H, + IDR_AURA_WINDOW_MAXIMIZED_RESTORE2_P)); + EXPECT_TRUE(ImagesMatch(t.close_button(), + IDR_AURA_WINDOW_MAXIMIZED_CLOSE2, + IDR_AURA_WINDOW_MAXIMIZED_CLOSE2_H, + IDR_AURA_WINDOW_MAXIMIZED_CLOSE2_P)); + + // The buttons are visible during a reveal of the top-of-window views in + // immersive fullscreen. They should use the same images as maximized. + widget->SetFullscreen(true); + container.Layout(); + EXPECT_TRUE(ImagesMatch(t.size_button(), + IDR_AURA_WINDOW_MAXIMIZED_RESTORE2, + IDR_AURA_WINDOW_MAXIMIZED_RESTORE2_H, + IDR_AURA_WINDOW_MAXIMIZED_RESTORE2_P)); + EXPECT_TRUE(ImagesMatch(t.close_button(), + IDR_AURA_WINDOW_MAXIMIZED_CLOSE2, + IDR_AURA_WINDOW_MAXIMIZED_CLOSE2_H, + IDR_AURA_WINDOW_MAXIMIZED_CLOSE2_P)); + + // AppNonClientFrameViewAsh has a dedicated set of images. + container.set_header_style( + FrameCaptionButtonContainerView::HEADER_STYLE_MAXIMIZED_HOSTED_APP); + container.Layout(); + EXPECT_TRUE(ImagesMatch(t.size_button(), + IDR_AURA_WINDOW_FULLSCREEN_RESTORE, + IDR_AURA_WINDOW_FULLSCREEN_RESTORE_H, + IDR_AURA_WINDOW_FULLSCREEN_RESTORE_P)); + EXPECT_TRUE(ImagesMatch(t.close_button(), + IDR_AURA_WINDOW_FULLSCREEN_CLOSE, + IDR_AURA_WINDOW_FULLSCREEN_CLOSE_H, + IDR_AURA_WINDOW_FULLSCREEN_CLOSE_P)); +} + } // namespace ash diff --git a/chrome/browser/ui/views/frame/app_non_client_frame_view_ash.cc b/chrome/browser/ui/views/frame/app_non_client_frame_view_ash.cc index f4491b1..a70e813 100644 --- a/chrome/browser/ui/views/frame/app_non_client_frame_view_ash.cc +++ b/chrome/browser/ui/views/frame/app_non_client_frame_view_ash.cc @@ -116,9 +116,13 @@ const char AppNonClientFrameViewAsh::kControlWindowName[] = AppNonClientFrameViewAsh::AppNonClientFrameViewAsh( BrowserFrame* frame, BrowserView* browser_view) : BrowserNonClientFrameView(frame, browser_view), - control_view_(new ash::FrameCaptionButtonContainerView(this, frame)), + control_view_(NULL), control_widget_(NULL), frame_observer_(new FrameObserver(this)) { + control_view_ = new ash::FrameCaptionButtonContainerView(this, frame, + ash::FrameCaptionButtonContainerView::MINIMIZE_ALLOWED); + control_view_->set_header_style( + ash::FrameCaptionButtonContainerView::HEADER_STYLE_MAXIMIZED_HOSTED_APP); control_view_->set_border(new ControlViewBorder()); control_view_->set_background(new ControlViewBackground( browser_view->IsOffTheRecord())); diff --git a/chrome/browser/ui/views/frame/app_non_client_frame_view_ash.h b/chrome/browser/ui/views/frame/app_non_client_frame_view_ash.h index 2c0058f..4c60d94 100644 --- a/chrome/browser/ui/views/frame/app_non_client_frame_view_ash.h +++ b/chrome/browser/ui/views/frame/app_non_client_frame_view_ash.h @@ -12,6 +12,10 @@ namespace aura { class Window; } +namespace ash { +class FrameCaptionButtonContainerView; +} + // NonClientFrameViewAsh implementation for maximized apps. class AppNonClientFrameViewAsh : public BrowserNonClientFrameView { public: @@ -54,7 +58,7 @@ class AppNonClientFrameViewAsh : public BrowserNonClientFrameView { void CloseControlWidget(); // The View containing the restore and close buttons. - views::View* control_view_; + ash::FrameCaptionButtonContainerView* control_view_; // The widget holding the control_view_. views::Widget* control_widget_; // Observer for browser frame close. 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 67a8e69..010e8aa 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 @@ -4,11 +4,9 @@ #include "chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h" -#include "ash/shell_delegate.h" #include "ash/wm/frame_painter.h" -#include "ash/wm/workspace/frame_maximize_button.h" +#include "ash/wm/workspace/frame_caption_button_container_view.h" #include "chrome/browser/themes/theme_properties.h" -#include "chrome/browser/ui/ash/chrome_shell_delegate.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/immersive_fullscreen_configuration.h" #include "chrome/browser/ui/views/avatar_menu_button.h" @@ -19,7 +17,6 @@ #include "chrome/browser/ui/views/tabs/tab_strip.h" #include "content/public/browser/web_contents.h" #include "grit/ash_resources.h" -#include "grit/generated_resources.h" // Accessibility names #include "grit/theme_resources.h" #include "ui/aura/client/aura_constants.h" #include "ui/aura/window.h" @@ -30,10 +27,8 @@ #include "ui/base/resource/resource_bundle.h" #include "ui/base/theme_provider.h" #include "ui/compositor/layer_animator.h" -#include "ui/compositor/scoped_animation_duration_scale_mode.h" #include "ui/gfx/canvas.h" #include "ui/gfx/image/image_skia.h" -#include "ui/views/controls/button/image_button.h" #include "ui/views/controls/label.h" #include "ui/views/layout/layout_constants.h" #include "ui/views/widget/widget.h" @@ -77,28 +72,18 @@ const char BrowserNonClientFrameViewAsh::kViewClassName[] = BrowserNonClientFrameViewAsh::BrowserNonClientFrameViewAsh( BrowserFrame* frame, BrowserView* browser_view) : BrowserNonClientFrameView(frame, browser_view), - size_button_(NULL), - close_button_(NULL), + caption_button_container_(NULL), window_icon_(NULL), - frame_painter_(new ash::FramePainter), - size_button_minimizes_(false) { + frame_painter_(new ash::FramePainter) { } BrowserNonClientFrameViewAsh::~BrowserNonClientFrameViewAsh() { } void BrowserNonClientFrameViewAsh::Init() { - // Panels only minimize. - ash::FramePainter::SizeButtonBehavior size_button_behavior; - size_button_ = new ash::FrameMaximizeButton(this, this); - size_button_behavior = ash::FramePainter::SIZE_BUTTON_MAXIMIZES; - size_button_->SetAccessibleName( - l10n_util::GetStringUTF16(IDS_ACCNAME_MAXIMIZE)); - AddChildView(size_button_); - close_button_ = new views::ImageButton(this); - close_button_->SetAccessibleName( - l10n_util::GetStringUTF16(IDS_ACCNAME_CLOSE)); - AddChildView(close_button_); + caption_button_container_ = new ash::FrameCaptionButtonContainerView(this, + frame(), ash::FrameCaptionButtonContainerView::MINIMIZE_ALLOWED); + AddChildView(caption_button_container_); // Initializing the TabIconView is expensive, so only do it if we need to. if (browser_view()->ShouldShowWindowIcon()) { @@ -111,9 +96,8 @@ void BrowserNonClientFrameViewAsh::Init() { // Create incognito icon if necessary. UpdateAvatarInfo(); - // Frame painter handles layout of these buttons. - frame_painter_->Init(frame(), window_icon_, size_button_, close_button_, - size_button_behavior); + // Frame painter handles layout. + frame_painter_->Init(frame(), window_icon_, caption_button_container_); } /////////////////////////////////////////////////////////////////////////////// @@ -195,11 +179,9 @@ void BrowserNonClientFrameViewAsh::ResetWindowControls() { // is visible because it's confusing when the user hovers or clicks in the // top-right of the screen and hits one. bool button_visibility = !UseImmersiveLightbarHeaderStyle(); - size_button_->SetVisible(button_visibility); - close_button_->SetVisible(button_visibility); + caption_button_container_->SetVisible(button_visibility); - size_button_->SetState(views::CustomButton::STATE_NORMAL); - // The close button isn't affected by this constraint. + caption_button_container_->ResetWindowControls(); } void BrowserNonClientFrameViewAsh::UpdateWindowIcon() { @@ -328,48 +310,6 @@ void BrowserNonClientFrameViewAsh::OnThemeChanged() { } /////////////////////////////////////////////////////////////////////////////// -// views::ButtonListener overrides: - -void BrowserNonClientFrameViewAsh::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)); - } - - ash::UserMetricsAction action = - ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_MAXIMIZE; - - if (sender == size_button_) { - // The maximize button may move out from under the cursor. - ResetWindowControls(); - if (size_button_minimizes_) { - frame()->Minimize(); - action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_MINIMIZE; - } else if (frame()->IsFullscreen()) { // Can be clicked in immersive mode. - 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(); - } - // |this| may be deleted - some windows delete their frames on maximize. - } else if (sender == close_button_) { - frame()->Close(); - action = ash::UMA_WINDOW_CLOSE_BUTTON_CLICK; - } else { - return; - } - ChromeShellDelegate::instance()->RecordUserMetricsAction(action); -} - -/////////////////////////////////////////////////////////////////////////////// // chrome::TabIconViewModel overrides: bool BrowserNonClientFrameViewAsh::ShouldTabIconViewAnimate() const { @@ -390,7 +330,6 @@ gfx::ImageSkia BrowserNonClientFrameViewAsh::GetFaviconForTabIconView() { /////////////////////////////////////////////////////////////////////////////// // BrowserNonClientFrameViewAsh, private: - int BrowserNonClientFrameViewAsh::NonClientTopBorderHeight( bool force_restored) const { if (force_restored) @@ -405,7 +344,7 @@ int BrowserNonClientFrameViewAsh::NonClientTopBorderHeight( } // For windows without a tab strip (popups, etc.) ensure we have enough space // to see the window caption buttons. - return close_button_->bounds().bottom() - kContentShadowHeight; + return caption_button_container_->bounds().bottom() - kContentShadowHeight; } bool BrowserNonClientFrameViewAsh::UseShortHeader() const { @@ -414,8 +353,8 @@ bool BrowserNonClientFrameViewAsh::UseShortHeader() const { // Fullscreen browser, no immersive reveal -> hidden or super short light bar // Fullscreen browser, immersive reveal -> short header // Popup&App window -> tall header - // Panel -> short header - // Dialogs use short header and are handled via CustomFrameViewAsh. + // Panels use short header and are handled via ash::PanelFrameView. + // Dialogs use short header and are handled via ash::CustomFrameViewAsh. Browser* browser = browser_view()->browser(); switch (browser->type()) { case Browser::TYPE_TABBED: @@ -558,7 +497,7 @@ void BrowserNonClientFrameViewAsh::PaintToolbarBackground(gfx::Canvas* canvas) { } void BrowserNonClientFrameViewAsh::PaintContentEdge(gfx::Canvas* canvas) { - canvas->FillRect(gfx::Rect(0, close_button_->bounds().bottom(), + canvas->FillRect(gfx::Rect(0, caption_button_container_->bounds().bottom(), width(), kClientEdgeThickness), ThemeProperties::GetDefaultColor( ThemeProperties::COLOR_TOOLBAR_SEPARATOR)); 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 5b914ee..b2bf31f 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 @@ -9,11 +9,11 @@ #include "base/memory/scoped_ptr.h" #include "chrome/browser/ui/views/frame/browser_non_client_frame_view.h" #include "chrome/browser/ui/views/tab_icon_view_model.h" -#include "ui/views/controls/button/button.h" // ButtonListener class TabIconView; namespace ash { +class FrameCaptionButtonContainerView; class FramePainter; } namespace views { @@ -23,7 +23,6 @@ class ToggleImageButton; class BrowserNonClientFrameViewAsh : public BrowserNonClientFrameView, - public views::ButtonListener, public chrome::TabIconViewModel { public: static const char kViewClassName[]; @@ -59,10 +58,6 @@ class BrowserNonClientFrameViewAsh virtual gfx::Size GetMinimumSize() OVERRIDE; virtual void OnThemeChanged() OVERRIDE; - // views::ButtonListener overrides: - virtual void ButtonPressed(views::Button* sender, - const ui::Event& event) OVERRIDE; - // Overridden from chrome::TabIconViewModel: virtual bool ShouldTabIconViewAnimate() const OVERRIDE; virtual gfx::ImageSkia GetFaviconForTabIconView() OVERRIDE; @@ -111,10 +106,8 @@ class BrowserNonClientFrameViewAsh // 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_|. - views::ImageButton* size_button_; - views::ImageButton* close_button_; + // View which contains the window controls. + ash::FrameCaptionButtonContainerView* caption_button_container_; // For popups, the window icon. TabIconView* window_icon_; @@ -122,10 +115,6 @@ class BrowserNonClientFrameViewAsh // Painter for the frame header. scoped_ptr<ash::FramePainter> frame_painter_; - // If true the |size_button_| minimizes, otherwise it toggles between - // maximized and restored. - bool size_button_minimizes_; - DISALLOW_COPY_AND_ASSIGN(BrowserNonClientFrameViewAsh); }; diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc index 9bf74a2..06365fa 100644 --- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc +++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc @@ -6,6 +6,7 @@ #include "ash/ash_constants.h" #include "ash/ash_switches.h" +#include "ash/wm/workspace/frame_caption_button_container_view.h" #include "base/command_line.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_commands.h" @@ -17,7 +18,6 @@ #include "chrome/test/base/in_process_browser_test.h" #include "ui/base/hit_test.h" #include "ui/compositor/scoped_animation_duration_scale_mode.h" -#include "ui/views/controls/button/image_button.h" #include "ui/views/widget/widget.h" using views::Widget; @@ -123,8 +123,7 @@ IN_PROC_BROWSER_TEST_F(BrowserNonClientFrameViewAshTest, waiter->Wait(); } EXPECT_TRUE(frame_view->ShouldPaint()); - EXPECT_TRUE(frame_view->size_button_->visible()); - EXPECT_TRUE(frame_view->close_button_->visible()); + EXPECT_TRUE(frame_view->caption_button_container_->visible()); } IN_PROC_BROWSER_TEST_F(BrowserNonClientFrameViewAshTest, ImmersiveFullscreen) { @@ -160,8 +159,7 @@ IN_PROC_BROWSER_TEST_F(BrowserNonClientFrameViewAshTest, ImmersiveFullscreen) { immersive_mode_controller->StartRevealForTest(true); EXPECT_TRUE(immersive_mode_controller->IsRevealed()); EXPECT_TRUE(frame_view->ShouldPaint()); - EXPECT_TRUE(frame_view->size_button_->visible()); - EXPECT_TRUE(frame_view->close_button_->visible()); + EXPECT_TRUE(frame_view->caption_button_container_->visible()); EXPECT_TRUE(frame_view->UseShortHeader()); EXPECT_FALSE(frame_view->UseImmersiveLightbarHeaderStyle()); @@ -182,8 +180,7 @@ IN_PROC_BROWSER_TEST_F(BrowserNonClientFrameViewAshTest, ImmersiveFullscreen) { immersive_mode_controller->StartRevealForTest(true); EXPECT_TRUE(immersive_mode_controller->IsRevealed()); EXPECT_TRUE(frame_view->ShouldPaint()); - EXPECT_TRUE(frame_view->size_button_->visible()); - EXPECT_TRUE(frame_view->close_button_->visible()); + EXPECT_TRUE(frame_view->caption_button_container_->visible()); EXPECT_TRUE(frame_view->UseShortHeader()); EXPECT_FALSE(frame_view->UseImmersiveLightbarHeaderStyle()); @@ -191,8 +188,7 @@ IN_PROC_BROWSER_TEST_F(BrowserNonClientFrameViewAshTest, ImmersiveFullscreen) { // be in the lightbar style. immersive_mode_controller->SetMouseHoveredForTest(false); EXPECT_TRUE(frame_view->ShouldPaint()); - EXPECT_FALSE(frame_view->size_button_->visible()); - EXPECT_FALSE(frame_view->close_button_->visible()); + EXPECT_FALSE(frame_view->caption_button_container_->visible()); EXPECT_TRUE(frame_view->UseShortHeader()); EXPECT_TRUE(frame_view->UseImmersiveLightbarHeaderStyle()); @@ -202,7 +198,6 @@ IN_PROC_BROWSER_TEST_F(BrowserNonClientFrameViewAshTest, ImmersiveFullscreen) { // Exiting immersive mode makes controls and frame visible again. EXPECT_TRUE(frame_view->ShouldPaint()); - EXPECT_TRUE(frame_view->size_button_->visible()); - EXPECT_TRUE(frame_view->close_button_->visible()); + EXPECT_TRUE(frame_view->caption_button_container_->visible()); EXPECT_FALSE(frame_view->UseImmersiveLightbarHeaderStyle()); } |