• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "ash/frame/caption_buttons/frame_caption_button_container_view.h"
6 
7 #include <cmath>
8 #include <map>
9 
10 #include "ash/ash_switches.h"
11 #include "ash/frame/caption_buttons/frame_caption_button.h"
12 #include "ash/frame/caption_buttons/frame_size_button.h"
13 #include "ash/metrics/user_metrics_recorder.h"
14 #include "ash/shell.h"
15 #include "ash/wm/maximize_mode/maximize_mode_controller.h"
16 #include "grit/ui_strings.h"  // Accessibility names
17 #include "ui/base/hit_test.h"
18 #include "ui/base/l10n/l10n_util.h"
19 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
20 #include "ui/gfx/canvas.h"
21 #include "ui/gfx/insets.h"
22 #include "ui/gfx/point.h"
23 #include "ui/views/widget/widget.h"
24 #include "ui/views/widget/widget_delegate.h"
25 
26 namespace ash {
27 
28 namespace {
29 
30 // Converts |point| from |src| to |dst| and hittests against |dst|.
ConvertPointToViewAndHitTest(const views::View * src,const views::View * dst,const gfx::Point & point)31 bool ConvertPointToViewAndHitTest(const views::View* src,
32                                   const views::View* dst,
33                                   const gfx::Point& point) {
34   gfx::Point converted(point);
35   views::View::ConvertPointToTarget(src, dst, &converted);
36   return dst->HitTestPoint(converted);
37 }
38 
39 }  // namespace
40 
41 // static
42 const char FrameCaptionButtonContainerView::kViewClassName[] =
43     "FrameCaptionButtonContainerView";
44 
FrameCaptionButtonContainerView(views::Widget * frame,MinimizeAllowed minimize_allowed)45 FrameCaptionButtonContainerView::FrameCaptionButtonContainerView(
46     views::Widget* frame,
47     MinimizeAllowed minimize_allowed)
48     : frame_(frame),
49       minimize_button_(NULL),
50       size_button_(NULL),
51       close_button_(NULL) {
52   // Insert the buttons left to right.
53   minimize_button_ = new FrameCaptionButton(this, CAPTION_BUTTON_ICON_MINIMIZE);
54   minimize_button_->SetAccessibleName(
55       l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MINIMIZE));
56   minimize_button_->SetVisible(minimize_allowed == MINIMIZE_ALLOWED);
57   AddChildView(minimize_button_);
58 
59   size_button_ = new FrameSizeButton(this, frame, this);
60   size_button_->SetAccessibleName(
61       l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MAXIMIZE));
62   UpdateSizeButtonVisibility();
63   AddChildView(size_button_);
64 
65   close_button_ = new FrameCaptionButton(this, CAPTION_BUTTON_ICON_CLOSE);
66   close_button_->SetAccessibleName(
67       l10n_util::GetStringUTF16(IDS_APP_ACCNAME_CLOSE));
68   AddChildView(close_button_);
69 }
70 
~FrameCaptionButtonContainerView()71 FrameCaptionButtonContainerView::~FrameCaptionButtonContainerView() {
72 }
73 
SetButtonImages(CaptionButtonIcon icon,int icon_image_id,int inactive_icon_image_id,int hovered_background_image_id,int pressed_background_image_id)74 void FrameCaptionButtonContainerView::SetButtonImages(
75     CaptionButtonIcon icon,
76     int icon_image_id,
77     int inactive_icon_image_id,
78     int hovered_background_image_id,
79     int pressed_background_image_id) {
80   button_icon_id_map_[icon] = ButtonIconIds(icon_image_id,
81                                             inactive_icon_image_id,
82                                             hovered_background_image_id,
83                                             pressed_background_image_id);
84   FrameCaptionButton* buttons[] = {
85     minimize_button_, size_button_, close_button_
86   };
87   for (size_t i = 0; i < arraysize(buttons); ++i) {
88     if (buttons[i]->icon() == icon) {
89       buttons[i]->SetImages(icon,
90                             FrameCaptionButton::ANIMATE_NO,
91                             icon_image_id,
92                             inactive_icon_image_id,
93                             hovered_background_image_id,
94                             pressed_background_image_id);
95     }
96   }
97 }
98 
SetPaintAsActive(bool paint_as_active)99 void FrameCaptionButtonContainerView::SetPaintAsActive(bool paint_as_active) {
100   minimize_button_->set_paint_as_active(paint_as_active);
101   size_button_->set_paint_as_active(paint_as_active);
102   close_button_->set_paint_as_active(paint_as_active);
103 }
104 
ResetWindowControls()105 void FrameCaptionButtonContainerView::ResetWindowControls() {
106   SetButtonsToNormal(ANIMATE_NO);
107 }
108 
NonClientHitTest(const gfx::Point & point) const109 int FrameCaptionButtonContainerView::NonClientHitTest(
110     const gfx::Point& point) const {
111   if (close_button_->visible() &&
112       ConvertPointToViewAndHitTest(this, close_button_, point)) {
113     return HTCLOSE;
114   } else if (size_button_->visible() &&
115              ConvertPointToViewAndHitTest(this, size_button_, point)) {
116     return HTMAXBUTTON;
117   } else if (minimize_button_->visible() &&
118              ConvertPointToViewAndHitTest(this, minimize_button_, point)) {
119     return HTMINBUTTON;
120   }
121   return HTNOWHERE;
122 }
123 
UpdateSizeButtonVisibility()124 void FrameCaptionButtonContainerView::UpdateSizeButtonVisibility() {
125   size_button_->SetVisible(
126       !Shell::GetInstance()->maximize_mode_controller()->
127           IsMaximizeModeWindowManagerEnabled() &&
128       frame_->widget_delegate()->CanMaximize());
129 }
130 
GetPreferredSize() const131 gfx::Size FrameCaptionButtonContainerView::GetPreferredSize() const {
132   int width = 0;
133   for (int i = 0; i < child_count(); ++i) {
134     const views::View* child = child_at(i);
135     if (child->visible())
136       width += child_at(i)->GetPreferredSize().width();
137   }
138   return gfx::Size(width, close_button_->GetPreferredSize().height());
139 }
140 
Layout()141 void FrameCaptionButtonContainerView::Layout() {
142   int x = 0;
143   for (int i = 0; i < child_count(); ++i) {
144     views::View* child = child_at(i);
145     if (!child->visible())
146       continue;
147 
148     gfx::Size size = child->GetPreferredSize();
149     child->SetBounds(x, 0, size.width(), size.height());
150     x += size.width();
151   }
152 }
153 
GetClassName() const154 const char* FrameCaptionButtonContainerView::GetClassName() const {
155   return kViewClassName;
156 }
157 
SetButtonIcon(FrameCaptionButton * button,CaptionButtonIcon icon,Animate animate)158 void FrameCaptionButtonContainerView::SetButtonIcon(FrameCaptionButton* button,
159                                                     CaptionButtonIcon icon,
160                                                     Animate animate) {
161   // The early return is dependant on |animate| because callers use
162   // SetButtonIcon() with ANIMATE_NO to progress |button|'s crossfade animation
163   // to the end.
164   if (button->icon() == icon &&
165       (animate == ANIMATE_YES || !button->IsAnimatingImageSwap())) {
166     return;
167   }
168 
169   FrameCaptionButton::Animate fcb_animate = (animate == ANIMATE_YES) ?
170       FrameCaptionButton::ANIMATE_YES : FrameCaptionButton::ANIMATE_NO;
171   std::map<CaptionButtonIcon, ButtonIconIds>::const_iterator it =
172       button_icon_id_map_.find(icon);
173   if (it != button_icon_id_map_.end()) {
174     button->SetImages(icon,
175                       fcb_animate,
176                       it->second.icon_image_id,
177                       it->second.inactive_icon_image_id,
178                       it->second.hovered_background_image_id,
179                       it->second.pressed_background_image_id);
180   }
181 }
182 
ButtonPressed(views::Button * sender,const ui::Event & event)183 void FrameCaptionButtonContainerView::ButtonPressed(views::Button* sender,
184                                                     const ui::Event& event) {
185   // When shift-clicking, slow down animations for visual debugging.
186   // We used to do this via an event filter that looked for the shift key being
187   // pressed but this interfered with several normal keyboard shortcuts.
188   scoped_ptr<ui::ScopedAnimationDurationScaleMode> slow_duration_mode;
189   if (event.IsShiftDown()) {
190     slow_duration_mode.reset(new ui::ScopedAnimationDurationScaleMode(
191         ui::ScopedAnimationDurationScaleMode::SLOW_DURATION));
192   }
193 
194   // Abort any animations of the button icons.
195   SetButtonsToNormal(ANIMATE_NO);
196 
197   ash::UserMetricsAction action =
198       ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_MINIMIZE;
199   if (sender == minimize_button_) {
200     frame_->Minimize();
201   } else if (sender == size_button_) {
202     if (frame_->IsFullscreen()) {  // Can be clicked in immersive fullscreen.
203       frame_->SetFullscreen(false);
204       action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_EXIT_FULLSCREEN;
205     } else if (frame_->IsMaximized()) {
206       frame_->Restore();
207       action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_RESTORE;
208     } else {
209       frame_->Maximize();
210       action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_MAXIMIZE;
211     }
212   } else if (sender == close_button_) {
213     frame_->Close();
214     action = ash::UMA_WINDOW_CLOSE_BUTTON_CLICK;
215   } else {
216     return;
217   }
218   ash::Shell::GetInstance()->metrics()->RecordUserMetricsAction(action);
219 }
220 
IsMinimizeButtonVisible() const221 bool FrameCaptionButtonContainerView::IsMinimizeButtonVisible() const {
222   return minimize_button_->visible();
223 }
224 
SetButtonsToNormal(Animate animate)225 void FrameCaptionButtonContainerView::SetButtonsToNormal(Animate animate) {
226   SetButtonIcons(CAPTION_BUTTON_ICON_MINIMIZE, CAPTION_BUTTON_ICON_CLOSE,
227       animate);
228   minimize_button_->SetState(views::Button::STATE_NORMAL);
229   size_button_->SetState(views::Button::STATE_NORMAL);
230   close_button_->SetState(views::Button::STATE_NORMAL);
231 }
232 
SetButtonIcons(CaptionButtonIcon minimize_button_icon,CaptionButtonIcon close_button_icon,Animate animate)233 void FrameCaptionButtonContainerView::SetButtonIcons(
234     CaptionButtonIcon minimize_button_icon,
235     CaptionButtonIcon close_button_icon,
236     Animate animate) {
237   SetButtonIcon(minimize_button_, minimize_button_icon, animate);
238   SetButtonIcon(close_button_, close_button_icon, animate);
239 }
240 
GetButtonClosestTo(const gfx::Point & position_in_screen) const241 const FrameCaptionButton* FrameCaptionButtonContainerView::GetButtonClosestTo(
242     const gfx::Point& position_in_screen) const {
243   // Since the buttons all have the same size, the closest button is the button
244   // with the center point closest to |position_in_screen|.
245   // TODO(pkotwicz): Make the caption buttons not overlap.
246   gfx::Point position(position_in_screen);
247   views::View::ConvertPointFromScreen(this, &position);
248 
249   FrameCaptionButton* buttons[] = {
250     minimize_button_, size_button_, close_button_
251   };
252   int min_squared_distance = INT_MAX;
253   FrameCaptionButton* closest_button = NULL;
254   for (size_t i = 0; i < arraysize(buttons); ++i) {
255     FrameCaptionButton* button = buttons[i];
256     if (!button->visible())
257       continue;
258 
259     gfx::Point center_point = button->GetLocalBounds().CenterPoint();
260     views::View::ConvertPointToTarget(button, this, &center_point);
261     int squared_distance = static_cast<int>(
262         pow(static_cast<double>(position.x() - center_point.x()), 2) +
263         pow(static_cast<double>(position.y() - center_point.y()), 2));
264     if (squared_distance < min_squared_distance) {
265       min_squared_distance = squared_distance;
266       closest_button = button;
267     }
268   }
269   return closest_button;
270 }
271 
SetHoveredAndPressedButtons(const FrameCaptionButton * to_hover,const FrameCaptionButton * to_press)272 void FrameCaptionButtonContainerView::SetHoveredAndPressedButtons(
273     const FrameCaptionButton* to_hover,
274     const FrameCaptionButton* to_press) {
275   FrameCaptionButton* buttons[] = {
276     minimize_button_, size_button_, close_button_
277   };
278   for (size_t i = 0; i < arraysize(buttons); ++i) {
279     FrameCaptionButton* button = buttons[i];
280     views::Button::ButtonState new_state = views::Button::STATE_NORMAL;
281     if (button == to_hover)
282       new_state = views::Button::STATE_HOVERED;
283     else if (button == to_press)
284       new_state = views::Button::STATE_PRESSED;
285     button->SetState(new_state);
286   }
287 }
288 
ButtonIconIds()289 FrameCaptionButtonContainerView::ButtonIconIds::ButtonIconIds()
290     : icon_image_id(-1),
291       inactive_icon_image_id(-1),
292       hovered_background_image_id(-1),
293       pressed_background_image_id(-1) {
294 }
295 
ButtonIconIds(int icon_id,int inactive_icon_id,int hovered_background_id,int pressed_background_id)296 FrameCaptionButtonContainerView::ButtonIconIds::ButtonIconIds(
297     int icon_id,
298     int inactive_icon_id,
299     int hovered_background_id,
300     int pressed_background_id)
301     : icon_image_id(icon_id),
302       inactive_icon_image_id(inactive_icon_id),
303       hovered_background_image_id(hovered_background_id),
304       pressed_background_image_id(pressed_background_id) {
305 }
306 
~ButtonIconIds()307 FrameCaptionButtonContainerView::ButtonIconIds::~ButtonIconIds() {
308 }
309 
310 }  // namespace ash
311