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