summaryrefslogtreecommitdiffstats
path: root/ash/wm
diff options
context:
space:
mode:
authorpkotwicz@chromium.org <pkotwicz@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-09-12 02:04:53 +0000
committerpkotwicz@chromium.org <pkotwicz@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-09-12 02:04:53 +0000
commit0e60de7a85d03497a3dffab2391324aaca317b42 (patch)
tree5c31e2d6870e0437ad0663dbf64adf5daf0fa1da /ash/wm
parent56f10afdd3053c149af1b893d8363ebe13e858cb (diff)
downloadchromium_src-0e60de7a85d03497a3dffab2391324aaca317b42.zip
chromium_src-0e60de7a85d03497a3dffab2391324aaca317b42.tar.gz
chromium_src-0e60de7a85d03497a3dffab2391324aaca317b42.tar.bz2
Basic implementation of the bubble burst effect for the new caption button style.
BUG=271304 TEST=Visual, see bug Review URL: https://chromiumcodereview.appspot.com/23532030 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@222700 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ash/wm')
-rw-r--r--ash/wm/custom_frame_view_ash_unittest.cc4
-rw-r--r--ash/wm/workspace/alternate_frame_caption_button.cc230
-rw-r--r--ash/wm/workspace/alternate_frame_caption_button.h69
-rw-r--r--ash/wm/workspace/frame_caption_button_container_view.cc251
-rw-r--r--ash/wm/workspace/frame_caption_button_container_view.h44
-rw-r--r--ash/wm/workspace/frame_caption_button_container_view_unittest.cc144
6 files changed, 618 insertions, 124 deletions
diff --git a/ash/wm/custom_frame_view_ash_unittest.cc b/ash/wm/custom_frame_view_ash_unittest.cc
index 7c81b05..0499f29 100644
--- a/ash/wm/custom_frame_view_ash_unittest.cc
+++ b/ash/wm/custom_frame_view_ash_unittest.cc
@@ -4,6 +4,7 @@
#include "ash/wm/custom_frame_view_ash.h"
+#include "ash/ash_switches.h"
#include "ash/shell.h"
#include "ash/test/ash_test_base.h"
#include "ash/wm/maximize_bubble_controller.h"
@@ -114,6 +115,9 @@ class CustomFrameViewAshTest : public ash::test::AshTestBase {
virtual void SetUp() OVERRIDE {
AshTestBase::SetUp();
+ CommandLine::ForCurrentProcess()->AppendSwitch(
+ switches::kAshDisableAlternateFrameCaptionButtonStyle);
+
widget_ = CreateWidget();
CustomFrameViewAsh* frame = static_cast<CustomFrameViewAsh*>(
widget()->non_client_view()->frame_view());
diff --git a/ash/wm/workspace/alternate_frame_caption_button.cc b/ash/wm/workspace/alternate_frame_caption_button.cc
new file mode 100644
index 0000000..ffdb539
--- /dev/null
+++ b/ash/wm/workspace/alternate_frame_caption_button.cc
@@ -0,0 +1,230 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/wm/workspace/alternate_frame_caption_button.h"
+
+#include "ui/base/animation/slide_animation.h"
+#include "ui/gfx/canvas.h"
+
+namespace ash {
+
+namespace {
+
+// The width and height of the region of the button which does not overlap
+// with other buttons.
+const int kSize = 32;
+
+// A bubble is painted in the background when the mouse button is pressed.
+// When the button is pressed:
+// - The bubble is faded in from opacity 0 to |kShownBubbleOpacity|.
+// - The bubble is expanded from |kInitialGrowBubbleRadius| to
+// |kFullyGrownBubbleRadius|.
+// When the button is unpressed (STATE_NORMAL)
+// - The bubble is faded out from its current opacity back to 0.
+// - The bubble is further expanded from its current radius to
+// |kFinalBurstBubbleRadius|.
+const int kInitialGrowBubbleRadius = 16;
+const int kFullyGrownBubbleRadius = 22;
+const int kFinalBurstBubbleRadius = 26;
+const int kShownBubbleOpacity = 100;
+
+// The duraton of the animations for hiding and showing the bubble.
+const int kBubbleAnimationDuration = 100;
+
+// TODO(pkotwicz): Replace these colors with colors from UX.
+const SkColor kPathColor = SK_ColorDKGRAY;
+const SkColor kPressedHoveredPathColor = SK_ColorBLACK;
+const SkColor kBubbleColor = SK_ColorWHITE;
+
+struct Line {
+ int x1, y1, x2, y2;
+};
+
+// Line segments for the button icons. The line segments are painted in a 12x12
+// centered box.
+const Line kMinimizeLineSegments[] = {
+ {1, 11, 11, 11}
+};
+const Line kMaximizeRestoreLineSegments[] = {
+ {1, 1, 11, 1},
+ {11, 1, 11, 11},
+ {11, 11, 1, 11},
+ {1, 11, 1, 1}
+};
+const Line kCloseLineSegments[] = {
+ {1, 1, 11, 11},
+ {1, 11, 11, 1}
+};
+
+// The amount that the origin of the icon's 12x12 box should be offset from the
+// center of the button.
+const int kIconOffsetFromCenter = -6;
+
+// Sets |line_segments| to the line segments for the icon for |action|.
+void GetLineSegmentsForAction(AlternateFrameCaptionButton::Action action,
+ const Line** line_segments,
+ size_t* num_line_segments) {
+ switch(action) {
+ case AlternateFrameCaptionButton::ACTION_MINIMIZE:
+ *line_segments = kMinimizeLineSegments;
+ *num_line_segments = arraysize(kMinimizeLineSegments);
+ break;
+ case AlternateFrameCaptionButton::ACTION_MAXIMIZE_RESTORE:
+ *line_segments = kMaximizeRestoreLineSegments;
+ *num_line_segments = arraysize(kMaximizeRestoreLineSegments);
+ break;
+ case AlternateFrameCaptionButton::ACTION_CLOSE:
+ *line_segments = kCloseLineSegments;
+ *num_line_segments = arraysize(kCloseLineSegments);
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+}
+
+} // namespace
+
+const char AlternateFrameCaptionButton::kViewClassName[] =
+ "AlternateFrameCaptionButton";
+
+AlternateFrameCaptionButton::AlternateFrameCaptionButton(
+ views::ButtonListener* listener,
+ Action action)
+ : views::CustomButton(listener),
+ action_(action),
+ hidden_bubble_radius_(0),
+ shown_bubble_radius_(0),
+ bubble_animation_(new ui::SlideAnimation(this)) {
+}
+
+AlternateFrameCaptionButton::~AlternateFrameCaptionButton() {
+}
+
+// static
+int AlternateFrameCaptionButton::GetXOverlap() {
+ return kFinalBurstBubbleRadius - kSize / 2;
+}
+
+gfx::Size AlternateFrameCaptionButton::GetPreferredSize() {
+ gfx::Insets insets(GetInsets());
+ return gfx::Size(
+ std::max(kFinalBurstBubbleRadius * 2, kSize + insets.width()),
+ kSize + insets.height());
+}
+
+const char* AlternateFrameCaptionButton::GetClassName() const {
+ return kViewClassName;
+}
+
+bool AlternateFrameCaptionButton::HitTestRect(const gfx::Rect& rect) const {
+ gfx::Rect bounds(GetLocalBounds());
+ if (state_ == STATE_PRESSED)
+ return bounds.Intersects(rect);
+
+ int x_overlap = GetXOverlap();
+ bounds.set_x(x_overlap);
+ bounds.set_width(width() - x_overlap * 2);
+ return bounds.Intersects(rect);
+}
+
+void AlternateFrameCaptionButton::OnPaint(gfx::Canvas* canvas) {
+ gfx::Point content_bounds_center(GetContentsBounds().CenterPoint());
+
+ int bubble_alpha = bubble_animation_->CurrentValueBetween(
+ 0, kShownBubbleOpacity);
+ if (bubble_alpha != 0) {
+ int bubble_radius = bubble_animation_->CurrentValueBetween(
+ hidden_bubble_radius_, shown_bubble_radius_);
+
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setStyle(SkPaint::kFill_Style);
+ paint.setColor(SkColorSetA(kBubbleColor, bubble_alpha));
+ canvas->DrawCircle(content_bounds_center, bubble_radius, paint);
+ }
+
+ SkColor color = kPathColor;
+ if (state_ == STATE_HOVERED || state_ == STATE_PRESSED)
+ color = kPressedHoveredPathColor;
+
+ const Line* line_segments = NULL;
+ size_t num_line_segments = 0;
+ GetLineSegmentsForAction(action_, &line_segments, &num_line_segments);
+
+ gfx::Vector2d top_left_offset(
+ content_bounds_center.x() + kIconOffsetFromCenter,
+ content_bounds_center.y() + kIconOffsetFromCenter);
+ canvas->Translate(top_left_offset);
+ SkPaint paint;
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setStrokeWidth(SkIntToScalar(2));
+ paint.setStrokeCap(SkPaint::kSquare_Cap);
+ paint.setColor(color);
+ for (size_t i = 0; i < num_line_segments; ++i) {
+ canvas->DrawLine(gfx::Point(line_segments[i].x1, line_segments[i].y1),
+ gfx::Point(line_segments[i].x2, line_segments[i].y2),
+ paint);
+ }
+ canvas->Translate(-top_left_offset);
+}
+
+void AlternateFrameCaptionButton::MaybeStartNewBubbleAnimation() {
+ bool should_show = (state_ == STATE_PRESSED);
+ if (should_show == bubble_animation_->IsShowing())
+ return;
+
+ if (!bubble_animation_->is_animating()) {
+ if (should_show)
+ hidden_bubble_radius_ = kInitialGrowBubbleRadius;
+ else
+ hidden_bubble_radius_ = kFinalBurstBubbleRadius;
+ shown_bubble_radius_ = kFullyGrownBubbleRadius;
+
+ bubble_animation_->SetSlideDuration(kBubbleAnimationDuration);
+ if (should_show)
+ bubble_animation_->Show();
+ else
+ bubble_animation_->Hide();
+ } else {
+ if (!should_show) {
+ // The change in radius during a hide animation if there was no currently
+ // running animation.
+ int normal_radius_change =
+ kFinalBurstBubbleRadius - kFullyGrownBubbleRadius;
+
+ // Start a fade out animation from the bubble's current radius and
+ // opacity. Update the bubble radius and opacity at the same rate that it
+ // gets updated during a normal hide animation.
+ int current_bubble_radius = bubble_animation_->CurrentValueBetween(
+ kInitialGrowBubbleRadius, kFullyGrownBubbleRadius);
+ hidden_bubble_radius_ = current_bubble_radius +
+ bubble_animation_->GetCurrentValue() * normal_radius_change;
+ shown_bubble_radius_ = hidden_bubble_radius_ - normal_radius_change;
+ bubble_animation_->SetSlideDuration(
+ bubble_animation_->GetCurrentValue() * kBubbleAnimationDuration);
+ bubble_animation_->Hide();
+ }
+ // Else: The bubble is currently fading out. Wait till the hide animation
+ // completes before starting an animation to show a new bubble.
+ }
+}
+
+void AlternateFrameCaptionButton::StateChanged() {
+ MaybeStartNewBubbleAnimation();
+}
+
+void AlternateFrameCaptionButton::AnimationProgressed(
+ const ui::Animation* animation) {
+ SchedulePaint();
+}
+
+void AlternateFrameCaptionButton::AnimationEnded(
+ const ui::Animation* animation) {
+ // The bubble animation was postponed if the button became pressed when the
+ // bubble was fading out. Do the animation now.
+ MaybeStartNewBubbleAnimation();
+}
+
+} // namespace ash
diff --git a/ash/wm/workspace/alternate_frame_caption_button.h b/ash/wm/workspace/alternate_frame_caption_button.h
new file mode 100644
index 0000000..b0f9581
--- /dev/null
+++ b/ash/wm/workspace/alternate_frame_caption_button.h
@@ -0,0 +1,69 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_WM_WORKSPACE_ALTERNATE_FRAME_CAPTION_BUTTON_H_
+#define ASH_WM_WORKSPACE_ALTERNATE_FRAME_CAPTION_BUTTON_H_
+
+#include "ash/ash_export.h"
+#include "base/memory/scoped_ptr.h"
+#include "ui/views/controls/button/custom_button.h"
+
+namespace ui {
+class SlideAnimation;
+}
+
+namespace ash {
+
+// Base class for buttons using the alternate button style.
+class ASH_EXPORT AlternateFrameCaptionButton : public views::CustomButton {
+ public:
+ static const char kViewClassName[];
+
+ enum Action {
+ ACTION_MINIMIZE,
+ ACTION_MAXIMIZE_RESTORE,
+ ACTION_CLOSE
+ };
+
+ AlternateFrameCaptionButton(views::ButtonListener* listener, Action action);
+ virtual ~AlternateFrameCaptionButton();
+
+ // Returns the amount in pixels that the button should overlap with the button
+ // on the left and right of it.
+ static int GetXOverlap();
+
+ // views::View overrides:
+ virtual gfx::Size GetPreferredSize() OVERRIDE;
+ virtual const char* GetClassName() const OVERRIDE;
+ virtual bool HitTestRect(const gfx::Rect& rect) const OVERRIDE;
+ virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
+
+ private:
+ // Animates the background bubble for the current views::ButtonState.
+ void MaybeStartNewBubbleAnimation();
+
+ // views::CustomButton override:
+ virtual void StateChanged() OVERRIDE;
+
+ // ui::AnimateDelegate overrides. (views::CustomButton inherits from
+ // ui::AnimationDelegate).
+ virtual void AnimationProgressed(const ui::Animation* animation) OVERRIDE;
+ virtual void AnimationEnded(const ui::Animation* animation) OVERRIDE;
+
+ Action action_;
+
+ // The radius of the background bubble when it is hidden.
+ double hidden_bubble_radius_;
+
+ // The radius of the background bubble when it is visible.
+ double shown_bubble_radius_;
+
+ scoped_ptr<ui::SlideAnimation> bubble_animation_;
+
+ DISALLOW_COPY_AND_ASSIGN(AlternateFrameCaptionButton);
+};
+
+} // namespace ash
+
+#endif // ASH_WM_WORKSPACE_ALTERNATE_FRAME_CAPTION_BUTTON_H_
diff --git a/ash/wm/workspace/frame_caption_button_container_view.cc b/ash/wm/workspace/frame_caption_button_container_view.cc
index ddf602f..264c57a 100644
--- a/ash/wm/workspace/frame_caption_button_container_view.cc
+++ b/ash/wm/workspace/frame_caption_button_container_view.cc
@@ -4,9 +4,11 @@
#include "ash/wm/workspace/frame_caption_button_container_view.h"
+#include "ash/ash_switches.h"
#include "ash/shell.h"
#include "ash/shell_delegate.h"
#include "ash/wm/window_settings.h"
+#include "ash/wm/workspace/alternate_frame_caption_button.h"
#include "ash/wm/workspace/frame_maximize_button.h"
#include "grit/ash_resources.h"
#include "grit/ui_strings.h" // Accessibility names
@@ -15,6 +17,7 @@
#include "ui/base/resource/resource_bundle.h"
#include "ui/compositor/scoped_animation_duration_scale_mode.h"
#include "ui/gfx/canvas.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"
@@ -24,8 +27,36 @@ namespace ash {
namespace {
-// The space between the buttons.
-const int kButtonGap = -1;
+// Constants for normal button style -------------------------------------------
+
+// The distance between buttons. AlternateFrameCaptionButton::GetXOverlap() is
+// used to compute the distance between buttons for the alternate button style.
+const int kDistanceBetweenButtons = -1;
+
+// Constants for alternate button style ----------------------------------------
+
+// Spacings between the buttons and the view's top and bottom borders (if any).
+const int kAlternateStyleShortHeaderTopInset = 0;
+const int kAlternateStyleTallHeaderTopInset = 2;
+const int kAlternateStyleShortHeaderBottomInset = 0;
+const int kAlternateStyleTallHeaderBottomInset = 1;
+
+// Ideal spacing between:
+// - Right edge of the leftmost button's overlappable region and the view's left
+// edge.
+// - Left edge of the rightmost button's overlappable region and the view's
+// right edge.
+// Used in GetLeftInset() and GetRightInset().
+const int kAlternateStyleSideInset = 5;
+
+// Converts |point| from |src| to |dst| and hittests against |dst|.
+bool ConvertPointToViewAndHitTest(const views::View* src,
+ const views::View* dst,
+ const gfx::Point& point) {
+ gfx::Point converted(point);
+ views::View::ConvertPointToTarget(src, dst, &converted);
+ return dst->HitTestPoint(converted);
+}
} // namespace
@@ -39,18 +70,34 @@ FrameCaptionButtonContainerView::FrameCaptionButtonContainerView(
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)) {
+ minimize_button_(NULL),
+ size_button_(NULL),
+ close_button_(NULL) {
+ bool alternate_style = switches::UseAlternateFrameCaptionButtonStyle();
+
// Insert the buttons left to right.
+ if (alternate_style) {
+ minimize_button_ = new AlternateFrameCaptionButton(this,
+ AlternateFrameCaptionButton::ACTION_MINIMIZE);
+ size_button_ = new AlternateFrameCaptionButton(this,
+ AlternateFrameCaptionButton::ACTION_MAXIMIZE_RESTORE);
+ close_button_ = new AlternateFrameCaptionButton(this,
+ AlternateFrameCaptionButton::ACTION_CLOSE);
+ } else {
+ minimize_button_ = new views::ImageButton(this);
+ size_button_ = new FrameMaximizeButton(this, frame_view);
+ close_button_ = new views::ImageButton(this);
+ }
+
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.
+ // Hide |minimize_button_| when using the non-alternate button style because
+ // |size_button_| is capable of minimizing in this case.
// 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());
+ minimize_button_->SetVisible(
+ minimize_allowed == MINIMIZE_ALLOWED &&
+ (alternate_style || !frame_->widget_delegate()->CanMaximize()));
AddChildView(minimize_button_);
size_button_->SetAccessibleName(
@@ -78,19 +125,21 @@ void FrameCaptionButtonContainerView::ResetWindowControls() {
int FrameCaptionButtonContainerView::NonClientHitTest(
const gfx::Point& point) const {
if (close_button_->visible() &&
- close_button_->GetMirroredBounds().Contains(point)) {
+ ConvertPointToViewAndHitTest(this, close_button_, point)) {
return HTCLOSE;
} else if (size_button_->visible() &&
- size_button_->GetMirroredBounds().Contains(point)) {
+ ConvertPointToViewAndHitTest(this, size_button_, point)) {
return HTMAXBUTTON;
} else if (minimize_button_->visible() &&
- minimize_button_->GetMirroredBounds().Contains(point)) {
+ ConvertPointToViewAndHitTest(this, minimize_button_, point)) {
return HTMINBUTTON;
}
return HTNOWHERE;
}
gfx::Size FrameCaptionButtonContainerView::GetPreferredSize() {
+ int button_separation = GetDistanceBetweenButtons();
+
int width = 0;
bool first_visible = true;
for (int i = 0; i < child_count(); ++i) {
@@ -100,68 +149,85 @@ gfx::Size FrameCaptionButtonContainerView::GetPreferredSize() {
width += child_at(i)->GetPreferredSize().width();
if (!first_visible)
- width += kButtonGap;
+ width += button_separation;
first_visible = false;
}
gfx::Insets insets(GetInsets());
return gfx::Size(
- width + insets.width(),
+ width + insets.width() + GetLeftInset() + GetRightInset(),
close_button_->GetPreferredSize().height() + insets.height());
}
void FrameCaptionButtonContainerView::Layout() {
- 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()) &&
- wm::GetWindowSettings(
- frame_->GetNativeWindow())->tracked_by_workspace()) {
+ if (switches::UseAlternateFrameCaptionButtonStyle()) {
+ int top_inset = kAlternateStyleShortHeaderTopInset;
+ int bottom_inset = kAlternateStyleShortHeaderBottomInset;
+ if (header_style_ == HEADER_STYLE_TALL) {
+ top_inset = kAlternateStyleTallHeaderTopInset;
+ bottom_inset = kAlternateStyleTallHeaderBottomInset;
+ }
+
+ minimize_button_->set_border(
+ views::Border::CreateEmptyBorder(top_inset, 0, bottom_inset, 0));
+ size_button_->set_border(
+ views::Border::CreateEmptyBorder(top_inset, 0, bottom_inset, 0));
+ close_button_->set_border(
+ views::Border::CreateEmptyBorder(top_inset, 0, bottom_inset, 0));
+ } else {
+ 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_MAXIMIZED_RESTORE2,
- IDR_AURA_WINDOW_MAXIMIZED_RESTORE2_H,
- IDR_AURA_WINDOW_MAXIMIZED_RESTORE2_P);
+ IDR_AURA_WINDOW_FULLSCREEN_RESTORE,
+ IDR_AURA_WINDOW_FULLSCREEN_RESTORE_H,
+ IDR_AURA_WINDOW_FULLSCREEN_RESTORE_P);
SetButtonImages(close_button_,
- IDR_AURA_WINDOW_MAXIMIZED_CLOSE2,
- IDR_AURA_WINDOW_MAXIMIZED_CLOSE2_H,
- IDR_AURA_WINDOW_MAXIMIZED_CLOSE2_P);
+ 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()) &&
+ wm::GetWindowSettings(
+ frame_->GetNativeWindow())->tracked_by_workspace()) {
+ 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_MAXIMIZED_RESTORE,
- IDR_AURA_WINDOW_MAXIMIZED_RESTORE_H,
- IDR_AURA_WINDOW_MAXIMIZED_RESTORE_P);
+ IDR_AURA_WINDOW_MAXIMIZE,
+ IDR_AURA_WINDOW_MAXIMIZE_H,
+ IDR_AURA_WINDOW_MAXIMIZE_P);
SetButtonImages(close_button_,
- IDR_AURA_WINDOW_MAXIMIZED_CLOSE,
- IDR_AURA_WINDOW_MAXIMIZED_CLOSE_H,
- IDR_AURA_WINDOW_MAXIMIZED_CLOSE_P);
+ IDR_AURA_WINDOW_CLOSE,
+ IDR_AURA_WINDOW_CLOSE_H,
+ IDR_AURA_WINDOW_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();
+ int x = insets.left() + GetLeftInset();
int y_inset = insets.top();
+ int button_separation = GetDistanceBetweenButtons();
for (int i = 0; i < child_count(); ++i) {
views::View* child = child_at(i);
if (!child->visible())
@@ -169,7 +235,12 @@ void FrameCaptionButtonContainerView::Layout() {
gfx::Size size = child->GetPreferredSize();
child->SetBounds(x, y_inset, size.width(), size.height());
- x += size.width() + kButtonGap;
+
+ // Do not allow |child| to paint over the left border.
+ child->set_clip_insets(
+ gfx::Insets(0, std::max(0, insets.left() - x), 0, 0));
+
+ x += size.width() + button_separation;
}
}
@@ -180,8 +251,10 @@ const char* FrameCaptionButtonContainerView::GetClassName() const {
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) {
+ // The alternate button style and AppNonClientFrameViewAsh do not paint the
+ // button separator.
+ if (header_style_ != HEADER_STYLE_MAXIMIZED_HOSTED_APP &&
+ !switches::UseAlternateFrameCaptionButtonStyle()) {
// We should have at most two visible buttons. The button separator is
// always painted underneath the close button regardless of whether a
// button other than the close button is visible.
@@ -193,18 +266,36 @@ void FrameCaptionButtonContainerView::OnPaint(gfx::Canvas* canvas) {
}
}
-void FrameCaptionButtonContainerView::SetButtonImages(
- views::ImageButton* button,
- int normal_image_id,
- int hot_image_id,
- int pushed_image_id) {
- ui::ResourceBundle& resource_bundle = ui::ResourceBundle::GetSharedInstance();
- button->SetImage(views::CustomButton::STATE_NORMAL,
- resource_bundle.GetImageSkiaNamed(normal_image_id));
- button->SetImage(views::CustomButton::STATE_HOVERED,
- resource_bundle.GetImageSkiaNamed(hot_image_id));
- button->SetImage(views::CustomButton::STATE_PRESSED,
- resource_bundle.GetImageSkiaNamed(pushed_image_id));
+int FrameCaptionButtonContainerView::GetDistanceBetweenButtons() const {
+ if (switches::UseAlternateFrameCaptionButtonStyle())
+ return AlternateFrameCaptionButton::GetXOverlap() * -2;
+ return kDistanceBetweenButtons;
+}
+
+int FrameCaptionButtonContainerView::GetLeftInset() const {
+ if (switches::UseAlternateFrameCaptionButtonStyle()) {
+ // If using the alternate button style and there is a border, clip the
+ // left overlappable region of the leftmost button to
+ // |kAlternateStyleSideInset|.
+ // Otherwise, allow enough room for the entire left overlappable region of
+ // the leftmost button to fit in the view.
+ if (border() && border()->GetInsets().left()) {
+ return kAlternateStyleSideInset -
+ AlternateFrameCaptionButton::GetXOverlap();
+ }
+ }
+ return 0;
+}
+
+int FrameCaptionButtonContainerView::GetRightInset() const {
+ if (switches::UseAlternateFrameCaptionButtonStyle()) {
+ // Always clip the right overlappable region of the rightmost button to
+ // |kAlternateStyleSideInset| because the caption buttons are always
+ // at the right edge of the screen. (The left edge in RTL mode).
+ return kAlternateStyleSideInset -
+ AlternateFrameCaptionButton::GetXOverlap();
+ }
+ return 0;
}
void FrameCaptionButtonContainerView::ButtonPressed(views::Button* sender,
@@ -246,4 +337,22 @@ void FrameCaptionButtonContainerView::ButtonPressed(views::Button* sender,
ash::Shell::GetInstance()->delegate()->RecordUserMetricsAction(action);
}
+void FrameCaptionButtonContainerView::SetButtonImages(
+ views::CustomButton* button,
+ int normal_image_id,
+ int hot_image_id,
+ int pushed_image_id) {
+ // When using the alternate button style, |button| does not inherit from
+ // views::ImageButton.
+ DCHECK(!switches::UseAlternateFrameCaptionButtonStyle());
+ views::ImageButton* image_button = static_cast<views::ImageButton*>(button);
+ ui::ResourceBundle& resource_bundle = ui::ResourceBundle::GetSharedInstance();
+ image_button->SetImage(views::CustomButton::STATE_NORMAL,
+ resource_bundle.GetImageSkiaNamed(normal_image_id));
+ image_button->SetImage(views::CustomButton::STATE_HOVERED,
+ resource_bundle.GetImageSkiaNamed(hot_image_id));
+ image_button->SetImage(views::CustomButton::STATE_PRESSED,
+ resource_bundle.GetImageSkiaNamed(pushed_image_id));
+}
+
} // namespace ash
diff --git a/ash/wm/workspace/frame_caption_button_container_view.h b/ash/wm/workspace/frame_caption_button_container_view.h
index b1bb377..be76e77 100644
--- a/ash/wm/workspace/frame_caption_button_container_view.h
+++ b/ash/wm/workspace/frame_caption_button_container_view.h
@@ -11,7 +11,7 @@
#include "ui/views/view.h"
namespace views {
-class ImageButton;
+class CustomButton;
class NonClientFrameView;
class Widget;
}
@@ -63,15 +63,15 @@ class ASH_EXPORT FrameCaptionButtonContainerView
: container_view_(container_view) {
}
- views::ImageButton* minimize_button() const {
+ views::CustomButton* minimize_button() const {
return container_view_->minimize_button_;
}
- views::ImageButton* size_button() const {
+ views::CustomButton* size_button() const {
return container_view_->size_button_;
}
- views::ImageButton* close_button() const {
+ views::CustomButton* close_button() const {
return container_view_->close_button_;
}
@@ -101,16 +101,32 @@ class ASH_EXPORT FrameCaptionButtonContainerView
virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
private:
- // Sets the images for a button based on the given ids.
- void SetButtonImages(views::ImageButton* button,
- int normal_image_id,
- int hot_image_id,
- int pushed_image_id);
+ friend class FrameCaptionButtonContainerViewTest;
+
+ // Returns the distance between buttons which are next to each other. A
+ // negative value is returned if the buttons overlap.
+ int GetDistanceBetweenButtons() const;
+
+ // Returns the inset of the leftmost visible button from the view's border
+ // (if any).
+ int GetLeftInset() const;
+
+ // Returns the inset of the rightmost visible button from the view's border
+ // (if any).
+ int GetRightInset() const;
// views::ButtonListener override:
virtual void ButtonPressed(views::Button* sender,
const ui::Event& event) OVERRIDE;
+ // Methods specific to normal button style -----------------------------------
+ //
+ // Sets the images for a button based on the given ids.
+ void SetButtonImages(views::CustomButton* button,
+ int normal_image_id,
+ int hot_image_id,
+ int pushed_image_id);
+
// The widget that the buttons act on.
views::Widget* frame_;
@@ -119,11 +135,11 @@ class ASH_EXPORT FrameCaptionButtonContainerView
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_;
+ // The buttons. In the normal button style, at most one of |minimize_button_|
+ // and |size_button_| is visible.
+ views::CustomButton* minimize_button_;
+ views::CustomButton* size_button_;
+ views::CustomButton* close_button_;
DISALLOW_COPY_AND_ASSIGN(FrameCaptionButtonContainerView);
};
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 2f520cf..cb732bf 100644
--- a/ash/wm/workspace/frame_caption_button_container_view_unittest.cc
+++ b/ash/wm/workspace/frame_caption_button_container_view_unittest.cc
@@ -11,6 +11,7 @@
#include "ui/aura/root_window.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/views/border.h"
+#include "ui/views/controls/button/custom_button.h"
#include "ui/views/controls/button/image_button.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_delegate.h"
@@ -19,42 +20,6 @@ namespace ash {
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) {
@@ -101,12 +66,74 @@ class FrameCaptionButtonContainerViewTest : public ash::test::AshTestBase {
return widget;
}
+ // Tests that |leftmost| and |rightmost| are at |container|'s edges.
+ bool CheckButtonsAtEdges(FrameCaptionButtonContainerView* container,
+ const views::CustomButton& leftmost,
+ const views::CustomButton& rightmost) {
+ gfx::Rect expected(container->GetPreferredSize());
+ expected.Inset(container->GetLeftInset(), 0, container->GetRightInset(), 0);
+
+ gfx::Rect container_size(container->GetPreferredSize());
+ if (leftmost.y() == rightmost.y() &&
+ leftmost.height() == rightmost.height() &&
+ leftmost.x() == expected.x() &&
+ leftmost.y() == expected.y() &&
+ leftmost.height() == expected.height() &&
+ rightmost.bounds().right() == expected.right()) {
+ return true;
+ }
+
+ LOG(ERROR) << "Buttons " << leftmost.bounds().ToString() << " "
+ << rightmost.bounds().ToString() << " not at edges of "
+ << expected.ToString();
+ return false;
+ }
+
private:
DISALLOW_COPY_AND_ASSIGN(FrameCaptionButtonContainerViewTest);
};
+class FrameCaptionButtonContainerViewTestOldStyle
+ : public FrameCaptionButtonContainerViewTest {
+ public:
+ FrameCaptionButtonContainerViewTestOldStyle() {
+ }
+
+ virtual ~FrameCaptionButtonContainerViewTestOldStyle() {
+ }
+
+ // Returns true if the images for |button|'s states match the passed in ids.
+ bool ImagesMatch(views::CustomButton* custom_button,
+ int normal_image_id,
+ int hovered_image_id,
+ int pressed_image_id) {
+ views::ImageButton* button =
+ static_cast<views::ImageButton*>(custom_button);
+ 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;
+ gfx::ImageSkia actual_normal = button->GetImage(Button::STATE_NORMAL);
+ gfx::ImageSkia actual_hovered = button->GetImage(Button::STATE_HOVERED);
+ gfx::ImageSkia actual_pressed = button->GetImage(Button::STATE_PRESSED);
+ return actual_normal.BackedBySameObjectAs(*normal) &&
+ actual_hovered.BackedBySameObjectAs(*hovered) &&
+ actual_pressed.BackedBySameObjectAs(*pressed);
+ }
+
+ virtual void SetUp() OVERRIDE {
+ FrameCaptionButtonContainerViewTest::SetUp();
+ CommandLine::ForCurrentProcess()->AppendSwitch(
+ switches::kAshDisableAlternateFrameCaptionButtonStyle);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FrameCaptionButtonContainerViewTestOldStyle);
+};
+
// Test how the allowed actions affect which caption buttons are visible.
-TEST_F(FrameCaptionButtonContainerViewTest, ButtonVisibility) {
+TEST_F(FrameCaptionButtonContainerViewTestOldStyle, 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(
@@ -162,7 +189,7 @@ TEST_F(FrameCaptionButtonContainerViewTest, ButtonVisibility) {
}
// Test the layout when a border is set on the container.
-TEST_F(FrameCaptionButtonContainerViewTest, LayoutBorder) {
+TEST_F(FrameCaptionButtonContainerViewTestOldStyle, LayoutBorder) {
const int kTopInset = 1;
const int kLeftInset = 2;
const int kBottomInset = 3;
@@ -185,7 +212,7 @@ TEST_F(FrameCaptionButtonContainerViewTest, LayoutBorder) {
}
// Test how the header style affects which images are used for the buttons.
-TEST_F(FrameCaptionButtonContainerViewTest, HeaderStyle) {
+TEST_F(FrameCaptionButtonContainerViewTestOldStyle, HeaderStyle) {
scoped_ptr<views::Widget> widget(CreateTestWidget(MAXIMIZE_ALLOWED));
FrameCaptionButtonContainerView container(NULL, widget.get(),
FrameCaptionButtonContainerView::MINIMIZE_ALLOWED);
@@ -256,4 +283,43 @@ TEST_F(FrameCaptionButtonContainerViewTest, HeaderStyle) {
IDR_AURA_WINDOW_FULLSCREEN_CLOSE_P));
}
+class FrameCaptionButtonContainerViewTestAlternateStyle
+ : public FrameCaptionButtonContainerViewTest {
+ public:
+ FrameCaptionButtonContainerViewTestAlternateStyle() {
+ }
+
+ virtual ~FrameCaptionButtonContainerViewTestAlternateStyle() {
+ }
+
+ virtual void SetUp() OVERRIDE {
+ FrameCaptionButtonContainerViewTest::SetUp();
+ CommandLine::ForCurrentProcess()->AppendSwitch(
+ switches::kAshEnableAlternateFrameCaptionButtonStyle);
+ ASSERT_TRUE(switches::UseAlternateFrameCaptionButtonStyle());
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FrameCaptionButtonContainerViewTestAlternateStyle);
+};
+
+// Test how the alternate button style affects which buttons are visible in the
+// default case.
+TEST_F(FrameCaptionButtonContainerViewTestAlternateStyle, ButtonVisibility) {
+ // Both the minimize button and the maximize button should be visible when
+ // both minimizing and maximizing are allowed when using the alternate
+ // button style.
+ scoped_ptr<views::Widget> widget_can_maximize(
+ CreateTestWidget(MAXIMIZE_ALLOWED));
+ FrameCaptionButtonContainerView container(NULL, widget_can_maximize.get(),
+ FrameCaptionButtonContainerView::MINIMIZE_ALLOWED);
+ container.Layout();
+ FrameCaptionButtonContainerView::TestApi t(&container);
+ EXPECT_TRUE(t.minimize_button()->visible());
+ EXPECT_TRUE(t.size_button()->visible());
+ EXPECT_TRUE(t.close_button()->visible());
+ EXPECT_TRUE(CheckButtonsAtEdges(
+ &container, *t.minimize_button(), *t.close_button()));
+}
+
} // namespace ash