diff options
author | sadrul@chromium.org <sadrul@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-08-14 03:53:45 +0000 |
---|---|---|
committer | sadrul@chromium.org <sadrul@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-08-14 03:55:03 +0000 |
commit | 1d359848a45de76562b8f62d78dd6599a5897653 (patch) | |
tree | 669d1240f3a09e0f2ef88bf496194e766448b7a8 | |
parent | 34afc93c435d2155a3a43e278fec32eff87d20dc (diff) | |
download | chromium_src-1d359848a45de76562b8f62d78dd6599a5897653.zip chromium_src-1d359848a45de76562b8f62d78dd6599a5897653.tar.gz chromium_src-1d359848a45de76562b8f62d78dd6599a5897653.tar.bz2 |
athena: Allow getting into split-view mode from overview-mode.
When a window is dragged horizontally in the overview-mode, create an
OverviewToolbar object. The toolbar shows a couple of ActionButtons for 'close'
and 'split' options. If the user drags (and drops) the window over the 'split'
button, then the split-view mode is triggered. The buttons are highlighted
(scaled up 50%) to indicate the action that will be performed.
BUG=402598
R=mfomitchev@chromium.org, mukai@chromium.org
Review URL: https://codereview.chromium.org/459613008
Cr-Commit-Position: refs/heads/master@{#289454}
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@289454 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | athena/athena.gyp | 3 | ||||
-rw-r--r-- | athena/resources/athena_resources.grd | 2 | ||||
-rw-r--r-- | athena/resources/default_100_percent/overview_split.png | bin | 0 -> 4264 bytes | |||
-rw-r--r-- | athena/resources/default_100_percent/overview_trash.png | bin | 0 -> 4583 bytes | |||
-rw-r--r-- | athena/wm/DEPS | 1 | ||||
-rw-r--r-- | athena/wm/overview_toolbar.cc | 196 | ||||
-rw-r--r-- | athena/wm/overview_toolbar.h | 68 | ||||
-rw-r--r-- | athena/wm/split_view_controller.cc | 37 | ||||
-rw-r--r-- | athena/wm/split_view_controller.h | 5 | ||||
-rw-r--r-- | athena/wm/window_manager_impl.cc | 8 | ||||
-rw-r--r-- | athena/wm/window_overview_mode.cc | 74 | ||||
-rw-r--r-- | athena/wm/window_overview_mode.h | 6 |
12 files changed, 388 insertions, 12 deletions
diff --git a/athena/athena.gyp b/athena/athena.gyp index 87a175a..a8ae58c 100644 --- a/athena/athena.gyp +++ b/athena/athena.gyp @@ -18,6 +18,7 @@ '../ui/events/events.gyp:events_base', '../ui/strings/ui_strings.gyp:ui_strings', '../ui/views/views.gyp:views', + 'resources/athena_resources.gyp:athena_resources', ], 'defines': [ 'ATHENA_IMPLEMENTATION', @@ -73,6 +74,8 @@ 'wm/bezel_controller.h', 'wm/mru_window_tracker.cc', 'wm/mru_window_tracker.h', + 'wm/overview_toolbar.cc', + 'wm/overview_toolbar.h', 'wm/public/window_list_provider.h', 'wm/public/window_manager.h', 'wm/public/window_manager_observer.h', diff --git a/athena/resources/athena_resources.grd b/athena/resources/athena_resources.grd index ee1665a..12e2ff4 100644 --- a/athena/resources/athena_resources.grd +++ b/athena/resources/athena_resources.grd @@ -11,6 +11,8 @@ <structures fallback_to_low_resolution="true"> <!-- KEEP THESE IN ALPHABETICAL ORDER! --> <structure type="chrome_scaled_image" name="IDR_ATHENA_BACKGROUND" file="background_wallpaper.png" /> + <structure type="chrome_scaled_image" name="IDR_ATHENA_OVERVIEW_SPLIT" file="overview_split.png" /> + <structure type="chrome_scaled_image" name="IDR_ATHENA_OVERVIEW_TRASH" file="overview_trash.png" /> </structures> </release> </grit> diff --git a/athena/resources/default_100_percent/overview_split.png b/athena/resources/default_100_percent/overview_split.png Binary files differnew file mode 100644 index 0000000..3874397 --- /dev/null +++ b/athena/resources/default_100_percent/overview_split.png diff --git a/athena/resources/default_100_percent/overview_trash.png b/athena/resources/default_100_percent/overview_trash.png Binary files differnew file mode 100644 index 0000000..c8ffde1 --- /dev/null +++ b/athena/resources/default_100_percent/overview_trash.png diff --git a/athena/wm/DEPS b/athena/wm/DEPS index a3cece5..2777ab4 100644 --- a/athena/wm/DEPS +++ b/athena/wm/DEPS @@ -1,6 +1,7 @@ include_rules = [ "+athena/common", "+athena/input/public", + "+athena/resources", "+athena/screen/public", "+ui/aura", "+ui/base", diff --git a/athena/wm/overview_toolbar.cc b/athena/wm/overview_toolbar.cc new file mode 100644 index 0000000..5c7de40 --- /dev/null +++ b/athena/wm/overview_toolbar.cc @@ -0,0 +1,196 @@ +// Copyright 2014 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 "athena/wm/overview_toolbar.h" + +#include "athena/common/closure_animation_observer.h" +#include "athena/resources/grit/athena_resources.h" +#include "base/bind.h" +#include "base/strings/utf_string_conversions.h" +#include "base/time/time.h" +#include "ui/aura/window.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/compositor/layer.h" +#include "ui/compositor/layer_delegate.h" +#include "ui/compositor/scoped_layer_animation_settings.h" +#include "ui/events/event.h" +#include "ui/gfx/canvas.h" +#include "ui/gfx/transform.h" + +namespace { + +const int kActionButtonImageSize = 54; +const int kActionButtonTextSize = 20; +const int kActionButtonPaddingFromRight = 32; +} + +namespace athena { + +class ActionButton : public ui::LayerDelegate { + public: + ActionButton(int resource_id, const std::string& label) + : resource_id_(resource_id), label_(base::UTF8ToUTF16(label)) { + layer_.reset(new ui::Layer(ui::LAYER_TEXTURED)); + layer_->set_delegate(this); + layer_->SetFillsBoundsOpaquely(false); + layer_->SetVisible(true); + layer_->SetOpacity(0); + } + + virtual ~ActionButton() {} + + static void DestroyAfterFadeout(scoped_ptr<ActionButton> button) { + ui::Layer* layer = button->layer(); + ui::ScopedLayerAnimationSettings settings(layer->GetAnimator()); + settings.AddObserver(new ClosureAnimationObserver( + base::Bind(&ActionButton::DestroyImmediately, base::Passed(&button)))); + layer->SetOpacity(0); + } + + void SetPosition(const gfx::Point& position) { + layer_->SetBounds( + gfx::Rect(position, + gfx::Size(kActionButtonImageSize, + kActionButtonImageSize + kActionButtonTextSize))); + } + + ui::Layer* layer() { return layer_.get(); } + + private: + static void DestroyImmediately(scoped_ptr<ActionButton> button) { + button.reset(); + } + + // ui::LayerDelegate: + virtual void OnPaintLayer(gfx::Canvas* canvas) OVERRIDE { + ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); + canvas->DrawImageInt(*bundle.GetImageSkiaNamed(resource_id_), 0, 0); + gfx::ShadowValues shadow; + shadow.push_back(gfx::ShadowValue(gfx::Point(0, 1), 2, SK_ColorBLACK)); + shadow.push_back(gfx::ShadowValue(gfx::Point(0, -1), 2, SK_ColorBLACK)); + canvas->DrawStringRectWithShadows(label_, + gfx::FontList(), + SK_ColorWHITE, + gfx::Rect(0, + kActionButtonImageSize, + kActionButtonImageSize, + kActionButtonTextSize), + 0, + gfx::Canvas::TEXT_ALIGN_CENTER, + shadow); + } + + virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE {} + virtual base::Closure PrepareForLayerBoundsChange() OVERRIDE { + return base::Closure(); + } + + int resource_id_; + base::string16 label_; + scoped_ptr<ui::Layer> layer_; + + DISALLOW_COPY_AND_ASSIGN(ActionButton); +}; + +OverviewToolbar::OverviewToolbar(aura::Window* container) + : shown_(false), + close_(new ActionButton(IDR_ATHENA_OVERVIEW_TRASH, "Close")), + split_(new ActionButton(IDR_ATHENA_OVERVIEW_SPLIT, "Split")), + current_action_(ACTION_TYPE_NONE), + container_bounds_(container->bounds()) { + const int kPaddingFromBottom = 200; + const int kPaddingBetweenButtons = 200; + + int x = container_bounds_.right() - + (kActionButtonPaddingFromRight + kActionButtonImageSize); + int y = container_bounds_.bottom() - + (kPaddingFromBottom + kActionButtonImageSize); + split_->SetPosition(gfx::Point(x, y)); + y -= kPaddingBetweenButtons; + close_->SetPosition(gfx::Point(x, y)); + + container->layer()->Add(split_->layer()); + container->layer()->Add(close_->layer()); +} + +OverviewToolbar::~OverviewToolbar() { + // If the buttons are visible, then fade them out, instead of destroying them + // immediately. + if (shown_) { + ActionButton::DestroyAfterFadeout(split_.Pass()); + ActionButton::DestroyAfterFadeout(close_.Pass()); + } +} + +OverviewToolbar::ActionType OverviewToolbar::GetHighlightAction( + const ui::GestureEvent& event) const { + if (IsEventOverButton(split_.get(), event)) + return ACTION_TYPE_SPLIT; + if (IsEventOverButton(close_.get(), event)) + return ACTION_TYPE_CLOSE; + return ACTION_TYPE_NONE; +} + +void OverviewToolbar::SetHighlightAction(ActionType action) { + if (current_action_ == action) + return; + current_action_ = action; + if (!shown_) { + ShowActionButtons(); + } else { + TransformButton(close_.get()); + TransformButton(split_.get()); + } +} + +void OverviewToolbar::ShowActionButtons() { + if (!shown_) + ToggleActionButtonsVisibility(); +} + +void OverviewToolbar::HideActionButtons() { + if (shown_) + ToggleActionButtonsVisibility(); +} + +void OverviewToolbar::ToggleActionButtonsVisibility() { + shown_ = !shown_; + TransformButton(close_.get()); + TransformButton(split_.get()); +} + +bool OverviewToolbar::IsEventOverButton(ActionButton* button, + const ui::GestureEvent& event) const { + const int kBoundsInsetForTarget = 30; + gfx::RectF bounds = button->layer()->bounds(); + bounds.Inset(-kBoundsInsetForTarget, -kBoundsInsetForTarget); + return bounds.Contains(event.location()); +} + +gfx::Transform OverviewToolbar::ComputeTransformFor( + ActionButton* button) const { + if (!shown_) + return gfx::Transform(); + + const float kHighlightScale = 1.5; + bool button_is_highlighted = + (current_action_ == ACTION_TYPE_CLOSE && button == close_.get()) || + (current_action_ == ACTION_TYPE_SPLIT && button == split_.get()); + gfx::Transform transform; + if (button_is_highlighted) { + transform.Translate(-kActionButtonImageSize * (kHighlightScale - 1) / 2, 0); + transform.Scale(kHighlightScale, kHighlightScale); + } + return transform; +} + +void OverviewToolbar::TransformButton(ActionButton* button) { + ui::ScopedLayerAnimationSettings split_settings( + button->layer()->GetAnimator()); + split_settings.SetTweenType(gfx::Tween::SMOOTH_IN_OUT); + button->layer()->SetTransform(ComputeTransformFor(button)); + button->layer()->SetOpacity(shown_ ? 1 : 0); +} + +} // namespace athena diff --git a/athena/wm/overview_toolbar.h b/athena/wm/overview_toolbar.h new file mode 100644 index 0000000..a6fdb3b --- /dev/null +++ b/athena/wm/overview_toolbar.h @@ -0,0 +1,68 @@ +// Copyright 2014 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 ATHENA_WM_OVERVIEW_TOOLBAR_H_ +#define ATHENA_WM_OVERVIEW_TOOLBAR_H_ + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "ui/gfx/geometry/rect.h" + +namespace aura { +class Window; +} + +namespace gfx { +class Transform; +} + +namespace ui { +class GestureEvent; +} + +namespace athena { + +class ActionButton; + +// Responsible for showing action-buttons at the right edge of the screen during +// overview mode. +class OverviewToolbar { + public: + enum ActionType { + ACTION_TYPE_NONE, + ACTION_TYPE_CLOSE, + ACTION_TYPE_SPLIT, + }; + + explicit OverviewToolbar(aura::Window* container); + virtual ~OverviewToolbar(); + + ActionType current_action() const { return current_action_; } + + // Returns the action the gesture-event is targeting. + ActionType GetHighlightAction(const ui::GestureEvent& event) const; + + void SetHighlightAction(ActionType action); + void ShowActionButtons(); + void HideActionButtons(); + + private: + void ToggleActionButtonsVisibility(); + bool IsEventOverButton(ActionButton* button, + const ui::GestureEvent& event) const; + gfx::Transform ComputeTransformFor(ActionButton* button) const; + void TransformButton(ActionButton* button); + + bool shown_; + scoped_ptr<ActionButton> close_; + scoped_ptr<ActionButton> split_; + ActionType current_action_; + const gfx::Rect container_bounds_; + + DISALLOW_COPY_AND_ASSIGN(OverviewToolbar); +}; + +} // namespace athena + +#endif // ATHENA_WM_OVERVIEW_TOOLBAR_H_ diff --git a/athena/wm/split_view_controller.cc b/athena/wm/split_view_controller.cc index 595a61a..cde4657 100644 --- a/athena/wm/split_view_controller.cc +++ b/athena/wm/split_view_controller.cc @@ -67,6 +67,43 @@ bool SplitViewController::IsSplitViewModeActive() const { return state_ == ACTIVE; } +void SplitViewController::ActivateSplitMode(aura::Window* left, + aura::Window* right) { + aura::Window::Windows windows = window_list_provider_->GetWindowList(); + aura::Window::Windows::reverse_iterator iter = windows.rbegin(); + if (state_ == ACTIVE) { + if (!left) + left = left_window_; + if (!right) + right = right_window_; + } + + if (!left && iter != windows.rend()) { + left = *iter; + iter++; + if (left == right && iter != windows.rend()) { + left = *iter; + iter++; + } + } + + if (!right && iter != windows.rend()) { + right = *iter; + iter++; + if (right == left && iter != windows.rend()) { + right = *iter; + iter++; + } + } + + state_ = ACTIVE; + left_window_ = left; + right_window_ = right; + container_->StackChildAtTop(right_window_); + container_->StackChildAtTop(left_window_); + UpdateLayout(true); +} + void SplitViewController::UpdateLayout(bool animate) { if (!left_window_) return; diff --git a/athena/wm/split_view_controller.h b/athena/wm/split_view_controller.h index 337b75b..d7dc20c 100644 --- a/athena/wm/split_view_controller.h +++ b/athena/wm/split_view_controller.h @@ -33,6 +33,11 @@ class SplitViewController : public BezelController::ScrollDelegate, bool IsSplitViewModeActive() const; + // Activates split-view mode with |left| and |right| windows. If |left| and/or + // |right| is NULL, then the first window in the window-list (which is neither + // |left| nor |right|) is selected instead. + void ActivateSplitMode(aura::Window* left, aura::Window* right); + private: enum State { // Split View mode is not active. |left_window_| and |right_window| are diff --git a/athena/wm/window_manager_impl.cc b/athena/wm/window_manager_impl.cc index 37daf43..4d75263 100644 --- a/athena/wm/window_manager_impl.cc +++ b/athena/wm/window_manager_impl.cc @@ -60,6 +60,8 @@ class WindowManagerImpl : public WindowManager, // WindowOverviewModeDelegate: virtual void OnSelectWindow(aura::Window* window) OVERRIDE; + virtual void OnSplitViewMode(aura::Window* left, + aura::Window* right) OVERRIDE; // aura::WindowObserver virtual void OnWindowAdded(aura::Window* new_window) OVERRIDE; @@ -211,6 +213,12 @@ void WindowManagerImpl::OnSelectWindow(aura::Window* window) { SetInOverview(false); } +void WindowManagerImpl::OnSplitViewMode(aura::Window* left, + aura::Window* right) { + SetInOverview(false); + split_view_controller_->ActivateSplitMode(left, right); +} + void WindowManagerImpl::OnWindowAdded(aura::Window* new_window) { if (new_window->type() == ui::wm::WINDOW_TYPE_NORMAL) SetInOverview(false); diff --git a/athena/wm/window_overview_mode.cc b/athena/wm/window_overview_mode.cc index 91ee8c9..b36ec11 100644 --- a/athena/wm/window_overview_mode.cc +++ b/athena/wm/window_overview_mode.cc @@ -9,6 +9,7 @@ #include <vector> #include "athena/common/closure_animation_observer.h" +#include "athena/wm/overview_toolbar.h" #include "athena/wm/public/window_list_provider.h" #include "base/bind.h" #include "base/macros.h" @@ -290,6 +291,7 @@ class WindowOverviewModeImpl : public WindowOverviewMode, void DragWindow(const ui::GestureEvent& event) { CHECK(dragged_window_); CHECK_EQ(ui::ET_GESTURE_SCROLL_UPDATE, event.type()); + CHECK(overview_toolbar_); gfx::Vector2dF dragged_distance = dragged_start_location_ - event.location(); WindowOverviewState* dragged_state = @@ -299,11 +301,50 @@ class WindowOverviewModeImpl : public WindowOverviewMode, transform.Translate(-dragged_distance.x(), 0); dragged_window_->SetTransform(transform); + // Update the toolbar. + const int kMinDistanceForActionButtons = 20; + if (fabs(dragged_distance.x()) > kMinDistanceForActionButtons) + overview_toolbar_->ShowActionButtons(); + else + overview_toolbar_->HideActionButtons(); + + // See if the touch-point is above one of the action-buttons. + OverviewToolbar::ActionType new_action = + overview_toolbar_->GetHighlightAction(event); + + // If the touch-point is not above any of the action buttons, then highlight + // the close-button by default, if the user has dragged enough to close the + // window. + if (new_action == OverviewToolbar::ACTION_TYPE_NONE) { + if (fabs(dragged_distance.x()) > kMinDistanceForDismissal) + new_action = OverviewToolbar::ACTION_TYPE_CLOSE; + else + new_action = OverviewToolbar::ACTION_TYPE_NONE; + } + OverviewToolbar::ActionType previous_action = + overview_toolbar_->current_action(); + overview_toolbar_->SetHighlightAction(new_action); + + // If the user has selected to get into split-view mode, then show the + // window with full opacity. Otherwise, fade it out as it closes. Animate + // the opacity if transitioning to/from the split-view button. + bool animate_opacity = + (new_action != previous_action) && + ((new_action == OverviewToolbar::ACTION_TYPE_SPLIT) || + (previous_action == OverviewToolbar::ACTION_TYPE_SPLIT)); float ratio = std::min( 1.f, std::abs(dragged_distance.x()) / kMinDistanceForDismissal); float opacity = - gfx::Tween::FloatValueBetween(ratio, kMaxOpacity, kMinOpacity); - dragged_window_->layer()->SetOpacity(opacity); + (new_action == OverviewToolbar::ACTION_TYPE_SPLIT) + ? 1 + : gfx::Tween::FloatValueBetween(ratio, kMaxOpacity, kMinOpacity); + if (animate_opacity) { + ui::ScopedLayerAnimationSettings settings( + dragged_window_->layer()->GetAnimator()); + dragged_window_->layer()->SetOpacity(opacity); + } else { + dragged_window_->layer()->SetOpacity(opacity); + } } bool ShouldCloseDragWindow(const ui::GestureEvent& event) const { @@ -382,6 +423,19 @@ class WindowOverviewModeImpl : public WindowOverviewMode, dragged_window_ = NULL; } + void EndDragWindow(const ui::GestureEvent& gesture) { + CHECK(dragged_window_); + CHECK(overview_toolbar_); + OverviewToolbar::ActionType action = overview_toolbar_->current_action(); + overview_toolbar_.reset(); + if (action == OverviewToolbar::ACTION_TYPE_SPLIT) + delegate_->OnSplitViewMode(NULL, dragged_window_); + else if (ShouldCloseDragWindow(gesture)) + CloseDragWindow(gesture); + else + RestoreDragWindow(); + } + // ui::EventHandler: virtual void OnMouseEvent(ui::MouseEvent* mouse) OVERRIDE { if (mouse->type() == ui::ET_MOUSE_PRESSED) { @@ -412,6 +466,8 @@ class WindowOverviewModeImpl : public WindowOverviewMode, std::abs(gesture->details().scroll_y_hint()) * 2) { dragged_start_location_ = gesture->location(); dragged_window_ = SelectWindowAt(gesture); + if (dragged_window_) + overview_toolbar_.reset(new OverviewToolbar(container_)); } } else if (gesture->type() == ui::ET_GESTURE_SCROLL_UPDATE) { if (dragged_window_) @@ -420,19 +476,12 @@ class WindowOverviewModeImpl : public WindowOverviewMode, DoScroll(gesture->details().scroll_y()); gesture->SetHandled(); } else if (gesture->type() == ui::ET_GESTURE_SCROLL_END) { - if (dragged_window_) { - if (ShouldCloseDragWindow(*gesture)) - CloseDragWindow(*gesture); - else - RestoreDragWindow(); - } + if (dragged_window_) + EndDragWindow(*gesture); gesture->SetHandled(); } else if (gesture->type() == ui::ET_SCROLL_FLING_START) { if (dragged_window_) { - if (ShouldCloseDragWindow(*gesture)) - CloseDragWindow(*gesture); - else - RestoreDragWindow(); + EndDragWindow(*gesture); } else { CreateFlingerFor(*gesture); AddAnimationObserver(); @@ -477,6 +526,7 @@ class WindowOverviewModeImpl : public WindowOverviewMode, aura::Window* dragged_window_; gfx::Point dragged_start_location_; + scoped_ptr<OverviewToolbar> overview_toolbar_; DISALLOW_COPY_AND_ASSIGN(WindowOverviewModeImpl); }; diff --git a/athena/wm/window_overview_mode.h b/athena/wm/window_overview_mode.h index a95a0fb..157b525 100644 --- a/athena/wm/window_overview_mode.h +++ b/athena/wm/window_overview_mode.h @@ -16,6 +16,12 @@ class WindowOverviewModeDelegate { virtual ~WindowOverviewModeDelegate() {} virtual void OnSelectWindow(aura::Window* window) = 0; + + // Gets into split-view mode with |left| on the left-side of the screen, and + // |right| on the right-side. If |left| or |right| is NULL, then the delegate + // selects the best option in its place. + virtual void OnSplitViewMode(aura::Window* left, + aura::Window* right) = 0; }; class WindowOverviewMode { |