Upstream version 5.34.104.0
[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 OnPostWindowShowTypeChange(
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   // Layout |header_view_| because layout affects the result of
361   // GetPreferredOnScreenHeight().
362   header_view_->Layout();
363
364   int onscreen_height = header_view_->GetPreferredOnScreenHeight();
365   if (onscreen_height == 0) {
366     header_view_->SetVisible(false);
367   } else {
368     int height = header_view_->GetPreferredHeight();
369     header_view_->SetBounds(0, onscreen_height - height, width(), height);
370     header_view_->SetVisible(true);
371   }
372 }
373
374 bool CustomFrameViewAsh::OverlayView::HitTestRect(const gfx::Rect& rect) const {
375   // Grab events in the header view. Return false for other events so that they
376   // can be handled by the client view.
377   return header_view_->HitTestRect(rect);
378 }
379
380 ////////////////////////////////////////////////////////////////////////////////
381 // CustomFrameViewAsh, public:
382
383 // static
384 const char CustomFrameViewAsh::kViewClassName[] = "CustomFrameViewAsh";
385
386 CustomFrameViewAsh::CustomFrameViewAsh(views::Widget* frame)
387     : frame_(frame),
388       header_view_(new HeaderView(frame)),
389       frame_border_hit_test_controller_(
390           new FrameBorderHitTestController(frame_)) {
391   // |header_view_| is set as the non client view's overlay view so that it can
392   // overlay the web contents in immersive fullscreen.
393   frame->non_client_view()->SetOverlayView(new OverlayView(header_view_));
394
395   // A delegate for a more complex way of fullscreening the window may already
396   // be set. This is the case for packaged apps.
397   wm::WindowState* window_state = wm::GetWindowState(frame->GetNativeWindow());
398   if (!window_state->HasDelegate()) {
399     window_state->SetDelegate(scoped_ptr<wm::WindowStateDelegate>(
400         new CustomFrameViewAshWindowStateDelegate(
401             window_state, this)));
402   }
403 }
404
405 CustomFrameViewAsh::~CustomFrameViewAsh() {
406 }
407
408 void CustomFrameViewAsh::InitImmersiveFullscreenControllerForView(
409     ImmersiveFullscreenController* immersive_fullscreen_controller) {
410   immersive_fullscreen_controller->Init(header_view_, frame_, header_view_);
411 }
412
413 ////////////////////////////////////////////////////////////////////////////////
414 // CustomFrameViewAsh, views::NonClientFrameView overrides:
415
416 gfx::Rect CustomFrameViewAsh::GetBoundsForClientView() const {
417   int top_height = NonClientTopBorderHeight();
418   return HeaderPainter::GetBoundsForClientView(top_height, bounds());
419 }
420
421 gfx::Rect CustomFrameViewAsh::GetWindowBoundsForClientBounds(
422     const gfx::Rect& client_bounds) const {
423   int top_height = NonClientTopBorderHeight();
424   return HeaderPainter::GetWindowBoundsForClientBounds(top_height,
425                                                        client_bounds);
426 }
427
428 int CustomFrameViewAsh::NonClientHitTest(const gfx::Point& point) {
429   return FrameBorderHitTestController::NonClientHitTest(this,
430       header_view_->header_painter(), point);
431 }
432
433 void CustomFrameViewAsh::GetWindowMask(const gfx::Size& size,
434                                        gfx::Path* window_mask) {
435   // No window masks in Aura.
436 }
437
438 void CustomFrameViewAsh::ResetWindowControls() {
439   header_view_->ResetWindowControls();
440 }
441
442 void CustomFrameViewAsh::UpdateWindowIcon() {
443 }
444
445 void CustomFrameViewAsh::UpdateWindowTitle() {
446   header_view_->SchedulePaintForTitle();
447 }
448
449 ////////////////////////////////////////////////////////////////////////////////
450 // CustomFrameViewAsh, views::View overrides:
451
452 gfx::Size CustomFrameViewAsh::GetPreferredSize() {
453   gfx::Size pref = frame_->client_view()->GetPreferredSize();
454   gfx::Rect bounds(0, 0, pref.width(), pref.height());
455   return frame_->non_client_view()->GetWindowBoundsForClientBounds(
456       bounds).size();
457 }
458
459 const char* CustomFrameViewAsh::GetClassName() const {
460   return kViewClassName;
461 }
462
463 gfx::Size CustomFrameViewAsh::GetMinimumSize() {
464   gfx::Size min_client_view_size(frame_->client_view()->GetMinimumSize());
465   return gfx::Size(
466       std::max(header_view_->GetMinimumWidth(), min_client_view_size.width()),
467       NonClientTopBorderHeight() + min_client_view_size.height());
468 }
469
470 gfx::Size CustomFrameViewAsh::GetMaximumSize() {
471   return frame_->client_view()->GetMaximumSize();
472 }
473
474 void CustomFrameViewAsh::SchedulePaintInRect(const gfx::Rect& r) {
475   // We may end up here before |header_view_| has been added to the Widget.
476   if (header_view_->GetWidget()) {
477     // The HeaderView is not a child of CustomFrameViewAsh. Redirect the paint
478     // to HeaderView instead.
479     gfx::RectF to_paint(r);
480     views::View::ConvertRectToTarget(this, header_view_, &to_paint);
481     header_view_->SchedulePaintInRect(gfx::ToEnclosingRect(to_paint));
482   } else {
483     views::NonClientFrameView::SchedulePaintInRect(r);
484   }
485 }
486
487 bool CustomFrameViewAsh::HitTestRect(const gfx::Rect& rect) const {
488   // NonClientView hit tests the NonClientFrameView first instead of going in
489   // z-order. Return false so that events get to the OverlayView.
490   return false;
491 }
492
493 views::View* CustomFrameViewAsh::GetHeaderView() {
494   return header_view_;
495 }
496
497 ////////////////////////////////////////////////////////////////////////////////
498 // CustomFrameViewAsh, private:
499
500 int CustomFrameViewAsh::NonClientTopBorderHeight() const {
501   return frame_->IsFullscreen() ? 0 : header_view_->GetPreferredHeight();
502 }
503
504 }  // namespace ash