50e9e2365ca922ade58056ce038a2dd75700bc15
[platform/framework/web/crosswalk.git] / src / ash / wm / custom_frame_view_ash.cc
1 // Copyright (c) 2012 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/wm/custom_frame_view_ash.h"
6
7 #include "ash/ash_switches.h"
8 #include "ash/wm/caption_buttons/frame_caption_button_container_view.h"
9 #include "ash/wm/caption_buttons/frame_maximize_button.h"
10 #include "ash/wm/caption_buttons/frame_maximize_button_observer.h"
11 #include "ash/wm/frame_border_hit_test_controller.h"
12 #include "ash/wm/header_painter.h"
13 #include "ash/wm/immersive_fullscreen_controller.h"
14 #include "ash/wm/window_state.h"
15 #include "ash/wm/window_state_delegate.h"
16 #include "ash/wm/window_state_observer.h"
17 #include "base/command_line.h"
18 #include "base/debug/leak_annotations.h"
19 #include "grit/ash_resources.h"
20 #include "ui/aura/client/aura_constants.h"
21 #include "ui/aura/window.h"
22 #include "ui/aura/window_observer.h"
23 #include "ui/gfx/canvas.h"
24 #include "ui/gfx/font_list.h"
25 #include "ui/gfx/rect.h"
26 #include "ui/gfx/rect_conversions.h"
27 #include "ui/gfx/size.h"
28 #include "ui/views/view.h"
29 #include "ui/views/widget/native_widget_aura.h"
30 #include "ui/views/widget/widget.h"
31 #include "ui/views/widget/widget_delegate.h"
32 #include "ui/views/widget/widget_deletion_observer.h"
33
34 namespace {
35
36 const gfx::FontList& GetTitleFontList() {
37   static const gfx::FontList* title_font_list =
38       new gfx::FontList(views::NativeWidgetAura::GetWindowTitleFontList());
39   ANNOTATE_LEAKING_OBJECT_PTR(title_font_list);
40   return *title_font_list;
41 }
42
43 ///////////////////////////////////////////////////////////////////////////////
44 // CustomFrameViewAshWindowStateDelegate
45
46 // Handles a user's fullscreen request (Shift+F4/F4). Puts the window into
47 // immersive fullscreen if immersive fullscreen is enabled for non-browser
48 // windows.
49 class CustomFrameViewAshWindowStateDelegate
50     : public ash::wm::WindowStateDelegate,
51       public ash::wm::WindowStateObserver,
52       public aura::WindowObserver {
53  public:
54   CustomFrameViewAshWindowStateDelegate(
55       ash::wm::WindowState* window_state,
56       ash::CustomFrameViewAsh* custom_frame_view)
57       : window_state_(NULL) {
58 #if defined(OS_CHROMEOS)
59     // TODO(pkotwicz): Investigate if immersive fullscreen can be enabled for
60     // Windows Ash.
61     if (ash::switches::UseImmersiveFullscreenForAllWindows()) {
62       immersive_fullscreen_controller_.reset(
63           new ash::ImmersiveFullscreenController);
64       custom_frame_view->InitImmersiveFullscreenControllerForView(
65           immersive_fullscreen_controller_.get());
66
67       // Add a window state observer to exit fullscreen properly in case
68       // fullscreen is exited without going through
69       // WindowState::ToggleFullscreen(). This is the case when exiting
70       // immersive fullscreen via the "Restore" window control.
71       // TODO(pkotwicz): This is a hack. Remove ASAP. http://crbug.com/319048
72       window_state_ = window_state;
73       window_state_->AddObserver(this);
74       window_state_->window()->AddObserver(this);
75     }
76 #endif
77   }
78   virtual ~CustomFrameViewAshWindowStateDelegate() {
79     if (window_state_) {
80       window_state_->RemoveObserver(this);
81       window_state_->window()->RemoveObserver(this);
82     }
83   }
84  private:
85   // Overridden from ash::wm::WindowStateDelegate:
86   virtual bool ToggleFullscreen(ash::wm::WindowState* window_state) OVERRIDE {
87     bool enter_fullscreen = !window_state->IsFullscreen();
88     if (enter_fullscreen) {
89       window_state->window()->SetProperty(aura::client::kShowStateKey,
90                                            ui::SHOW_STATE_FULLSCREEN);
91     } else {
92       window_state->Restore();
93     }
94     if (immersive_fullscreen_controller_) {
95       immersive_fullscreen_controller_->SetEnabled(
96           ash::ImmersiveFullscreenController::WINDOW_TYPE_OTHER,
97           enter_fullscreen);
98     }
99     return true;
100   }
101   // Overridden from aura::WindowObserver:
102   virtual void OnWindowDestroying(aura::Window* window) OVERRIDE {
103     window_state_->RemoveObserver(this);
104     window_state_->window()->RemoveObserver(this);
105     window_state_ = NULL;
106   }
107   // Overridden from ash::wm::WindowStateObserver:
108   virtual void OnWindowShowTypeChanged(
109       ash::wm::WindowState* window_state,
110       ash::wm::WindowShowType old_type) OVERRIDE {
111     if (!window_state->IsFullscreen() &&
112         !window_state->IsMinimized() &&
113         immersive_fullscreen_controller_.get() &&
114         immersive_fullscreen_controller_->IsEnabled()) {
115       immersive_fullscreen_controller_->SetEnabled(
116           ash::ImmersiveFullscreenController::WINDOW_TYPE_OTHER,
117           false);
118     }
119   }
120
121   ash::wm::WindowState* window_state_;
122   scoped_ptr<ash::ImmersiveFullscreenController>
123       immersive_fullscreen_controller_;
124
125   DISALLOW_COPY_AND_ASSIGN(CustomFrameViewAshWindowStateDelegate);
126 };
127
128 }  // namespace
129
130 namespace ash {
131
132 ///////////////////////////////////////////////////////////////////////////////
133 // CustomFrameViewAsh::HeaderView
134
135 // View which paints the header. It slides off and on screen in immersive
136 // fullscreen.
137 class CustomFrameViewAsh::HeaderView
138     : public views::View,
139       public ImmersiveFullscreenController::Delegate,
140       public FrameMaximizeButtonObserver {
141  public:
142   // |frame| is the widget that the caption buttons act on.
143   explicit HeaderView(views::Widget* frame);
144   virtual ~HeaderView();
145
146   // Schedules a repaint for the entire title.
147   void SchedulePaintForTitle();
148
149   // Tells the window controls to reset themselves to the normal state.
150   void ResetWindowControls();
151
152   // Returns the amount of the view's pixels which should be on screen.
153   int GetPreferredOnScreenHeight() const;
154
155   // Returns the view's preferred height.
156   int GetPreferredHeight() const;
157
158   // Returns the view's minimum width.
159   int GetMinimumWidth() const;
160
161   // views::View overrides:
162   virtual void Layout() OVERRIDE;
163   virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
164
165   HeaderPainter* header_painter() {
166     return header_painter_.get();
167   }
168
169  private:
170   // ImmersiveFullscreenController::Delegate overrides:
171   virtual void OnImmersiveRevealStarted() OVERRIDE;
172   virtual void OnImmersiveRevealEnded() OVERRIDE;
173   virtual void OnImmersiveFullscreenExited() OVERRIDE;
174   virtual void SetVisibleFraction(double visible_fraction) OVERRIDE;
175   virtual std::vector<gfx::Rect> GetVisibleBoundsInScreen() const OVERRIDE;
176
177   // FrameMaximizeButtonObserver overrides:
178   virtual void OnMaximizeBubbleShown(views::Widget* bubble) OVERRIDE;
179
180   // The widget that the caption buttons act on.
181   views::Widget* frame_;
182
183   // Helper for painting the header.
184   scoped_ptr<HeaderPainter> header_painter_;
185
186   // View which contains the window caption buttons.
187   FrameCaptionButtonContainerView* caption_button_container_;
188
189   // The maximize bubble widget. |maximize_bubble_| may be non-NULL but have
190   // been already destroyed.
191   views::Widget* maximize_bubble_;
192
193   // Keeps track of whether |maximize_bubble_| is still alive.
194   scoped_ptr<views::WidgetDeletionObserver> maximize_bubble_lifetime_observer_;
195
196   // The fraction of the header's height which is visible while in fullscreen.
197   // This value is meaningless when not in fullscreen.
198   double fullscreen_visible_fraction_;
199
200   DISALLOW_COPY_AND_ASSIGN(HeaderView);
201 };
202
203 CustomFrameViewAsh::HeaderView::HeaderView(views::Widget* frame)
204     : frame_(frame),
205       header_painter_(new ash::HeaderPainter),
206       caption_button_container_(NULL),
207       maximize_bubble_(NULL),
208       fullscreen_visible_fraction_(0) {
209   // Unfortunately, there is no views::WidgetDelegate::CanMinimize(). Assume
210   // that the window frame can be minimized if it can be maximized.
211   FrameCaptionButtonContainerView::MinimizeAllowed minimize_allowed =
212       frame_->widget_delegate()->CanMaximize() ?
213           FrameCaptionButtonContainerView::MINIMIZE_ALLOWED :
214           FrameCaptionButtonContainerView::MINIMIZE_DISALLOWED;
215   caption_button_container_ = new FrameCaptionButtonContainerView(frame_,
216       minimize_allowed);
217   AddChildView(caption_button_container_);
218   FrameMaximizeButton* frame_maximize_button =
219       caption_button_container_->GetOldStyleSizeButton();
220   if (frame_maximize_button)
221     frame_maximize_button->AddObserver(this);
222
223   header_painter_->Init(frame_, this, NULL, caption_button_container_);
224 }
225
226 CustomFrameViewAsh::HeaderView::~HeaderView() {
227   FrameMaximizeButton* frame_maximize_button =
228       caption_button_container_->GetOldStyleSizeButton();
229   if (frame_maximize_button)
230     frame_maximize_button->RemoveObserver(this);
231 }
232
233 void CustomFrameViewAsh::HeaderView::SchedulePaintForTitle() {
234   header_painter_->SchedulePaintForTitle(GetTitleFontList());
235 }
236
237 void CustomFrameViewAsh::HeaderView::ResetWindowControls() {
238   caption_button_container_->ResetWindowControls();
239 }
240
241 int CustomFrameViewAsh::HeaderView::GetPreferredOnScreenHeight() const {
242   if (frame_->IsFullscreen()) {
243     return static_cast<int>(
244         GetPreferredHeight() * fullscreen_visible_fraction_);
245   }
246   return GetPreferredHeight();
247 }
248
249 int CustomFrameViewAsh::HeaderView::GetPreferredHeight() const {
250   // Reserve enough space to see the buttons and the separator line.
251   return caption_button_container_->bounds().bottom() +
252       header_painter_->HeaderContentSeparatorSize();
253 }
254
255 int CustomFrameViewAsh::HeaderView::GetMinimumWidth() const {
256   return header_painter_->GetMinimumHeaderWidth();
257 }
258
259 void CustomFrameViewAsh::HeaderView::Layout() {
260   header_painter_->LayoutHeader(true);
261   header_painter_->set_header_height(GetPreferredHeight());
262 }
263
264 void CustomFrameViewAsh::HeaderView::OnPaint(gfx::Canvas* canvas) {
265   int theme_image_id = 0;
266   if (frame_->IsMaximized() || frame_->IsFullscreen())
267     theme_image_id = IDR_AURA_WINDOW_HEADER_BASE_MINIMAL;
268   else if (frame_->non_client_view()->frame_view()->ShouldPaintAsActive())
269     theme_image_id = IDR_AURA_WINDOW_HEADER_BASE_ACTIVE;
270   else
271     theme_image_id = IDR_AURA_WINDOW_HEADER_BASE_INACTIVE;
272
273   header_painter_->PaintHeader(
274       canvas,
275       theme_image_id,
276       0);
277   header_painter_->PaintTitleBar(canvas, GetTitleFontList());
278   header_painter_->PaintHeaderContentSeparator(canvas);
279 }
280
281 void CustomFrameViewAsh::HeaderView::OnImmersiveRevealStarted() {
282   fullscreen_visible_fraction_ = 0;
283   SetPaintToLayer(true);
284   parent()->Layout();
285 }
286
287 void CustomFrameViewAsh::HeaderView::OnImmersiveRevealEnded() {
288   fullscreen_visible_fraction_ = 0;
289   SetPaintToLayer(false);
290   parent()->Layout();
291 }
292
293 void CustomFrameViewAsh::HeaderView::OnImmersiveFullscreenExited() {
294   fullscreen_visible_fraction_ = 0;
295   SetPaintToLayer(false);
296   parent()->Layout();
297 }
298
299 void CustomFrameViewAsh::HeaderView::SetVisibleFraction(
300     double visible_fraction) {
301   if (fullscreen_visible_fraction_ != visible_fraction) {
302     fullscreen_visible_fraction_ = visible_fraction;
303     parent()->Layout();
304   }
305 }
306
307 std::vector<gfx::Rect>
308 CustomFrameViewAsh::HeaderView::GetVisibleBoundsInScreen() const {
309   // TODO(pkotwicz): Implement views::View::ConvertRectToScreen().
310   gfx::Rect visible_bounds(GetVisibleBounds());
311   gfx::Point visible_origin_in_screen(visible_bounds.origin());
312   views::View::ConvertPointToScreen(this, &visible_origin_in_screen);
313   std::vector<gfx::Rect> bounds_in_screen;
314   bounds_in_screen.push_back(
315       gfx::Rect(visible_origin_in_screen, visible_bounds.size()));
316   if (maximize_bubble_lifetime_observer_.get() &&
317       maximize_bubble_lifetime_observer_->IsWidgetAlive()) {
318     bounds_in_screen.push_back(maximize_bubble_->GetWindowBoundsInScreen());
319   }
320   return bounds_in_screen;
321 }
322
323 void CustomFrameViewAsh::HeaderView::OnMaximizeBubbleShown(
324     views::Widget* bubble) {
325   maximize_bubble_ = bubble;
326   maximize_bubble_lifetime_observer_.reset(
327       new views::WidgetDeletionObserver(bubble));
328 }
329
330 ///////////////////////////////////////////////////////////////////////////////
331 // CustomFrameViewAsh::OverlayView
332
333 // View which takes up the entire widget and contains the HeaderView. HeaderView
334 // is a child of OverlayView to avoid creating a larger texture than necessary
335 // when painting the HeaderView to its own layer.
336 class CustomFrameViewAsh::OverlayView : public views::View {
337  public:
338   explicit OverlayView(HeaderView* header_view);
339   virtual ~OverlayView();
340
341   // views::View override:
342   virtual void Layout() OVERRIDE;
343   virtual bool HitTestRect(const gfx::Rect& rect) const OVERRIDE;
344
345  private:
346   HeaderView* header_view_;
347
348   DISALLOW_COPY_AND_ASSIGN(OverlayView);
349 };
350
351 CustomFrameViewAsh::OverlayView::OverlayView(HeaderView* header_view)
352     : header_view_(header_view) {
353   AddChildView(header_view);
354 }
355
356 CustomFrameViewAsh::OverlayView::~OverlayView() {
357 }
358
359 void CustomFrameViewAsh::OverlayView::Layout() {
360   int onscreen_height = header_view_->GetPreferredOnScreenHeight();
361   if (onscreen_height == 0) {
362     header_view_->SetVisible(false);
363   } else {
364     int height = header_view_->GetPreferredHeight();
365     header_view_->SetBounds(0, onscreen_height - height, width(), height);
366     header_view_->SetVisible(true);
367   }
368 }
369
370 bool CustomFrameViewAsh::OverlayView::HitTestRect(const gfx::Rect& rect) const {
371   // Grab events in the header view. Return false for other events so that they
372   // can be handled by the client view.
373   return header_view_->HitTestRect(rect);
374 }
375
376 ////////////////////////////////////////////////////////////////////////////////
377 // CustomFrameViewAsh, public:
378
379 // static
380 const char CustomFrameViewAsh::kViewClassName[] = "CustomFrameViewAsh";
381
382 CustomFrameViewAsh::CustomFrameViewAsh(views::Widget* frame)
383     : frame_(frame),
384       header_view_(new HeaderView(frame)),
385       frame_border_hit_test_controller_(
386           new FrameBorderHitTestController(frame_)) {
387   // |header_view_| is set as the non client view's overlay view so that it can
388   // overlay the web contents in immersive fullscreen.
389   frame->non_client_view()->SetOverlayView(new OverlayView(header_view_));
390
391   // A delegate for a more complex way of fullscreening the window may already
392   // be set. This is the case for packaged apps.
393   wm::WindowState* window_state = wm::GetWindowState(frame->GetNativeWindow());
394   if (!window_state->HasDelegate()) {
395     window_state->SetDelegate(scoped_ptr<wm::WindowStateDelegate>(
396         new CustomFrameViewAshWindowStateDelegate(
397             window_state, this)).Pass());
398   }
399 }
400
401 CustomFrameViewAsh::~CustomFrameViewAsh() {
402 }
403
404 void CustomFrameViewAsh::InitImmersiveFullscreenControllerForView(
405     ImmersiveFullscreenController* immersive_fullscreen_controller) {
406   immersive_fullscreen_controller->Init(header_view_, frame_, header_view_);
407 }
408
409 ////////////////////////////////////////////////////////////////////////////////
410 // CustomFrameViewAsh, views::NonClientFrameView overrides:
411
412 gfx::Rect CustomFrameViewAsh::GetBoundsForClientView() const {
413   int top_height = NonClientTopBorderHeight();
414   return HeaderPainter::GetBoundsForClientView(top_height, bounds());
415 }
416
417 gfx::Rect CustomFrameViewAsh::GetWindowBoundsForClientBounds(
418     const gfx::Rect& client_bounds) const {
419   int top_height = NonClientTopBorderHeight();
420   return HeaderPainter::GetWindowBoundsForClientBounds(top_height,
421                                                        client_bounds);
422 }
423
424 int CustomFrameViewAsh::NonClientHitTest(const gfx::Point& point) {
425   return FrameBorderHitTestController::NonClientHitTest(this,
426       header_view_->header_painter(), point);
427 }
428
429 void CustomFrameViewAsh::GetWindowMask(const gfx::Size& size,
430                                        gfx::Path* window_mask) {
431   // No window masks in Aura.
432 }
433
434 void CustomFrameViewAsh::ResetWindowControls() {
435   header_view_->ResetWindowControls();
436 }
437
438 void CustomFrameViewAsh::UpdateWindowIcon() {
439 }
440
441 void CustomFrameViewAsh::UpdateWindowTitle() {
442   header_view_->SchedulePaintForTitle();
443 }
444
445 ////////////////////////////////////////////////////////////////////////////////
446 // CustomFrameViewAsh, views::View overrides:
447
448 gfx::Size CustomFrameViewAsh::GetPreferredSize() {
449   gfx::Size pref = frame_->client_view()->GetPreferredSize();
450   gfx::Rect bounds(0, 0, pref.width(), pref.height());
451   return frame_->non_client_view()->GetWindowBoundsForClientBounds(
452       bounds).size();
453 }
454
455 const char* CustomFrameViewAsh::GetClassName() const {
456   return kViewClassName;
457 }
458
459 gfx::Size CustomFrameViewAsh::GetMinimumSize() {
460   gfx::Size min_client_view_size(frame_->client_view()->GetMinimumSize());
461   return gfx::Size(
462       std::max(header_view_->GetMinimumWidth(), min_client_view_size.width()),
463       NonClientTopBorderHeight() + min_client_view_size.height());
464 }
465
466 gfx::Size CustomFrameViewAsh::GetMaximumSize() {
467   return frame_->client_view()->GetMaximumSize();
468 }
469
470 void CustomFrameViewAsh::SchedulePaintInRect(const gfx::Rect& r) {
471   // The HeaderView is not a child of CustomFrameViewAsh. Redirect the paint to
472   // HeaderView instead.
473   gfx::RectF to_paint(r);
474   views::View::ConvertRectToTarget(this, header_view_, &to_paint);
475   header_view_->SchedulePaintInRect(gfx::ToEnclosingRect(to_paint));
476 }
477
478 bool CustomFrameViewAsh::HitTestRect(const gfx::Rect& rect) const {
479   // NonClientView hit tests the NonClientFrameView first instead of going in
480   // z-order. Return false so that events get to the OverlayView.
481   return false;
482 }
483
484 views::View* CustomFrameViewAsh::GetHeaderView() {
485   return header_view_;
486 }
487
488 ////////////////////////////////////////////////////////////////////////////////
489 // CustomFrameViewAsh, private:
490
491 int CustomFrameViewAsh::NonClientTopBorderHeight() const {
492   return frame_->IsFullscreen() ? 0 : header_view_->GetPreferredHeight();
493 }
494
495 }  // namespace ash