From 8cd42c68a9c68fe579a8bf794f7502b1fe3021f8 Mon Sep 17 00:00:00 2001 From: mgiuca Date: Tue, 25 Nov 2014 00:27:51 -0800 Subject: [PATCH] App list start page view: Clicking the custom page now opens it up. Added an invisible widget over the custom page, to block all mouse events that would be received by the custom page. The widget has a button in it, so when it is clicked, we open up the custom page. BUG=434624 Review URL: https://codereview.chromium.org/745513002 Cr-Commit-Position: refs/heads/master@{#305602} --- ui/app_list/views/app_list_main_view.cc | 75 +++++++++++++++++++++++++++++++-- ui/app_list/views/app_list_main_view.h | 13 ++++++ ui/app_list/views/app_list_view.cc | 5 +++ ui/app_list/views/contents_view.cc | 18 +++++++- ui/app_list/views/contents_view.h | 3 ++ ui/app_list/views/start_page_view.cc | 20 +++++++++ ui/app_list/views/start_page_view.h | 7 +++ 7 files changed, 137 insertions(+), 4 deletions(-) diff --git a/ui/app_list/views/app_list_main_view.cc b/ui/app_list/views/app_list_main_view.cc index e93422884936..fbcfb44068f2 100644 --- a/ui/app_list/views/app_list_main_view.cc +++ b/ui/app_list/views/app_list_main_view.cc @@ -27,6 +27,8 @@ #include "ui/app_list/views/contents_view.h" #include "ui/app_list/views/search_box_view.h" #include "ui/views/border.h" +#include "ui/views/controls/button/button.h" +#include "ui/views/controls/button/custom_button.h" #include "ui/views/controls/textfield/textfield.h" #include "ui/views/layout/box_layout.h" #include "ui/views/layout/fill_layout.h" @@ -39,6 +41,35 @@ namespace { // The maximum allowed time to wait for icon loading in milliseconds. const int kMaxIconLoadingWaitTimeInMs = 50; +// Button for the custom page click zone. Receives click events when the user +// clicks on the custom page, and in response, switches to the custom page. +class CustomPageButton : public views::CustomButton, + public views::ButtonListener { + public: + explicit CustomPageButton(AppListMainView* app_list_main_view); + + // ButtonListener overrides: + void ButtonPressed(views::Button* sender, const ui::Event& event) override; + + private: + AppListMainView* app_list_main_view_; + + DISALLOW_COPY_AND_ASSIGN(CustomPageButton); +}; + +CustomPageButton::CustomPageButton(AppListMainView* app_list_main_view) + : views::CustomButton(this), app_list_main_view_(app_list_main_view) { +} + +void CustomPageButton::ButtonPressed(views::Button* sender, + const ui::Event& event) { + // Switch to the custom page. + ContentsView* contents_view = app_list_main_view_->contents_view(); + int custom_page_index = contents_view->GetPageIndexForState( + AppListModel::STATE_CUSTOM_LAUNCHER_PAGE); + contents_view->SetActivePage(custom_page_index); +} + } // namespace //////////////////////////////////////////////////////////////////////////////// @@ -78,9 +109,10 @@ class AppListMainView::IconLoader : public AppListItemObserver { AppListMainView::AppListMainView(AppListViewDelegate* delegate) : delegate_(delegate), model_(delegate->GetModel()), - search_box_view_(NULL), - contents_view_(NULL), - contents_switcher_view_(NULL), + search_box_view_(nullptr), + contents_view_(nullptr), + contents_switcher_view_(nullptr), + custom_page_clickzone_(nullptr), weak_ptr_factory_(this) { SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0)); } @@ -191,6 +223,17 @@ void AppListMainView::OnStartPageSearchTextfieldChanged( search_box_view_->search_box()->RequestFocus(); } +views::Widget* AppListMainView::GetCustomPageClickzone() const { + // During shutdown, the widgets may be deleted, which means + // |custom_page_clickzone_| will be a dangling pointer. Therefore, always + // check that the main app list widget (its parent) is still alive before + // returning the pointer. + if (!GetWidget()) + return nullptr; + + return custom_page_clickzone_; +} + void AppListMainView::SetDragAndDropHostOfCurrentAppList( ApplicationDragAndDropHost* drag_and_drop_host) { contents_view_->SetDragAndDropHostOfCurrentAppList(drag_and_drop_host); @@ -260,6 +303,32 @@ void AppListMainView::NotifySearchBoxVisibilityChanged() { parent()->SchedulePaint(); } +void AppListMainView::InitWidgets() { + // The widget that receives click events to transition to the custom page. + views::Widget::InitParams custom_page_clickzone_params( + views::Widget::InitParams::TYPE_CONTROL); + + custom_page_clickzone_params.parent = GetWidget()->GetNativeView(); + custom_page_clickzone_params.layer_type = aura::WINDOW_LAYER_NOT_DRAWN; + + gfx::Rect custom_page_bounds = contents_view_->GetCustomPageCollapsedBounds(); + custom_page_bounds.Intersect(contents_view_->bounds()); + custom_page_bounds = contents_view_->ConvertRectToWidget(custom_page_bounds); + custom_page_clickzone_params.bounds = custom_page_bounds; + + // Create a widget for the custom page click zone. This widget masks click + // events from the WebContents that rests underneath it. (It has to be a + // widget, not an ordinary view, so that it can be placed in front of the + // WebContents.) + custom_page_clickzone_ = new views::Widget; + custom_page_clickzone_->Init(custom_page_clickzone_params); + custom_page_clickzone_->SetContentsView(new CustomPageButton(this)); + // The widget is shown by default. If the ContentsView determines that we do + // not need a clickzone upon startup, hide it. + if (!contents_view_->ShouldShowCustomPageClickzone()) + custom_page_clickzone_->Hide(); +} + void AppListMainView::ActivateApp(AppListItem* item, int event_flags) { // TODO(jennyz): Activate the folder via AppListModel notification. if (item->GetItemType() == AppListFolderItem::kItemType) diff --git a/ui/app_list/views/app_list_main_view.h b/ui/app_list/views/app_list_main_view.h index be4e502a658f..db54f5511220 100644 --- a/ui/app_list/views/app_list_main_view.h +++ b/ui/app_list/views/app_list_main_view.h @@ -61,6 +61,11 @@ class APP_LIST_EXPORT AppListMainView : public views::View, SearchBoxView* search_box_view() const { return search_box_view_; } + // Gets the invisible widget that sits partly over the bottom of the app list, + // covering the collapsed-state custom page. May return nullptr, if the widget + // has not been initialized or has already been destroyed. + views::Widget* GetCustomPageClickzone() const; + // If |drag_and_drop_host| is not NULL it will be called upon drag and drop // operations outside the application list. void SetDragAndDropHostOfCurrentAppList( @@ -79,6 +84,9 @@ class APP_LIST_EXPORT AppListMainView : public views::View, // Called when the search box's visibility is changed. void NotifySearchBoxVisibilityChanged(); + // Initialize widgets that live inside the app list's main widget. + void InitWidgets(); + private: class IconLoader; @@ -123,6 +131,11 @@ class APP_LIST_EXPORT AppListMainView : public views::View, // Owned by views hierarchy. NULL in the non-experimental app list. ContentsSwitcherView* contents_switcher_view_; + // Invisible widget that sits partly over the bottom of the app list, covering + // the collapsed-state custom page, and intercepts click events. Always use + // GetCustomPageClickzone() to access this (do not access it directly). + views::Widget* custom_page_clickzone_; // Owned by the app list widget. + // A timer that fires when maximum allowed time to wait for icon loading has // passed. base::OneShotTimer icon_loading_wait_timer_; diff --git a/ui/app_list/views/app_list_view.cc b/ui/app_list/views/app_list_view.cc index 02276da3be1d..b7ae99ab0961 100644 --- a/ui/app_list/views/app_list_view.cc +++ b/ui/app_list/views/app_list_view.cc @@ -403,9 +403,14 @@ void AppListView::InitAsBubbleInternal(gfx::NativeView parent, set_border_accepts_events(border_accepts_events); set_shadow(SupportsShadow() ? views::BubbleBorder::BIG_SHADOW : views::BubbleBorder::NO_SHADOW_OPAQUE_BORDER); + // This creates the app list widget. (Before this, child widgets cannot be + // created.) views::BubbleDelegateView::CreateBubble(this); SetBubbleArrow(arrow); + // We can now create the internal widgets. + app_list_main_view_->InitWidgets(); + #if defined(USE_AURA) aura::Window* window = GetWidget()->GetNativeWindow(); window->layer()->SetMasksToBounds(true); diff --git a/ui/app_list/views/contents_view.cc b/ui/app_list/views/contents_view.cc index 0e397bf79623..42f02b87d1bf 100644 --- a/ui/app_list/views/contents_view.cc +++ b/ui/app_list/views/contents_view.cc @@ -109,6 +109,9 @@ void ContentsView::Init(AppListModel* model) { DCHECK_GE(initial_page_index, 0); page_before_search_ = initial_page_index; + // Must only call SetTotalPages once all the launcher pages have been added + // (as it will trigger a SelectedPageChanged call). + pagination_model_.SetTotalPages(view_model_->view_size()); pagination_model_.SelectPage(initial_page_index, false); ActivePageChanged(); @@ -321,6 +324,7 @@ views::View* ContentsView::GetPageView(int index) const { void ContentsView::AddBlankPageForTesting() { AddLauncherPage(new views::View, 0); + pagination_model_.SetTotalPages(view_model_->view_size()); } int ContentsView::AddLauncherPage(views::View* view, int resource_id) { @@ -329,7 +333,6 @@ int ContentsView::AddLauncherPage(views::View* view, int resource_id) { view_model_->Add(view, page_index); if (contents_switcher_view_ && resource_id) contents_switcher_view_->AddSwitcherButton(resource_id, page_index); - pagination_model_.SetTotalPages(view_model_->view_size()); return page_index; } @@ -373,6 +376,10 @@ gfx::Rect ContentsView::GetCustomPageCollapsedBounds() const { return bounds; } +bool ContentsView::ShouldShowCustomPageClickzone() const { + return custom_page_view_ && IsStateActive(AppListModel::STATE_START); +} + bool ContentsView::Back() { AppListModel::State state = view_to_state_[GetActivePageIndex()]; switch (state) { @@ -461,6 +468,15 @@ void ContentsView::TotalPagesChanged() { } void ContentsView::SelectedPageChanged(int old_selected, int new_selected) { + // TODO(mgiuca): This should be generalized so we call a virtual OnShow and + // OnHide method for each page. + if (!start_page_view_) + return; + + if (ShouldShowCustomPageClickzone()) + start_page_view_->OnShow(); + else + start_page_view_->OnHide(); } void ContentsView::TransitionStarted() { diff --git a/ui/app_list/views/contents_view.h b/ui/app_list/views/contents_view.h index 8f06fd93e64d..fbcea7043512 100644 --- a/ui/app_list/views/contents_view.h +++ b/ui/app_list/views/contents_view.h @@ -126,6 +126,9 @@ class APP_LIST_EXPORT ContentsView : public views::View, return GetAnimatorForTransition(from_page, to_page, reverse); } + // Determines whether the custom page clickzone widget should be displayed. + bool ShouldShowCustomPageClickzone() const; + // Performs the 'back' action for the active page. Returns whether the action // was handled. bool Back(); diff --git a/ui/app_list/views/start_page_view.cc b/ui/app_list/views/start_page_view.cc index 290fc9d72cd1..f949e3d3cf8b 100644 --- a/ui/app_list/views/start_page_view.cc +++ b/ui/app_list/views/start_page_view.cc @@ -162,6 +162,26 @@ TileItemView* StartPageView::all_apps_button() const { return all_apps_button_; } +void StartPageView::OnShow() { + // This can get called before InitWidgets(), so we cannot guarantee that + // custom_page_clickzone_ will not be null. + views::Widget* custom_page_clickzone = + app_list_main_view_->GetCustomPageClickzone(); + if (!custom_page_clickzone) + return; + + custom_page_clickzone->ShowInactive(); +} + +void StartPageView::OnHide() { + views::Widget* custom_page_clickzone = + app_list_main_view_->GetCustomPageClickzone(); + if (!custom_page_clickzone) + return; + + custom_page_clickzone->Hide(); +} + void StartPageView::Layout() { gfx::Rect bounds(GetContentsBounds()); bounds.set_height(instant_container_->GetHeightForWidth(bounds.width())); diff --git a/ui/app_list/views/start_page_view.h b/ui/app_list/views/start_page_view.h index f5d82ad05953..62364c6f72e3 100644 --- a/ui/app_list/views/start_page_view.h +++ b/ui/app_list/views/start_page_view.h @@ -36,6 +36,13 @@ class APP_LIST_EXPORT StartPageView : public SearchResultContainerView, TileItemView* all_apps_button() const; SearchBoxView* dummy_search_box_view() { return search_box_view_; } + // Called when the start page view is displayed. + void OnShow(); + + // Called when the start page view is hidden (while the app list is still + // open). + void OnHide(); + // Overridden from views::View: void Layout() override; -- 2.11.4.GIT