diff options
author | derat@chromium.org <derat@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-08-28 23:48:14 +0000 |
---|---|---|
committer | derat@chromium.org <derat@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-08-28 23:48:14 +0000 |
commit | 814e3e2f12f8c67228e0c4613c8149f30177b9dc (patch) | |
tree | 278adf6a4cd4d3638323bc25b76acebb43fdd892 /ui | |
parent | da36da98ce283c09d7b1cbdd3d2450df72ca659c (diff) | |
download | chromium_src-814e3e2f12f8c67228e0c4613c8149f30177b9dc.zip chromium_src-814e3e2f12f8c67228e0c4613c8149f30177b9dc.tar.gz chromium_src-814e3e2f12f8c67228e0c4613c8149f30177b9dc.tar.bz2 |
ash: Add support for additional icons in apps search list.
This adds the concept of "action icons" to
app_list::SearchResult. These are small icons that appear
at the right of a given search result and can be clicked to
invoke alternate actions. I'm planning to use this to
expose chat, call, email, etc. actions for contact results.
BUG=141877
TEST=none
Review URL: https://chromiumcodereview.appspot.com/10872099
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@153795 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ui')
-rw-r--r-- | ui/app_list/app_list_view.cc | 7 | ||||
-rw-r--r-- | ui/app_list/app_list_view.h | 3 | ||||
-rw-r--r-- | ui/app_list/app_list_view_delegate.h | 6 | ||||
-rw-r--r-- | ui/app_list/search_result.cc | 20 | ||||
-rw-r--r-- | ui/app_list/search_result.h | 32 | ||||
-rw-r--r-- | ui/app_list/search_result_list_view.cc | 31 | ||||
-rw-r--r-- | ui/app_list/search_result_list_view.h | 17 | ||||
-rw-r--r-- | ui/app_list/search_result_list_view_delegate.h | 6 | ||||
-rw-r--r-- | ui/app_list/search_result_observer.h | 5 | ||||
-rw-r--r-- | ui/app_list/search_result_view.cc | 87 | ||||
-rw-r--r-- | ui/app_list/search_result_view.h | 16 | ||||
-rw-r--r-- | ui/app_list/search_result_view_delegate.h | 34 |
12 files changed, 231 insertions, 33 deletions
diff --git a/ui/app_list/app_list_view.cc b/ui/app_list/app_list_view.cc index d0b56e4..d1c0b43 100644 --- a/ui/app_list/app_list_view.cc +++ b/ui/app_list/app_list_view.cc @@ -193,4 +193,11 @@ void AppListView::OpenResult(const SearchResult& result, int event_flags) { delegate_->OpenSearchResult(result, event_flags); } +void AppListView::InvokeResultAction(const SearchResult& result, + int action_index, + int event_flags) { + if (delegate_.get()) + delegate_->InvokeSearchResultAction(result, action_index, event_flags); +} + } // namespace app_list diff --git a/ui/app_list/app_list_view.h b/ui/app_list/app_list_view.h index 5bdebe6..708d55a 100644 --- a/ui/app_list/app_list_view.h +++ b/ui/app_list/app_list_view.h @@ -70,6 +70,9 @@ class APP_LIST_EXPORT AppListView : public views::BubbleDelegateView, // Overridden from SearchResultListViewDelegate: virtual void OpenResult(const SearchResult& result, int event_flags) OVERRIDE; + virtual void InvokeResultAction(const SearchResult& result, + int action_index, + int event_flags) OVERRIDE; scoped_ptr<AppListModel> model_; scoped_ptr<AppListViewDelegate> delegate_; diff --git a/ui/app_list/app_list_view_delegate.h b/ui/app_list/app_list_view_delegate.h index 159d616..d7e0c82 100644 --- a/ui/app_list/app_list_view_delegate.h +++ b/ui/app_list/app_list_view_delegate.h @@ -38,6 +38,12 @@ class APP_LIST_EXPORT AppListViewDelegate { virtual void OpenSearchResult(const SearchResult& result, int event_flags) = 0; + // Called to invoke a custom action on |result|. |action_index| corresponds + // to the index of an icon in |result.action_icons()|. + virtual void InvokeSearchResultAction(const SearchResult& result, + int action_index, + int event_flags) = 0; + // Invoked to close app list. virtual void Close() = 0; }; diff --git a/ui/app_list/search_result.cc b/ui/app_list/search_result.cc index b0704fb..597ea60 100644 --- a/ui/app_list/search_result.cc +++ b/ui/app_list/search_result.cc @@ -8,6 +8,19 @@ namespace app_list { +SearchResult::ActionIconSet::ActionIconSet(const gfx::ImageSkia& base_image, + const gfx::ImageSkia& hover_image, + const gfx::ImageSkia& pressed_image, + const string16& tooltip_text) + : base_image(base_image), + hover_image(hover_image), + pressed_image(pressed_image), + tooltip_text(tooltip_text) { +} + +SearchResult::ActionIconSet::~ActionIconSet() { +} + SearchResult::SearchResult() { } @@ -21,6 +34,13 @@ void SearchResult::SetIcon(const gfx::ImageSkia& icon) { OnIconChanged()); } +void SearchResult::SetActionIcons(const std::vector<ActionIconSet>& sets) { + action_icons_ = sets; + FOR_EACH_OBSERVER(SearchResultObserver, + observers_, + OnActionIconsChanged()); +} + void SearchResult::AddObserver(SearchResultObserver* observer) { observers_.AddObserver(observer); } diff --git a/ui/app_list/search_result.h b/ui/app_list/search_result.h index f96d8d4..c7b2760 100644 --- a/ui/app_list/search_result.h +++ b/ui/app_list/search_result.h @@ -45,12 +45,26 @@ class APP_LIST_EXPORT SearchResult { }; typedef std::vector<Tag> Tags; + // A collection of images representing an action that can be performed on this + // search result. + struct ActionIconSet { + ActionIconSet(const gfx::ImageSkia& base_image, + const gfx::ImageSkia& hover_image, + const gfx::ImageSkia& pressed_image, + const string16& tooltip_text); + ~ActionIconSet(); + + gfx::ImageSkia base_image; + gfx::ImageSkia hover_image; + gfx::ImageSkia pressed_image; + + string16 tooltip_text; + }; + typedef std::vector<ActionIconSet> ActionIconSets; + SearchResult(); virtual ~SearchResult(); - void AddObserver(SearchResultObserver* observer); - void RemoveObserver(SearchResultObserver* observer); - const gfx::ImageSkia& icon() const { return icon_; } void SetIcon(const gfx::ImageSkia& icon); @@ -66,6 +80,14 @@ class APP_LIST_EXPORT SearchResult { const Tags& details_tags() const { return details_tags_; } void set_details_tags(const Tags& tags) { details_tags_ = tags; } + const ActionIconSets& action_icons() const { + return action_icons_; + } + void SetActionIcons(const ActionIconSets& sets); + + void AddObserver(SearchResultObserver* observer); + void RemoveObserver(SearchResultObserver* observer); + private: gfx::ImageSkia icon_; @@ -75,6 +97,10 @@ class APP_LIST_EXPORT SearchResult { string16 details_; Tags details_tags_; + // Optional list of icons representing additional actions that can be + // performed on this result. + ActionIconSets action_icons_; + ObserverList<SearchResultObserver> observers_; DISALLOW_COPY_AND_ASSIGN(SearchResult); diff --git a/ui/app_list/search_result_list_view.cc b/ui/app_list/search_result_list_view.cc index 64df98a..d5d9647 100644 --- a/ui/app_list/search_result_list_view.cc +++ b/ui/app_list/search_result_list_view.cc @@ -83,7 +83,7 @@ bool SearchResultListView::OnKeyPressed(const ui::KeyEvent& event) { return true; case ui::VKEY_RETURN: if (selected_index_ >= 0) - ButtonPressed(GetResultViewAt(selected_index_), event); + SearchResultActivated(GetResultViewAt(selected_index_), event); return true; default: break; @@ -127,20 +127,6 @@ void SearchResultListView::ScheduleUpdate() { } } -void SearchResultListView::ButtonPressed(views::Button* sender, - const ui::Event& event) { - if (sender->GetClassName() != SearchResultView::kViewClassName) - return; - - if (delegate_) { - const SearchResult* result = - static_cast<SearchResultView*>(sender)->result(); - - if (result) - delegate_->OpenResult(*result, event.flags()); - } -} - void SearchResultListView::ListItemsAdded(size_t start, size_t count) { ScheduleUpdate(); } @@ -157,4 +143,19 @@ void SearchResultListView::ListItemsChanged(size_t start, size_t count) { ScheduleUpdate(); } +void SearchResultListView::SearchResultActivated(SearchResultView* view, + const ui::Event& event) { + if (delegate_ && view->result()) + delegate_->OpenResult(*(view->result()), event.flags()); +} + +void SearchResultListView::SearchResultActionActivated(SearchResultView* view, + int action_index, + const ui::Event& event) { + if (delegate_ && view->result()) { + delegate_->InvokeResultAction( + *(view->result()), action_index, event.flags()); + } +} + } // namespace app_list diff --git a/ui/app_list/search_result_list_view.h b/ui/app_list/search_result_list_view.h index 90311d5..8f0ca93 100644 --- a/ui/app_list/search_result_list_view.h +++ b/ui/app_list/search_result_list_view.h @@ -8,8 +8,8 @@ #include "base/basictypes.h" #include "base/memory/weak_ptr.h" #include "ui/app_list/app_list_model.h" +#include "ui/app_list/search_result_view_delegate.h" #include "ui/base/models/list_model_observer.h" -#include "ui/views/controls/button/button.h" #include "ui/views/view.h" namespace app_list { @@ -20,8 +20,8 @@ class SearchResultView; // SearchResultListView displays AppListModel::SearchResults with a list of // SearchResultView. class SearchResultListView : public views::View, - public views::ButtonListener, - public ui::ListModelObserver { + public ui::ListModelObserver, + public SearchResultViewDelegate { public: explicit SearchResultListView(SearchResultListViewDelegate* delegate); virtual ~SearchResultListView(); @@ -46,15 +46,18 @@ class SearchResultListView : public views::View, // pending call. void ScheduleUpdate(); - // Overridden from views::ButtonListener: - virtual void ButtonPressed(views::Button* sender, - const ui::Event& event) OVERRIDE; - // Overridden from ListModelObserver: virtual void ListItemsAdded(size_t start, size_t count) OVERRIDE; virtual void ListItemsRemoved(size_t start, size_t count) OVERRIDE; virtual void ListItemsChanged(size_t start, size_t count) OVERRIDE; + // Overridden from SearchResultViewDelegate: + virtual void SearchResultActivated(SearchResultView* view, + const ui::Event& event) OVERRIDE; + virtual void SearchResultActionActivated(SearchResultView* view, + int action_index, + const ui::Event& event) OVERRIDE; + SearchResultListViewDelegate* delegate_; // Not owned. AppListModel::SearchResults* results_; // Owned by AppListModel. diff --git a/ui/app_list/search_result_list_view_delegate.h b/ui/app_list/search_result_list_view_delegate.h index 419bb3e..3022e3f 100644 --- a/ui/app_list/search_result_list_view_delegate.h +++ b/ui/app_list/search_result_list_view_delegate.h @@ -19,6 +19,12 @@ class APP_LIST_EXPORT SearchResultListViewDelegate { virtual void OpenResult(const SearchResult& result, int event_flags) = 0; + // Called to invoke a custom action on |result|. |action_index| corresponds + // to the index of the icon in |result.action_icons()| that was activated. + virtual void InvokeResultAction(const SearchResult& result, + int action_index, + int event_flags) = 0; + protected: virtual ~SearchResultListViewDelegate() {} }; diff --git a/ui/app_list/search_result_observer.h b/ui/app_list/search_result_observer.h index e539c72..b78c054 100644 --- a/ui/app_list/search_result_observer.h +++ b/ui/app_list/search_result_observer.h @@ -11,9 +11,12 @@ namespace app_list { class APP_LIST_EXPORT SearchResultObserver { public: - // Invoked when SearchResult icon has changed. + // Invoked when the SearchResult's icon has changed. virtual void OnIconChanged() = 0; + // Invoked when the SearchResult's action icons have been changed. + virtual void OnActionIconsChanged() = 0; + protected: virtual ~SearchResultObserver() {} }; diff --git a/ui/app_list/search_result_view.cc b/ui/app_list/search_result_view.cc index 7c71e8e..98f7405 100644 --- a/ui/app_list/search_result_view.cc +++ b/ui/app_list/search_result_view.cc @@ -8,7 +8,9 @@ #include "ui/app_list/search_result_list_view.h" #include "ui/gfx/canvas.h" #include "ui/gfx/font.h" +#include "ui/gfx/image/image_skia_operations.h" #include "ui/gfx/render_text.h" +#include "ui/views/controls/button/image_button.h" #include "ui/views/controls/image_view.h" namespace { @@ -21,6 +23,15 @@ const int kIconViewWidth = kIconDimension + 2 * kIconPadding; const int kTextTrailPadding = kIconPadding; const int kBorderSize = 1; +// Maximum dimensions of a search result's action icons. +const int kActionIconDimension = 24; + +// Total space (including padding) used for each action icon's button. +const int kActionButtonWidth = 32; + +// Extra margin at the right of the rightmost action icon. +const int kActionButtonRightMargin = 8; + const SkColor kBorderColor = SkColorSetARGB(0x0F, 0, 0, 0); const SkColor kDefaultTextColor = SkColorSetRGB(0x33, 0x33, 0x33); @@ -89,10 +100,11 @@ namespace app_list { const char SearchResultView::kViewClassName[] = "ui/app_list/SearchResultView"; SearchResultView::SearchResultView(SearchResultListView* list_view, - views::ButtonListener* listener) - : views::CustomButton(listener), + SearchResultViewDelegate* delegate) + : views::CustomButton(this), result_(NULL), list_view_(list_view), + delegate_(delegate), icon_(new IconView) { AddChildView(icon_); } @@ -109,6 +121,7 @@ void SearchResultView::SetResult(SearchResult* result) { result_->AddObserver(this); OnIconChanged(); + OnActionIconsChanged(); UpdateTitleText(); UpdateDetailsText(); SchedulePaint(); @@ -155,6 +168,17 @@ void SearchResultView::Layout() { icon_bounds.set_width(kIconViewWidth); icon_bounds.Inset(kIconPadding, (rect.height() - kIconDimension) / 2); icon_->SetBoundsRect(icon_bounds.Intersect(rect)); + + size_t num_buttons = action_buttons_.size(); + for (size_t i = 0; i < num_buttons; ++i) { + views::ImageButton* button = action_buttons_[i]; + gfx::Rect button_bounds( + rect.right() - kActionButtonRightMargin - + (num_buttons - i) * kActionButtonWidth, + (rect.y() + rect.height() - kActionIconDimension) / 2, + kActionButtonWidth, kActionIconDimension); + button->SetBoundsRect(button_bounds); + } } void SearchResultView::OnPaint(gfx::Canvas* canvas) { @@ -178,7 +202,10 @@ void SearchResultView::OnPaint(gfx::Canvas* canvas) { gfx::Rect text_bounds(rect); text_bounds.set_x(kIconViewWidth); - text_bounds.set_width(rect.width() - kIconViewWidth - kTextTrailPadding); + text_bounds.set_width( + rect.width() - kIconViewWidth - kTextTrailPadding - + action_buttons_.size() * kActionButtonWidth - + (!action_buttons_.empty() ? kActionButtonRightMargin : 0)); if (title_text_.get() && details_text_.get()) { gfx::Size title_size(text_bounds.width(), @@ -204,6 +231,22 @@ void SearchResultView::OnPaint(gfx::Canvas* canvas) { } } +void SearchResultView::ButtonPressed(views::Button* sender, + const ui::Event& event) { + if (sender == this) { + delegate_->SearchResultActivated(this, event); + return; + } + + for (size_t i = 0; i < action_buttons_.size(); ++i) { + if (sender == action_buttons_[i]) { + delegate_->SearchResultActionActivated(this, i, event); + return; + } + } + NOTREACHED() << "Got unexpected button press on " << sender; +} + void SearchResultView::OnIconChanged() { gfx::ImageSkia image(result_ ? result_->icon() : gfx::ImageSkia()); // Note this might leave the view with an old icon. But it is needed to avoid @@ -215,12 +258,44 @@ void SearchResultView::OnIconChanged() { return; // Scales down big icons but leave small ones unchanged. - if (image.width() > kIconDimension || image.height() > kIconDimension) - icon_->SetImageSize(gfx::Size(kIconDimension, kIconDimension)); - else + if (image.width() > kIconDimension || image.height() > kIconDimension) { + image = gfx::ImageSkiaOperations::CreateResizedImage( + image, + skia::ImageOperations::RESIZE_BEST, + gfx::Size(kIconDimension, kIconDimension)); + } else { icon_->ResetImageSize(); + } icon_->SetImage(image); } +void SearchResultView::OnActionIconsChanged() { + while (action_buttons_.size() > + (result_ ? result_->action_icons().size() : 0)) { + RemoveChildView(action_buttons_.back()); + action_buttons_.pop_back(); + } + + if (result_) { + while (action_buttons_.size() < result_->action_icons().size()) { + views::ImageButton* button = new views::ImageButton(this); + button->SetImageAlignment(views::ImageButton::ALIGN_CENTER, + views::ImageButton::ALIGN_MIDDLE); + AddChildView(button); + action_buttons_.push_back(button); + } + + const SearchResult::ActionIconSets& icons = result_->action_icons(); + for (size_t i = 0; i < icons.size(); ++i) { + const SearchResult::ActionIconSet& icon = icons.at(i); + views::ImageButton* button = action_buttons_[i]; + button->SetImage(views::CustomButton::BS_NORMAL, &icon.base_image); + button->SetImage(views::CustomButton::BS_HOT, &icon.hover_image); + button->SetImage(views::CustomButton::BS_PUSHED, &icon.pressed_image); + button->SetTooltipText(icon.tooltip_text); + } + } +} + } // namespace app_list diff --git a/ui/app_list/search_result_view.h b/ui/app_list/search_result_view.h index 78627ef..c8a373f 100644 --- a/ui/app_list/search_result_view.h +++ b/ui/app_list/search_result_view.h @@ -17,6 +17,7 @@ class RenderText; } namespace views { +class ImageButton; class ImageView; } @@ -24,16 +25,18 @@ namespace app_list { class SearchResult; class SearchResultListView; +class SearchResultViewDelegate; // SearchResultView displays a SearchResult. class SearchResultView : public views::CustomButton, + public views::ButtonListener, public SearchResultObserver { public: // Internal class name. static const char kViewClassName[]; SearchResultView(SearchResultListView* list_view, - views::ButtonListener* listener); + SearchResultViewDelegate* delegate); virtual ~SearchResultView(); // Sets/gets SearchResult displayed by this view. @@ -53,18 +56,29 @@ class SearchResultView : public views::CustomButton, virtual void Layout() OVERRIDE; virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; + // views::ButtonListener overrides: + virtual void ButtonPressed(views::Button* sender, + const ui::Event& event) OVERRIDE; + // SearchResultObserver overrides: virtual void OnIconChanged() OVERRIDE; + virtual void OnActionIconsChanged() OVERRIDE; SearchResult* result_; // Owned by AppListModel::SearchResults. // Parent list view. Owned by views hierarchy. SearchResultListView* list_view_; + // Not owned by us. + SearchResultViewDelegate* delegate_; + views::ImageView* icon_; // Owned by views hierarchy. scoped_ptr<gfx::RenderText> title_text_; scoped_ptr<gfx::RenderText> details_text_; + // Owned by the views hierarchy. + std::vector<views::ImageButton*> action_buttons_; + DISALLOW_COPY_AND_ASSIGN(SearchResultView); }; diff --git a/ui/app_list/search_result_view_delegate.h b/ui/app_list/search_result_view_delegate.h new file mode 100644 index 0000000..9b4b467 --- /dev/null +++ b/ui/app_list/search_result_view_delegate.h @@ -0,0 +1,34 @@ +// 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. + +#ifndef UI_APP_LIST_SEARCH_RESULT_VIEW_DELEGATE_H_ +#define UI_APP_LIST_SEARCH_RESULT_VIEW_DELEGATE_H_ + +namespace ui { +class Event; +} + +namespace app_list { + +class SearchResultView; + +class SearchResultViewDelegate { + public: + // Called when the search result is activated. + virtual void SearchResultActivated(SearchResultView* view, + const ui::Event& event) = 0; + + // Called when one of the search result's optional action icons is activated. + // |action_index| contains the 0-based index of the action. + virtual void SearchResultActionActivated(SearchResultView* view, + int action_index, + const ui::Event& event) = 0; + + protected: + virtual ~SearchResultViewDelegate() {} +}; + +} // namespace ash + +#endif // UI_APP_LIST_SEARCH_RESULT_VIEW_DELEGATE_H_ |