#include "ui/app_list/views/app_list_view.h"
+#include <algorithm>
+
#include "base/command_line.h"
+#include "base/metrics/histogram.h"
#include "base/strings/string_util.h"
#include "base/win/windows_version.h"
#include "ui/app_list/app_list_constants.h"
#include "ui/app_list/app_list_model.h"
+#include "ui/app_list/app_list_switches.h"
#include "ui/app_list/app_list_view_delegate.h"
-#include "ui/app_list/pagination_model.h"
-#include "ui/app_list/signin_delegate.h"
#include "ui/app_list/speech_ui_model.h"
#include "ui/app_list/views/app_list_background.h"
+#include "ui/app_list/views/app_list_folder_view.h"
#include "ui/app_list/views/app_list_main_view.h"
#include "ui/app_list/views/app_list_view_observer.h"
+#include "ui/app_list/views/apps_container_view.h"
+#include "ui/app_list/views/contents_view.h"
#include "ui/app_list/views/search_box_view.h"
-#include "ui/app_list/views/signin_view.h"
#include "ui/app_list/views/speech_view.h"
+#include "ui/app_list/views/start_page_view.h"
#include "ui/base/ui_base_switches.h"
#include "ui/compositor/layer.h"
+#include "ui/compositor/layer_animation_observer.h"
#include "ui/compositor/scoped_layer_animation_settings.h"
+#include "ui/gfx/canvas.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/insets.h"
#include "ui/gfx/path.h"
#include "ui/gfx/skia_util.h"
+#include "ui/resources/grit/ui_resources.h"
#include "ui/views/bubble/bubble_frame_view.h"
-#include "ui/views/bubble/bubble_window_targeter.h"
+#include "ui/views/controls/image_view.h"
#include "ui/views/controls/textfield/textfield.h"
#include "ui/views/layout/fill_layout.h"
#include "ui/views/widget/widget.h"
#if defined(USE_AURA)
#include "ui/aura/window.h"
-#include "ui/aura/root_window.h"
+#include "ui/aura/window_tree_host.h"
+#include "ui/views/bubble/bubble_window_targeter.h"
#if defined(OS_WIN)
#include "ui/base/win/shell.h"
#endif
namespace {
-void (*g_next_paint_callback)();
-
// The margin from the edge to the speech UI.
const int kSpeechUIMargin = 12;
// Determines whether the current environment supports shadows bubble borders.
bool SupportsShadow() {
-#if defined(USE_AURA) && defined(OS_WIN)
- // Shadows are not supported on Windows Aura without Aero Glass.
+#if defined(OS_WIN)
+ // Shadows are not supported on Windows without Aero Glass.
if (!ui::win::IsAeroGlassEnabled() ||
CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kDisableDwmComposition)) {
+ ::switches::kDisableDwmComposition)) {
return false;
}
-#elif defined(OS_LINUX) && !defined(USE_ASH)
+#elif defined(OS_LINUX) && !defined(OS_CHROMEOS)
// Shadows are not supported on (non-ChromeOS) Linux.
return false;
#endif
return true;
}
+// The background for the App List overlay, which appears as a white rounded
+// rectangle with the given radius and the same size as the target view.
+class AppListOverlayBackground : public views::Background {
+ public:
+ AppListOverlayBackground(int corner_radius)
+ : corner_radius_(corner_radius) {};
+ virtual ~AppListOverlayBackground() {};
+
+ // Overridden from views::Background:
+ virtual void Paint(gfx::Canvas* canvas, views::View* view) const OVERRIDE {
+ SkPaint paint;
+ paint.setStyle(SkPaint::kFill_Style);
+ paint.setColor(SK_ColorWHITE);
+ canvas->DrawRoundRect(view->GetContentsBounds(), corner_radius_, paint);
+ }
+
+ private:
+ const int corner_radius_;
+
+ DISALLOW_COPY_AND_ASSIGN(AppListOverlayBackground);
+};
+
} // namespace
// An animation observer to hide the view at the end of the animation.
}
void SetTarget(views::View* target) {
- if (!target_)
+ if (target_)
StopObservingImplicitAnimations();
target_ = target;
}
target_ = NULL;
// Should update the background by invoking SchedulePaint().
- frame_->SchedulePaint();
+ if (frame_)
+ frame_->SchedulePaint();
}
}
AppListView::AppListView(AppListViewDelegate* delegate)
: delegate_(delegate),
app_list_main_view_(NULL),
- signin_view_(NULL),
speech_view_(NULL),
+ experimental_banner_view_(NULL),
+ overlay_view_(NULL),
animation_observer_(new HideViewAnimationObserver()) {
CHECK(delegate);
void AppListView::InitAsBubbleAttachedToAnchor(
gfx::NativeView parent,
- PaginationModel* pagination_model,
+ int initial_apps_page,
views::View* anchor,
const gfx::Vector2d& anchor_offset,
views::BubbleBorder::Arrow arrow,
bool border_accepts_events) {
SetAnchorView(anchor);
InitAsBubbleInternal(
- parent, pagination_model, arrow, border_accepts_events, anchor_offset);
+ parent, initial_apps_page, arrow, border_accepts_events, anchor_offset);
}
void AppListView::InitAsBubbleAtFixedLocation(
gfx::NativeView parent,
- PaginationModel* pagination_model,
+ int initial_apps_page,
const gfx::Point& anchor_point_in_screen,
views::BubbleBorder::Arrow arrow,
bool border_accepts_events) {
SetAnchorView(NULL);
SetAnchorRect(gfx::Rect(anchor_point_in_screen, gfx::Size()));
InitAsBubbleInternal(
- parent, pagination_model, arrow, border_accepts_events, gfx::Vector2d());
+ parent, initial_apps_page, arrow, border_accepts_events, gfx::Vector2d());
}
void AppListView::SetBubbleArrow(views::BubbleBorder::Arrow arrow) {
SizeToContents();
}
-gfx::Size AppListView::GetPreferredSize() {
+void AppListView::SetAppListOverlayVisible(bool visible) {
+ DCHECK(overlay_view_);
+
+ // Display the overlay immediately so we can begin the animation.
+ overlay_view_->SetVisible(true);
+
+ ui::ScopedLayerAnimationSettings settings(
+ overlay_view_->layer()->GetAnimator());
+ settings.SetTweenType(gfx::Tween::LINEAR);
+
+ // If we're dismissing the overlay, hide the view at the end of the animation.
+ if (!visible) {
+ // Since only one animation is visible at a time, it's safe to re-use
+ // animation_observer_ here.
+ animation_observer_->set_frame(NULL);
+ animation_observer_->SetTarget(overlay_view_);
+ settings.AddObserver(animation_observer_.get());
+ }
+
+ const float kOverlayFadeInMilliseconds = 125;
+ settings.SetTransitionDuration(
+ base::TimeDelta::FromMilliseconds(kOverlayFadeInMilliseconds));
+
+ const float kOverlayOpacity = 0.75f;
+ overlay_view_->layer()->SetOpacity(visible ? kOverlayOpacity : 0.0f);
+}
+
+bool AppListView::ShouldCenterWindow() const {
+ return delegate_->ShouldCenterWindow();
+}
+
+gfx::Size AppListView::GetPreferredSize() const {
return app_list_main_view_->GetPreferredSize();
}
-void AppListView::Paint(gfx::Canvas* canvas) {
- views::BubbleDelegateView::Paint(canvas);
- if (g_next_paint_callback) {
- g_next_paint_callback();
- g_next_paint_callback = NULL;
+void AppListView::Paint(gfx::Canvas* canvas, const views::CullSet& cull_set) {
+ views::BubbleDelegateView::Paint(canvas, cull_set);
+ if (!next_paint_callback_.is_null()) {
+ next_paint_callback_.Run();
+ next_paint_callback_.Reset();
}
}
void AppListView::OnThemeChanged() {
-#if defined(USE_AURA) && defined(OS_WIN)
+#if defined(OS_WIN)
GetWidget()->Close();
#endif
}
}
void AppListView::OnProfilesChanged() {
- SigninDelegate* signin_delegate =
- delegate_ ? delegate_->GetSigninDelegate() : NULL;
- bool show_signin_view = signin_delegate && signin_delegate->NeedSignin();
-
- signin_view_->SetVisible(show_signin_view);
- app_list_main_view_->SetVisible(!show_signin_view);
app_list_main_view_->search_box_view()->InvalidateMenu();
}
+void AppListView::OnShutdown() {
+ // Nothing to do on views - the widget will soon be closed, which will tear
+ // everything down.
+}
+
void AppListView::SetProfileByPath(const base::FilePath& profile_path) {
delegate_->SetProfileByPath(profile_path);
app_list_main_view_->ModelChanged();
}
// static
-void AppListView::SetNextPaintCallback(void (*callback)()) {
- g_next_paint_callback = callback;
+void AppListView::SetNextPaintCallback(const base::Closure& callback) {
+ next_paint_callback_ = callback;
}
#if defined(OS_WIN)
HWND AppListView::GetHWND() const {
-#if defined(USE_AURA)
gfx::NativeWindow window =
GetWidget()->GetTopLevelWidget()->GetNativeWindow();
- return window->GetDispatcher()->host()->GetAcceleratedWidget();
-#else
- return GetWidget()->GetTopLevelWidget()->GetNativeWindow();
-#endif
+ return window->GetHost()->GetAcceleratedWidget();
}
#endif
+PaginationModel* AppListView::GetAppsPaginationModel() {
+ return app_list_main_view_->contents_view()
+ ->apps_container_view()
+ ->apps_grid_view()
+ ->pagination_model();
+}
+
void AppListView::InitAsBubbleInternal(gfx::NativeView parent,
- PaginationModel* pagination_model,
+ int initial_apps_page,
views::BubbleBorder::Arrow arrow,
bool border_accepts_events,
const gfx::Vector2d& anchor_offset) {
- app_list_main_view_ = new AppListMainView(delegate_.get(),
- pagination_model,
- parent);
+ base::Time start_time = base::Time::Now();
+
+ app_list_main_view_ =
+ new AppListMainView(delegate_, initial_apps_page, parent);
AddChildView(app_list_main_view_);
-#if defined(USE_AURA)
app_list_main_view_->SetPaintToLayer(true);
app_list_main_view_->SetFillsBoundsOpaquely(false);
app_list_main_view_->layer()->SetMasksToBounds(true);
-#endif
-
- signin_view_ =
- new SigninView(delegate_->GetSigninDelegate(),
- app_list_main_view_->GetPreferredSize().width());
- AddChildView(signin_view_);
// Speech recognition is available only when the start page exists.
- if (delegate_ && delegate_->GetSpeechRecognitionContents()) {
- speech_view_ = new SpeechView(delegate_.get());
+ if (delegate_ && delegate_->IsSpeechRecognitionEnabled()) {
+ speech_view_ = new SpeechView(delegate_);
speech_view_->SetVisible(false);
-#if defined(USE_AURA)
speech_view_->SetPaintToLayer(true);
speech_view_->SetFillsBoundsOpaquely(false);
speech_view_->layer()->SetOpacity(0.0f);
-#endif
AddChildView(speech_view_);
}
+ if (app_list::switches::IsExperimentalAppListEnabled()) {
+ // Draw a banner in the corner of the experimental app list.
+ experimental_banner_view_ = new views::ImageView;
+ const gfx::ImageSkia& experimental_icon =
+ *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
+ IDR_APP_LIST_EXPERIMENTAL_ICON);
+ experimental_banner_view_->SetImage(experimental_icon);
+ experimental_banner_view_->SetPaintToLayer(true);
+ experimental_banner_view_->SetFillsBoundsOpaquely(false);
+ AddChildView(experimental_banner_view_);
+ }
+
OnProfilesChanged();
set_color(kContentsBackgroundColor);
set_margins(gfx::Insets());
- set_move_with_anchor(true);
set_parent_window(parent);
set_close_on_deactivate(false);
set_close_on_esc(false);
GetBubbleFrameView()->bubble_border()->GetBorderCornerRadius(),
app_list_main_view_));
set_background(NULL);
- window->set_event_targeter(scoped_ptr<ui::EventTargeter>(
+ window->SetEventTargeter(scoped_ptr<ui::EventTargeter>(
new views::BubbleWindowTargeter(this)));
#else
set_background(new AppListBackground(
GetWidget()->Hide();
#endif
+ // To make the overlay view, construct a view with a white background, rather
+ // than a white rectangle in it. This is because we need overlay_view_ to be
+ // drawn to its own layer (so it appears correctly in the foreground).
+ overlay_view_ = new views::View();
+ overlay_view_->SetPaintToLayer(true);
+ overlay_view_->SetBoundsRect(GetContentsBounds());
+ overlay_view_->SetVisible(false);
+ overlay_view_->layer()->SetOpacity(0.0f);
+ // On platforms that don't support a shadow, the rounded border of the app
+ // list is constructed _inside_ the view, so a rectangular background goes
+ // over the border in the rounded corners. To fix this, give the background a
+ // corner radius 1px smaller than the outer border, so it just reaches but
+ // doesn't cover it.
+ const int kOverlayCornerRadius =
+ GetBubbleFrameView()->bubble_border()->GetBorderCornerRadius();
+ overlay_view_->set_background(new AppListOverlayBackground(
+ kOverlayCornerRadius - (SupportsShadow() ? 0 : 1)));
+ AddChildView(overlay_view_);
+
if (delegate_)
delegate_->ViewInitialized();
+
+ UMA_HISTOGRAM_TIMES("Apps.AppListCreationTime",
+ base::Time::Now() - start_time);
}
void AppListView::OnBeforeBubbleWidgetInit(
}
views::View* AppListView::GetInitiallyFocusedView() {
- return app_list_main_view_->search_box_view()->search_box();
+ return app_list::switches::IsExperimentalAppListEnabled()
+ ? app_list_main_view_->contents_view()
+ ->start_page_view()
+ ->dummy_search_box_view()
+ ->search_box()
+ : app_list_main_view_->search_box_view()->search_box();
}
gfx::ImageSkia AppListView::GetWindowIcon() {
if (accelerator.key_code() == ui::VKEY_ESCAPE) {
if (app_list_main_view_->search_box_view()->HasSearch()) {
app_list_main_view_->search_box_view()->ClearSearch();
+ } else if (app_list_main_view_->contents_view()
+ ->apps_container_view()
+ ->IsInFolderView()) {
+ app_list_main_view_->contents_view()
+ ->apps_container_view()
+ ->app_list_folder_view()
+ ->CloseFolderPage();
+ return true;
} else {
GetWidget()->Deactivate();
Close();
void AppListView::Layout() {
const gfx::Rect contents_bounds = GetContentsBounds();
app_list_main_view_->SetBoundsRect(contents_bounds);
- signin_view_->SetBoundsRect(contents_bounds);
if (speech_view_) {
gfx::Rect speech_bounds = contents_bounds;
speech_bounds.Inset(-speech_view_->GetInsets());
speech_view_->SetBoundsRect(speech_bounds);
}
+
+ if (experimental_banner_view_) {
+ // Position the experimental banner in the lower right corner.
+ gfx::Rect image_bounds = experimental_banner_view_->GetImageBounds();
+ image_bounds.set_origin(
+ gfx::Point(contents_bounds.width() - image_bounds.width(),
+ contents_bounds.height() - image_bounds.height()));
+ experimental_banner_view_->SetBoundsRect(image_bounds);
+ }
+}
+
+void AppListView::SchedulePaintInRect(const gfx::Rect& rect) {
+ BubbleDelegateView::SchedulePaintInRect(rect);
+ if (GetBubbleFrameView())
+ GetBubbleFrameView()->SchedulePaint();
}
void AppListView::OnWidgetDestroying(views::Widget* widget) {
if (widget != GetWidget())
return;
- // We clear the search when hiding so the next time the app list appears it is
- // not showing search results.
if (!visible)
- app_list_main_view_->search_box_view()->ClearSearch();
-
- // Whether we need to signin or not may have changed since last time we were
- // shown.
- Layout();
+ app_list_main_view_->ResetForShow();
}
void AppListView::OnSpeechRecognitionStateChanged(
SpeechRecognitionState new_state) {
- DCHECK(!signin_view_->visible());
+ if (!speech_view_)
+ return;
- bool recognizing = new_state != SPEECH_RECOGNITION_NOT_STARTED;
+ bool will_appear = (new_state == SPEECH_RECOGNITION_RECOGNIZING ||
+ new_state == SPEECH_RECOGNITION_IN_SPEECH ||
+ new_state == SPEECH_RECOGNITION_NETWORK_ERROR);
// No change for this class.
- if (speech_view_->visible() == recognizing)
+ if (speech_view_->visible() == will_appear)
return;
- if (recognizing)
+ if (will_appear)
speech_view_->Reset();
-#if defined(USE_AURA)
animation_observer_->set_frame(GetBubbleFrameView());
gfx::Transform speech_transform;
speech_transform.Translate(
0, SkFloatToMScalar(kSpeechUIAppearingPosition));
- if (recognizing)
+ if (will_appear)
speech_view_->layer()->SetTransform(speech_transform);
{
ui::ScopedLayerAnimationSettings main_settings(
app_list_main_view_->layer()->GetAnimator());
- if (recognizing) {
+ if (will_appear) {
animation_observer_->SetTarget(app_list_main_view_);
main_settings.AddObserver(animation_observer_.get());
}
- app_list_main_view_->layer()->SetOpacity(recognizing ? 0.0f : 1.0f);
+ app_list_main_view_->layer()->SetOpacity(will_appear ? 0.0f : 1.0f);
}
{
ui::ScopedLayerAnimationSettings speech_settings(
speech_view_->layer()->GetAnimator());
- if (!recognizing) {
+ if (!will_appear) {
animation_observer_->SetTarget(speech_view_);
speech_settings.AddObserver(animation_observer_.get());
}
- speech_view_->layer()->SetOpacity(recognizing ? 1.0f : 0.0f);
- if (recognizing)
+ speech_view_->layer()->SetOpacity(will_appear ? 1.0f : 0.0f);
+ if (will_appear)
speech_view_->layer()->SetTransform(gfx::Transform());
else
speech_view_->layer()->SetTransform(speech_transform);
}
- if (recognizing)
+ if (will_appear)
speech_view_->SetVisible(true);
else
app_list_main_view_->SetVisible(true);
-#else
- speech_view_->SetVisible(recognizing);
- app_list_main_view_->SetVisible(!recognizing);
-
- // Needs to schedule paint of AppListView itself, to repaint the background.
- GetBubbleFrameView()->SchedulePaint();
-#endif
}
} // namespace app_list