summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorpkotwicz@chromium.org <pkotwicz@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-08-24 00:33:20 +0000
committerpkotwicz@chromium.org <pkotwicz@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-08-24 00:33:20 +0000
commit87625ab4b1771132b3908f445ce8e3d84f83774b (patch)
tree52c1bae27dcfd53aa4f8c44b61198a39e98839a7
parent50f53ac54c5c3beaed05bb09f6db7da568a95550 (diff)
downloadchromium_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.cc70
-rw-r--r--ash/wm/custom_frame_view_ash.h37
-rw-r--r--ash/wm/custom_frame_view_ash_unittest.cc248
-rw-r--r--ash/wm/frame_painter.cc175
-rw-r--r--ash/wm/frame_painter.h23
-rw-r--r--ash/wm/frame_painter_unittest.cc114
-rw-r--r--ash/wm/panels/panel_frame_view.cc35
-rw-r--r--ash/wm/panels/panel_frame_view.h11
-rw-r--r--ash/wm/workspace/frame_caption_button_container_view.cc147
-rw-r--r--ash/wm/workspace/frame_caption_button_container_view.h49
-rw-r--r--ash/wm/workspace/frame_caption_button_container_view_unittest.cc269
-rw-r--r--chrome/browser/ui/views/frame/app_non_client_frame_view_ash.cc6
-rw-r--r--chrome/browser/ui/views/frame/app_non_client_frame_view_ash.h6
-rw-r--r--chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.cc89
-rw-r--r--chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h17
-rw-r--r--chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc17
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());
}