blob: ca3fb39f240897a8dd168b76c1251b6f6c2f6ea4 [file] [log] [blame]
// 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 "ash/launcher/launcher.h"
#include <algorithm>
#include <cmath>
#include "ash/focus_cycler.h"
#include "ash/launcher/launcher_delegate.h"
#include "ash/launcher/launcher_model.h"
#include "ash/launcher/launcher_navigator.h"
#include "ash/launcher/launcher_view.h"
#include "ash/root_window_controller.h"
#include "ash/shell.h"
#include "ash/shell_delegate.h"
#include "ash/shell_window_ids.h"
#include "ash/wm/shelf_layout_manager.h"
#include "ash/wm/window_properties.h"
#include "grit/ash_resources.h"
#include "ui/aura/root_window.h"
#include "ui/aura/window.h"
#include "ui/aura/window_observer.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/compositor/layer.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/image/image.h"
#include "ui/views/accessible_pane_view.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_delegate.h"
namespace {
// Size of black border at bottom (or side) of launcher.
const int kNumBlackPixels = 3;
// Alpha to paint dimming image with.
const int kDimAlpha = 96;
}
namespace ash {
// The contents view of the Widget. This view contains LauncherView and
// sizes it to the width of the widget minus the size of the status area.
class Launcher::DelegateView : public views::WidgetDelegate,
public views::AccessiblePaneView,
public internal::BackgroundAnimatorDelegate {
public:
explicit DelegateView(Launcher* launcher);
virtual ~DelegateView();
void set_focus_cycler(internal::FocusCycler* focus_cycler) {
focus_cycler_ = focus_cycler;
}
internal::FocusCycler* focus_cycler() {
return focus_cycler_;
}
// views::View overrides
virtual gfx::Size GetPreferredSize() OVERRIDE;
virtual void Layout() OVERRIDE;
virtual void OnPaintBackground(gfx::Canvas* canvas) OVERRIDE;
// views::WidgetDelegateView overrides:
virtual views::Widget* GetWidget() OVERRIDE {
return View::GetWidget();
}
virtual const views::Widget* GetWidget() const OVERRIDE {
return View::GetWidget();
}
virtual bool CanActivate() const OVERRIDE {
// We don't want mouse clicks to activate us, but we need to allow
// activation when the user is using the keyboard (FocusCycler).
return focus_cycler_ && focus_cycler_->widget_activating() == GetWidget();
}
// BackgroundAnimatorDelegate overrides:
virtual void UpdateBackground(int alpha) OVERRIDE;
private:
Launcher* launcher_;
internal::FocusCycler* focus_cycler_;
int alpha_;
DISALLOW_COPY_AND_ASSIGN(DelegateView);
};
// Class used to slightly dim shelf items when maximized and visible. It also
// makes sure the widget changes size to always be of the same size as the
// shelf.
class DimmerView : public views::WidgetDelegateView,
public aura::WindowObserver {
public:
explicit DimmerView(views::Widget* launcher)
: launcher_(launcher) {
launcher_->GetNativeWindow()->AddObserver(this);
}
~DimmerView() {
if (launcher_)
launcher_->GetNativeWindow()->RemoveObserver(this);
}
private:
// views::View overrides:
virtual void OnPaintBackground(gfx::Canvas* canvas) OVERRIDE {
ResourceBundle& rb = ResourceBundle::GetSharedInstance();
const gfx::ImageSkia* launcher_background = rb.GetImageSkiaNamed(
internal::ShelfLayoutManager::ForLauncher(
launcher_->GetNativeView())->
SelectValueForShelfAlignment(IDR_AURA_LAUNCHER_DIMMING_BOTTOM,
IDR_AURA_LAUNCHER_DIMMING_LEFT,
IDR_AURA_LAUNCHER_DIMMING_RIGHT));
SkPaint paint;
paint.setAlpha(kDimAlpha);
canvas->DrawImageInt(
*launcher_background,
0, 0, launcher_background->width(), launcher_background->height(),
0, 0, width(), height(),
false,
paint);
}
// aura::WindowObserver overrides:
virtual void OnWindowBoundsChanged(aura::Window* window,
const gfx::Rect& old_bounds,
const gfx::Rect& new_bounds) OVERRIDE {
CHECK_EQ(window, launcher_->GetNativeWindow());
GetWidget()->GetNativeWindow()->SetBounds(window->bounds());
}
virtual void OnWindowDestroying(aura::Window* window) OVERRIDE {
CHECK_EQ(window, launcher_->GetNativeWindow());
launcher_->GetNativeWindow()->RemoveObserver(this);
launcher_ = NULL;
}
views::Widget* launcher_;
DISALLOW_COPY_AND_ASSIGN(DimmerView);
};
Launcher::DelegateView::DelegateView(Launcher* launcher)
: launcher_(launcher),
focus_cycler_(NULL),
alpha_(0) {
}
Launcher::DelegateView::~DelegateView() {
}
gfx::Size Launcher::DelegateView::GetPreferredSize() {
return child_count() > 0 ? child_at(0)->GetPreferredSize() : gfx::Size();
}
void Launcher::DelegateView::Layout() {
if (child_count() == 0)
return;
View* launcher_view = child_at(0);
if (launcher_->alignment_ == SHELF_ALIGNMENT_BOTTOM) {
int w = std::max(0, width() - launcher_->status_size_.width());
launcher_view->SetBounds(0, 0, w, height());
} else {
int h = std::max(0, height() - launcher_->status_size_.height());
launcher_view->SetBounds(0, 0, width(), h);
}
}
void Launcher::DelegateView::OnPaintBackground(gfx::Canvas* canvas) {
if (launcher_->alignment_ == SHELF_ALIGNMENT_BOTTOM) {
SkPaint paint;
ResourceBundle& rb = ResourceBundle::GetSharedInstance();
const gfx::ImageSkia* launcher_background = rb.GetImageSkiaNamed(
internal::ShelfLayoutManager::ForLauncher(
launcher_->widget()->GetNativeView())->
SelectValueForShelfAlignment(IDR_AURA_LAUNCHER_BACKGROUND_BOTTOM,
IDR_AURA_LAUNCHER_BACKGROUND_LEFT,
IDR_AURA_LAUNCHER_BACKGROUND_RIGHT));
paint.setAlpha(alpha_);
canvas->DrawImageInt(
*launcher_background,
0, 0, launcher_background->width(), launcher_background->height(),
0, 0, width(), height(),
false,
paint);
canvas->FillRect(
gfx::Rect(0, height() - kNumBlackPixels, width(), kNumBlackPixels),
SK_ColorBLACK);
} else {
// TODO(davemoore): when we get an image for the side launcher background
// use it, and handle black border.
canvas->DrawColor(SkColorSetARGB(alpha_, 0, 0, 0));
}
}
void Launcher::DelegateView::UpdateBackground(int alpha) {
alpha_ = alpha;
SchedulePaint();
}
// Launcher --------------------------------------------------------------------
Launcher::Launcher(aura::Window* window_container,
internal::ShelfLayoutManager* shelf_layout_manager)
: widget_(NULL),
window_container_(window_container),
delegate_view_(new DelegateView(this)),
launcher_view_(NULL),
alignment_(SHELF_ALIGNMENT_BOTTOM),
background_animator_(delegate_view_, 0, kLauncherBackgroundAlpha) {
model_.reset(new LauncherModel);
delegate_.reset(
Shell::GetInstance()->delegate()->CreateLauncherDelegate(model_.get()));
widget_.reset(new views::Widget);
views::Widget::InitParams params(
views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
params.transparent = true;
params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
params.parent = Shell::GetContainer(
window_container_->GetRootWindow(),
ash::internal::kShellWindowId_LauncherContainer);
launcher_view_ = new internal::LauncherView(
model_.get(), delegate_.get(), shelf_layout_manager);
launcher_view_->Init();
delegate_view_->AddChildView(launcher_view_);
params.delegate = delegate_view_;
widget_->Init(params);
widget_->GetNativeWindow()->SetName("LauncherWindow");
// The launcher should not take focus when it is initially shown.
widget_->set_focus_on_creation(false);
widget_->SetContentsView(delegate_view_);
widget_->GetNativeView()->SetName("LauncherView");
widget_->GetNativeView()->SetProperty(internal::kStayInSameRootWindowKey,
true);
// SetBounds() has to be called after kStayInSameRootWindowKey is set.
gfx::Size pref =
static_cast<views::View*>(launcher_view_)->GetPreferredSize();
widget_->SetBounds(gfx::Rect(pref));
}
Launcher::~Launcher() {
}
// static
Launcher* Launcher::ForPrimaryDisplay() {
return internal::RootWindowController::ForLauncher(
Shell::GetPrimaryRootWindow())->launcher();
}
// static
Launcher* Launcher::ForWindow(aura::Window* window) {
return internal::RootWindowController::ForLauncher(window)->launcher();
}
void Launcher::SetFocusCycler(internal::FocusCycler* focus_cycler) {
delegate_view_->set_focus_cycler(focus_cycler);
if (focus_cycler)
focus_cycler->AddWidget(widget_.get());
}
internal::FocusCycler* Launcher::GetFocusCycler() {
return delegate_view_->focus_cycler();
}
void Launcher::SetAlignment(ShelfAlignment alignment) {
alignment_ = alignment;
launcher_view_->OnShelfAlignmentChanged();
// ShelfLayoutManager will resize the launcher.
}
void Launcher::SetPaintsBackground(
bool value,
internal::BackgroundAnimator::ChangeType change_type) {
background_animator_.SetPaintsBackground(value, change_type);
}
void Launcher::SetDimsShelf(bool value) {
if (value == (dimmer_.get() != NULL))
return;
if (!value) {
dimmer_.reset();
return;
}
dimmer_.reset(new views::Widget);
views::Widget::InitParams params(
views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
params.transparent = true;
params.can_activate = false;
params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
params.parent = Shell::GetContainer(
window_container_->GetRootWindow(),
ash::internal::kShellWindowId_LauncherContainer);
params.accept_events = false;
dimmer_->Init(params);
dimmer_->GetNativeWindow()->SetName("LauncherDimmer");
dimmer_->SetBounds(widget_->GetWindowBoundsInScreen());
// The launcher should not take focus when it is initially shown.
dimmer_->set_focus_on_creation(false);
dimmer_->SetContentsView(new DimmerView(widget_.get()));
dimmer_->GetNativeView()->SetName("LauncherDimmerView");
dimmer_->GetNativeView()->SetProperty(internal::kStayInSameRootWindowKey,
true);
dimmer_->Show();
}
bool Launcher::GetDimsShelf() const {
return dimmer_.get() && dimmer_->IsVisible();
}
void Launcher::SetStatusSize(const gfx::Size& size) {
if (status_size_ == size)
return;
status_size_ = size;
delegate_view_->Layout();
}
gfx::Rect Launcher::GetScreenBoundsOfItemIconForWindow(aura::Window* window) {
LauncherID id = delegate_->GetIDByWindow(window);
gfx::Rect bounds(launcher_view_->GetIdealBoundsOfItemIcon(id));
if (bounds.IsEmpty())
return bounds;
gfx::Point screen_origin;
views::View::ConvertPointToScreen(launcher_view_, &screen_origin);
return gfx::Rect(screen_origin.x() + bounds.x(),
screen_origin.y() + bounds.y(),
bounds.width(),
bounds.height());
}
void Launcher::ActivateLauncherItem(int index) {
DCHECK(delegate_.get());
const ash::LauncherItems& items = model_->items();
delegate_->ItemClicked(items[index], ui::EF_NONE);
}
void Launcher::CycleWindowLinear(CycleDirection direction) {
int item_index = GetNextActivatedItemIndex(*model(), direction);
if (item_index >= 0)
ActivateLauncherItem(item_index);
}
void Launcher::AddIconObserver(LauncherIconObserver* observer) {
launcher_view_->AddIconObserver(observer);
}
void Launcher::RemoveIconObserver(LauncherIconObserver* observer) {
launcher_view_->RemoveIconObserver(observer);
}
bool Launcher::IsShowingMenu() const {
return launcher_view_->IsShowingMenu();
}
void Launcher::ShowContextMenu(const gfx::Point& location) {
launcher_view_->ShowContextMenu(location, false);
}
bool Launcher::IsShowingOverflowBubble() const {
return launcher_view_->IsShowingOverflowBubble();
}
void Launcher::SetVisible(bool visible) const {
delegate_view_->SetVisible(visible);
}
views::View* Launcher::GetAppListButtonView() const {
return launcher_view_->GetAppListButtonView();
}
void Launcher::SetWidgetBounds(const gfx::Rect bounds) {
widget_->SetBounds(bounds);
if (dimmer_.get())
dimmer_->SetBounds(bounds);
}
internal::LauncherView* Launcher::GetLauncherViewForTest() {
return launcher_view_;
}
} // namespace ash