Commit e9a92274 authored by wutao's avatar wutao Committed by Commit Bot

cros: Defer init of background panes in Shortcut Viewer

Most of the startup time of Shortcut Viewer is spent to layout the
text. This patch only layouts the shortcuts list in the first category
and layouts other categories panes in the background.

When tested on Linux build:
The CreateWidget wall duration is reduced from 403ms to 137ms.
The StartupTime is reduced from 1100ms to 900ms.

Tested on Eve:
The CreateWidget wall duration is reduced from 224ms to 73ms.
The StartupTime is reduced from 529ms to 297ms.

Bug: 849413
Test: manual.
Change-Id: Ia59a90b4eec9c12ba5846883501ee73e8afc8a59
Reviewed-on: https://chromium-review.googlesource.com/1150775
Commit-Queue: Tao Wu <wutao@chromium.org>
Reviewed-by: default avatarJames Cook <jamescook@chromium.org>
Cr-Commit-Position: refs/heads/master@{#579130}
parent a8e9bac6
...@@ -51,6 +51,8 @@ namespace { ...@@ -51,6 +51,8 @@ namespace {
KeyboardShortcutView* g_ksv_view = nullptr; KeyboardShortcutView* g_ksv_view = nullptr;
constexpr base::nullopt_t kAllCategories = base::nullopt;
// Setups the illustration views for search states, including an icon and a // Setups the illustration views for search states, including an icon and a
// descriptive text. // descriptive text.
void SetupSearchIllustrationView(views::View* illustration_view, void SetupSearchIllustrationView(views::View* illustration_view,
...@@ -146,6 +148,8 @@ views::Widget* KeyboardShortcutView::Toggle(base::TimeTicks start_time) { ...@@ -146,6 +148,8 @@ views::Widget* KeyboardShortcutView::Toggle(base::TimeTicks start_time) {
g_ksv_view->AddAccelerator( g_ksv_view->AddAccelerator(
ui::Accelerator(ui::VKEY_W, ui::EF_CONTROL_DOWN)); ui::Accelerator(ui::VKEY_W, ui::EF_CONTROL_DOWN));
g_ksv_view->needs_init_all_categories_ = false;
g_ksv_view->did_first_paint_ = false;
g_ksv_view->GetWidget()->Show(); g_ksv_view->GetWidget()->Show();
g_ksv_view->search_box_view_->search_box()->RequestFocus(); g_ksv_view->search_box_view_->search_box()->RequestFocus();
...@@ -206,6 +210,30 @@ gfx::Size KeyboardShortcutView::CalculatePreferredSize() const { ...@@ -206,6 +210,30 @@ gfx::Size KeyboardShortcutView::CalculatePreferredSize() const {
return gfx::Size(800, 512); return gfx::Size(800, 512);
} }
void KeyboardShortcutView::OnPaint(gfx::Canvas* canvas) {
views::View::OnPaint(canvas);
// Skip if it is the first OnPaint event.
if (!did_first_paint_) {
did_first_paint_ = true;
needs_init_all_categories_ = true;
return;
}
if (!needs_init_all_categories_)
return;
needs_init_all_categories_ = false;
// Cannot post a task right after initializing the first category, it will
// have a chance to end up in the same group of drawing commands sent to
// compositor. We can wait for the second OnPaint, which means previous
// drawing commands have been sent to compositor for the next frame and new
// coming commands will be sent for the next-next frame.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&KeyboardShortcutView::InitCategoriesTabbedPane,
weak_factory_.GetWeakPtr(), kAllCategories));
}
void KeyboardShortcutView::QueryChanged(search_box::SearchBoxViewBase* sender) { void KeyboardShortcutView::QueryChanged(search_box::SearchBoxViewBase* sender) {
const bool query_empty = sender->IsSearchBoxTrimmedQueryEmpty(); const bool query_empty = sender->IsSearchBoxTrimmedQueryEmpty();
if (is_search_box_empty_ != query_empty) { if (is_search_box_empty_ != query_empty) {
...@@ -244,7 +272,7 @@ void KeyboardShortcutView::ActiveChanged( ...@@ -244,7 +272,7 @@ void KeyboardShortcutView::ActiveChanged(
UpdateViewsLayout(is_search_box_active); UpdateViewsLayout(is_search_box_active);
} }
KeyboardShortcutView::KeyboardShortcutView() { KeyboardShortcutView::KeyboardShortcutView() : weak_factory_(this) {
DCHECK_EQ(g_ksv_view, nullptr); DCHECK_EQ(g_ksv_view, nullptr);
g_ksv_view = this; g_ksv_view = this;
...@@ -296,10 +324,16 @@ void KeyboardShortcutView::InitViews() { ...@@ -296,10 +324,16 @@ void KeyboardShortcutView::InitViews() {
new views::TabbedPane(views::TabbedPane::Orientation::kVertical, new views::TabbedPane(views::TabbedPane::Orientation::kVertical,
views::TabbedPane::TabStripStyle::kHighlight); views::TabbedPane::TabStripStyle::kHighlight);
AddChildView(categories_tabbed_pane_); AddChildView(categories_tabbed_pane_);
InitCategoriesTabbedPane();
// Initial Layout of KeyboardShortcutItemView is time consuming. To speed up
// the startup time, we only initialize the first category pane, which is
// visible to user, and defer initialization of other categories in the
// background.
InitCategoriesTabbedPane(ShortcutCategory::kPopular);
} }
void KeyboardShortcutView::InitCategoriesTabbedPane() { void KeyboardShortcutView::InitCategoriesTabbedPane(
base::Optional<ShortcutCategory> initial_category) {
// If the tab count is 0, |GetSelectedTabIndex()| will return -1, which we do // If the tab count is 0, |GetSelectedTabIndex()| will return -1, which we do
// not want to cache. // not want to cache.
active_tab_index_ = active_tab_index_ =
...@@ -314,6 +348,7 @@ void KeyboardShortcutView::InitCategoriesTabbedPane() { ...@@ -314,6 +348,7 @@ void KeyboardShortcutView::InitCategoriesTabbedPane() {
categories_tabbed_pane_->child_at(0)->RemoveAllChildViews(true); categories_tabbed_pane_->child_at(0)->RemoveAllChildViews(true);
categories_tabbed_pane_->child_at(1)->RemoveAllChildViews(true); categories_tabbed_pane_->child_at(1)->RemoveAllChildViews(true);
const bool first_init = initial_category.has_value();
ShortcutCategory current_category = ShortcutCategory::kUnknown; ShortcutCategory current_category = ShortcutCategory::kUnknown;
KeyboardShortcutItemListView* item_list_view; KeyboardShortcutItemListView* item_list_view;
for (const auto& item_view : shortcut_views_) { for (const auto& item_view : shortcut_views_) {
...@@ -327,6 +362,13 @@ void KeyboardShortcutView::InitCategoriesTabbedPane() { ...@@ -327,6 +362,13 @@ void KeyboardShortcutView::InitCategoriesTabbedPane() {
categories_tabbed_pane_->AddTab(GetStringForCategory(current_category), categories_tabbed_pane_->AddTab(GetStringForCategory(current_category),
scroller); scroller);
} }
// If |first_init| is true, we only initialize the pane with the
// KeyboardShortcutItemView in the specific category in |initial_category|.
// Otherwise, we will initialize all the panes.
if (first_init && category != initial_category.value())
continue;
if (item_list_view->has_children()) if (item_list_view->has_children())
item_list_view->AddHorizontalSeparator(); item_list_view->AddHorizontalSeparator();
views::StyledLabel* description_label_view = views::StyledLabel* description_label_view =
...@@ -337,6 +379,7 @@ void KeyboardShortcutView::InitCategoriesTabbedPane() { ...@@ -337,6 +379,7 @@ void KeyboardShortcutView::InitCategoriesTabbedPane() {
// Remove the search query highlight. // Remove the search query highlight.
description_label_view->Layout(); description_label_view->Layout();
} }
Layout();
} }
void KeyboardShortcutView::UpdateViewsLayout(bool is_search_box_active) { void KeyboardShortcutView::UpdateViewsLayout(bool is_search_box_active) {
...@@ -355,7 +398,7 @@ void KeyboardShortcutView::UpdateViewsLayout(bool is_search_box_active) { ...@@ -355,7 +398,7 @@ void KeyboardShortcutView::UpdateViewsLayout(bool is_search_box_active) {
if (!categories_tabbed_pane_->visible()) { if (!categories_tabbed_pane_->visible()) {
// Repopulate |categories_tabbed_pane_| child views, which were removed // Repopulate |categories_tabbed_pane_| child views, which were removed
// when they were added to |search_results_container_|. // when they were added to |search_results_container_|.
InitCategoriesTabbedPane(); InitCategoriesTabbedPane(kAllCategories);
// Select the category that was active before entering search mode. // Select the category that was active before entering search mode.
categories_tabbed_pane_->SelectTabAt(active_tab_index_); categories_tabbed_pane_->SelectTabAt(active_tab_index_);
} }
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <vector> #include <vector>
#include "base/macros.h" #include "base/macros.h"
#include "base/optional.h"
#include "base/timer/timer.h" #include "base/timer/timer.h"
#include "ui/chromeos/search_box/search_box_view_delegate.h" #include "ui/chromeos/search_box/search_box_view_delegate.h"
#include "ui/views/widget/widget_delegate.h" #include "ui/views/widget/widget_delegate.h"
...@@ -48,6 +49,7 @@ class KeyboardShortcutView : public views::WidgetDelegateView, ...@@ -48,6 +49,7 @@ class KeyboardShortcutView : public views::WidgetDelegateView,
bool AcceleratorPressed(const ui::Accelerator& accelerator) override; bool AcceleratorPressed(const ui::Accelerator& accelerator) override;
void Layout() override; void Layout() override;
gfx::Size CalculatePreferredSize() const override; gfx::Size CalculatePreferredSize() const override;
void OnPaint(gfx::Canvas* canvas) override;
// search_box::SearchBoxViewDelegate: // search_box::SearchBoxViewDelegate:
void QueryChanged(search_box::SearchBoxViewBase* sender) override; void QueryChanged(search_box::SearchBoxViewBase* sender) override;
...@@ -63,7 +65,10 @@ class KeyboardShortcutView : public views::WidgetDelegateView, ...@@ -63,7 +65,10 @@ class KeyboardShortcutView : public views::WidgetDelegateView,
// Initialize |categories_tabbed_pane_| with category tabs and containers of // Initialize |categories_tabbed_pane_| with category tabs and containers of
// |shortcut_views_|, called on construction and when exiting search mode. // |shortcut_views_|, called on construction and when exiting search mode.
void InitCategoriesTabbedPane(); // If |initial_category| has value, we will initialize the specified category,
// otherwise all the categories will be intialized.
void InitCategoriesTabbedPane(
base::Optional<ShortcutCategory> initial_category);
// Update views' layout based on search box status. // Update views' layout based on search box status.
void UpdateViewsLayout(bool is_search_box_active); void UpdateViewsLayout(bool is_search_box_active);
...@@ -114,6 +119,15 @@ class KeyboardShortcutView : public views::WidgetDelegateView, ...@@ -114,6 +119,15 @@ class KeyboardShortcutView : public views::WidgetDelegateView,
// Debounce for search queries. // Debounce for search queries.
base::OneShotTimer debounce_timer_; base::OneShotTimer debounce_timer_;
// Ture if need to initialize all the categories.
// False if only initialize the first category.
bool needs_init_all_categories_ = false;
// Indicates if recieved the first OnPaint event. Used to schedule
// initialization of background panes in the following frame.
bool did_first_paint_ = false;
base::WeakPtrFactory<KeyboardShortcutView> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(KeyboardShortcutView); DISALLOW_COPY_AND_ASSIGN(KeyboardShortcutView);
}; };
......
...@@ -145,6 +145,11 @@ TEST_F(KeyboardShortcutViewTest, TopLineCenterAlignedInItemView) { ...@@ -145,6 +145,11 @@ TEST_F(KeyboardShortcutViewTest, TopLineCenterAlignedInItemView) {
views::Widget* widget = KeyboardShortcutView::Toggle(base::TimeTicks()); views::Widget* widget = KeyboardShortcutView::Toggle(base::TimeTicks());
for (const auto& item_view : GetShortcutViews()) { for (const auto& item_view : GetShortcutViews()) {
// We only initialize the first visible category and other non-visible panes
// are deferred initialized.
if (item_view->category() != ShortcutCategory::kPopular)
continue;
DCHECK(item_view->child_count() == 2); DCHECK(item_view->child_count() == 2);
// The top lines in both |description_label_view_| and // The top lines in both |description_label_view_| and
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment