root/ui/app_list/views/search_result_list_view.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. update_factory_
  2. SetResults
  3. SetSelectedIndex
  4. IsResultViewSelected
  5. UpdateAutoLaunchState
  6. OnKeyPressed
  7. SetAutoLaunchTimeout
  8. CancelAutoLaunchTimeout
  9. GetResultViewAt
  10. Update
  11. ScheduleUpdate
  12. ForceAutoLaunchForTest
  13. Layout
  14. GetPreferredSize
  15. GetHeightForWidth
  16. VisibilityChanged
  17. AnimationEnded
  18. AnimationProgressed
  19. ListItemsAdded
  20. ListItemsRemoved
  21. ListItemMoved
  22. ListItemsChanged
  23. SearchResultActivated
  24. SearchResultActionActivated
  25. OnSearchResultInstalled
  26. OnSearchResultUninstalled

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/app_list/views/search_result_list_view.h"

#include <algorithm>

#include "base/bind.h"
#include "base/message_loop/message_loop.h"
#include "base/time/time.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/app_list/app_list_view_delegate.h"
#include "ui/app_list/views/search_result_list_view_delegate.h"
#include "ui/app_list/views/search_result_view.h"
#include "ui/events/event.h"
#include "ui/gfx/animation/linear_animation.h"
#include "ui/views/background.h"
#include "ui/views/layout/box_layout.h"

namespace {

const int kMaxResults = 6;
const int kTimeoutIndicatorHeight = 2;
const int kTimeoutFramerate = 60;
const SkColor kTimeoutIndicatorColor = SkColorSetRGB(30, 144, 255);

}  // namespace

namespace app_list {

SearchResultListView::SearchResultListView(
    SearchResultListViewDelegate* delegate,
    AppListViewDelegate* view_delegate)
    : delegate_(delegate),
      view_delegate_(view_delegate),
      results_(NULL),
      results_container_(new views::View),
      auto_launch_indicator_(new views::View),
      last_visible_index_(0),
      selected_index_(-1),
      update_factory_(this) {
  results_container_->SetLayoutManager(
      new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0));

  for (int i = 0; i < kMaxResults; ++i)
    results_container_->AddChildView(new SearchResultView(this));
  AddChildView(results_container_);

  auto_launch_indicator_->set_background(
      views::Background::CreateSolidBackground(kTimeoutIndicatorColor));
  auto_launch_indicator_->SetVisible(false);

  AddChildView(auto_launch_indicator_);
}

SearchResultListView::~SearchResultListView() {
  if (results_)
    results_->RemoveObserver(this);
}

void SearchResultListView::SetResults(AppListModel::SearchResults* results) {
  if (results_)
    results_->RemoveObserver(this);

  results_ = results;
  if (results_)
    results_->AddObserver(this);

  Update();
}

void SearchResultListView::SetSelectedIndex(int selected_index) {
  if (selected_index_ == selected_index)
    return;

  if (selected_index_ >= 0) {
    SearchResultView* selected_view  = GetResultViewAt(selected_index_);
    selected_view->ClearSelectedAction();
    selected_view->SchedulePaint();
  }

  selected_index_ = selected_index;

  if (selected_index_ >= 0) {
    SearchResultView* selected_view  = GetResultViewAt(selected_index_);
    selected_view->ClearSelectedAction();
    selected_view->SchedulePaint();
    selected_view->NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS,
                                            true);
  }
  if (auto_launch_animation_)
    CancelAutoLaunchTimeout();
}

bool SearchResultListView::IsResultViewSelected(
    const SearchResultView* result_view) const {
  if (selected_index_ < 0)
    return false;

  return static_cast<const SearchResultView*>(
      results_container_->child_at(selected_index_)) == result_view;
}

void SearchResultListView::UpdateAutoLaunchState() {
  SetAutoLaunchTimeout(view_delegate_->GetAutoLaunchTimeout());
}

bool SearchResultListView::OnKeyPressed(const ui::KeyEvent& event) {
  if (selected_index_ >= 0 &&
      results_container_->child_at(selected_index_)->OnKeyPressed(event)) {
    return true;
  }

  switch (event.key_code()) {
    case ui::VKEY_TAB:
      if (event.IsShiftDown())
        SetSelectedIndex(std::max(selected_index_ - 1, 0));
      else
        SetSelectedIndex(std::min(selected_index_ + 1, last_visible_index_));
      return true;
    case ui::VKEY_UP:
      SetSelectedIndex(std::max(selected_index_ - 1, 0));
      return true;
    case ui::VKEY_DOWN:
      SetSelectedIndex(std::min(selected_index_ + 1, last_visible_index_));
      return true;
    default:
      break;
  }

  return false;
}

void SearchResultListView::SetAutoLaunchTimeout(
    const base::TimeDelta& timeout) {
  if (timeout > base::TimeDelta()) {
    auto_launch_indicator_->SetVisible(true);
    auto_launch_indicator_->SetBounds(0, 0, 0, kTimeoutIndicatorHeight);
    auto_launch_animation_.reset(new gfx::LinearAnimation(
        timeout.InMilliseconds(), kTimeoutFramerate, this));
    auto_launch_animation_->Start();
  } else {
    auto_launch_indicator_->SetVisible(false);
    auto_launch_animation_.reset();
  }
}

void SearchResultListView::CancelAutoLaunchTimeout() {
  SetAutoLaunchTimeout(base::TimeDelta());
  view_delegate_->AutoLaunchCanceled();
}

SearchResultView* SearchResultListView::GetResultViewAt(int index) {
  DCHECK(index >= 0 && index < results_container_->child_count());
  return static_cast<SearchResultView*>(results_container_->child_at(index));
}

void SearchResultListView::Update() {
  last_visible_index_ = 0;
  for (size_t i = 0; i < static_cast<size_t>(results_container_->child_count());
       ++i) {
    SearchResultView* result_view = GetResultViewAt(i);
    if (i < results_->item_count()) {
      result_view->SetResult(results_->GetItemAt(i));
      result_view->SetVisible(true);
      last_visible_index_ = i;
    } else {
      result_view->SetResult(NULL);
      result_view->SetVisible(false);
    }
  }
  if (selected_index_ > last_visible_index_)
    SetSelectedIndex(last_visible_index_);

  Layout();
  update_factory_.InvalidateWeakPtrs();
  UpdateAutoLaunchState();
}

void SearchResultListView::ScheduleUpdate() {
  // When search results are added one by one, each addition generates an update
  // request. Consolidates those update requests into one Update call.
  if (!update_factory_.HasWeakPtrs()) {
    base::MessageLoop::current()->PostTask(
        FROM_HERE,
        base::Bind(&SearchResultListView::Update,
                   update_factory_.GetWeakPtr()));
  }
}

void SearchResultListView::ForceAutoLaunchForTest() {
  if (auto_launch_animation_)
    AnimationEnded(auto_launch_animation_.get());
}

void SearchResultListView::Layout() {
  results_container_->SetBoundsRect(GetLocalBounds());
}

gfx::Size SearchResultListView::GetPreferredSize() {
  return results_container_->GetPreferredSize();
}

int SearchResultListView::GetHeightForWidth(int w) {
  return results_container_->GetHeightForWidth(w);
}

void SearchResultListView::VisibilityChanged(views::View* starting_from,
                                             bool is_visible) {
  if (is_visible)
    UpdateAutoLaunchState();
  else
    CancelAutoLaunchTimeout();
}

void SearchResultListView::AnimationEnded(const gfx::Animation* animation) {
  DCHECK_EQ(auto_launch_animation_.get(), animation);
  view_delegate_->OpenSearchResult(results_->GetItemAt(0), true, ui::EF_NONE);

  // The auto-launch has to be canceled explicitly. Think that one of searcher
  // is extremely slow. Sometimes the events would happen in the following
  // order:
  //  1. The search results arrive, auto-launch is dispatched
  //  2. Timed out and auto-launch the first search result
  //  3. Then another searcher adds search results more
  // At the step 3, we shouldn't dispatch the auto-launch again.
  CancelAutoLaunchTimeout();
}

void SearchResultListView::AnimationProgressed(
    const gfx::Animation* animation) {
  DCHECK_EQ(auto_launch_animation_.get(), animation);
  int indicator_width = auto_launch_animation_->CurrentValueBetween(0, width());
  auto_launch_indicator_->SetBounds(
      0, 0, indicator_width, kTimeoutIndicatorHeight);
}

void SearchResultListView::ListItemsAdded(size_t start, size_t count) {
  ScheduleUpdate();
}

void SearchResultListView::ListItemsRemoved(size_t start, size_t count) {
  size_t last = std::min(
      start + count,
      static_cast<size_t>(results_container_->child_count()));
  for (size_t i = start; i < last; ++i)
    GetResultViewAt(i)->ClearResultNoRepaint();

  ScheduleUpdate();
}

void SearchResultListView::ListItemMoved(size_t index, size_t target_index) {
  NOTREACHED();
}

void SearchResultListView::ListItemsChanged(size_t start, size_t count) {
  ScheduleUpdate();
}

void SearchResultListView::SearchResultActivated(SearchResultView* view,
                                                 int event_flags) {
  if (view_delegate_ && view->result())
    view_delegate_->OpenSearchResult(view->result(), false, event_flags);
}

void SearchResultListView::SearchResultActionActivated(SearchResultView* view,
                                                       size_t action_index,
                                                       int event_flags) {
  if (view_delegate_ && view->result()) {
    view_delegate_->InvokeSearchResultAction(
        view->result(), action_index, event_flags);
  }
}

void SearchResultListView::OnSearchResultInstalled(SearchResultView* view) {
  if (delegate_ && view->result())
    delegate_->OnResultInstalled(view->result());
}

void SearchResultListView::OnSearchResultUninstalled(SearchResultView* view) {
  if (delegate_ && view->result())
    delegate_->OnResultUninstalled(view->result());
}

}  // namespace app_list

/* [<][>][^][v][top][bottom][index][help] */