Home | History | Annotate | Download | only in views
      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 "ui/app_list/views/app_list_view.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "base/command_line.h"
     10 #include "base/strings/string_util.h"
     11 #include "base/win/windows_version.h"
     12 #include "ui/app_list/app_list_constants.h"
     13 #include "ui/app_list/app_list_model.h"
     14 #include "ui/app_list/app_list_view_delegate.h"
     15 #include "ui/app_list/speech_ui_model.h"
     16 #include "ui/app_list/views/app_list_background.h"
     17 #include "ui/app_list/views/app_list_folder_view.h"
     18 #include "ui/app_list/views/app_list_main_view.h"
     19 #include "ui/app_list/views/app_list_view_observer.h"
     20 #include "ui/app_list/views/apps_container_view.h"
     21 #include "ui/app_list/views/contents_view.h"
     22 #include "ui/app_list/views/search_box_view.h"
     23 #include "ui/app_list/views/speech_view.h"
     24 #include "ui/base/ui_base_switches.h"
     25 #include "ui/compositor/layer.h"
     26 #include "ui/compositor/layer_animation_observer.h"
     27 #include "ui/compositor/scoped_layer_animation_settings.h"
     28 #include "ui/gfx/image/image_skia.h"
     29 #include "ui/gfx/insets.h"
     30 #include "ui/gfx/path.h"
     31 #include "ui/gfx/skia_util.h"
     32 #include "ui/views/bubble/bubble_frame_view.h"
     33 #include "ui/views/controls/textfield/textfield.h"
     34 #include "ui/views/layout/fill_layout.h"
     35 #include "ui/views/widget/widget.h"
     36 
     37 #if defined(USE_AURA)
     38 #include "ui/aura/window.h"
     39 #include "ui/aura/window_tree_host.h"
     40 #include "ui/views/bubble/bubble_window_targeter.h"
     41 #if defined(OS_WIN)
     42 #include "ui/base/win/shell.h"
     43 #endif
     44 #if !defined(OS_CHROMEOS)
     45 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
     46 #endif
     47 #endif  // defined(USE_AURA)
     48 
     49 namespace app_list {
     50 
     51 namespace {
     52 
     53 // The margin from the edge to the speech UI.
     54 const int kSpeechUIMargin = 12;
     55 
     56 // The vertical position for the appearing animation of the speech UI.
     57 const float kSpeechUIAppearingPosition = 12;
     58 
     59 // The distance between the arrow tip and edge of the anchor view.
     60 const int kArrowOffset = 10;
     61 
     62 // Determines whether the current environment supports shadows bubble borders.
     63 bool SupportsShadow() {
     64 #if defined(OS_WIN)
     65   // Shadows are not supported on Windows without Aero Glass.
     66   if (!ui::win::IsAeroGlassEnabled() ||
     67       CommandLine::ForCurrentProcess()->HasSwitch(
     68           switches::kDisableDwmComposition)) {
     69     return false;
     70   }
     71 #elif defined(OS_LINUX) && !defined(OS_CHROMEOS)
     72   // Shadows are not supported on (non-ChromeOS) Linux.
     73   return false;
     74 #endif
     75   return true;
     76 }
     77 
     78 }  // namespace
     79 
     80 // An animation observer to hide the view at the end of the animation.
     81 class HideViewAnimationObserver : public ui::ImplicitAnimationObserver {
     82  public:
     83   HideViewAnimationObserver()
     84       : frame_(NULL),
     85         target_(NULL) {
     86   }
     87 
     88   virtual ~HideViewAnimationObserver() {
     89     if (target_)
     90       StopObservingImplicitAnimations();
     91   }
     92 
     93   void SetTarget(views::View* target) {
     94     if (!target_)
     95       StopObservingImplicitAnimations();
     96     target_ = target;
     97   }
     98 
     99   void set_frame(views::BubbleFrameView* frame) { frame_ = frame; }
    100 
    101  private:
    102   // Overridden from ui::ImplicitAnimationObserver:
    103   virtual void OnImplicitAnimationsCompleted() OVERRIDE {
    104     if (target_) {
    105       target_->SetVisible(false);
    106       target_ = NULL;
    107 
    108       // Should update the background by invoking SchedulePaint().
    109       frame_->SchedulePaint();
    110     }
    111   }
    112 
    113   views::BubbleFrameView* frame_;
    114   views::View* target_;
    115 
    116   DISALLOW_COPY_AND_ASSIGN(HideViewAnimationObserver);
    117 };
    118 
    119 ////////////////////////////////////////////////////////////////////////////////
    120 // AppListView:
    121 
    122 AppListView::AppListView(AppListViewDelegate* delegate)
    123     : delegate_(delegate),
    124       app_list_main_view_(NULL),
    125       speech_view_(NULL),
    126       animation_observer_(new HideViewAnimationObserver()) {
    127   CHECK(delegate);
    128 
    129   delegate_->AddObserver(this);
    130   delegate_->GetSpeechUI()->AddObserver(this);
    131 }
    132 
    133 AppListView::~AppListView() {
    134   delegate_->GetSpeechUI()->RemoveObserver(this);
    135   delegate_->RemoveObserver(this);
    136   animation_observer_.reset();
    137   // Remove child views first to ensure no remaining dependencies on delegate_.
    138   RemoveAllChildViews(true);
    139 }
    140 
    141 void AppListView::InitAsBubbleAttachedToAnchor(
    142     gfx::NativeView parent,
    143     int initial_apps_page,
    144     views::View* anchor,
    145     const gfx::Vector2d& anchor_offset,
    146     views::BubbleBorder::Arrow arrow,
    147     bool border_accepts_events) {
    148   SetAnchorView(anchor);
    149   InitAsBubbleInternal(
    150       parent, initial_apps_page, arrow, border_accepts_events, anchor_offset);
    151 }
    152 
    153 void AppListView::InitAsBubbleAtFixedLocation(
    154     gfx::NativeView parent,
    155     int initial_apps_page,
    156     const gfx::Point& anchor_point_in_screen,
    157     views::BubbleBorder::Arrow arrow,
    158     bool border_accepts_events) {
    159   SetAnchorView(NULL);
    160   SetAnchorRect(gfx::Rect(anchor_point_in_screen, gfx::Size()));
    161   InitAsBubbleInternal(
    162       parent, initial_apps_page, arrow, border_accepts_events, gfx::Vector2d());
    163 }
    164 
    165 void AppListView::SetBubbleArrow(views::BubbleBorder::Arrow arrow) {
    166   GetBubbleFrameView()->bubble_border()->set_arrow(arrow);
    167   SizeToContents();  // Recalcuates with new border.
    168   GetBubbleFrameView()->SchedulePaint();
    169 }
    170 
    171 void AppListView::SetAnchorPoint(const gfx::Point& anchor_point) {
    172   SetAnchorRect(gfx::Rect(anchor_point, gfx::Size()));
    173 }
    174 
    175 void AppListView::SetDragAndDropHostOfCurrentAppList(
    176     ApplicationDragAndDropHost* drag_and_drop_host) {
    177   app_list_main_view_->SetDragAndDropHostOfCurrentAppList(drag_and_drop_host);
    178 }
    179 
    180 void AppListView::ShowWhenReady() {
    181   app_list_main_view_->ShowAppListWhenReady();
    182 }
    183 
    184 void AppListView::Close() {
    185   app_list_main_view_->Close();
    186   delegate_->Dismiss();
    187 }
    188 
    189 void AppListView::UpdateBounds() {
    190   SizeToContents();
    191 }
    192 
    193 bool AppListView::ShouldCenterWindow() const {
    194   return delegate_->ShouldCenterWindow();
    195 }
    196 
    197 gfx::Size AppListView::GetPreferredSize() const {
    198   return app_list_main_view_->GetPreferredSize();
    199 }
    200 
    201 void AppListView::Paint(gfx::Canvas* canvas, const views::CullSet& cull_set) {
    202   views::BubbleDelegateView::Paint(canvas, cull_set);
    203   if (!next_paint_callback_.is_null()) {
    204     next_paint_callback_.Run();
    205     next_paint_callback_.Reset();
    206   }
    207 }
    208 
    209 void AppListView::OnThemeChanged() {
    210 #if defined(OS_WIN)
    211   GetWidget()->Close();
    212 #endif
    213 }
    214 
    215 bool AppListView::ShouldHandleSystemCommands() const {
    216   return true;
    217 }
    218 
    219 void AppListView::Prerender() {
    220   app_list_main_view_->Prerender();
    221 }
    222 
    223 void AppListView::OnProfilesChanged() {
    224   app_list_main_view_->search_box_view()->InvalidateMenu();
    225 }
    226 
    227 void AppListView::SetProfileByPath(const base::FilePath& profile_path) {
    228   delegate_->SetProfileByPath(profile_path);
    229   app_list_main_view_->ModelChanged();
    230 }
    231 
    232 void AppListView::AddObserver(AppListViewObserver* observer) {
    233   observers_.AddObserver(observer);
    234 }
    235 
    236 void AppListView::RemoveObserver(AppListViewObserver* observer) {
    237   observers_.RemoveObserver(observer);
    238 }
    239 
    240 // static
    241 void AppListView::SetNextPaintCallback(const base::Closure& callback) {
    242   next_paint_callback_ = callback;
    243 }
    244 
    245 #if defined(OS_WIN)
    246 HWND AppListView::GetHWND() const {
    247   gfx::NativeWindow window =
    248       GetWidget()->GetTopLevelWidget()->GetNativeWindow();
    249   return window->GetHost()->GetAcceleratedWidget();
    250 }
    251 #endif
    252 
    253 PaginationModel* AppListView::GetAppsPaginationModel() {
    254   return app_list_main_view_->contents_view()
    255       ->apps_container_view()
    256       ->apps_grid_view()
    257       ->pagination_model();
    258 }
    259 
    260 void AppListView::InitAsBubbleInternal(gfx::NativeView parent,
    261                                        int initial_apps_page,
    262                                        views::BubbleBorder::Arrow arrow,
    263                                        bool border_accepts_events,
    264                                        const gfx::Vector2d& anchor_offset) {
    265   app_list_main_view_ =
    266       new AppListMainView(delegate_.get(), initial_apps_page, parent);
    267   AddChildView(app_list_main_view_);
    268   app_list_main_view_->SetPaintToLayer(true);
    269   app_list_main_view_->SetFillsBoundsOpaquely(false);
    270   app_list_main_view_->layer()->SetMasksToBounds(true);
    271 
    272   // Speech recognition is available only when the start page exists.
    273   if (delegate_ && delegate_->IsSpeechRecognitionEnabled()) {
    274     speech_view_ = new SpeechView(delegate_.get());
    275     speech_view_->SetVisible(false);
    276     speech_view_->SetPaintToLayer(true);
    277     speech_view_->SetFillsBoundsOpaquely(false);
    278     speech_view_->layer()->SetOpacity(0.0f);
    279     AddChildView(speech_view_);
    280   }
    281 
    282   OnProfilesChanged();
    283   set_color(kContentsBackgroundColor);
    284   set_margins(gfx::Insets());
    285   set_parent_window(parent);
    286   set_close_on_deactivate(false);
    287   set_close_on_esc(false);
    288   set_anchor_view_insets(gfx::Insets(kArrowOffset + anchor_offset.y(),
    289                                      kArrowOffset + anchor_offset.x(),
    290                                      kArrowOffset - anchor_offset.y(),
    291                                      kArrowOffset - anchor_offset.x()));
    292   set_border_accepts_events(border_accepts_events);
    293   set_shadow(SupportsShadow() ? views::BubbleBorder::BIG_SHADOW
    294                               : views::BubbleBorder::NO_SHADOW_OPAQUE_BORDER);
    295   views::BubbleDelegateView::CreateBubble(this);
    296   SetBubbleArrow(arrow);
    297 
    298 #if defined(USE_AURA)
    299   aura::Window* window = GetWidget()->GetNativeWindow();
    300   window->layer()->SetMasksToBounds(true);
    301   GetBubbleFrameView()->set_background(new AppListBackground(
    302       GetBubbleFrameView()->bubble_border()->GetBorderCornerRadius(),
    303       app_list_main_view_));
    304   set_background(NULL);
    305   window->SetEventTargeter(scoped_ptr<ui::EventTargeter>(
    306       new views::BubbleWindowTargeter(this)));
    307 #else
    308   set_background(new AppListBackground(
    309       GetBubbleFrameView()->bubble_border()->GetBorderCornerRadius(),
    310       app_list_main_view_));
    311 
    312   // On non-aura the bubble has two widgets, and it's possible for the border
    313   // to be shown independently in odd situations. Explicitly hide the bubble
    314   // widget to ensure that any WM_WINDOWPOSCHANGED messages triggered by the
    315   // window manager do not have the SWP_SHOWWINDOW flag set which would cause
    316   // the border to be shown. See http://crbug.com/231687 .
    317   GetWidget()->Hide();
    318 #endif
    319 
    320   if (delegate_)
    321     delegate_->ViewInitialized();
    322 }
    323 
    324 void AppListView::OnBeforeBubbleWidgetInit(
    325     views::Widget::InitParams* params,
    326     views::Widget* widget) const {
    327 #if defined(USE_AURA) && !defined(OS_CHROMEOS)
    328   if (delegate_ && delegate_->ForceNativeDesktop())
    329     params->native_widget = new views::DesktopNativeWidgetAura(widget);
    330 #endif
    331 #if defined(OS_WIN)
    332   // Windows 7 and higher offer pinning to the taskbar, but we need presence
    333   // on the taskbar for the user to be able to pin us. So, show the window on
    334   // the taskbar for these versions of Windows.
    335   if (base::win::GetVersion() >= base::win::VERSION_WIN7)
    336     params->force_show_in_taskbar = true;
    337 #elif defined(OS_LINUX)
    338   // Set up a custom WM_CLASS for the app launcher window. This allows task
    339   // switchers in X11 environments to distinguish it from main browser windows.
    340   params->wm_class_name = kAppListWMClass;
    341   // Show the window in the taskbar, even though it is a bubble, which would not
    342   // normally be shown.
    343   params->force_show_in_taskbar = true;
    344 #endif
    345 }
    346 
    347 views::View* AppListView::GetInitiallyFocusedView() {
    348   return app_list_main_view_->search_box_view()->search_box();
    349 }
    350 
    351 gfx::ImageSkia AppListView::GetWindowIcon() {
    352   if (delegate_)
    353     return delegate_->GetWindowIcon();
    354 
    355   return gfx::ImageSkia();
    356 }
    357 
    358 bool AppListView::WidgetHasHitTestMask() const {
    359   return true;
    360 }
    361 
    362 void AppListView::GetWidgetHitTestMask(gfx::Path* mask) const {
    363   DCHECK(mask);
    364   mask->addRect(gfx::RectToSkRect(
    365       GetBubbleFrameView()->GetContentsBounds()));
    366 }
    367 
    368 bool AppListView::AcceleratorPressed(const ui::Accelerator& accelerator) {
    369   // The accelerator is added by BubbleDelegateView.
    370   if (accelerator.key_code() == ui::VKEY_ESCAPE) {
    371     if (app_list_main_view_->search_box_view()->HasSearch()) {
    372       app_list_main_view_->search_box_view()->ClearSearch();
    373     } else if (app_list_main_view_->contents_view()
    374                    ->apps_container_view()
    375                    ->IsInFolderView()) {
    376       app_list_main_view_->contents_view()
    377           ->apps_container_view()
    378           ->app_list_folder_view()
    379           ->CloseFolderPage();
    380       return true;
    381     } else {
    382       GetWidget()->Deactivate();
    383       Close();
    384     }
    385     return true;
    386   }
    387 
    388   return false;
    389 }
    390 
    391 void AppListView::Layout() {
    392   const gfx::Rect contents_bounds = GetContentsBounds();
    393   app_list_main_view_->SetBoundsRect(contents_bounds);
    394 
    395   if (speech_view_) {
    396     gfx::Rect speech_bounds = contents_bounds;
    397     int preferred_height = speech_view_->GetPreferredSize().height();
    398     speech_bounds.Inset(kSpeechUIMargin, kSpeechUIMargin);
    399     speech_bounds.set_height(std::min(speech_bounds.height(),
    400                                       preferred_height));
    401     speech_bounds.Inset(-speech_view_->GetInsets());
    402     speech_view_->SetBoundsRect(speech_bounds);
    403   }
    404 }
    405 
    406 void AppListView::SchedulePaintInRect(const gfx::Rect& rect) {
    407   BubbleDelegateView::SchedulePaintInRect(rect);
    408   if (GetBubbleFrameView())
    409     GetBubbleFrameView()->SchedulePaint();
    410 }
    411 
    412 void AppListView::OnWidgetDestroying(views::Widget* widget) {
    413   BubbleDelegateView::OnWidgetDestroying(widget);
    414   if (delegate_ && widget == GetWidget())
    415     delegate_->ViewClosing();
    416 }
    417 
    418 void AppListView::OnWidgetActivationChanged(views::Widget* widget,
    419                                             bool active) {
    420   // Do not called inherited function as the bubble delegate auto close
    421   // functionality is not used.
    422   if (widget == GetWidget())
    423     FOR_EACH_OBSERVER(AppListViewObserver, observers_,
    424                       OnActivationChanged(widget, active));
    425 }
    426 
    427 void AppListView::OnWidgetVisibilityChanged(views::Widget* widget,
    428                                             bool visible) {
    429   BubbleDelegateView::OnWidgetVisibilityChanged(widget, visible);
    430 
    431   if (widget != GetWidget())
    432     return;
    433 
    434   if (!visible)
    435     app_list_main_view_->ResetForShow();
    436 }
    437 
    438 void AppListView::OnSpeechRecognitionStateChanged(
    439     SpeechRecognitionState new_state) {
    440   if (!speech_view_)
    441     return;
    442 
    443   bool will_appear = (new_state == SPEECH_RECOGNITION_RECOGNIZING ||
    444                       new_state == SPEECH_RECOGNITION_IN_SPEECH ||
    445                       new_state == SPEECH_RECOGNITION_NETWORK_ERROR);
    446   // No change for this class.
    447   if (speech_view_->visible() == will_appear)
    448     return;
    449 
    450   if (will_appear)
    451     speech_view_->Reset();
    452 
    453   animation_observer_->set_frame(GetBubbleFrameView());
    454   gfx::Transform speech_transform;
    455   speech_transform.Translate(
    456       0, SkFloatToMScalar(kSpeechUIAppearingPosition));
    457   if (will_appear)
    458     speech_view_->layer()->SetTransform(speech_transform);
    459 
    460   {
    461     ui::ScopedLayerAnimationSettings main_settings(
    462         app_list_main_view_->layer()->GetAnimator());
    463     if (will_appear) {
    464       animation_observer_->SetTarget(app_list_main_view_);
    465       main_settings.AddObserver(animation_observer_.get());
    466     }
    467     app_list_main_view_->layer()->SetOpacity(will_appear ? 0.0f : 1.0f);
    468   }
    469 
    470   {
    471     ui::ScopedLayerAnimationSettings speech_settings(
    472         speech_view_->layer()->GetAnimator());
    473     if (!will_appear) {
    474       animation_observer_->SetTarget(speech_view_);
    475       speech_settings.AddObserver(animation_observer_.get());
    476     }
    477 
    478     speech_view_->layer()->SetOpacity(will_appear ? 1.0f : 0.0f);
    479     if (will_appear)
    480       speech_view_->layer()->SetTransform(gfx::Transform());
    481     else
    482       speech_view_->layer()->SetTransform(speech_transform);
    483   }
    484 
    485   if (will_appear)
    486     speech_view_->SetVisible(true);
    487   else
    488     app_list_main_view_->SetVisible(true);
    489 }
    490 
    491 }  // namespace app_list
    492