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 |