1f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
2f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
3f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// found in the LICENSE file.
4f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
5f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "ash/wm/caption_buttons/maximize_bubble_controller_bubble.h"
6f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
7d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)#include "ash/metrics/user_metrics_recorder.h"
8d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)#include "ash/shell.h"
9f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "ash/shell_window_ids.h"
10f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "ash/wm/caption_buttons/bubble_contents_button_row.h"
11f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "ash/wm/caption_buttons/frame_maximize_button.h"
12f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "ash/wm/caption_buttons/maximize_bubble_controller.h"
13f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "grit/ash_strings.h"
14f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "ui/base/resource/resource_bundle.h"
15f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "ui/gfx/animation/animation.h"
16f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "ui/gfx/canvas.h"
17f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "ui/gfx/path.h"
18f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "ui/views/bubble/bubble_frame_view.h"
19f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "ui/views/controls/label.h"
20f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "ui/views/layout/box_layout.h"
21f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "ui/views/mouse_watcher.h"
22f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
23f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
24f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)namespace ash {
25f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
26f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// BubbleContentsView ---------------------------------------------------------
27f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
28f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// A class which creates the content of the bubble: The buttons, and the label.
29f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)class BubbleContentsView : public views::View {
30f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) public:
31f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  BubbleContentsView(MaximizeBubbleControllerBubble* bubble,
32f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                     SnapType initial_snap_type);
33f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  virtual ~BubbleContentsView();
34f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
35f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // Set the label content to reflect the currently selected |snap_type|.
36f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // This function can be executed through the frame maximize button as well as
37f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // through hover operations.
38f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  void SetSnapType(SnapType snap_type);
39f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
40f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // Added for unit test: Retrieve the button for an action.
41f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // |state| can be either SNAP_LEFT, SNAP_RIGHT or SNAP_MINIMIZE.
42f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  views::CustomButton* GetButtonForUnitTest(SnapType state);
43f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
44f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) private:
45f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // The owning class.
46f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  MaximizeBubbleControllerBubble* bubble_;
47f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
48f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // The object which owns all the buttons.
49f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  BubbleContentsButtonRow* buttons_view_;
50f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
51f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // The label object which shows the user the selected action.
52f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  views::Label* label_view_;
53f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
54f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(BubbleContentsView);
55f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)};
56f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
57f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)BubbleContentsView::BubbleContentsView(
58f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    MaximizeBubbleControllerBubble* bubble,
59f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    SnapType initial_snap_type)
60f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    : bubble_(bubble),
61f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      buttons_view_(NULL),
62f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      label_view_(NULL) {
63f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  SetLayoutManager(new views::BoxLayout(
64f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      views::BoxLayout::kVertical, 0, 0,
65f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      MaximizeBubbleControllerBubble::kLayoutSpacing));
66f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  set_background(views::Background::CreateSolidBackground(
67f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      MaximizeBubbleControllerBubble::kBubbleBackgroundColor));
68f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
69f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  buttons_view_ = new BubbleContentsButtonRow(bubble);
70f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  AddChildView(buttons_view_);
71f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
72f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  label_view_ = new views::Label();
73f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  SetSnapType(initial_snap_type);
74f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  label_view_->SetBackgroundColor(
75f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      MaximizeBubbleControllerBubble::kBubbleBackgroundColor);
76f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  const SkColor kBubbleTextColor = SK_ColorWHITE;
77f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  label_view_->SetEnabledColor(kBubbleTextColor);
78f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  const int kLabelSpacing = 4;
79f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  label_view_->set_border(views::Border::CreateEmptyBorder(
80f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      kLabelSpacing, 0, kLabelSpacing, 0));
81f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  AddChildView(label_view_);
82f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
83f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
84f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)BubbleContentsView::~BubbleContentsView() {
85f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
86f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
87f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// Set the label content to reflect the currently selected |snap_type|.
88f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// This function can be executed through the frame maximize button as well as
89f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// through hover operations.
90f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void BubbleContentsView::SetSnapType(SnapType snap_type) {
91f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (!bubble_->controller())
92f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return;
93f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
94f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
95f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  int id = 0;
96f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  switch (snap_type) {
97f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    case SNAP_LEFT:
98f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      id = IDS_ASH_SNAP_WINDOW_LEFT;
99f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      break;
100f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    case SNAP_RIGHT:
101f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      id = IDS_ASH_SNAP_WINDOW_RIGHT;
102f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      break;
103f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    case SNAP_MAXIMIZE:
104f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      DCHECK_NE(FRAME_STATE_FULL, bubble_->controller()->maximize_type());
105f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      id = IDS_ASH_MAXIMIZE_WINDOW;
106f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      break;
107f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    case SNAP_MINIMIZE:
108f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      id = IDS_ASH_MINIMIZE_WINDOW;
109f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      break;
110f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    case SNAP_RESTORE:
111f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      DCHECK_NE(FRAME_STATE_NONE, bubble_->controller()->maximize_type());
112f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      id = IDS_ASH_RESTORE_WINDOW;
113f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      break;
114f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    default:
115f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      // If nothing is selected, we automatically select the click operation.
116f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      id = bubble_->controller()->maximize_type() == FRAME_STATE_FULL ?
117f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)               IDS_ASH_RESTORE_WINDOW : IDS_ASH_MAXIMIZE_WINDOW;
118f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      break;
119f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  }
120f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  label_view_->SetText(rb.GetLocalizedString(id));
121f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
122f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
123f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)views::CustomButton* BubbleContentsView::GetButtonForUnitTest(SnapType state) {
124f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  return buttons_view_->GetButtonForUnitTest(state);
125f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
126f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
127f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
128f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// MaximizeBubbleBorder -------------------------------------------------------
129f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
130f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)namespace {
131f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
132f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)const int kLineWidth = 1;
133f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)const int kArrowHeight = 10;
134f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)const int kArrowWidth = 20;
135f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
136f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}  // namespace
137f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
138f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)class MaximizeBubbleBorder : public views::BubbleBorder {
139f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) public:
140f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  MaximizeBubbleBorder(views::View* content_view, views::View* anchor);
141f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
142f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  virtual ~MaximizeBubbleBorder() {}
143f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
144f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // Get the mouse active area of the window.
145f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  void GetMask(gfx::Path* mask);
146f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
147f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // views::BubbleBorder:
148f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  virtual gfx::Rect GetBounds(const gfx::Rect& position_relative_to,
149f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                              const gfx::Size& contents_size) const OVERRIDE;
150f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  virtual void Paint(const views::View& view, gfx::Canvas* canvas) OVERRIDE;
151f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  virtual gfx::Size GetMinimumSize() const OVERRIDE;
152f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
153f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) private:
154f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // Note: Animations can continue after then main window frame was destroyed.
155f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // To avoid this problem, the owning screen metrics get extracted upon
156f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // creation.
157f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  gfx::Size anchor_size_;
158f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  gfx::Point anchor_screen_origin_;
159f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  views::View* content_view_;
160f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
161f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(MaximizeBubbleBorder);
162f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)};
163f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
164f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)MaximizeBubbleBorder::MaximizeBubbleBorder(views::View* content_view,
165f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                                           views::View* anchor)
166f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    : views::BubbleBorder(
167f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)          views::BubbleBorder::TOP_RIGHT, views::BubbleBorder::NO_SHADOW,
168f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)          MaximizeBubbleControllerBubble::kBubbleBackgroundColor),
169f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      anchor_size_(anchor->size()),
170f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      anchor_screen_origin_(0, 0),
171f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      content_view_(content_view) {
172f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  views::View::ConvertPointToScreen(anchor, &anchor_screen_origin_);
173f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  set_alignment(views::BubbleBorder::ALIGN_EDGE_TO_ANCHOR_EDGE);
174f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
175f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
176f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void MaximizeBubbleBorder::GetMask(gfx::Path* mask) {
177f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  gfx::Insets inset = GetInsets();
178f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // Note: Even though the tip could be added as activatable, it is left out
179f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // since it would not change the action behavior in any way plus it makes
180f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // more sense to keep the focus on the underlying button for clicks.
181f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  int left = inset.left() - kLineWidth;
182f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  int right = inset.left() + content_view_->width() + kLineWidth;
183f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  int top = inset.top() - kLineWidth;
184f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  int bottom = inset.top() + content_view_->height() + kLineWidth;
185f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  mask->moveTo(left, top);
186f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  mask->lineTo(right, top);
187f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  mask->lineTo(right, bottom);
188f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  mask->lineTo(left, bottom);
189f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  mask->lineTo(left, top);
190f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  mask->close();
191f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
192f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
193f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)gfx::Rect MaximizeBubbleBorder::GetBounds(
194f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    const gfx::Rect& position_relative_to,
195f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    const gfx::Size& contents_size) const {
196f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  gfx::Size border_size(contents_size);
197f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  gfx::Insets insets = GetInsets();
198f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  border_size.Enlarge(insets.width(), insets.height());
199f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
200f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // Position the bubble to center the box on the anchor.
201f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  int x = (anchor_size_.width() - border_size.width()) / 2;
202f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // Position the bubble under the anchor, overlapping the arrow with it.
203f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  int y = anchor_size_.height() - insets.top();
204f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
205f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  gfx::Point view_origin(x + anchor_screen_origin_.x(),
206f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                         y + anchor_screen_origin_.y());
207f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
208f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  return gfx::Rect(view_origin, border_size);
209f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
210f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
211f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void MaximizeBubbleBorder::Paint(const views::View& view, gfx::Canvas* canvas) {
212f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  gfx::Insets inset = GetInsets();
213f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
214f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // Draw the border line around everything.
215f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  int y = inset.top();
216f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // Top
217f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  canvas->FillRect(gfx::Rect(inset.left(),
218f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                             y - kLineWidth,
219f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                             content_view_->width(),
220f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                             kLineWidth),
221f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                   MaximizeBubbleControllerBubble::kBubbleBackgroundColor);
222f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // Bottom
223f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  canvas->FillRect(gfx::Rect(inset.left(),
224f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                             y + content_view_->height(),
225f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                             content_view_->width(),
226f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                             kLineWidth),
227f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                   MaximizeBubbleControllerBubble::kBubbleBackgroundColor);
228f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // Left
229f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  canvas->FillRect(gfx::Rect(inset.left() - kLineWidth,
230f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                             y - kLineWidth,
231f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                             kLineWidth,
232f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                             content_view_->height() + 2 * kLineWidth),
233f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                   MaximizeBubbleControllerBubble::kBubbleBackgroundColor);
234f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // Right
235f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  canvas->FillRect(gfx::Rect(inset.left() + content_view_->width(),
236f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                             y - kLineWidth,
237f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                             kLineWidth,
238f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                             content_view_->height() + 2 * kLineWidth),
239f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                   MaximizeBubbleControllerBubble::kBubbleBackgroundColor);
240f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
241f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // Draw the arrow afterwards covering the border.
242f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  SkPath path;
243f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  path.incReserve(4);
244f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // The center of the tip should be in the middle of the button.
245f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  int tip_x = inset.left() + content_view_->width() / 2;
246f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  int left_base_x = tip_x - kArrowWidth / 2;
247f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  int left_base_y = y;
248f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  int tip_y = left_base_y - kArrowHeight;
249f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  path.moveTo(SkIntToScalar(left_base_x), SkIntToScalar(left_base_y));
250f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  path.lineTo(SkIntToScalar(tip_x), SkIntToScalar(tip_y));
251f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  path.lineTo(SkIntToScalar(left_base_x + kArrowWidth),
252f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)              SkIntToScalar(left_base_y));
253f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
254f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  SkPaint paint;
255f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  paint.setStyle(SkPaint::kFill_Style);
256f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  paint.setColor(MaximizeBubbleControllerBubble::kBubbleBackgroundColor);
257f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  canvas->DrawPath(path, paint);
258f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
259f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
260f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)gfx::Size MaximizeBubbleBorder::GetMinimumSize() const {
261f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  return gfx::Size(kLineWidth * 2 + kArrowWidth,
262f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                   std::max(kLineWidth, kArrowHeight) + kLineWidth);
263f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
264f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
265f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
266f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// BubbleMouseWatcherHost -----------------------------------------------------
267f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
268f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// The mouse watcher host which makes sure that the bubble does not get closed
269f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// while the mouse cursor is over the maximize button or the balloon content.
270f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// Note: This object gets destroyed when the MouseWatcher gets destroyed.
271f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)class BubbleMouseWatcherHost: public views::MouseWatcherHost {
272f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) public:
273f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  explicit BubbleMouseWatcherHost(MaximizeBubbleControllerBubble* bubble);
274f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  virtual ~BubbleMouseWatcherHost();
275f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
276f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // views::MouseWatcherHost:
277f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  virtual bool Contains(const gfx::Point& screen_point,
278f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                        views::MouseWatcherHost::MouseEventType type) OVERRIDE;
279f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) private:
280f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  MaximizeBubbleControllerBubble* bubble_;
281f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
282f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(BubbleMouseWatcherHost);
283f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)};
284f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
285f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)BubbleMouseWatcherHost::BubbleMouseWatcherHost(
286f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    MaximizeBubbleControllerBubble* bubble)
287f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    : bubble_(bubble) {
288f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
289f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
290f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)BubbleMouseWatcherHost::~BubbleMouseWatcherHost() {
291f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
292f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
293f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)bool BubbleMouseWatcherHost::Contains(
294f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    const gfx::Point& screen_point,
295f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    views::MouseWatcherHost::MouseEventType type) {
296f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  return bubble_->Contains(screen_point, type);
297f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
298f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
299f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
300f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// MaximizeBubbleControllerBubble ---------------------------------------------
301f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
302f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// static
303f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)const SkColor MaximizeBubbleControllerBubble::kBubbleBackgroundColor =
304f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    0xFF141414;
305f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)const int MaximizeBubbleControllerBubble::kLayoutSpacing = -1;
306f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
307f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)MaximizeBubbleControllerBubble::MaximizeBubbleControllerBubble(
308f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    MaximizeBubbleController* owner,
309f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    int appearance_delay_ms,
310f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    SnapType initial_snap_type)
311f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    : views::BubbleDelegateView(owner->frame_maximize_button(),
312f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                                views::BubbleBorder::TOP_RIGHT),
313f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      shutting_down_(false),
314f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      owner_(owner),
315f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      bubble_widget_(NULL),
316f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      contents_view_(NULL),
317f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      bubble_border_(NULL),
318f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      appearance_delay_ms_(appearance_delay_ms) {
319f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  set_margins(gfx::Insets());
320f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
321f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // The window needs to be owned by the root so that the SnapSizer does not
322f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // cover it upon animation.
323f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  aura::Window* parent = Shell::GetContainer(
324f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      Shell::GetTargetRootWindow(),
325f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      internal::kShellWindowId_ShelfContainer);
326f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  set_parent_window(parent);
327f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
328f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  set_notify_enter_exit_on_child(true);
329f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  set_adjust_if_offscreen(false);
330f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  SetPaintToLayer(true);
331f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  set_color(kBubbleBackgroundColor);
332f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  set_close_on_deactivate(false);
333f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  set_background(
334f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      views::Background::CreateSolidBackground(kBubbleBackgroundColor));
335f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
336f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  SetLayoutManager(new views::BoxLayout(
337f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      views::BoxLayout::kVertical, 0, 0, kLayoutSpacing));
338f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
339f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  contents_view_ = new BubbleContentsView(this, initial_snap_type);
340f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  AddChildView(contents_view_);
341f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
342f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // Note that the returned widget has an observer which points to our
343f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // functions.
344f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  bubble_widget_ = views::BubbleDelegateView::CreateBubble(this);
345f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  bubble_widget_->set_focus_on_creation(false);
346f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
347f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  SetAlignment(views::BubbleBorder::ALIGN_EDGE_TO_ANCHOR_EDGE);
348f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  bubble_widget_->non_client_view()->frame_view()->set_background(NULL);
349f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
350f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  bubble_border_ = new MaximizeBubbleBorder(this, GetAnchorView());
351f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  GetBubbleFrameView()->SetBubbleBorder(bubble_border_);
352f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  GetBubbleFrameView()->set_background(NULL);
353f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
354f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // Recalculate size with new border.
355f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  SizeToContents();
356f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
357f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (!appearance_delay_ms_)
358f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    GetWidget()->Show();
359f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  else
360f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    StartFade(true);
361f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
362d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles)  ash::Shell::GetInstance()->metrics()->RecordUserMetricsAction(
363f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      ash::UMA_WINDOW_MAXIMIZE_BUTTON_SHOW_BUBBLE);
364f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
365f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  mouse_watcher_.reset(new views::MouseWatcher(
366f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      new BubbleMouseWatcherHost(this),
367f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      this));
368f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  mouse_watcher_->Start();
369f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
370f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
371f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)MaximizeBubbleControllerBubble::~MaximizeBubbleControllerBubble() {
372f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
373f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
374f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)aura::Window* MaximizeBubbleControllerBubble::GetBubbleWindow() {
375f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  return bubble_widget_ ? bubble_widget_->GetNativeWindow() : NULL;
376f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
377f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
378f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)gfx::Rect MaximizeBubbleControllerBubble::GetAnchorRect() {
379f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (!owner_)
380f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return gfx::Rect();
381f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
382f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  gfx::Rect anchor_rect =
383f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      owner_->frame_maximize_button()->GetBoundsInScreen();
384f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  return anchor_rect;
385f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
386f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
387f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void MaximizeBubbleControllerBubble::AnimationProgressed(
388f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    const gfx::Animation* animation) {
389f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // First do everything needed for the fade by calling the base function.
390f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  BubbleDelegateView::AnimationProgressed(animation);
391f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // When fading in we are done.
392f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (!shutting_down_)
393f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return;
394f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // Upon fade out an additional shift is required.
395f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  const int kBubbleAnimationOffsetY = 5;
396f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  int shift = animation->CurrentValueBetween(kBubbleAnimationOffsetY, 0);
397f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  gfx::Rect rect = initial_position_;
398f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
399f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  rect.set_y(rect.y() + shift);
400f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  bubble_widget_->GetNativeWindow()->SetBounds(rect);
401f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
402f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
403f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)bool MaximizeBubbleControllerBubble::CanActivate() const {
404f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  return false;
405f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
406f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
407f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)bool MaximizeBubbleControllerBubble::WidgetHasHitTestMask() const {
408f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  return bubble_border_ != NULL;
409f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
410f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
411f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void MaximizeBubbleControllerBubble::GetWidgetHitTestMask(
412f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    gfx::Path* mask) const {
413f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  DCHECK(mask);
414f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  DCHECK(bubble_border_);
415f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  bubble_border_->GetMask(mask);
416f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
417f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
418f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void MaximizeBubbleControllerBubble::MouseMovedOutOfHost() {
419f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (!owner_ || shutting_down_)
420f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return;
421f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // When we leave the bubble, we might be still be in gesture mode or over
422f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // the maximize button. So only close if none of the other cases apply.
423f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (!owner_->frame_maximize_button()->is_snap_enabled()) {
424f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    gfx::Point screen_location = Shell::GetScreen()->GetCursorScreenPoint();
425f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    if (!owner_->frame_maximize_button()->GetBoundsInScreen().Contains(
426f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        screen_location)) {
427f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      owner_->RequestDestructionThroughOwner();
428f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    }
429f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  }
430f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
431f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
432f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)bool MaximizeBubbleControllerBubble::Contains(
433f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    const gfx::Point& screen_point,
434f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    views::MouseWatcherHost::MouseEventType type) {
435f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (!owner_ || shutting_down_)
436f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return false;
437f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  bool inside_button =
438f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      owner_->frame_maximize_button()->GetBoundsInScreen().Contains(
439f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)          screen_point);
440f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (!owner_->frame_maximize_button()->is_snap_enabled() && inside_button) {
441f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    SetSnapType(controller()->maximize_type() == FRAME_STATE_FULL ?
442f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)        SNAP_RESTORE : SNAP_MAXIMIZE);
443f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return true;
444f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  }
445f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // Check if either a gesture is taking place (=> bubble stays no matter what
446f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // the mouse does) or the mouse is over the maximize button or the bubble
447f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // content.
448f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  return (owner_->frame_maximize_button()->is_snap_enabled() ||
449f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)          inside_button ||
450f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)          contents_view_->GetBoundsInScreen().Contains(screen_point));
451f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
452f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
453f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)gfx::Size MaximizeBubbleControllerBubble::GetPreferredSize() {
454f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  return contents_view_->GetPreferredSize();
455f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
456f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
457f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void MaximizeBubbleControllerBubble::OnWidgetDestroying(views::Widget* widget) {
458f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (bubble_widget_ == widget) {
459f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    mouse_watcher_->Stop();
460f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
461f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    if (owner_) {
462f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      // If the bubble destruction was triggered by some other external
463f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      // influence then ourselves, the owner needs to be informed that the menu
464f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      // is gone.
465f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      shutting_down_ = true;
466f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      owner_->RequestDestructionThroughOwner();
467f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      owner_ = NULL;
468f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    }
469f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  }
470f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  BubbleDelegateView::OnWidgetDestroying(widget);
471f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
472f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
473f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void MaximizeBubbleControllerBubble::ControllerRequestsCloseAndDelete() {
474f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // This only gets called from the owning base class once it is deleted.
475f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (shutting_down_)
476f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return;
477f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  shutting_down_ = true;
478f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  owner_ = NULL;
479f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
480f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // Close the widget asynchronously after the hide animation is finished.
481f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  initial_position_ = bubble_widget_->GetNativeWindow()->bounds();
482f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (!appearance_delay_ms_)
483f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    bubble_widget_->CloseNow();
484f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  else
485f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    StartFade(false);
486f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
487f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
488f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void MaximizeBubbleControllerBubble::SetSnapType(SnapType snap_type) {
489f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (contents_view_)
490f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    contents_view_->SetSnapType(snap_type);
491f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
492f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
493f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)views::CustomButton* MaximizeBubbleControllerBubble::GetButtonForUnitTest(
494f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    SnapType state) {
495f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  return contents_view_->GetButtonForUnitTest(state);
496f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
497f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
498f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}  // namespace ash
499