| OLD | NEW |
| (Empty) |
| 1 // Copyright 2014 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_size_button.h" | |
| 6 | |
| 7 #include "ash/aura/wm_window_aura.h" | |
| 8 #include "ash/common/wm/window_positioning_utils.h" | |
| 9 #include "ash/common/wm/window_state.h" | |
| 10 #include "ash/common/wm/wm_event.h" | |
| 11 #include "ash/common/wm/workspace/phantom_window_controller.h" | |
| 12 #include "ash/common/wm_shell.h" | |
| 13 #include "ash/screen_util.h" | |
| 14 #include "ash/wm/window_state_aura.h" | |
| 15 #include "ash/wm/window_util.h" | |
| 16 #include "base/i18n/rtl.h" | |
| 17 #include "ui/aura/window.h" | |
| 18 #include "ui/gfx/geometry/vector2d.h" | |
| 19 #include "ui/views/widget/widget.h" | |
| 20 | |
| 21 namespace ash { | |
| 22 | |
| 23 namespace { | |
| 24 | |
| 25 // The default delay between the user pressing the size button and the buttons | |
| 26 // adjacent to the size button morphing into buttons for snapping left and | |
| 27 // right. | |
| 28 const int kSetButtonsToSnapModeDelayMs = 150; | |
| 29 | |
| 30 // The amount that a user can overshoot one of the caption buttons while in | |
| 31 // "snap mode" and keep the button hovered/pressed. | |
| 32 const int kMaxOvershootX = 200; | |
| 33 const int kMaxOvershootY = 50; | |
| 34 | |
| 35 // Returns true if a mouse drag while in "snap mode" at |location_in_screen| | |
| 36 // would hover/press |button| or keep it hovered/pressed. | |
| 37 bool HitTestButton(const FrameCaptionButton* button, | |
| 38 const gfx::Point& location_in_screen) { | |
| 39 gfx::Rect expanded_bounds_in_screen = button->GetBoundsInScreen(); | |
| 40 if (button->state() == views::Button::STATE_HOVERED || | |
| 41 button->state() == views::Button::STATE_PRESSED) { | |
| 42 expanded_bounds_in_screen.Inset(-kMaxOvershootX, -kMaxOvershootY); | |
| 43 } | |
| 44 return expanded_bounds_in_screen.Contains(location_in_screen); | |
| 45 } | |
| 46 | |
| 47 } // namespace | |
| 48 | |
| 49 FrameSizeButton::FrameSizeButton(views::ButtonListener* listener, | |
| 50 views::Widget* frame, | |
| 51 FrameSizeButtonDelegate* delegate) | |
| 52 : FrameCaptionButton(listener, CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE), | |
| 53 frame_(frame), | |
| 54 delegate_(delegate), | |
| 55 set_buttons_to_snap_mode_delay_ms_(kSetButtonsToSnapModeDelayMs), | |
| 56 in_snap_mode_(false), | |
| 57 snap_type_(SNAP_NONE) {} | |
| 58 | |
| 59 FrameSizeButton::~FrameSizeButton() {} | |
| 60 | |
| 61 bool FrameSizeButton::OnMousePressed(const ui::MouseEvent& event) { | |
| 62 // The minimize and close buttons are set to snap left and right when snapping | |
| 63 // is enabled. Do not enable snapping if the minimize button is not visible. | |
| 64 // The close button is always visible. | |
| 65 if (IsTriggerableEvent(event) && !in_snap_mode_ && | |
| 66 delegate_->IsMinimizeButtonVisible()) { | |
| 67 StartSetButtonsToSnapModeTimer(event); | |
| 68 } | |
| 69 FrameCaptionButton::OnMousePressed(event); | |
| 70 return true; | |
| 71 } | |
| 72 | |
| 73 bool FrameSizeButton::OnMouseDragged(const ui::MouseEvent& event) { | |
| 74 UpdateSnapType(event); | |
| 75 // By default a FrameCaptionButton reverts to STATE_NORMAL once the mouse | |
| 76 // leaves its bounds. Skip FrameCaptionButton's handling when | |
| 77 // |in_snap_mode_| == true because we want different behavior. | |
| 78 if (!in_snap_mode_) | |
| 79 FrameCaptionButton::OnMouseDragged(event); | |
| 80 return true; | |
| 81 } | |
| 82 | |
| 83 void FrameSizeButton::OnMouseReleased(const ui::MouseEvent& event) { | |
| 84 if (!IsTriggerableEvent(event) || !CommitSnap(event)) | |
| 85 FrameCaptionButton::OnMouseReleased(event); | |
| 86 } | |
| 87 | |
| 88 void FrameSizeButton::OnMouseCaptureLost() { | |
| 89 SetButtonsToNormalMode(FrameSizeButtonDelegate::ANIMATE_YES); | |
| 90 FrameCaptionButton::OnMouseCaptureLost(); | |
| 91 } | |
| 92 | |
| 93 void FrameSizeButton::OnMouseMoved(const ui::MouseEvent& event) { | |
| 94 // Ignore any synthetic mouse moves during a drag. | |
| 95 if (!in_snap_mode_) | |
| 96 FrameCaptionButton::OnMouseMoved(event); | |
| 97 } | |
| 98 | |
| 99 void FrameSizeButton::OnGestureEvent(ui::GestureEvent* event) { | |
| 100 if (event->details().touch_points() > 1) { | |
| 101 SetButtonsToNormalMode(FrameSizeButtonDelegate::ANIMATE_YES); | |
| 102 return; | |
| 103 } | |
| 104 | |
| 105 if (event->type() == ui::ET_GESTURE_TAP_DOWN) { | |
| 106 StartSetButtonsToSnapModeTimer(*event); | |
| 107 // Go through FrameCaptionButton's handling so that the button gets pressed. | |
| 108 FrameCaptionButton::OnGestureEvent(event); | |
| 109 return; | |
| 110 } | |
| 111 | |
| 112 if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN || | |
| 113 event->type() == ui::ET_GESTURE_SCROLL_UPDATE) { | |
| 114 UpdateSnapType(*event); | |
| 115 event->SetHandled(); | |
| 116 return; | |
| 117 } | |
| 118 | |
| 119 if (event->type() == ui::ET_GESTURE_TAP || | |
| 120 event->type() == ui::ET_GESTURE_SCROLL_END || | |
| 121 event->type() == ui::ET_SCROLL_FLING_START || | |
| 122 event->type() == ui::ET_GESTURE_END) { | |
| 123 if (CommitSnap(*event)) { | |
| 124 event->SetHandled(); | |
| 125 return; | |
| 126 } | |
| 127 } | |
| 128 | |
| 129 FrameCaptionButton::OnGestureEvent(event); | |
| 130 } | |
| 131 | |
| 132 void FrameSizeButton::StartSetButtonsToSnapModeTimer( | |
| 133 const ui::LocatedEvent& event) { | |
| 134 set_buttons_to_snap_mode_timer_event_location_ = event.location(); | |
| 135 if (set_buttons_to_snap_mode_delay_ms_ == 0) { | |
| 136 AnimateButtonsToSnapMode(); | |
| 137 } else { | |
| 138 set_buttons_to_snap_mode_timer_.Start( | |
| 139 FROM_HERE, | |
| 140 base::TimeDelta::FromMilliseconds(set_buttons_to_snap_mode_delay_ms_), | |
| 141 this, &FrameSizeButton::AnimateButtonsToSnapMode); | |
| 142 } | |
| 143 } | |
| 144 | |
| 145 void FrameSizeButton::AnimateButtonsToSnapMode() { | |
| 146 SetButtonsToSnapMode(FrameSizeButtonDelegate::ANIMATE_YES); | |
| 147 } | |
| 148 | |
| 149 void FrameSizeButton::SetButtonsToSnapMode( | |
| 150 FrameSizeButtonDelegate::Animate animate) { | |
| 151 in_snap_mode_ = true; | |
| 152 | |
| 153 // When using a right-to-left layout the close button is left of the size | |
| 154 // button and the minimize button is right of the size button. | |
| 155 if (base::i18n::IsRTL()) { | |
| 156 delegate_->SetButtonIcons(CAPTION_BUTTON_ICON_RIGHT_SNAPPED, | |
| 157 CAPTION_BUTTON_ICON_LEFT_SNAPPED, animate); | |
| 158 } else { | |
| 159 delegate_->SetButtonIcons(CAPTION_BUTTON_ICON_LEFT_SNAPPED, | |
| 160 CAPTION_BUTTON_ICON_RIGHT_SNAPPED, animate); | |
| 161 } | |
| 162 } | |
| 163 | |
| 164 void FrameSizeButton::UpdateSnapType(const ui::LocatedEvent& event) { | |
| 165 if (!in_snap_mode_) { | |
| 166 // Set the buttons adjacent to the size button to snap left and right early | |
| 167 // if the user drags past the drag threshold. | |
| 168 // |set_buttons_to_snap_mode_timer_| is checked to avoid entering the snap | |
| 169 // mode as a result of an unsupported drag type (e.g. only the right mouse | |
| 170 // button is pressed). | |
| 171 gfx::Vector2d delta(event.location() - | |
| 172 set_buttons_to_snap_mode_timer_event_location_); | |
| 173 if (!set_buttons_to_snap_mode_timer_.IsRunning() || | |
| 174 !views::View::ExceededDragThreshold(delta)) { | |
| 175 return; | |
| 176 } | |
| 177 AnimateButtonsToSnapMode(); | |
| 178 } | |
| 179 | |
| 180 gfx::Point event_location_in_screen(event.location()); | |
| 181 views::View::ConvertPointToScreen(this, &event_location_in_screen); | |
| 182 const FrameCaptionButton* to_hover = | |
| 183 GetButtonToHover(event_location_in_screen); | |
| 184 bool press_size_button = | |
| 185 to_hover || HitTestButton(this, event_location_in_screen); | |
| 186 | |
| 187 if (to_hover) { | |
| 188 // Progress the minimize and close icon morph animations to the end if they | |
| 189 // are in progress. | |
| 190 SetButtonsToSnapMode(FrameSizeButtonDelegate::ANIMATE_NO); | |
| 191 } | |
| 192 | |
| 193 delegate_->SetHoveredAndPressedButtons(to_hover, | |
| 194 press_size_button ? this : NULL); | |
| 195 | |
| 196 snap_type_ = SNAP_NONE; | |
| 197 if (to_hover) { | |
| 198 switch (to_hover->icon()) { | |
| 199 case CAPTION_BUTTON_ICON_LEFT_SNAPPED: | |
| 200 snap_type_ = SNAP_LEFT; | |
| 201 break; | |
| 202 case CAPTION_BUTTON_ICON_RIGHT_SNAPPED: | |
| 203 snap_type_ = SNAP_RIGHT; | |
| 204 break; | |
| 205 case CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE: | |
| 206 case CAPTION_BUTTON_ICON_MINIMIZE: | |
| 207 case CAPTION_BUTTON_ICON_CLOSE: | |
| 208 case CAPTION_BUTTON_ICON_BACK: | |
| 209 case CAPTION_BUTTON_ICON_LOCATION: | |
| 210 case CAPTION_BUTTON_ICON_COUNT: | |
| 211 NOTREACHED(); | |
| 212 break; | |
| 213 } | |
| 214 } | |
| 215 | |
| 216 if (snap_type_ == SNAP_LEFT || snap_type_ == SNAP_RIGHT) { | |
| 217 aura::Window* window = frame_->GetNativeWindow(); | |
| 218 if (!phantom_window_controller_.get()) { | |
| 219 phantom_window_controller_.reset( | |
| 220 new PhantomWindowController(WmWindowAura::Get(window))); | |
| 221 } | |
| 222 gfx::Rect phantom_bounds_in_parent = | |
| 223 (snap_type_ == SNAP_LEFT) | |
| 224 ? wm::GetDefaultLeftSnappedWindowBoundsInParent( | |
| 225 WmWindowAura::Get(window)) | |
| 226 : wm::GetDefaultRightSnappedWindowBoundsInParent( | |
| 227 WmWindowAura::Get(window)); | |
| 228 phantom_window_controller_->Show(ScreenUtil::ConvertRectToScreen( | |
| 229 window->parent(), phantom_bounds_in_parent)); | |
| 230 } else { | |
| 231 phantom_window_controller_.reset(); | |
| 232 } | |
| 233 } | |
| 234 | |
| 235 const FrameCaptionButton* FrameSizeButton::GetButtonToHover( | |
| 236 const gfx::Point& event_location_in_screen) const { | |
| 237 const FrameCaptionButton* closest_button = | |
| 238 delegate_->GetButtonClosestTo(event_location_in_screen); | |
| 239 if ((closest_button->icon() == CAPTION_BUTTON_ICON_LEFT_SNAPPED || | |
| 240 closest_button->icon() == CAPTION_BUTTON_ICON_RIGHT_SNAPPED) && | |
| 241 HitTestButton(closest_button, event_location_in_screen)) { | |
| 242 return closest_button; | |
| 243 } | |
| 244 return NULL; | |
| 245 } | |
| 246 | |
| 247 bool FrameSizeButton::CommitSnap(const ui::LocatedEvent& event) { | |
| 248 // The position of |event| may be different than the position of the previous | |
| 249 // event. | |
| 250 UpdateSnapType(event); | |
| 251 | |
| 252 if (in_snap_mode_ && (snap_type_ == SNAP_LEFT || snap_type_ == SNAP_RIGHT)) { | |
| 253 wm::WindowState* window_state = | |
| 254 wm::GetWindowState(frame_->GetNativeWindow()); | |
| 255 const wm::WMEvent snap_event(snap_type_ == SNAP_LEFT | |
| 256 ? wm::WM_EVENT_SNAP_LEFT | |
| 257 : wm::WM_EVENT_SNAP_RIGHT); | |
| 258 window_state->OnWMEvent(&snap_event); | |
| 259 WmShell::Get()->RecordUserMetricsAction( | |
| 260 snap_type_ == SNAP_LEFT ? UMA_WINDOW_MAXIMIZE_BUTTON_MAXIMIZE_LEFT | |
| 261 : UMA_WINDOW_MAXIMIZE_BUTTON_MAXIMIZE_RIGHT); | |
| 262 SetButtonsToNormalMode(FrameSizeButtonDelegate::ANIMATE_NO); | |
| 263 return true; | |
| 264 } | |
| 265 SetButtonsToNormalMode(FrameSizeButtonDelegate::ANIMATE_YES); | |
| 266 return false; | |
| 267 } | |
| 268 | |
| 269 void FrameSizeButton::SetButtonsToNormalMode( | |
| 270 FrameSizeButtonDelegate::Animate animate) { | |
| 271 in_snap_mode_ = false; | |
| 272 snap_type_ = SNAP_NONE; | |
| 273 set_buttons_to_snap_mode_timer_.Stop(); | |
| 274 delegate_->SetButtonsToNormal(animate); | |
| 275 phantom_window_controller_.reset(); | |
| 276 } | |
| 277 | |
| 278 } // namespace ash | |
| OLD | NEW |