| OLD | NEW |
| (Empty) |
| 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/common/ash_switches.h" | |
| 11 #include "ash/common/wm/maximize_mode/maximize_mode_controller.h" | |
| 12 #include "ash/common/wm_shell.h" | |
| 13 #include "ash/frame/caption_buttons/frame_caption_button.h" | |
| 14 #include "ash/frame/caption_buttons/frame_size_button.h" | |
| 15 #include "ash/shell.h" | |
| 16 #include "ui/base/hit_test.h" | |
| 17 #include "ui/base/l10n/l10n_util.h" | |
| 18 #include "ui/compositor/scoped_animation_duration_scale_mode.h" | |
| 19 #include "ui/gfx/animation/slide_animation.h" | |
| 20 #include "ui/gfx/animation/tween.h" | |
| 21 #include "ui/gfx/canvas.h" | |
| 22 #include "ui/gfx/geometry/insets.h" | |
| 23 #include "ui/gfx/geometry/point.h" | |
| 24 #include "ui/gfx/vector_icons_public.h" | |
| 25 #include "ui/strings/grit/ui_strings.h" // Accessibility names | |
| 26 #include "ui/views/widget/widget.h" | |
| 27 #include "ui/views/widget/widget_delegate.h" | |
| 28 | |
| 29 namespace ash { | |
| 30 | |
| 31 namespace { | |
| 32 | |
| 33 // Duration of the animation of the position of |minimize_button_|. | |
| 34 const int kPositionAnimationDurationMs = 500; | |
| 35 | |
| 36 // Duration of the animation of the alpha of |size_button_|. | |
| 37 const int kAlphaAnimationDurationMs = 250; | |
| 38 | |
| 39 // Delay during |maximize_mode_animation_| hide to wait before beginning to | |
| 40 // animate the position of |minimize_button_|. | |
| 41 const int kHidePositionDelayMs = 100; | |
| 42 | |
| 43 // Duration of |maximize_mode_animation_| hiding. | |
| 44 // Hiding size button 250 | |
| 45 // |------------------------| | |
| 46 // Delay 100 Slide minimize button 500 | |
| 47 // |---------|-------------------------------------------------| | |
| 48 const int kHideAnimationDurationMs = | |
| 49 kHidePositionDelayMs + kPositionAnimationDurationMs; | |
| 50 | |
| 51 // Delay during |maximize_mode_animation_| show to wait before beginning to | |
| 52 // animate the alpha of |size_button_|. | |
| 53 const int kShowAnimationAlphaDelayMs = 100; | |
| 54 | |
| 55 // Duration of |maximize_mode_animation_| showing. | |
| 56 // Slide minimize button 500 | |
| 57 // |-------------------------------------------------| | |
| 58 // Delay 100 Show size button 250 | |
| 59 // |---------|-----------------------| | |
| 60 const int kShowAnimationDurationMs = kPositionAnimationDurationMs; | |
| 61 | |
| 62 // Value of |maximize_mode_animation_| showing to begin animating alpha of | |
| 63 // |size_button_|. | |
| 64 float SizeButtonShowStartValue() { | |
| 65 return static_cast<float>(kShowAnimationAlphaDelayMs) / | |
| 66 kShowAnimationDurationMs; | |
| 67 } | |
| 68 | |
| 69 // Amount of |maximize_mode_animation_| showing to animate the alpha of | |
| 70 // |size_button_|. | |
| 71 float SizeButtonShowDuration() { | |
| 72 return static_cast<float>(kAlphaAnimationDurationMs) / | |
| 73 kShowAnimationDurationMs; | |
| 74 } | |
| 75 | |
| 76 // Amount of |maximize_mode_animation_| hiding to animate the alpha of | |
| 77 // |size_button_|. | |
| 78 float SizeButtonHideDuration() { | |
| 79 return static_cast<float>(kAlphaAnimationDurationMs) / | |
| 80 kHideAnimationDurationMs; | |
| 81 } | |
| 82 | |
| 83 // Value of |maximize_mode_animation_| hiding to begin animating the position of | |
| 84 // |minimize_button_|. | |
| 85 float HidePositionStartValue() { | |
| 86 return 1.0f - | |
| 87 static_cast<float>(kHidePositionDelayMs) / kHideAnimationDurationMs; | |
| 88 } | |
| 89 | |
| 90 // Converts |point| from |src| to |dst| and hittests against |dst|. | |
| 91 bool ConvertPointToViewAndHitTest(const views::View* src, | |
| 92 const views::View* dst, | |
| 93 const gfx::Point& point) { | |
| 94 gfx::Point converted(point); | |
| 95 views::View::ConvertPointToTarget(src, dst, &converted); | |
| 96 return dst->HitTestPoint(converted); | |
| 97 } | |
| 98 | |
| 99 // Bounds animation values to the range 0.0 - 1.0. Allows for mapping of offset | |
| 100 // animations to the expected range so that gfx::Tween::CalculateValue() can be | |
| 101 // used. | |
| 102 double CapAnimationValue(double value) { | |
| 103 return std::min(1.0, std::max(0.0, value)); | |
| 104 } | |
| 105 | |
| 106 } // namespace | |
| 107 | |
| 108 // static | |
| 109 const char FrameCaptionButtonContainerView::kViewClassName[] = | |
| 110 "FrameCaptionButtonContainerView"; | |
| 111 | |
| 112 FrameCaptionButtonContainerView::FrameCaptionButtonContainerView( | |
| 113 views::Widget* frame) | |
| 114 : frame_(frame), | |
| 115 minimize_button_(NULL), | |
| 116 size_button_(NULL), | |
| 117 close_button_(NULL) { | |
| 118 bool size_button_visibility = ShouldSizeButtonBeVisible(); | |
| 119 maximize_mode_animation_.reset(new gfx::SlideAnimation(this)); | |
| 120 maximize_mode_animation_->SetTweenType(gfx::Tween::LINEAR); | |
| 121 | |
| 122 // Ensure animation tracks visibility of size button. | |
| 123 if (size_button_visibility) | |
| 124 maximize_mode_animation_->Reset(1.0f); | |
| 125 | |
| 126 // Insert the buttons left to right. | |
| 127 minimize_button_ = new FrameCaptionButton(this, CAPTION_BUTTON_ICON_MINIMIZE); | |
| 128 minimize_button_->SetAccessibleName( | |
| 129 l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MINIMIZE)); | |
| 130 minimize_button_->SetVisible(frame_->widget_delegate()->CanMinimize()); | |
| 131 AddChildView(minimize_button_); | |
| 132 | |
| 133 size_button_ = new FrameSizeButton(this, frame, this); | |
| 134 size_button_->SetAccessibleName( | |
| 135 l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MAXIMIZE)); | |
| 136 size_button_->SetVisible(size_button_visibility); | |
| 137 AddChildView(size_button_); | |
| 138 | |
| 139 close_button_ = new FrameCaptionButton(this, CAPTION_BUTTON_ICON_CLOSE); | |
| 140 close_button_->SetAccessibleName( | |
| 141 l10n_util::GetStringUTF16(IDS_APP_ACCNAME_CLOSE)); | |
| 142 AddChildView(close_button_); | |
| 143 } | |
| 144 | |
| 145 FrameCaptionButtonContainerView::~FrameCaptionButtonContainerView() {} | |
| 146 | |
| 147 void FrameCaptionButtonContainerView::TestApi::EndAnimations() { | |
| 148 container_view_->maximize_mode_animation_->End(); | |
| 149 } | |
| 150 | |
| 151 void FrameCaptionButtonContainerView::SetButtonImage( | |
| 152 CaptionButtonIcon icon, | |
| 153 gfx::VectorIconId icon_image_id) { | |
| 154 button_icon_id_map_[icon] = icon_image_id; | |
| 155 | |
| 156 FrameCaptionButton* buttons[] = {minimize_button_, size_button_, | |
| 157 close_button_}; | |
| 158 for (size_t i = 0; i < arraysize(buttons); ++i) { | |
| 159 if (buttons[i]->icon() == icon) | |
| 160 buttons[i]->SetImage(icon, FrameCaptionButton::ANIMATE_NO, icon_image_id); | |
| 161 } | |
| 162 } | |
| 163 | |
| 164 void FrameCaptionButtonContainerView::SetPaintAsActive(bool paint_as_active) { | |
| 165 minimize_button_->set_paint_as_active(paint_as_active); | |
| 166 size_button_->set_paint_as_active(paint_as_active); | |
| 167 close_button_->set_paint_as_active(paint_as_active); | |
| 168 } | |
| 169 | |
| 170 void FrameCaptionButtonContainerView::SetUseLightImages(bool light) { | |
| 171 minimize_button_->set_use_light_images(light); | |
| 172 size_button_->set_use_light_images(light); | |
| 173 close_button_->set_use_light_images(light); | |
| 174 } | |
| 175 | |
| 176 void FrameCaptionButtonContainerView::ResetWindowControls() { | |
| 177 SetButtonsToNormal(ANIMATE_NO); | |
| 178 } | |
| 179 | |
| 180 int FrameCaptionButtonContainerView::NonClientHitTest( | |
| 181 const gfx::Point& point) const { | |
| 182 if (close_button_->visible() && | |
| 183 ConvertPointToViewAndHitTest(this, close_button_, point)) { | |
| 184 return HTCLOSE; | |
| 185 } else if (size_button_->visible() && | |
| 186 ConvertPointToViewAndHitTest(this, size_button_, point)) { | |
| 187 return HTMAXBUTTON; | |
| 188 } else if (minimize_button_->visible() && | |
| 189 ConvertPointToViewAndHitTest(this, minimize_button_, point)) { | |
| 190 return HTMINBUTTON; | |
| 191 } | |
| 192 return HTNOWHERE; | |
| 193 } | |
| 194 | |
| 195 void FrameCaptionButtonContainerView::UpdateSizeButtonVisibility() { | |
| 196 bool visible = ShouldSizeButtonBeVisible(); | |
| 197 if (visible) { | |
| 198 size_button_->SetVisible(true); | |
| 199 maximize_mode_animation_->SetSlideDuration(kShowAnimationDurationMs); | |
| 200 maximize_mode_animation_->Show(); | |
| 201 } else { | |
| 202 maximize_mode_animation_->SetSlideDuration(kHideAnimationDurationMs); | |
| 203 maximize_mode_animation_->Hide(); | |
| 204 } | |
| 205 } | |
| 206 | |
| 207 void FrameCaptionButtonContainerView::SetButtonSize(const gfx::Size& size) { | |
| 208 minimize_button_->set_size(size); | |
| 209 size_button_->set_size(size); | |
| 210 close_button_->set_size(size); | |
| 211 } | |
| 212 | |
| 213 gfx::Size FrameCaptionButtonContainerView::GetPreferredSize() const { | |
| 214 int width = 0; | |
| 215 for (int i = 0; i < child_count(); ++i) { | |
| 216 const views::View* child = child_at(i); | |
| 217 if (child->visible()) | |
| 218 width += child_at(i)->GetPreferredSize().width(); | |
| 219 } | |
| 220 return gfx::Size(width, close_button_->GetPreferredSize().height()); | |
| 221 } | |
| 222 | |
| 223 void FrameCaptionButtonContainerView::Layout() { | |
| 224 int x = 0; | |
| 225 for (int i = 0; i < child_count(); ++i) { | |
| 226 views::View* child = child_at(i); | |
| 227 if (!child->visible()) | |
| 228 continue; | |
| 229 | |
| 230 gfx::Size size = child->GetPreferredSize(); | |
| 231 child->SetBounds(x, 0, size.width(), size.height()); | |
| 232 x += size.width(); | |
| 233 } | |
| 234 if (maximize_mode_animation_->is_animating()) { | |
| 235 AnimationProgressed(maximize_mode_animation_.get()); | |
| 236 } | |
| 237 } | |
| 238 | |
| 239 const char* FrameCaptionButtonContainerView::GetClassName() const { | |
| 240 return kViewClassName; | |
| 241 } | |
| 242 | |
| 243 void FrameCaptionButtonContainerView::AnimationEnded( | |
| 244 const gfx::Animation* animation) { | |
| 245 // Ensure that position is calculated at least once. | |
| 246 AnimationProgressed(animation); | |
| 247 | |
| 248 double current_value = maximize_mode_animation_->GetCurrentValue(); | |
| 249 if (current_value == 0.0) { | |
| 250 size_button_->SetVisible(false); | |
| 251 PreferredSizeChanged(); | |
| 252 } | |
| 253 } | |
| 254 | |
| 255 void FrameCaptionButtonContainerView::AnimationProgressed( | |
| 256 const gfx::Animation* animation) { | |
| 257 double current_value = animation->GetCurrentValue(); | |
| 258 int size_alpha = 0; | |
| 259 int minimize_x = 0; | |
| 260 if (maximize_mode_animation_->IsShowing()) { | |
| 261 double scaled_value = | |
| 262 CapAnimationValue((current_value - SizeButtonShowStartValue()) / | |
| 263 SizeButtonShowDuration()); | |
| 264 double tweened_value_alpha = | |
| 265 gfx::Tween::CalculateValue(gfx::Tween::EASE_OUT, scaled_value); | |
| 266 size_alpha = gfx::Tween::LinearIntValueBetween(tweened_value_alpha, 0, 255); | |
| 267 | |
| 268 double tweened_value_slide = | |
| 269 gfx::Tween::CalculateValue(gfx::Tween::EASE_OUT, current_value); | |
| 270 minimize_x = gfx::Tween::LinearIntValueBetween(tweened_value_slide, | |
| 271 size_button_->x(), 0); | |
| 272 } else { | |
| 273 double scaled_value_alpha = | |
| 274 CapAnimationValue((1.0f - current_value) / SizeButtonHideDuration()); | |
| 275 double tweened_value_alpha = | |
| 276 gfx::Tween::CalculateValue(gfx::Tween::EASE_IN, scaled_value_alpha); | |
| 277 size_alpha = gfx::Tween::LinearIntValueBetween(tweened_value_alpha, 255, 0); | |
| 278 | |
| 279 double scaled_value_position = CapAnimationValue( | |
| 280 (HidePositionStartValue() - current_value) / HidePositionStartValue()); | |
| 281 double tweened_value_position = | |
| 282 gfx::Tween::CalculateValue(gfx::Tween::EASE_OUT, scaled_value_position); | |
| 283 minimize_x = gfx::Tween::LinearIntValueBetween(tweened_value_position, 0, | |
| 284 size_button_->x()); | |
| 285 } | |
| 286 size_button_->SetAlpha(size_alpha); | |
| 287 minimize_button_->SetX(minimize_x); | |
| 288 } | |
| 289 | |
| 290 void FrameCaptionButtonContainerView::SetButtonIcon(FrameCaptionButton* button, | |
| 291 CaptionButtonIcon icon, | |
| 292 Animate animate) { | |
| 293 // The early return is dependant on |animate| because callers use | |
| 294 // SetButtonIcon() with ANIMATE_NO to progress |button|'s crossfade animation | |
| 295 // to the end. | |
| 296 if (button->icon() == icon && | |
| 297 (animate == ANIMATE_YES || !button->IsAnimatingImageSwap())) { | |
| 298 return; | |
| 299 } | |
| 300 | |
| 301 FrameCaptionButton::Animate fcb_animate = | |
| 302 (animate == ANIMATE_YES) ? FrameCaptionButton::ANIMATE_YES | |
| 303 : FrameCaptionButton::ANIMATE_NO; | |
| 304 auto it = button_icon_id_map_.find(icon); | |
| 305 if (it != button_icon_id_map_.end()) | |
| 306 button->SetImage(icon, fcb_animate, it->second); | |
| 307 } | |
| 308 | |
| 309 bool FrameCaptionButtonContainerView::ShouldSizeButtonBeVisible() const { | |
| 310 return !WmShell::Get() | |
| 311 ->maximize_mode_controller() | |
| 312 ->IsMaximizeModeWindowManagerEnabled() && | |
| 313 frame_->widget_delegate()->CanMaximize(); | |
| 314 } | |
| 315 | |
| 316 void FrameCaptionButtonContainerView::ButtonPressed(views::Button* sender, | |
| 317 const ui::Event& event) { | |
| 318 // Abort any animations of the button icons. | |
| 319 SetButtonsToNormal(ANIMATE_NO); | |
| 320 | |
| 321 UserMetricsAction action = UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_MINIMIZE; | |
| 322 if (sender == minimize_button_) { | |
| 323 frame_->Minimize(); | |
| 324 } else if (sender == size_button_) { | |
| 325 if (frame_->IsFullscreen()) { // Can be clicked in immersive fullscreen. | |
| 326 frame_->Restore(); | |
| 327 action = UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_EXIT_FULLSCREEN; | |
| 328 } else if (frame_->IsMaximized()) { | |
| 329 frame_->Restore(); | |
| 330 action = UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_RESTORE; | |
| 331 } else { | |
| 332 frame_->Maximize(); | |
| 333 action = UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_MAXIMIZE; | |
| 334 } | |
| 335 | |
| 336 if (event.IsGestureEvent()) | |
| 337 WmShell::Get()->RecordGestureAction(GESTURE_FRAMEMAXIMIZE_TAP); | |
| 338 } else if (sender == close_button_) { | |
| 339 frame_->Close(); | |
| 340 action = UMA_WINDOW_CLOSE_BUTTON_CLICK; | |
| 341 } else { | |
| 342 return; | |
| 343 } | |
| 344 WmShell::Get()->RecordUserMetricsAction(action); | |
| 345 } | |
| 346 | |
| 347 bool FrameCaptionButtonContainerView::IsMinimizeButtonVisible() const { | |
| 348 return minimize_button_->visible(); | |
| 349 } | |
| 350 | |
| 351 void FrameCaptionButtonContainerView::SetButtonsToNormal(Animate animate) { | |
| 352 SetButtonIcons(CAPTION_BUTTON_ICON_MINIMIZE, CAPTION_BUTTON_ICON_CLOSE, | |
| 353 animate); | |
| 354 minimize_button_->SetState(views::Button::STATE_NORMAL); | |
| 355 size_button_->SetState(views::Button::STATE_NORMAL); | |
| 356 close_button_->SetState(views::Button::STATE_NORMAL); | |
| 357 } | |
| 358 | |
| 359 void FrameCaptionButtonContainerView::SetButtonIcons( | |
| 360 CaptionButtonIcon minimize_button_icon, | |
| 361 CaptionButtonIcon close_button_icon, | |
| 362 Animate animate) { | |
| 363 SetButtonIcon(minimize_button_, minimize_button_icon, animate); | |
| 364 SetButtonIcon(close_button_, close_button_icon, animate); | |
| 365 } | |
| 366 | |
| 367 const FrameCaptionButton* FrameCaptionButtonContainerView::GetButtonClosestTo( | |
| 368 const gfx::Point& position_in_screen) const { | |
| 369 // Since the buttons all have the same size, the closest button is the button | |
| 370 // with the center point closest to |position_in_screen|. | |
| 371 // TODO(pkotwicz): Make the caption buttons not overlap. | |
| 372 gfx::Point position(position_in_screen); | |
| 373 views::View::ConvertPointFromScreen(this, &position); | |
| 374 | |
| 375 FrameCaptionButton* buttons[] = {minimize_button_, size_button_, | |
| 376 close_button_}; | |
| 377 int min_squared_distance = INT_MAX; | |
| 378 FrameCaptionButton* closest_button = NULL; | |
| 379 for (size_t i = 0; i < arraysize(buttons); ++i) { | |
| 380 FrameCaptionButton* button = buttons[i]; | |
| 381 if (!button->visible()) | |
| 382 continue; | |
| 383 | |
| 384 gfx::Point center_point = button->GetLocalBounds().CenterPoint(); | |
| 385 views::View::ConvertPointToTarget(button, this, ¢er_point); | |
| 386 int squared_distance = static_cast<int>( | |
| 387 pow(static_cast<double>(position.x() - center_point.x()), 2) + | |
| 388 pow(static_cast<double>(position.y() - center_point.y()), 2)); | |
| 389 if (squared_distance < min_squared_distance) { | |
| 390 min_squared_distance = squared_distance; | |
| 391 closest_button = button; | |
| 392 } | |
| 393 } | |
| 394 return closest_button; | |
| 395 } | |
| 396 | |
| 397 void FrameCaptionButtonContainerView::SetHoveredAndPressedButtons( | |
| 398 const FrameCaptionButton* to_hover, | |
| 399 const FrameCaptionButton* to_press) { | |
| 400 FrameCaptionButton* buttons[] = {minimize_button_, size_button_, | |
| 401 close_button_}; | |
| 402 for (size_t i = 0; i < arraysize(buttons); ++i) { | |
| 403 FrameCaptionButton* button = buttons[i]; | |
| 404 views::Button::ButtonState new_state = views::Button::STATE_NORMAL; | |
| 405 if (button == to_hover) | |
| 406 new_state = views::Button::STATE_HOVERED; | |
| 407 else if (button == to_press) | |
| 408 new_state = views::Button::STATE_PRESSED; | |
| 409 button->SetState(new_state); | |
| 410 } | |
| 411 } | |
| 412 | |
| 413 } // namespace ash | |
| OLD | NEW |