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