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/base/animation/throb_animation.h" |
| 9 #include "ui/base/resource/resource_bundle.h" |
| 10 #include "ui/gfx/canvas.h" |
| 11 #include "views/controls/button/custom_button.h" |
| 12 #include "views/widget/widget.h" |
| 13 #include "views/widget/widget_delegate.h" |
| 14 |
| 15 #if !defined(OS_WIN) |
| 16 #include "views/window/hit_test.h" |
| 17 #endif |
| 18 |
| 19 namespace aura_shell { |
| 20 namespace internal { |
| 21 |
| 22 namespace { |
| 23 // The thickness of the left, right and bottom edges of the frame. |
| 24 const int kFrameBorderThickness = 8; |
| 25 const int kFrameOpacity = 50; |
| 26 // The color used to fill the frame. |
| 27 const SkColor kFrameColor = SkColorSetARGB(kFrameOpacity, 0, 0, 0); |
| 28 const int kFrameBorderHiddenOpacity = 0; |
| 29 const int kFrameBorderVisibleOpacity = kFrameOpacity; |
| 30 // How long the hover animation takes if uninterrupted. |
| 31 const int kHoverFadeDurationMs = 250; |
| 32 } // namespace |
| 33 |
| 34 // Buttons for window controls - close, zoom, etc. |
| 35 class WindowControlButton : public views::CustomButton { |
| 36 public: |
| 37 WindowControlButton(views::ButtonListener* listener, |
| 38 SkColor color, |
| 39 const SkBitmap& icon) |
| 40 : views::CustomButton(listener), |
| 41 color_(color), |
| 42 icon_(icon) { |
| 43 } |
| 44 virtual ~WindowControlButton() {} |
| 45 |
| 46 // Overridden from views::View: |
| 47 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE { |
| 48 canvas->FillRectInt(GetBackgroundColor(), 0, 0, width(), height()); |
| 49 canvas->DrawBitmapInt(icon_, 0, 0); |
| 50 } |
| 51 virtual gfx::Size GetPreferredSize() OVERRIDE { |
| 52 gfx::Size size(icon_.width(), icon_.height()); |
| 53 size.Enlarge(3, 2); |
| 54 return size; |
| 55 } |
| 56 |
| 57 private: |
| 58 SkColor GetBackgroundColor() { |
| 59 return SkColorSetARGB(hover_animation_->CurrentValueBetween(0, 150), |
| 60 SkColorGetR(color_), |
| 61 SkColorGetG(color_), |
| 62 SkColorGetB(color_)); |
| 63 } |
| 64 |
| 65 SkColor color_; |
| 66 SkBitmap icon_; |
| 67 |
| 68 DISALLOW_COPY_AND_ASSIGN(WindowControlButton); |
| 69 }; |
| 70 |
| 71 class SizingBorder : public views::View, |
| 72 public ui::AnimationDelegate { |
| 73 public: |
| 74 SizingBorder() |
| 75 : ALLOW_THIS_IN_INITIALIZER_LIST( |
| 76 animation_(new ui::SlideAnimation(this))) { |
| 77 animation_->SetSlideDuration(kHoverFadeDurationMs); |
| 78 } |
| 79 virtual ~SizingBorder() {} |
| 80 |
| 81 void Configure(const gfx::Rect& hidden_bounds, |
| 82 const gfx::Rect& visible_bounds) { |
| 83 hidden_bounds_ = hidden_bounds; |
| 84 visible_bounds_ = visible_bounds; |
| 85 SetBoundsRect(hidden_bounds_); |
| 86 } |
| 87 |
| 88 void Show() { |
| 89 animation_->Show(); |
| 90 } |
| 91 |
| 92 void Hide() { |
| 93 animation_->Hide(); |
| 94 } |
| 95 |
| 96 bool IsShowing() const { |
| 97 return animation_->IsShowing(); |
| 98 } |
| 99 |
| 100 bool IsHiding() const { |
| 101 return animation_->IsClosing(); |
| 102 } |
| 103 |
| 104 private: |
| 105 // Overridden from views::View: |
| 106 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE { |
| 107 // Fill with current opacity value. |
| 108 int opacity = animation_->CurrentValueBetween(kFrameBorderHiddenOpacity, |
| 109 kFrameBorderVisibleOpacity); |
| 110 canvas->FillRectInt(SkColorSetARGB(opacity, |
| 111 SkColorGetR(kFrameColor), |
| 112 SkColorGetG(kFrameColor), |
| 113 SkColorGetB(kFrameColor)), |
| 114 0, 0, width(), height()); |
| 115 } |
| 116 |
| 117 // Overridden from ui::AnimationDelegate: |
| 118 void AnimationProgressed(const ui::Animation* animation) OVERRIDE { |
| 119 // TODO: update bounds. |
| 120 gfx::Rect current_bounds = animation_->CurrentValueBetween(hidden_bounds_, |
| 121 visible_bounds_); |
| 122 SetBoundsRect(current_bounds); |
| 123 SchedulePaint(); |
| 124 } |
| 125 |
| 126 // Each of these represents the hidden/visible states of the sizing border. |
| 127 // When the view is shown or hidden it animates between them. |
| 128 gfx::Rect hidden_bounds_; |
| 129 gfx::Rect visible_bounds_; |
| 130 |
| 131 scoped_ptr<ui::SlideAnimation> animation_; |
| 132 |
| 133 DISALLOW_COPY_AND_ASSIGN(SizingBorder); |
| 134 }; |
| 135 |
| 136 //////////////////////////////////////////////////////////////////////////////// |
| 137 // ToplevelFrameView, public: |
| 138 |
| 139 ToplevelFrameView::ToplevelFrameView() |
| 140 : current_hittest_code_(HTNOWHERE), |
| 141 left_edge_(new SizingBorder), |
| 142 right_edge_(new SizingBorder), |
| 143 bottom_edge_(new SizingBorder) { |
| 144 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); |
| 145 close_button_ = |
| 146 new WindowControlButton(this, SK_ColorRED, |
| 147 *rb.GetBitmapNamed(IDR_AURA_WINDOW_CLOSE_ICON)); |
| 148 zoom_button_ = |
| 149 new WindowControlButton(this, SK_ColorGREEN, |
| 150 *rb.GetBitmapNamed(IDR_AURA_WINDOW_ZOOM_ICON)); |
| 151 AddChildView(close_button_); |
| 152 AddChildView(zoom_button_); |
| 153 |
| 154 AddChildView(left_edge_); |
| 155 AddChildView(right_edge_); |
| 156 AddChildView(bottom_edge_); |
| 157 } |
| 158 |
| 159 ToplevelFrameView::~ToplevelFrameView() { |
| 160 } |
| 161 |
| 162 //////////////////////////////////////////////////////////////////////////////// |
| 163 // ToplevelFrameView, private: |
| 164 |
| 165 int ToplevelFrameView::NonClientBorderThickness() const { |
| 166 return kFrameBorderThickness; |
| 167 } |
| 168 |
| 169 int ToplevelFrameView::NonClientTopBorderHeight() const { |
| 170 return close_button_->GetPreferredSize().height(); |
| 171 } |
| 172 |
| 173 int ToplevelFrameView::NonClientHitTestImpl(const gfx::Point& point) { |
| 174 // Sanity check. |
| 175 if (!GetLocalBounds().Contains(point)) |
| 176 return HTNOWHERE; |
| 177 |
| 178 // The client view gets first crack at claiming the point. |
| 179 int frame_component = GetWidget()->client_view()->NonClientHitTest(point); |
| 180 if (frame_component != HTNOWHERE) |
| 181 return frame_component; |
| 182 |
| 183 // The window controls come second. |
| 184 if (close_button_->GetMirroredBounds().Contains(point)) |
| 185 return HTCLOSE; |
| 186 else if (zoom_button_->GetMirroredBounds().Contains(point)) |
| 187 return HTMAXBUTTON; |
| 188 |
| 189 // Finally other portions of the frame/sizing border. |
| 190 frame_component = |
| 191 GetHTComponentForFrame(point, |
| 192 NonClientBorderThickness(), |
| 193 NonClientBorderThickness(), |
| 194 NonClientBorderThickness(), |
| 195 NonClientBorderThickness(), |
| 196 GetWidget()->widget_delegate()->CanResize()); |
| 197 |
| 198 // Coerce HTCAPTION as a fallback. |
| 199 return frame_component == HTNOWHERE ? HTCAPTION : frame_component; |
| 200 } |
| 201 |
| 202 void ToplevelFrameView::ShowSizingBorder(SizingBorder* sizing_border) { |
| 203 if (sizing_border && !sizing_border->IsShowing()) |
| 204 sizing_border->Show(); |
| 205 if (left_edge_ != sizing_border && !left_edge_->IsHiding()) |
| 206 left_edge_->Hide(); |
| 207 if (right_edge_ != sizing_border && !right_edge_->IsHiding()) |
| 208 right_edge_->Hide(); |
| 209 if (bottom_edge_ != sizing_border && !bottom_edge_->IsHiding()) |
| 210 bottom_edge_->Hide(); |
| 211 } |
| 212 |
| 213 bool ToplevelFrameView::PointIsInChildView(views::View* child, |
| 214 const gfx::Point& point) const { |
| 215 gfx::Point child_point(point); |
| 216 ConvertPointToView(this, child, &child_point); |
| 217 return child->HitTest(child_point); |
| 218 } |
| 219 |
| 220 gfx::Rect ToplevelFrameView::GetHiddenBoundsForSizingBorder( |
| 221 int frame_component) const { |
| 222 int border_size = NonClientBorderThickness(); |
| 223 int caption_height = NonClientTopBorderHeight(); |
| 224 switch (frame_component) { |
| 225 case HTLEFT: |
| 226 return gfx::Rect(border_size, caption_height, border_size, |
| 227 height() - border_size - caption_height); |
| 228 case HTBOTTOM: |
| 229 return gfx::Rect(border_size, height() - 2 * border_size, |
| 230 width() - 2 * border_size, border_size); |
| 231 case HTRIGHT: |
| 232 return gfx::Rect(width() - 2 * border_size, caption_height, border_size, |
| 233 height() - border_size - caption_height); |
| 234 default: |
| 235 break; |
| 236 } |
| 237 return gfx::Rect(); |
| 238 } |
| 239 |
| 240 gfx::Rect ToplevelFrameView::GetVisibleBoundsForSizingBorder( |
| 241 int frame_component) const { |
| 242 int border_size = NonClientBorderThickness(); |
| 243 int caption_height = NonClientTopBorderHeight(); |
| 244 switch (frame_component) { |
| 245 case HTLEFT: |
| 246 return gfx::Rect(0, caption_height, border_size, |
| 247 height() - border_size - caption_height); |
| 248 case HTBOTTOM: |
| 249 return gfx::Rect(border_size, height() - border_size, |
| 250 width() - 2 * border_size, border_size); |
| 251 case HTRIGHT: |
| 252 return gfx::Rect(width() - border_size, caption_height, border_size, |
| 253 height() - border_size - caption_height); |
| 254 default: |
| 255 break; |
| 256 } |
| 257 return gfx::Rect(); |
| 258 } |
| 259 |
| 260 //////////////////////////////////////////////////////////////////////////////// |
| 261 // ToplevelFrameView, views::NonClientFrameView overrides: |
| 262 |
| 263 gfx::Rect ToplevelFrameView::GetBoundsForClientView() const { |
| 264 return client_view_bounds_; |
| 265 } |
| 266 |
| 267 gfx::Rect ToplevelFrameView::GetWindowBoundsForClientBounds( |
| 268 const gfx::Rect& client_bounds) const { |
| 269 gfx::Rect window_bounds = client_bounds; |
| 270 window_bounds.Inset(-NonClientBorderThickness(), |
| 271 -NonClientTopBorderHeight(), |
| 272 -NonClientBorderThickness(), |
| 273 -NonClientBorderThickness()); |
| 274 return window_bounds; |
| 275 } |
| 276 |
| 277 int ToplevelFrameView::NonClientHitTest(const gfx::Point& point) { |
| 278 int old_hittest_code = current_hittest_code_; |
| 279 current_hittest_code_ = NonClientHitTestImpl(point); |
| 280 return current_hittest_code_; |
| 281 } |
| 282 |
| 283 void ToplevelFrameView::GetWindowMask(const gfx::Size& size, |
| 284 gfx::Path* window_mask) { |
| 285 // Nothing. |
| 286 } |
| 287 |
| 288 void ToplevelFrameView::EnableClose(bool enable) { |
| 289 close_button_->SetEnabled(enable); |
| 290 } |
| 291 |
| 292 void ToplevelFrameView::ResetWindowControls() { |
| 293 NOTIMPLEMENTED(); |
| 294 } |
| 295 |
| 296 void ToplevelFrameView::UpdateWindowIcon() { |
| 297 NOTIMPLEMENTED(); |
| 298 } |
| 299 |
| 300 //////////////////////////////////////////////////////////////////////////////// |
| 301 // ToplevelFrameView, views::View overrides: |
| 302 |
| 303 void ToplevelFrameView::Layout() { |
| 304 client_view_bounds_ = GetLocalBounds(); |
| 305 client_view_bounds_.Inset(NonClientBorderThickness(), |
| 306 NonClientTopBorderHeight(), |
| 307 NonClientBorderThickness(), |
| 308 NonClientBorderThickness()); |
| 309 |
| 310 gfx::Size close_button_ps = close_button_->GetPreferredSize(); |
| 311 close_button_->SetBoundsRect( |
| 312 gfx::Rect(width() - close_button_ps.width() - NonClientBorderThickness(), |
| 313 0, close_button_ps.width(), close_button_ps.height())); |
| 314 |
| 315 gfx::Size zoom_button_ps = zoom_button_->GetPreferredSize(); |
| 316 zoom_button_->SetBoundsRect( |
| 317 gfx::Rect(close_button_->x() - zoom_button_ps.width(), 0, |
| 318 zoom_button_ps.width(), zoom_button_ps.height())); |
| 319 |
| 320 left_edge_->Configure(GetHiddenBoundsForSizingBorder(HTLEFT), |
| 321 GetVisibleBoundsForSizingBorder(HTLEFT)); |
| 322 right_edge_->Configure(GetHiddenBoundsForSizingBorder(HTRIGHT), |
| 323 GetVisibleBoundsForSizingBorder(HTRIGHT)); |
| 324 bottom_edge_->Configure(GetHiddenBoundsForSizingBorder(HTBOTTOM), |
| 325 GetVisibleBoundsForSizingBorder(HTBOTTOM)); |
| 326 } |
| 327 |
| 328 void ToplevelFrameView::OnPaint(gfx::Canvas* canvas) { |
| 329 gfx::Rect caption_rect(NonClientBorderThickness(), 0, |
| 330 width() - 2 * NonClientBorderThickness(), |
| 331 NonClientTopBorderHeight()); |
| 332 canvas->FillRectInt(kFrameColor, caption_rect.x(), caption_rect.y(), |
| 333 caption_rect.width(), caption_rect.height()); |
| 334 } |
| 335 |
| 336 void ToplevelFrameView::OnMouseMoved(const views::MouseEvent& event) { |
| 337 switch (current_hittest_code_) { |
| 338 case HTLEFT: |
| 339 ShowSizingBorder(left_edge_); |
| 340 break; |
| 341 case HTRIGHT: |
| 342 ShowSizingBorder(right_edge_); |
| 343 break; |
| 344 case HTBOTTOM: |
| 345 ShowSizingBorder(bottom_edge_); |
| 346 break; |
| 347 default: |
| 348 break; |
| 349 } |
| 350 } |
| 351 |
| 352 void ToplevelFrameView::OnMouseExited(const views::MouseEvent& event) { |
| 353 ShowSizingBorder(NULL); |
| 354 } |
| 355 |
| 356 views::View* ToplevelFrameView::GetEventHandlerForPoint( |
| 357 const gfx::Point& point) { |
| 358 if (PointIsInChildView(left_edge_, point) || |
| 359 PointIsInChildView(right_edge_, point) || |
| 360 PointIsInChildView(bottom_edge_, point)) { |
| 361 return this; |
| 362 } |
| 363 return View::GetEventHandlerForPoint(point); |
| 364 } |
| 365 |
| 366 |
| 367 //////////////////////////////////////////////////////////////////////////////// |
| 368 // ToplevelFrameView, views::ButtonListener implementation: |
| 369 |
| 370 void ToplevelFrameView::ButtonPressed(views::Button* sender, |
| 371 const views::Event& event) { |
| 372 if (sender == close_button_) { |
| 373 GetWidget()->Close(); |
| 374 } else if (sender == zoom_button_) { |
| 375 if (GetWidget()->IsMaximized()) |
| 376 GetWidget()->Restore(); |
| 377 else |
| 378 GetWidget()->Maximize(); |
| 379 } |
| 380 } |
| 381 |
| 382 } // namespace internal |
| 383 } // namespace aura_shell |
OLD | NEW |