| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2011 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 "ui/aura_shell/toplevel_frame_view.h" | |
| 6 | |
| 7 #include "grit/ui_resources.h" | |
| 8 #include "ui/aura/cursor.h" | |
| 9 #include "ui/base/animation/throb_animation.h" | |
| 10 #include "ui/base/hit_test.h" | |
| 11 #include "ui/base/resource/resource_bundle.h" | |
| 12 #include "ui/gfx/canvas.h" | |
| 13 #include "ui/views/controls/button/custom_button.h" | |
| 14 #include "ui/views/widget/widget.h" | |
| 15 #include "ui/views/widget/widget_delegate.h" | |
| 16 | |
| 17 namespace aura_shell { | |
| 18 namespace internal { | |
| 19 | |
| 20 namespace { | |
| 21 // The thickness of the left, right and bottom edges of the frame. | |
| 22 const int kFrameBorderThickness = 8; | |
| 23 const int kFrameOpacity = 50; | |
| 24 // The color used to fill the frame. | |
| 25 const SkColor kFrameColor = SkColorSetARGB(kFrameOpacity, 0, 0, 0); | |
| 26 const int kFrameBorderHiddenOpacity = 0; | |
| 27 const int kFrameBorderVisibleOpacity = kFrameOpacity; | |
| 28 // How long the hover animation takes if uninterrupted. | |
| 29 const int kHoverFadeDurationMs = 250; | |
| 30 } // namespace | |
| 31 | |
| 32 // Buttons for window controls - close, zoom, etc. | |
| 33 class WindowControlButton : public views::CustomButton { | |
| 34 public: | |
| 35 WindowControlButton(views::ButtonListener* listener, | |
| 36 SkColor color, | |
| 37 const SkBitmap& icon) | |
| 38 : views::CustomButton(listener), | |
| 39 color_(color), | |
| 40 icon_(icon) { | |
| 41 } | |
| 42 virtual ~WindowControlButton() {} | |
| 43 | |
| 44 // Overridden from views::View: | |
| 45 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE { | |
| 46 canvas->FillRect(GetBackgroundColor(), GetLocalBounds()); | |
| 47 canvas->DrawBitmapInt(icon_, 0, 0); | |
| 48 } | |
| 49 virtual gfx::Size GetPreferredSize() OVERRIDE { | |
| 50 gfx::Size size(icon_.width(), icon_.height()); | |
| 51 size.Enlarge(3, 2); | |
| 52 return size; | |
| 53 } | |
| 54 | |
| 55 private: | |
| 56 SkColor GetBackgroundColor() { | |
| 57 return SkColorSetARGB(hover_animation_->CurrentValueBetween(0, 150), | |
| 58 SkColorGetR(color_), | |
| 59 SkColorGetG(color_), | |
| 60 SkColorGetB(color_)); | |
| 61 } | |
| 62 | |
| 63 SkColor color_; | |
| 64 SkBitmap icon_; | |
| 65 | |
| 66 DISALLOW_COPY_AND_ASSIGN(WindowControlButton); | |
| 67 }; | |
| 68 | |
| 69 // Base class for all animatable frame components such as sizing borders and | |
| 70 // the window's caption. Provides shared animation and event-handling logic. | |
| 71 class FrameComponent : public views::View, | |
| 72 public ui::AnimationDelegate { | |
| 73 public: | |
| 74 virtual ~FrameComponent() { | |
| 75 } | |
| 76 | |
| 77 // Control animations. | |
| 78 void Show() { | |
| 79 animation_->Show(); | |
| 80 } | |
| 81 void Hide() { | |
| 82 animation_->Hide(); | |
| 83 } | |
| 84 | |
| 85 // Current animation state. | |
| 86 bool IsShowing() const { | |
| 87 return animation_->IsShowing(); | |
| 88 } | |
| 89 bool IsHiding() const { | |
| 90 return animation_->IsClosing(); | |
| 91 } | |
| 92 | |
| 93 // Returns true if the view ignores events to itself or its children at the | |
| 94 // specified point in its parent's coordinates. By default, any events within | |
| 95 // the bounds of this view are ignored so that the parent (the NCFV) can | |
| 96 // handle them instead. Derived classes can override to disable this for some | |
| 97 // of their children. | |
| 98 virtual bool IgnoreEventsForPoint(const gfx::Point& point) { | |
| 99 gfx::Point translated_point(point); | |
| 100 ConvertPointToView(parent(), this, &translated_point); | |
| 101 return HitTest(translated_point); | |
| 102 } | |
| 103 | |
| 104 protected: | |
| 105 FrameComponent() | |
| 106 : ALLOW_THIS_IN_INITIALIZER_LIST( | |
| 107 animation_(new ui::SlideAnimation(this))) { | |
| 108 animation_->SetSlideDuration(kHoverFadeDurationMs); | |
| 109 } | |
| 110 | |
| 111 // Most of the frame components are rendered with a transparent bg. | |
| 112 void PaintTransparentBackground(gfx::Canvas* canvas) { | |
| 113 // Fill with current opacity value. | |
| 114 int opacity = animation_->CurrentValueBetween(kFrameBorderHiddenOpacity, | |
| 115 kFrameBorderVisibleOpacity); | |
| 116 canvas->FillRect(SkColorSetARGB(opacity, | |
| 117 SkColorGetR(kFrameColor), | |
| 118 SkColorGetG(kFrameColor), | |
| 119 SkColorGetB(kFrameColor)), | |
| 120 GetLocalBounds()); | |
| 121 } | |
| 122 | |
| 123 // Overridden from ui::AnimationDelegate: | |
| 124 virtual void AnimationProgressed(const ui::Animation* animation) OVERRIDE { | |
| 125 SchedulePaint(); | |
| 126 } | |
| 127 | |
| 128 private: | |
| 129 scoped_ptr<ui::SlideAnimation> animation_; | |
| 130 | |
| 131 DISALLOW_COPY_AND_ASSIGN(FrameComponent); | |
| 132 }; | |
| 133 | |
| 134 // A view that renders the title bar of the window, and also hosts the window | |
| 135 // controls. | |
| 136 class WindowCaption : public FrameComponent, | |
| 137 public views::ButtonListener { | |
| 138 public: | |
| 139 WindowCaption() { | |
| 140 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | |
| 141 close_button_ = | |
| 142 new WindowControlButton(this, SK_ColorRED, | |
| 143 *rb.GetBitmapNamed(IDR_AURA_WINDOW_CLOSE_ICON)); | |
| 144 zoom_button_ = | |
| 145 new WindowControlButton(this, SK_ColorGREEN, | |
| 146 *rb.GetBitmapNamed(IDR_AURA_WINDOW_ZOOM_ICON)); | |
| 147 AddChildView(close_button_); | |
| 148 AddChildView(zoom_button_); | |
| 149 } | |
| 150 virtual ~WindowCaption() {} | |
| 151 | |
| 152 // Returns the hit-test code for the specified point in parent coordinates. | |
| 153 int NonClientHitTest(const gfx::Point& point) { | |
| 154 gfx::Point translated_point(point); | |
| 155 View::ConvertPointToView(parent(), this, &translated_point); | |
| 156 // The window controls come second. | |
| 157 if (close_button_->GetMirroredBounds().Contains(translated_point)) | |
| 158 return HTCLOSE; | |
| 159 else if (zoom_button_->GetMirroredBounds().Contains(translated_point)) | |
| 160 return HTMAXBUTTON; | |
| 161 return HTNOWHERE; | |
| 162 } | |
| 163 | |
| 164 // Overridden from FrameComponent: | |
| 165 virtual bool IgnoreEventsForPoint(const gfx::Point& point) OVERRIDE { | |
| 166 gfx::Point translated_point(point); | |
| 167 ConvertPointToView(parent(), this, &translated_point); | |
| 168 if (PointIsInChildView(close_button_, translated_point)) | |
| 169 return false; | |
| 170 if (PointIsInChildView(zoom_button_, translated_point)) | |
| 171 return false; | |
| 172 return FrameComponent::IgnoreEventsForPoint(point); | |
| 173 } | |
| 174 | |
| 175 // Overridden from views::View: | |
| 176 virtual gfx::Size GetPreferredSize() OVERRIDE { | |
| 177 return gfx::Size(0, close_button_->GetPreferredSize().height()); | |
| 178 } | |
| 179 | |
| 180 private: | |
| 181 // Returns true if the specified |point| in this view's coordinates hit tests | |
| 182 // against |child|, a child view of this view. | |
| 183 bool PointIsInChildView(views::View* child, | |
| 184 const gfx::Point& point) const { | |
| 185 gfx::Point child_point(point); | |
| 186 ConvertPointToView(this, child, &child_point); | |
| 187 return child->HitTest(child_point); | |
| 188 } | |
| 189 | |
| 190 // Overridden from views::View: | |
| 191 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE { | |
| 192 PaintTransparentBackground(canvas); | |
| 193 } | |
| 194 virtual void Layout() OVERRIDE { | |
| 195 gfx::Size close_button_ps = close_button_->GetPreferredSize(); | |
| 196 close_button_->SetBoundsRect( | |
| 197 gfx::Rect(width() - close_button_ps.width(), | |
| 198 0, close_button_ps.width(), close_button_ps.height())); | |
| 199 | |
| 200 gfx::Size zoom_button_ps = zoom_button_->GetPreferredSize(); | |
| 201 zoom_button_->SetBoundsRect( | |
| 202 gfx::Rect(close_button_->x() - zoom_button_ps.width(), 0, | |
| 203 zoom_button_ps.width(), zoom_button_ps.height())); | |
| 204 } | |
| 205 | |
| 206 // Overridden from views::ButtonListener: | |
| 207 virtual void ButtonPressed(views::Button* sender, | |
| 208 const views::Event& event) OVERRIDE { | |
| 209 if (sender == close_button_) { | |
| 210 GetWidget()->Close(); | |
| 211 } else if (sender == zoom_button_) { | |
| 212 if (GetWidget()->IsMaximized()) | |
| 213 GetWidget()->Restore(); | |
| 214 else | |
| 215 GetWidget()->Maximize(); | |
| 216 } | |
| 217 } | |
| 218 | |
| 219 views::Button* close_button_; | |
| 220 views::Button* zoom_button_; | |
| 221 | |
| 222 DISALLOW_COPY_AND_ASSIGN(WindowCaption); | |
| 223 }; | |
| 224 | |
| 225 // A class that renders the sizing border that appears when the user moves | |
| 226 // their mouse over a sizing edge. This view is not actually responsible for | |
| 227 // resizing the window, the EventFilter is. | |
| 228 class SizingBorder : public FrameComponent { | |
| 229 public: | |
| 230 SizingBorder() {} | |
| 231 virtual ~SizingBorder() {} | |
| 232 | |
| 233 void Configure(const gfx::Rect& hidden_bounds, | |
| 234 const gfx::Rect& visible_bounds) { | |
| 235 hidden_bounds_ = hidden_bounds; | |
| 236 visible_bounds_ = visible_bounds; | |
| 237 SetBoundsRect(hidden_bounds_); | |
| 238 } | |
| 239 | |
| 240 protected: | |
| 241 // Overridden from ui::AnimationDelegate: | |
| 242 virtual void AnimationProgressed(const ui::Animation* animation) OVERRIDE { | |
| 243 gfx::Rect current_bounds = animation->CurrentValueBetween(hidden_bounds_, | |
| 244 visible_bounds_); | |
| 245 SetBoundsRect(current_bounds); | |
| 246 FrameComponent::AnimationProgressed(animation); | |
| 247 } | |
| 248 | |
| 249 private: | |
| 250 // Overridden from views::View: | |
| 251 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE { | |
| 252 PaintTransparentBackground(canvas); | |
| 253 } | |
| 254 | |
| 255 // Each of these represents the hidden/visible states of the sizing border. | |
| 256 // When the view is shown or hidden it animates between them. | |
| 257 gfx::Rect hidden_bounds_; | |
| 258 gfx::Rect visible_bounds_; | |
| 259 | |
| 260 DISALLOW_COPY_AND_ASSIGN(SizingBorder); | |
| 261 }; | |
| 262 | |
| 263 //////////////////////////////////////////////////////////////////////////////// | |
| 264 // ToplevelFrameView, public: | |
| 265 | |
| 266 ToplevelFrameView::ToplevelFrameView() | |
| 267 : current_hittest_code_(HTNOWHERE), | |
| 268 caption_(new WindowCaption), | |
| 269 left_edge_(new SizingBorder), | |
| 270 right_edge_(new SizingBorder), | |
| 271 bottom_edge_(new SizingBorder) { | |
| 272 AddChildView(caption_); | |
| 273 AddChildView(left_edge_); | |
| 274 AddChildView(right_edge_); | |
| 275 AddChildView(bottom_edge_); | |
| 276 } | |
| 277 | |
| 278 ToplevelFrameView::~ToplevelFrameView() { | |
| 279 } | |
| 280 | |
| 281 //////////////////////////////////////////////////////////////////////////////// | |
| 282 // ToplevelFrameView, private: | |
| 283 | |
| 284 int ToplevelFrameView::NonClientBorderThickness() const { | |
| 285 return kFrameBorderThickness; | |
| 286 } | |
| 287 | |
| 288 int ToplevelFrameView::NonClientTopBorderHeight() const { | |
| 289 return caption_->GetPreferredSize().height(); | |
| 290 } | |
| 291 | |
| 292 int ToplevelFrameView::NonClientHitTestImpl(const gfx::Point& point) { | |
| 293 // Sanity check. | |
| 294 if (!GetLocalBounds().Contains(point)) | |
| 295 return HTNOWHERE; | |
| 296 | |
| 297 // The client view gets first crack at claiming the point. | |
| 298 int frame_component = GetWidget()->client_view()->NonClientHitTest(point); | |
| 299 if (frame_component != HTNOWHERE) | |
| 300 return frame_component; | |
| 301 | |
| 302 frame_component = caption_->NonClientHitTest(point); | |
| 303 if (frame_component != HTNOWHERE) | |
| 304 return frame_component; | |
| 305 | |
| 306 // Finally other portions of the frame/sizing border. | |
| 307 frame_component = | |
| 308 GetHTComponentForFrame(point, | |
| 309 NonClientBorderThickness(), | |
| 310 NonClientBorderThickness(), | |
| 311 NonClientBorderThickness(), | |
| 312 NonClientBorderThickness(), | |
| 313 GetWidget()->widget_delegate()->CanResize()); | |
| 314 | |
| 315 // Coerce HTCAPTION as a fallback. | |
| 316 return frame_component == HTNOWHERE ? HTCAPTION : frame_component; | |
| 317 } | |
| 318 | |
| 319 void ToplevelFrameView::ShowFrameComponent(FrameComponent* frame_component) { | |
| 320 if (frame_component && !frame_component->IsShowing()) | |
| 321 frame_component->Show(); | |
| 322 if (caption_ != frame_component && !caption_->IsHiding()) | |
| 323 caption_->Hide(); | |
| 324 if (left_edge_ != frame_component && !left_edge_->IsHiding()) | |
| 325 left_edge_->Hide(); | |
| 326 if (right_edge_ != frame_component && !right_edge_->IsHiding()) | |
| 327 right_edge_->Hide(); | |
| 328 if (bottom_edge_ != frame_component && !bottom_edge_->IsHiding()) | |
| 329 bottom_edge_->Hide(); | |
| 330 } | |
| 331 | |
| 332 gfx::Rect ToplevelFrameView::GetHiddenBoundsForSizingBorder( | |
| 333 int frame_component) const { | |
| 334 int border_size = NonClientBorderThickness(); | |
| 335 int caption_height = NonClientTopBorderHeight(); | |
| 336 switch (frame_component) { | |
| 337 case HTLEFT: | |
| 338 return gfx::Rect(border_size, caption_height, border_size, | |
| 339 height() - border_size - caption_height); | |
| 340 case HTBOTTOM: | |
| 341 return gfx::Rect(border_size, height() - 2 * border_size, | |
| 342 width() - 2 * border_size, border_size); | |
| 343 case HTRIGHT: | |
| 344 return gfx::Rect(width() - 2 * border_size, caption_height, border_size, | |
| 345 height() - border_size - caption_height); | |
| 346 default: | |
| 347 break; | |
| 348 } | |
| 349 return gfx::Rect(); | |
| 350 } | |
| 351 | |
| 352 gfx::Rect ToplevelFrameView::GetVisibleBoundsForSizingBorder( | |
| 353 int frame_component) const { | |
| 354 int border_size = NonClientBorderThickness(); | |
| 355 int caption_height = NonClientTopBorderHeight(); | |
| 356 switch (frame_component) { | |
| 357 case HTLEFT: | |
| 358 return gfx::Rect(0, caption_height, border_size, | |
| 359 height() - border_size - caption_height); | |
| 360 case HTBOTTOM: | |
| 361 return gfx::Rect(border_size, height() - border_size, | |
| 362 width() - 2 * border_size, border_size); | |
| 363 case HTRIGHT: | |
| 364 return gfx::Rect(width() - border_size, caption_height, border_size, | |
| 365 height() - border_size - caption_height); | |
| 366 default: | |
| 367 break; | |
| 368 } | |
| 369 return gfx::Rect(); | |
| 370 } | |
| 371 | |
| 372 //////////////////////////////////////////////////////////////////////////////// | |
| 373 // ToplevelFrameView, views::NonClientFrameView overrides: | |
| 374 | |
| 375 gfx::Rect ToplevelFrameView::GetBoundsForClientView() const { | |
| 376 return client_view_bounds_; | |
| 377 } | |
| 378 | |
| 379 gfx::Rect ToplevelFrameView::GetWindowBoundsForClientBounds( | |
| 380 const gfx::Rect& client_bounds) const { | |
| 381 gfx::Rect window_bounds = client_bounds; | |
| 382 window_bounds.Inset(-NonClientBorderThickness(), | |
| 383 -NonClientTopBorderHeight(), | |
| 384 -NonClientBorderThickness(), | |
| 385 -NonClientBorderThickness()); | |
| 386 return window_bounds; | |
| 387 } | |
| 388 | |
| 389 int ToplevelFrameView::NonClientHitTest(const gfx::Point& point) { | |
| 390 current_hittest_code_ = NonClientHitTestImpl(point); | |
| 391 return current_hittest_code_; | |
| 392 } | |
| 393 | |
| 394 void ToplevelFrameView::GetWindowMask(const gfx::Size& size, | |
| 395 gfx::Path* window_mask) { | |
| 396 // Nothing. | |
| 397 } | |
| 398 | |
| 399 void ToplevelFrameView::ResetWindowControls() { | |
| 400 NOTIMPLEMENTED(); | |
| 401 } | |
| 402 | |
| 403 void ToplevelFrameView::UpdateWindowIcon() { | |
| 404 NOTIMPLEMENTED(); | |
| 405 } | |
| 406 | |
| 407 //////////////////////////////////////////////////////////////////////////////// | |
| 408 // ToplevelFrameView, views::View overrides: | |
| 409 | |
| 410 void ToplevelFrameView::Layout() { | |
| 411 client_view_bounds_ = GetLocalBounds(); | |
| 412 client_view_bounds_.Inset(NonClientBorderThickness(), | |
| 413 NonClientTopBorderHeight(), | |
| 414 NonClientBorderThickness(), | |
| 415 NonClientBorderThickness()); | |
| 416 | |
| 417 caption_->SetBounds(NonClientBorderThickness(), 0, | |
| 418 width() - 2 * NonClientBorderThickness(), | |
| 419 NonClientTopBorderHeight()); | |
| 420 | |
| 421 left_edge_->Configure(GetHiddenBoundsForSizingBorder(HTLEFT), | |
| 422 GetVisibleBoundsForSizingBorder(HTLEFT)); | |
| 423 right_edge_->Configure(GetHiddenBoundsForSizingBorder(HTRIGHT), | |
| 424 GetVisibleBoundsForSizingBorder(HTRIGHT)); | |
| 425 bottom_edge_->Configure(GetHiddenBoundsForSizingBorder(HTBOTTOM), | |
| 426 GetVisibleBoundsForSizingBorder(HTBOTTOM)); | |
| 427 } | |
| 428 | |
| 429 void ToplevelFrameView::OnMouseMoved(const views::MouseEvent& event) { | |
| 430 switch (current_hittest_code_) { | |
| 431 case HTLEFT: | |
| 432 ShowFrameComponent(left_edge_); | |
| 433 break; | |
| 434 case HTRIGHT: | |
| 435 ShowFrameComponent(right_edge_); | |
| 436 break; | |
| 437 case HTBOTTOM: | |
| 438 ShowFrameComponent(bottom_edge_); | |
| 439 break; | |
| 440 case HTCAPTION: | |
| 441 ShowFrameComponent(caption_); | |
| 442 break; | |
| 443 default: | |
| 444 break; | |
| 445 } | |
| 446 } | |
| 447 | |
| 448 void ToplevelFrameView::OnMouseExited(const views::MouseEvent& event) { | |
| 449 ShowFrameComponent(NULL); | |
| 450 } | |
| 451 | |
| 452 views::View* ToplevelFrameView::GetEventHandlerForPoint( | |
| 453 const gfx::Point& point) { | |
| 454 if (left_edge_->IgnoreEventsForPoint(point) || | |
| 455 right_edge_->IgnoreEventsForPoint(point) || | |
| 456 bottom_edge_->IgnoreEventsForPoint(point) || | |
| 457 caption_->IgnoreEventsForPoint(point)) { | |
| 458 return this; | |
| 459 } | |
| 460 return View::GetEventHandlerForPoint(point); | |
| 461 } | |
| 462 | |
| 463 gfx::NativeCursor ToplevelFrameView::GetCursor(const views::MouseEvent& event) { | |
| 464 switch (current_hittest_code_) { | |
| 465 case HTBOTTOM: | |
| 466 return aura::kCursorSouthResize; | |
| 467 case HTBOTTOMLEFT: | |
| 468 return aura::kCursorSouthWestResize; | |
| 469 case HTBOTTOMRIGHT: | |
| 470 return aura::kCursorSouthEastResize; | |
| 471 case HTLEFT: | |
| 472 return aura::kCursorWestResize; | |
| 473 case HTRIGHT: | |
| 474 return aura::kCursorEastResize; | |
| 475 case HTTOP: | |
| 476 return aura::kCursorNorthResize; | |
| 477 case HTTOPLEFT: | |
| 478 return aura::kCursorNorthWestResize; | |
| 479 case HTTOPRIGHT: | |
| 480 return aura::kCursorNorthEastResize; | |
| 481 default: | |
| 482 return aura::kCursorNull; | |
| 483 } | |
| 484 } | |
| 485 | |
| 486 } // namespace internal | |
| 487 } // namespace aura_shell | |
| OLD | NEW |