diff options
author | skuhne@chromium.org <skuhne@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-08-04 04:39:39 +0000 |
---|---|---|
committer | skuhne@chromium.org <skuhne@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-08-04 04:39:39 +0000 |
commit | 9f9bb4144867f679e6682833d17e4e112e3abc9c (patch) | |
tree | dc28dd777d32c9326311b673728234e111bceb16 /ash | |
parent | 79a3233afcaadb7cca24a0494b0ec8eebbd245bf (diff) | |
download | chromium_src-9f9bb4144867f679e6682833d17e4e112e3abc9c.zip chromium_src-9f9bb4144867f679e6682833d17e4e112e3abc9c.tar.gz chromium_src-9f9bb4144867f679e6682833d17e4e112e3abc9c.tar.bz2 |
Adding new maximize menu according to specification.
BUG=132686
TEST=Visually tested
Review URL: https://chromiumcodereview.appspot.com/10823025
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@150012 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ash')
-rw-r--r-- | ash/ash.gyp | 3 | ||||
-rw-r--r-- | ash/ash_strings.grd | 19 | ||||
-rw-r--r-- | ash/wm/frame_painter.cc | 22 | ||||
-rw-r--r-- | ash/wm/maximize_bubble_controller.cc | 682 | ||||
-rw-r--r-- | ash/wm/maximize_bubble_controller.h | 88 | ||||
-rw-r--r-- | ash/wm/workspace/frame_maximize_button.cc | 171 | ||||
-rw-r--r-- | ash/wm/workspace/frame_maximize_button.h | 51 | ||||
-rw-r--r-- | ash/wm/workspace/phantom_window_controller.cc | 21 | ||||
-rw-r--r-- | ash/wm/workspace/phantom_window_controller.h | 11 | ||||
-rw-r--r-- | ash/wm/workspace/snap_types.h | 23 |
10 files changed, 992 insertions, 99 deletions
diff --git a/ash/ash.gyp b/ash/ash.gyp index 191e540..2647000 100644 --- a/ash/ash.gyp +++ b/ash/ash.gyp @@ -252,6 +252,8 @@ 'wm/frame_painter.h', 'wm/image_grid.cc', 'wm/image_grid.h', + 'wm/maximize_bubble_controller.cc', + 'wm/maximize_bubble_controller.h', 'wm/overlay_event_filter.cc', 'wm/overlay_event_filter.h', 'wm/panel_frame_view.cc', @@ -333,6 +335,7 @@ 'wm/workspace/phantom_window_controller.h', 'wm/workspace/snap_sizer.cc', 'wm/workspace/snap_sizer.h', + 'wm/workspace/snap_types.h', 'wm/workspace/workspace.cc', 'wm/workspace/workspace.h', 'wm/workspace/workspace_event_filter.cc', diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd index f7b9795..fb2ed57 100644 --- a/ash/ash_strings.grd +++ b/ash/ash_strings.grd @@ -196,10 +196,6 @@ This file contains the strings for ash. Set wallpaper... </message> - <message name="IDS_FRAME_MAXIMIZE_BUTTON_TOOLTIP" desc="Tooltip for the maximize/restore button in window frames in aura"> - Pull down to minimize, left or right to tile - </message> - <message name="IDS_ASH_KEYBOARD_OVERLAY_TITLE" desc="The title of the keyboard overlay."> Keyboard Overlay </message> @@ -441,6 +437,21 @@ Press Search key to cancel. <message name="IDS_ASH_WEB_NOTFICATION_TRAY_CLEAR_ALL" desc="The button for clearing all notifications."> Clear All </message> + <message name="IDS_ASH_MAXIMIZE_WINDOW" desc="A help text to show that when the button gets clicked the window get maximized."> + Maximize + </message> + <message name="IDS_ASH_SNAP_WINDOW_RIGHT" desc="A help text to show that when the button gets clicked the window get snapped to the right side."> + Snap to right side + </message> + <message name="IDS_ASH_SNAP_WINDOW_LEFT" desc="A help text to show that when the button gets clicked the window get snapped to the left side."> + Snap to left side + </message> + <message name="IDS_ASH_RESTORE_WINDOW" desc="A help text to show that when the button gets clicked the window gets restored to its normal - non maximized - state."> + Restore + </message> + <message name="IDS_ASH_MINIMIZE_WINDOW" desc="A help text to show that when the button gets clicked the window gets minimized."> + Minimize + </message> </messages> </release> </grit> diff --git a/ash/wm/frame_painter.cc b/ash/wm/frame_painter.cc index 5d190a6..553851d 100644 --- a/ash/wm/frame_painter.cc +++ b/ash/wm/frame_painter.cc @@ -466,25 +466,19 @@ void FramePainter::LayoutHeader(views::NonClientFrameView* view, IDR_AURA_WINDOW_MAXIMIZED_CLOSE, IDR_AURA_WINDOW_MAXIMIZED_CLOSE_H, IDR_AURA_WINDOW_MAXIMIZED_CLOSE_P); - if (size_button_behavior_ == SIZE_BUTTON_MINIMIZES) { - SetButtonImages(size_button_, - IDR_AURA_WINDOW_MAXIMIZED_MINIMIZE, - IDR_AURA_WINDOW_MAXIMIZED_MINIMIZE_H, - IDR_AURA_WINDOW_MAXIMIZED_MINIMIZE_P); - } else { - SetButtonImages(size_button_, - IDR_AURA_WINDOW_MAXIMIZED_RESTORE, - IDR_AURA_WINDOW_MAXIMIZED_RESTORE_H, - IDR_AURA_WINDOW_MAXIMIZED_RESTORE_P); - } + // The chat window cannot be restored but only minimized. + // Case: (size_button_behavior_ == SIZE_BUTTON_MINIMIZES). We used to have + // a special set of artwork to show this case, but per discussion we + // removed this. + SetButtonImages(size_button_, + IDR_AURA_WINDOW_MAXIMIZED_RESTORE, + IDR_AURA_WINDOW_MAXIMIZED_RESTORE_H, + IDR_AURA_WINDOW_MAXIMIZED_RESTORE_P); } else { SetButtonImages(close_button_, IDR_AURA_WINDOW_CLOSE, IDR_AURA_WINDOW_CLOSE_H, IDR_AURA_WINDOW_CLOSE_P); - // TODO(jamescook): If we ever have normal-layout windows (with the - // standard 35 pixel tall headers) that can only minimize, we'll need art - // assets for SIZE_BUTTON_MINIMIZES. As of R19 we don't use them. SetButtonImages(size_button_, IDR_AURA_WINDOW_MAXIMIZE, IDR_AURA_WINDOW_MAXIMIZE_H, diff --git a/ash/wm/maximize_bubble_controller.cc b/ash/wm/maximize_bubble_controller.cc new file mode 100644 index 0000000..601a999 --- /dev/null +++ b/ash/wm/maximize_bubble_controller.cc @@ -0,0 +1,682 @@ +// 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/wm/maximize_bubble_controller.h" + +#include "ash/shell.h" +#include "ash/shell_window_ids.h" +#include "ash/wm/window_animations.h" +#include "ash/wm/workspace/frame_maximize_button.h" +#include "base/timer.h" +#include "grit/ash_strings.h" +#include "grit/ui_resources.h" +#include "ui/aura/event.h" +#include "ui/aura/focus_manager.h" +#include "ui/aura/window.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/gfx/canvas.h" +#include "ui/gfx/screen.h" +#include "ui/views/bubble/bubble_delegate.h" +#include "ui/views/bubble/bubble_frame_view.h" +#include "ui/views/controls/button/button.h" +#include "ui/views/controls/button/image_button.h" +#include "ui/views/controls/label.h" +#include "ui/views/events/event.h" +#include "ui/views/layout/box_layout.h" +#include "ui/views/mouse_watcher.h" +#include "ui/views/widget/widget.h" + +namespace { + +// The spacing between two buttons. +const int kLayoutSpacing = -1; + +// The background color. +const SkColor kBubbleBackgroundColor = 0xc8141414; + +// The text color within the bubble. +const SkColor kBubbleTextColor = SK_ColorWHITE; + +// The line width of the bubble. +const int kLineWidth = 1; + +// The pixel dimensions of the arrow. +const int kArrowHeight = 10; +const int kArrowWidth = 20; + +// The delay of the bubble appearance. +const int kBubbleAppearanceDelayMS = 200; +const int kAnimationDurationForPopupMS = 200; + +class MaximizeBubbleBorder : public views::BubbleBorder { + public: + MaximizeBubbleBorder(views::View* content_view, views::View* anchor); + + virtual ~MaximizeBubbleBorder() {} + + // Overridden from views::BubbleBorder to match the design specs. + virtual gfx::Rect GetBounds(const gfx::Rect& position_relative_to, + const gfx::Size& contents_size) const OVERRIDE; + + // Overridden from views::Border. + virtual void Paint(const views::View& view, + gfx::Canvas* canvas) const OVERRIDE; + private: + views::View* anchor_; + views::View* content_view_; + + DISALLOW_COPY_AND_ASSIGN(MaximizeBubbleBorder); +}; + +MaximizeBubbleBorder::MaximizeBubbleBorder(views::View* content_view, + views::View* anchor) + : views::BubbleBorder(views::BubbleBorder::TOP_RIGHT, + views::BubbleBorder::NO_SHADOW), + anchor_(anchor), + content_view_(content_view) { + set_alignment(views::BubbleBorder::ALIGN_EDGE_TO_ANCHOR_EDGE); +} + +gfx::Rect MaximizeBubbleBorder::GetBounds( + const gfx::Rect& position_relative_to, + const gfx::Size& contents_size) const { + gfx::Size border_size(contents_size); + gfx::Insets insets; + GetInsets(&insets); + border_size.Enlarge(insets.width(), insets.height()); + + // Position the bubble to center the box on the anchor. + int x = (-border_size.width() + anchor_->width()) / 2; + // Position the bubble under the anchor, overlapping the arrow with it. + int y = anchor_->height() - insets.top(); + + gfx::Point view_origin(x, y); + views::View::ConvertPointToScreen(anchor_, &view_origin); + + return gfx::Rect(view_origin, border_size); +} + +void MaximizeBubbleBorder::Paint(const views::View& view, + gfx::Canvas* canvas) const { + gfx::Insets inset; + GetInsets(&inset); + + // Draw the border line around everything. + int y = inset.top(); + // Top + canvas->FillRect(gfx::Rect(inset.left(), + y - kLineWidth, + content_view_->width(), + kLineWidth), + kBubbleBackgroundColor); + // Bottom + canvas->FillRect(gfx::Rect(inset.left(), + y + content_view_->height(), + content_view_->width(), + kLineWidth), + kBubbleBackgroundColor); + // Left + canvas->FillRect(gfx::Rect(inset.left() - kLineWidth, + y - kLineWidth, + kLineWidth, + content_view_->height() + 2 * kLineWidth), + kBubbleBackgroundColor); + // Right + canvas->FillRect(gfx::Rect(inset.left() + content_view_->width(), + y - kLineWidth, + kLineWidth, + content_view_->height() + 2 * kLineWidth), + kBubbleBackgroundColor); + + // Draw the arrow afterwards covering the border. + SkPath path; + path.incReserve(4); + // The center of the tip should be in the middle of the button. + int tip_x = inset.left() + content_view_->width() / 2; + int left_base_x = tip_x - kArrowWidth / 2; + int left_base_y = y; + int tip_y = left_base_y - kArrowHeight; + path.moveTo(SkIntToScalar(left_base_x), SkIntToScalar(left_base_y)); + path.lineTo(SkIntToScalar(tip_x), SkIntToScalar(tip_y)); + path.lineTo(SkIntToScalar(left_base_x + kArrowWidth), + SkIntToScalar(left_base_y)); + + SkPaint paint; + paint.setStyle(SkPaint::kFill_Style); + paint.setColor(kBubbleBackgroundColor); + canvas->DrawPath(path, paint); +} + +} // namespace + +namespace ash { + +class BubbleContentsButtonRow; +class BubbleContentsView; +class BubbleDialogButton; + +// The mouse watcher host which makes sure that the bubble does not get closed +// while the mouse cursor is over the maximize button or the balloon content. +// Note: This object gets destroyed when the MouseWatcher gets destroyed. +class BubbleMouseWatcherHost: public views::MouseWatcherHost { + public: + explicit BubbleMouseWatcherHost(MaximizeBubbleController::Bubble* bubble) + : bubble_(bubble) {} + virtual ~BubbleMouseWatcherHost() {} + + // Implementation of MouseWatcherHost. + virtual bool Contains(const gfx::Point& screen_point, + views::MouseWatcherHost::MouseEventType type); + private: + MaximizeBubbleController::Bubble* bubble_; + + DISALLOW_COPY_AND_ASSIGN(BubbleMouseWatcherHost); +}; + +// The class which creates and manages the bubble menu element. +// It creates a 'bubble border' and the content accordingly. +// Note: Since the SnapSizer will show animations on top of the maximize button +// this menu gets created as a separate window and the SnapSizer will be +// created underneath this window. +class MaximizeBubbleController::Bubble : public views::BubbleDelegateView, + public views::MouseWatcherListener { + public: + explicit Bubble(MaximizeBubbleController* owner); + virtual ~Bubble() {} + + // The window of the menu under which the SnapSizer will get created. + aura::Window* GetBubbleWindow(); + + // Overridden from views::BubbleDelegateView. + virtual gfx::Rect GetAnchorRect() OVERRIDE; + + // Implementation of MouseWatcherListener. + virtual void MouseMovedOutOfHost(); + + // Implementation of MouseWatcherHost. + virtual bool Contains(const gfx::Point& screen_point, + views::MouseWatcherHost::MouseEventType type); + + // Overridden from views::View. + virtual gfx::Size GetPreferredSize() OVERRIDE; + + // Overridden from views::Widget::Observer. + virtual void OnWidgetClosing(views::Widget* widget) OVERRIDE; + + // Called from the controller class to indicate that the menu should get + // destroyed. + virtual void ControllerRequestsCloseAndDelete(); + + // Called from the owning class to change the menu content to the given + // |snap_type| so that the user knows what is selected. + void SetSnapType(SnapType snap_type); + + // Get the owning MaximizeBubbleController. This might return NULL in case + // of an asynchronous shutdown. + MaximizeBubbleController* controller() const { return owner_; } + + private: + // True if the shut down has been initiated. + bool shutting_down_; + + // Our owning class. + MaximizeBubbleController* owner_; + + // The widget which contains our menu and the bubble border. + views::Widget* bubble_widget_; + + // The content accessor of the menu. + BubbleContentsView* contents_view_; + + // The mouse watcher which takes care of out of window hover events. + scoped_ptr<views::MouseWatcher> mouse_watcher_; + + DISALLOW_COPY_AND_ASSIGN(Bubble); +}; + +// A class that creates all buttons and put them into a view. +class BubbleContentsButtonRow : public views::View, + public views::ButtonListener { + public: + explicit BubbleContentsButtonRow(MaximizeBubbleController::Bubble* bubble); + + virtual ~BubbleContentsButtonRow() {} + + // Overridden from ButtonListener. + virtual void ButtonPressed(views::Button* sender, + const views::Event& event) OVERRIDE; + // Called from BubbleDialogButton. + void ButtonHovered(BubbleDialogButton* sender); + + private: + // The owning object which gets notifications. + MaximizeBubbleController::Bubble* bubble_; + + // The created buttons for our menu. + BubbleDialogButton* left_button_; + BubbleDialogButton* minimize_button_; + BubbleDialogButton* right_button_; + + DISALLOW_COPY_AND_ASSIGN(BubbleContentsButtonRow); +}; + +// A class which creates the content of the bubble: The buttons, and the label. +class BubbleContentsView : public views::View { + public: + explicit BubbleContentsView(MaximizeBubbleController::Bubble* bubble); + + virtual ~BubbleContentsView() {} + + // Set the label content to reflect the currently selected |snap_type|. + // This function can be executed through the frame maximize button as well as + // through hover operations. + void SetSnapType(SnapType snap_type); + + private: + // The owning class. + MaximizeBubbleController::Bubble* bubble_; + + // The object which owns all the buttons. + BubbleContentsButtonRow* buttons_view_; + + // The label object which shows the user the selected action. + views::Label* label_view_; + + DISALLOW_COPY_AND_ASSIGN(BubbleContentsView); +}; + +// The image button gets overridden to be able to capture mouse hover events. +// The constructor also assigns all button states and +class BubbleDialogButton : public views::ImageButton { + public: + explicit BubbleDialogButton( + BubbleContentsButtonRow* button_row_listener, + int normal_image, + int hovered_image, + int pressed_image); + virtual ~BubbleDialogButton() {} + + // CustomButton overrides: + virtual void OnMouseCaptureLost() OVERRIDE; + virtual void OnMouseEntered(const views::MouseEvent& event) OVERRIDE; + virtual void OnMouseExited(const views::MouseEvent& event) OVERRIDE; + + private: + // The creating class which needs to get notified in case of a hover event. + BubbleContentsButtonRow* button_row_; + + DISALLOW_COPY_AND_ASSIGN(BubbleDialogButton); +}; + +MaximizeBubbleController::Bubble::Bubble(MaximizeBubbleController* owner) + : views::BubbleDelegateView(owner->frame_maximize_button(), + views::BubbleBorder::TOP_RIGHT), + shutting_down_(false), + owner_(owner), + bubble_widget_(NULL), + contents_view_(NULL) { + set_margins(gfx::Insets()); + + // The window needs to be owned by the root so that the SnapSizer does not + // cover it upon animation. + aura::Window* parent = Shell::GetContainer( + Shell::GetActiveRootWindow(), + internal::kShellWindowId_LauncherContainer); + set_parent_window(parent); + + set_notify_enter_exit_on_child(true); + set_try_mirroring_arrow(false); + SetPaintToLayer(true); + SetFillsBoundsOpaquely(false); + set_color(kBubbleBackgroundColor); + set_close_on_deactivate(false); + set_background( + views::Background::CreateSolidBackground(kBubbleBackgroundColor)); + + SetLayoutManager(new views::BoxLayout( + views::BoxLayout::kVertical, 0, 0, kLayoutSpacing)); + + contents_view_ = new BubbleContentsView(this); + AddChildView(contents_view_); + + // Note that the returned widget has an observer which points to our + // functions. + bubble_widget_ = views::BubbleDelegateView::CreateBubble(this); + + SetAlignment(views::BubbleBorder::ALIGN_EDGE_TO_ANCHOR_EDGE); + bubble_widget_->non_client_view()->frame_view()->set_background(NULL); + + MaximizeBubbleBorder* bubble_border = new MaximizeBubbleBorder( + this, + anchor_view()); + GetBubbleFrameView()->SetBubbleBorder(bubble_border); + GetBubbleFrameView()->set_background(NULL); + + // Recalculate size with new border. + SizeToContents(); + + // Setup animation. + ash::SetWindowVisibilityAnimationType( + bubble_widget_->GetNativeWindow(), + ash::WINDOW_VISIBILITY_ANIMATION_TYPE_FADE); + ash::SetWindowVisibilityAnimationTransition( + bubble_widget_->GetNativeWindow(), + ash::ANIMATE_BOTH); + ash::SetWindowVisibilityAnimationDuration( + bubble_widget_->GetNativeWindow(), + base::TimeDelta::FromMilliseconds(kAnimationDurationForPopupMS)); + + Show(); + // We don't want to lose the focus on our parent window because the button + // would otherwise lose the highlight when the "helper bubble" is shown. + views::Widget* widget = + owner_->frame_maximize_button()->parent()->GetWidget(); + if (widget) { + aura::Window* parent_window = widget->GetNativeWindow(); + parent_window->GetFocusManager()->SetFocusedWindow(parent_window, NULL); + } + mouse_watcher_.reset(new views::MouseWatcher( + new BubbleMouseWatcherHost(this), + this)); + mouse_watcher_->Start(); +} + +bool BubbleMouseWatcherHost::Contains( + const gfx::Point& screen_point, + views::MouseWatcherHost::MouseEventType type) { + return bubble_->Contains(screen_point, type); +} + +aura::Window* MaximizeBubbleController::Bubble::GetBubbleWindow() { + return bubble_widget_ ? bubble_widget_->GetNativeWindow() : NULL; +} + +gfx::Rect MaximizeBubbleController::Bubble::GetAnchorRect() { + if (!owner_) + return gfx::Rect(); + + gfx::Rect anchor_rect = + owner_->frame_maximize_button()->GetBoundsInScreen(); + return anchor_rect; +} + +void MaximizeBubbleController::Bubble::MouseMovedOutOfHost() { + if (!owner_ || shutting_down_) + return; + // When we leave the bubble, we might be still be in gesture mode or over + // the maximize button. So only close if none of the other cases apply. + if (!owner_->frame_maximize_button()->is_snap_enabled()) { + gfx::Point screen_location = gfx::Screen::GetCursorScreenPoint(); + if (!owner_->frame_maximize_button()->GetBoundsInScreen().Contains( + screen_location)) { + owner_->RequestDestructionThroughOwner(); + } + } +} + +bool MaximizeBubbleController::Bubble::Contains( + const gfx::Point& screen_point, + views::MouseWatcherHost::MouseEventType type) { + if (!owner_ || shutting_down_) + return false; + // Check if either a gesture is taking place (=> bubble stays no matter what + // the mouse does) or the mouse is over the maximize button or the bubble + // content. + return (owner_->frame_maximize_button()->is_snap_enabled() || + owner_->frame_maximize_button()->GetBoundsInScreen().Contains( + screen_point) || + contents_view_->GetBoundsInScreen().Contains(screen_point)); +} + +gfx::Size MaximizeBubbleController::Bubble::GetPreferredSize() { + return contents_view_->GetPreferredSize(); +} + +void MaximizeBubbleController::Bubble::OnWidgetClosing(views::Widget* widget) { + if (bubble_widget_ != widget) + return; + + mouse_watcher_->Stop(); + + if (owner_) { + // If the bubble destruction was triggered by some other external influence + // then ourselves, the owner needs to be informed that the menu is gone. + shutting_down_ = true; + owner_->RequestDestructionThroughOwner(); + owner_ = NULL; + } + // Remove any existing observers. + bubble_widget_->RemoveObserver(this); + anchor_widget()->RemoveObserver(this); +} + +void MaximizeBubbleController::Bubble::ControllerRequestsCloseAndDelete() { + // This only gets called from the owning base class once it is deleted. + if (shutting_down_) + return; + shutting_down_ = true; + owner_ = NULL; + + // Close the widget asynchronously. + bubble_widget_->Close(); +} + +void MaximizeBubbleController::Bubble::SetSnapType(SnapType snap_type) { + if (contents_view_) + contents_view_->SetSnapType(snap_type); +} + +BubbleContentsButtonRow::BubbleContentsButtonRow( + MaximizeBubbleController::Bubble* bubble) + : bubble_(bubble), + left_button_(NULL), + minimize_button_(NULL), + right_button_(NULL) { + SetLayoutManager(new views::BoxLayout( + views::BoxLayout::kHorizontal, 0, 0, kLayoutSpacing)); + set_background( + views::Background::CreateSolidBackground(kBubbleBackgroundColor)); + + left_button_ = new BubbleDialogButton( + this, + IDR_AURA_WINDOW_POSITION_LEFT, + IDR_AURA_WINDOW_POSITION_LEFT_H, + IDR_AURA_WINDOW_POSITION_LEFT_P); + minimize_button_ = new BubbleDialogButton( + this, + IDR_AURA_WINDOW_POSITION_MIDDLE, + IDR_AURA_WINDOW_POSITION_MIDDLE_H, + IDR_AURA_WINDOW_POSITION_MIDDLE_P); + right_button_ = new BubbleDialogButton( + this, + IDR_AURA_WINDOW_POSITION_RIGHT, + IDR_AURA_WINDOW_POSITION_RIGHT_H, + IDR_AURA_WINDOW_POSITION_RIGHT_P); +} + +// Overridden from ButtonListener. +void BubbleContentsButtonRow::ButtonPressed(views::Button* sender, + const views::Event& event) { + // While shutting down, the connection to the owner might already be broken. + if (!bubble_->controller()) + return; + if (sender == left_button_) + bubble_->controller()->OnButtonClicked(SNAP_LEFT); + else if (sender == minimize_button_) + bubble_->controller()->OnButtonClicked(SNAP_MINIMIZE); + else if (sender == right_button_) + bubble_->controller()->OnButtonClicked(SNAP_RIGHT); + else + NOTREACHED() << "Unknown button pressed."; +} + +// Called from BubbleDialogButton. +void BubbleContentsButtonRow::ButtonHovered(BubbleDialogButton* sender) { + // While shutting down, the connection to the owner might already be broken. + if (!bubble_->controller()) + return; + if (sender == left_button_) + bubble_->controller()->OnButtonHover(SNAP_LEFT); + else if (sender == minimize_button_) + bubble_->controller()->OnButtonHover(SNAP_MINIMIZE); + else if (sender == right_button_) + bubble_->controller()->OnButtonHover(SNAP_RIGHT); + else + bubble_->controller()->OnButtonHover(SNAP_NONE); +} + +BubbleContentsView::BubbleContentsView( + MaximizeBubbleController::Bubble* bubble) + : bubble_(bubble), + buttons_view_(NULL), + label_view_(NULL) { + SetLayoutManager(new views::BoxLayout( + views::BoxLayout::kVertical, 0, 0, kLayoutSpacing)); + set_background( + views::Background::CreateSolidBackground(kBubbleBackgroundColor)); + + buttons_view_ = new BubbleContentsButtonRow(bubble); + AddChildView(buttons_view_); + + label_view_ = new views::Label(); + SetSnapType(SNAP_NONE); + label_view_->SetHorizontalAlignment(views::Label::ALIGN_CENTER); + label_view_->SetBackgroundColor(kBubbleBackgroundColor); + label_view_->SetEnabledColor(kBubbleTextColor); + AddChildView(label_view_); +} + +// Set the label content to reflect the currently selected |snap_type|. +// This function can be executed through the frame maximize button as well as +// through hover operations. +void BubbleContentsView::SetSnapType(SnapType snap_type) { + if (!bubble_->controller()) + return; + + ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); + int id = 0; + switch (snap_type) { + case SNAP_LEFT: + id = IDS_ASH_SNAP_WINDOW_LEFT; + break; + case SNAP_RIGHT: + id = IDS_ASH_SNAP_WINDOW_RIGHT; + break; + case SNAP_MAXIMIZE: + DCHECK(!bubble_->controller()->is_maximized()); + id = IDS_ASH_MAXIMIZE_WINDOW; + break; + case SNAP_MINIMIZE: + id = IDS_ASH_MINIMIZE_WINDOW; + break; + case SNAP_RESTORE: + DCHECK(bubble_->controller()->is_maximized()); + id = IDS_ASH_RESTORE_WINDOW; + break; + default: + // If nothing is selected, we automatically select the click operation. + id = bubble_->controller()->is_maximized() ? IDS_ASH_RESTORE_WINDOW : + IDS_ASH_MAXIMIZE_WINDOW; + break; + } + label_view_->SetText(rb.GetLocalizedString(id)); +} + +MaximizeBubbleController::MaximizeBubbleController( + FrameMaximizeButton* frame_maximize_button, + bool is_maximized) + : frame_maximize_button_(frame_maximize_button), + bubble_(NULL), + is_maximized_(is_maximized) { + // Create the task which will create the bubble delayed. + base::OneShotTimer<MaximizeBubbleController>* new_timer = + new base::OneShotTimer<MaximizeBubbleController>(); + new_timer->Start( + FROM_HERE, + base::TimeDelta::FromMilliseconds(kBubbleAppearanceDelayMS), + this, + &MaximizeBubbleController::CreateBubble); + timer_.reset(new_timer); +} + +MaximizeBubbleController::~MaximizeBubbleController() { + // Note: The destructor only gets initiated through the owner. + timer_.reset(); + if (bubble_) { + bubble_->ControllerRequestsCloseAndDelete(); + bubble_ = NULL; + } +} + +void MaximizeBubbleController::SetSnapType(SnapType snap_type) { + if (bubble_) + bubble_->SetSnapType(snap_type); +} + +aura::Window* MaximizeBubbleController::GetBubbleWindow() { + return bubble_ ? bubble_->GetBubbleWindow() : NULL; +} + +void MaximizeBubbleController::DelayCreation() { + if (timer_.get() && timer_->IsRunning()) + timer_->Reset(); +} + +void MaximizeBubbleController::OnButtonClicked(SnapType snap_type) { + frame_maximize_button_->ExecuteSnapAndCloseMenu(snap_type); +} + +void MaximizeBubbleController::OnButtonHover(SnapType snap_type) { + frame_maximize_button_->SnapButtonHovered(snap_type); +} + +void MaximizeBubbleController::RequestDestructionThroughOwner() { + // Tell the parent to destroy us (if this didn't happen yet). + if (timer_.get()) { + timer_.reset(NULL); + // Informs the owner that the menu is gone and requests |this| destruction. + frame_maximize_button_->DestroyMaximizeMenu(); + // Note: After this call |this| is destroyed. + } +} + +void MaximizeBubbleController::CreateBubble() { + if (!bubble_) + bubble_ = new Bubble(this); + + timer_->Stop(); +} + +BubbleDialogButton::BubbleDialogButton( + BubbleContentsButtonRow* button_row, + int normal_image, + int hovered_image, + int pressed_image) + : views::ImageButton(button_row), + button_row_(button_row) { + ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); + SetImage(views::CustomButton::BS_NORMAL, rb.GetImageSkiaNamed(normal_image)); + SetImage(views::CustomButton::BS_HOT, rb.GetImageSkiaNamed(hovered_image)); + SetImage(views::CustomButton::BS_PUSHED, + rb.GetImageSkiaNamed(pressed_image)); + button_row->AddChildView(this); +} + +void BubbleDialogButton::OnMouseCaptureLost() { + button_row_->ButtonHovered(NULL); + views::ImageButton::OnMouseCaptureLost(); +} + +void BubbleDialogButton::OnMouseEntered(const views::MouseEvent& event) { + button_row_->ButtonHovered(this); + views::ImageButton::OnMouseEntered(event); +} + +void BubbleDialogButton::OnMouseExited(const views::MouseEvent& event) { + button_row_->ButtonHovered(NULL); + views::ImageButton::OnMouseExited(event); +} + +} // namespace ash diff --git a/ash/wm/maximize_bubble_controller.h b/ash/wm/maximize_bubble_controller.h new file mode 100644 index 0000000..5712a3c --- /dev/null +++ b/ash/wm/maximize_bubble_controller.h @@ -0,0 +1,88 @@ +// 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 ASH_WM_MAXIMIZE_BUBBLE_CONTROLLER_H_ +#define ASH_WM_MAXIMIZE_BUBBLE_CONTROLLER_H_ + +#include "ash/ash_export.h" +#include "ash/wm/workspace/snap_types.h" +#include "base/memory/scoped_ptr.h" + +namespace base { +class Timer; +} + +namespace aura { +class Window; +} + +namespace ash { + +class FrameMaximizeButton; + +// A class which shows a helper UI for the maximize button after a delay. +class ASH_EXPORT MaximizeBubbleController { + public: + class Bubble; + + MaximizeBubbleController(FrameMaximizeButton* frame_maximize_button, + bool is_maximized); + // Called from the outside to destroy the interface to the UI visuals. + // The visuals will then delete when possible (maybe asynchronously). + virtual ~MaximizeBubbleController(); + + // Update the UI visuals to reflect the previewed |snap_type| snapping state. + void SetSnapType(SnapType snap_type); + + // To achieve proper Z-sorting with the snap animation, this window will be + // presented above the phantom window. + aura::Window* GetBubbleWindow(); + + // Reset the delay of the menu creation (if it was not created yet). + void DelayCreation(); + + // Called to tell the owning FrameMaximizeButton that a button was clicked. + void OnButtonClicked(SnapType snap_type); + + // Called to tell the the owning FrameMaximizeButton that the hover status + // for a button has changed. |snap_type| can be either SNAP_LEFT, SNAP_RIGHT, + // SNAP_MINIMIZE or SNAP_NONE. + void OnButtonHover(SnapType snap_type); + + // Get the owning FrameMaximizeButton. + FrameMaximizeButton* frame_maximize_button() { + return frame_maximize_button_; + } + + // The status of the associated window: Maximized or normal. + bool is_maximized() const { return is_maximized_; } + + protected: + // Called from the the Bubble class to destroy itself: It tells the owning + // object that it will destroy itself asynchronously. The owner will then + // destroy |this|. + void RequestDestructionThroughOwner(); + + private: + // The function which creates the bubble once the delay is elapsed. + void CreateBubble(); + + // The owning button which is also the anchor for the menu. + FrameMaximizeButton* frame_maximize_button_; + + // The bubble menu. + Bubble* bubble_; + + // If true the owning window is maximized. + const bool is_maximized_; + + // The timer for the delayed creation of the menu. + scoped_ptr<base::Timer> timer_; + + DISALLOW_COPY_AND_ASSIGN(MaximizeBubbleController); +}; + +} // namespace ash + +#endif // ASH_WM_MAXIMIZE_BUBBLE_CONTROLLER_H_ diff --git a/ash/wm/workspace/frame_maximize_button.cc b/ash/wm/workspace/frame_maximize_button.cc index 213f4d2..89297c5d 100644 --- a/ash/wm/workspace/frame_maximize_button.cc +++ b/ash/wm/workspace/frame_maximize_button.cc @@ -8,6 +8,7 @@ #include "ash/screen_ash.h" #include "ash/shell.h" #include "ash/wm/property_util.h" +#include "ash/wm/maximize_bubble_controller.h" #include "ash/wm/workspace/phantom_window_controller.h" #include "ash/wm/workspace/snap_sizer.h" #include "grit/ash_strings.h" @@ -74,7 +75,7 @@ bool FrameMaximizeButton::EscapeEventFilter::PreHandleKeyEvent( aura::KeyEvent* event) { if (event->type() == ui::ET_KEY_PRESSED && event->key_code() == ui::VKEY_ESCAPE) { - button_->Cancel(); + button_->Cancel(false); } return false; } @@ -105,13 +106,78 @@ FrameMaximizeButton::FrameMaximizeButton(views::ButtonListener* listener, frame_(frame), is_snap_enabled_(false), exceeded_drag_threshold_(false), + window_(NULL), snap_type_(SNAP_NONE) { // TODO(sky): nuke this. It's temporary while we don't have good images. SetImageAlignment(ALIGN_LEFT, ALIGN_BOTTOM); - SetTooltipText(l10n_util::GetStringUTF16(IDS_FRAME_MAXIMIZE_BUTTON_TOOLTIP)); } FrameMaximizeButton::~FrameMaximizeButton() { + if (window_) + OnWindowDestroying(window_); +} + +void FrameMaximizeButton::SnapButtonHovered(SnapType type) { + // Make sure to only show hover operations when no button is pressed and + // a similar snap operation in progress does not get re-applied. + if (is_snap_enabled_ || (type == snap_type_ && snap_sizer_.get())) + return; + // Prime the mouse location with the center of the (local) button. + press_location_ = gfx::Point(width() / 2, height() / 2); + // Then get an adjusted mouse position to initiate the effect. + gfx::Point location = press_location_; + switch (type) { + case SNAP_LEFT: + location.set_x(location.x() - width()); + break; + case SNAP_RIGHT: + location.set_x(location.x() + width()); + break; + case SNAP_MINIMIZE: + location.set_y(location.y() + height()); + break; + case SNAP_MAXIMIZE: + case SNAP_RESTORE: + break; + case SNAP_NONE: + Cancel(true); + return; + default: + // We should not come here. + NOTREACHED(); + } + UpdateSnap(location); +} + +void FrameMaximizeButton::ExecuteSnapAndCloseMenu(SnapType snap_type) { + DCHECK_NE(snap_type_, SNAP_NONE); + snap_type_ = snap_type; + Snap(); + // Remove any pending snap previews. + SnapButtonHovered(SNAP_NONE); + // At this point the operation has been performed and the menu should be + // closed - if not, it'll get now closed. + maximizer_.reset(); +} + +void FrameMaximizeButton::DestroyMaximizeMenu() { + maximizer_.reset(); +} + +void FrameMaximizeButton::OnWindowBoundsChanged( + aura::Window* window, + const gfx::Rect& old_bounds, + const gfx::Rect& new_bounds) { + Cancel(false); +} + +void FrameMaximizeButton::OnWindowDestroying(aura::Window* window) { + maximizer_.reset(); + if (window_) { + CHECK_EQ(window_, window); + window_->RemoveObserver(this); + window_ = NULL; + } } bool FrameMaximizeButton::OnMousePressed(const views::MouseEvent& event) { @@ -124,10 +190,31 @@ bool FrameMaximizeButton::OnMousePressed(const views::MouseEvent& event) { void FrameMaximizeButton::OnMouseEntered(const views::MouseEvent& event) { ImageButton::OnMouseEntered(event); + if (!maximizer_.get()) { + DCHECK(GetWidget()); + if (!window_) { + window_ = frame_->GetWidget()->GetNativeWindow(); + window_->AddObserver(this); + } + maximizer_.reset(new MaximizeBubbleController( + this, + frame_->GetWidget()->IsMaximized())); + } } void FrameMaximizeButton::OnMouseExited(const views::MouseEvent& event) { ImageButton::OnMouseExited(event); + // Remove the bubble menu when the button is not pressed and the mouse is not + // within the bubble. + if (!is_snap_enabled_ && maximizer_.get() && maximizer_->GetBubbleWindow()) { + gfx::Point screen_location = gfx::Screen::GetCursorScreenPoint(); + if (!maximizer_->GetBubbleWindow()->GetBoundsInScreen().Contains( + screen_location)) { + maximizer_.reset(); + // Make sure that all remaining snap hover states get removed. + SnapButtonHovered(SNAP_NONE); + } + } } bool FrameMaximizeButton::OnMouseDragged(const views::MouseEvent& event) { @@ -137,12 +224,14 @@ bool FrameMaximizeButton::OnMouseDragged(const views::MouseEvent& event) { } void FrameMaximizeButton::OnMouseReleased(const views::MouseEvent& event) { + maximizer_.reset(); if (!ProcessEndEvent(event)) ImageButton::OnMouseReleased(event); + // At this point |this| might be already destroyed. } void FrameMaximizeButton::OnMouseCaptureLost() { - Cancel(); + Cancel(false); ImageButton::OnMouseCaptureLost(); } @@ -180,56 +269,17 @@ ui::GestureStatus FrameMaximizeButton::OnGestureEvent( return ImageButton::OnGestureEvent(event); } -gfx::ImageSkia FrameMaximizeButton::GetImageToPaint() { - if (is_snap_enabled_) { - int id = 0; - if (frame_->GetWidget()->IsMaximized()) { - switch (snap_type_) { - case SNAP_LEFT: - id = IDR_AURA_WINDOW_MAXIMIZED_SNAP_LEFT_P; - break; - case SNAP_RIGHT: - id = IDR_AURA_WINDOW_MAXIMIZED_SNAP_RIGHT_P; - break; - case SNAP_MAXIMIZE: - case SNAP_RESTORE: - case SNAP_NONE: - id = IDR_AURA_WINDOW_MAXIMIZED_SNAP_P; - break; - case SNAP_MINIMIZE: - id = IDR_AURA_WINDOW_MAXIMIZED_SNAP_MINIMIZE_P; - break; - default: - NOTREACHED(); - } - } else { - switch (snap_type_) { - case SNAP_LEFT: - id = IDR_AURA_WINDOW_SNAP_LEFT_P; - break; - case SNAP_RIGHT: - id = IDR_AURA_WINDOW_SNAP_RIGHT_P; - break; - case SNAP_MAXIMIZE: - case SNAP_RESTORE: - case SNAP_NONE: - id = IDR_AURA_WINDOW_SNAP_P; - break; - case SNAP_MINIMIZE: - id = IDR_AURA_WINDOW_SNAP_MINIMIZE_P; - break; - default: - NOTREACHED(); - } - } - return *ResourceBundle::GetSharedInstance().GetImageNamed(id).ToImageSkia(); - } - // Hot and pressed states handled by regular ImageButton. - return ImageButton::GetImageToPaint(); -} - void FrameMaximizeButton::ProcessStartEvent(const views::LocatedEvent& event) { DCHECK(is_snap_enabled_); + // Prepare the help menu. + if (!maximizer_.get()) { + maximizer_.reset(new MaximizeBubbleController( + this, + frame_->GetWidget()->IsMaximized())); + } else { + // If the menu did not show up yet, we delay it even a bit more. + maximizer_->DelayCreation(); + } snap_sizer_.reset(NULL); InstallEventFilter(); snap_type_ = SNAP_NONE; @@ -260,6 +310,9 @@ bool FrameMaximizeButton::ProcessEndEvent(const views::LocatedEvent& event) { bool should_snap = is_snap_enabled_; is_snap_enabled_ = false; + // Remove our help bubble. + maximizer_.reset(); + if (!should_snap || snap_type_ == SNAP_NONE) return false; @@ -272,11 +325,15 @@ bool FrameMaximizeButton::ProcessEndEvent(const views::LocatedEvent& event) { return true; } -void FrameMaximizeButton::Cancel() { - UninstallEventFilter(); - is_snap_enabled_ = false; +void FrameMaximizeButton::Cancel(bool keep_menu_open) { + if (!keep_menu_open) { + maximizer_.reset(); + UninstallEventFilter(); + is_snap_enabled_ = false; + } phantom_window_.reset(); snap_sizer_.reset(); + snap_type_ = SNAP_NONE; update_timer_.Stop(); SchedulePaint(); } @@ -333,10 +390,14 @@ void FrameMaximizeButton::UpdateSnap(const gfx::Point& location) { phantom_window_.reset(new internal::PhantomWindowController( frame_->GetWidget()->GetNativeWindow())); } + if (maximizer_.get()) { + phantom_window_->set_phantom_below_window(maximizer_->GetBubbleWindow()); + maximizer_->SetSnapType(snap_type_); + } phantom_window_->Show(ScreenBoundsForType(snap_type_)); } -FrameMaximizeButton::SnapType FrameMaximizeButton::SnapTypeForLocation( +SnapType FrameMaximizeButton::SnapTypeForLocation( const gfx::Point& location) const { int delta_x = location.x() - press_location_.x(); int delta_y = location.y() - press_location_.y(); diff --git a/ash/wm/workspace/frame_maximize_button.h b/ash/wm/workspace/frame_maximize_button.h index 0124c2f..2b0eec7 100644 --- a/ash/wm/workspace/frame_maximize_button.h +++ b/ash/wm/workspace/frame_maximize_button.h @@ -6,8 +6,10 @@ #define ASH_WM_WORKSPACE_FRAME_MAXIMIZE_BUTTON_H_ #include "ash/ash_export.h" +#include "ash/wm/workspace/snap_types.h" #include "base/memory/scoped_ptr.h" #include "base/timer.h" +#include "ui/aura/window_observer.h" #include "ui/views/controls/button/image_button.h" namespace views { @@ -21,13 +23,36 @@ class PhantomWindowController; class SnapSizer; } +class MaximizeBubbleController; + // Button used for the maximize control on the frame. Handles snapping logic. -class ASH_EXPORT FrameMaximizeButton : public views::ImageButton { +class ASH_EXPORT FrameMaximizeButton : public views::ImageButton, + public aura::WindowObserver { public: FrameMaximizeButton(views::ButtonListener* listener, views::NonClientFrameView* frame); virtual ~FrameMaximizeButton(); + // Updates |snap_type_| based on a a given snap type. This is used by + // external hover events from the button menu. + void SnapButtonHovered(SnapType type); + + // The user clicked the |type| button and the action needs to be performed, + // which will at the same time close the window. + void ExecuteSnapAndCloseMenu(SnapType type); + + // Remove the maximize menu from the screen (and destroy it). + void DestroyMaximizeMenu(); + + // Returns true when the user clicks and drags the button. + bool is_snap_enabled() const { return is_snap_enabled_; } + + // WindowObserver overrides: + virtual void OnWindowBoundsChanged(aura::Window* window, + const gfx::Rect& old_bounds, + const gfx::Rect& new_bounds) OVERRIDE; + virtual void OnWindowDestroying(aura::Window* window) OVERRIDE; + // ImageButton overrides: virtual bool OnMousePressed(const views::MouseEvent& event) OVERRIDE; virtual void OnMouseEntered(const views::MouseEvent& event) OVERRIDE; @@ -38,23 +63,9 @@ class ASH_EXPORT FrameMaximizeButton : public views::ImageButton { virtual ui::GestureStatus OnGestureEvent( const views::GestureEvent& event) OVERRIDE; - protected: - // ImageButton overrides: - virtual gfx::ImageSkia GetImageToPaint() OVERRIDE; - private: class EscapeEventFilter; - // Where to snap to. - enum SnapType { - SNAP_LEFT, - SNAP_RIGHT, - SNAP_MAXIMIZE, - SNAP_MINIMIZE, - SNAP_RESTORE, - SNAP_NONE - }; - // Initializes the snap-gesture based on the event. This should only be called // when the event is confirmed to have started a snap gesture. void ProcessStartEvent(const views::LocatedEvent& event); @@ -66,8 +77,9 @@ class ASH_EXPORT FrameMaximizeButton : public views::ImageButton { // Returns true if the window was snapped. Returns false otherwise. bool ProcessEndEvent(const views::LocatedEvent& event); - // Cancels snap behavior. - void Cancel(); + // Cancels snap behavior. If |keep_menu_open| is set, a possibly opened + // bubble help will remain open. + void Cancel(bool keep_menu_open); // Installs/uninstalls an EventFilter to track when escape is pressed. void InstallEventFilter(); @@ -106,6 +118,9 @@ class ASH_EXPORT FrameMaximizeButton : public views::ImageButton { // Did the user drag far enough to trigger snapping? bool exceeded_drag_threshold_; + // This is the Window we are contained in. + aura::Window* window_; + // Location of the press. gfx::Point press_location_; @@ -118,6 +133,8 @@ class ASH_EXPORT FrameMaximizeButton : public views::ImageButton { base::OneShotTimer<FrameMaximizeButton> update_timer_; + scoped_ptr<MaximizeBubbleController> maximizer_; + DISALLOW_COPY_AND_ASSIGN(FrameMaximizeButton); }; diff --git a/ash/wm/workspace/phantom_window_controller.cc b/ash/wm/workspace/phantom_window_controller.cc index 99861fd..656dc69 100644 --- a/ash/wm/workspace/phantom_window_controller.cc +++ b/ash/wm/workspace/phantom_window_controller.cc @@ -72,7 +72,9 @@ class EdgePainter : public views::Painter { } // namespace PhantomWindowController::PhantomWindowController(aura::Window* window) - : window_(window) { + : window_(window), + phantom_below_window_(NULL), + phantom_widget_(NULL) { } PhantomWindowController::~PhantomWindowController() { @@ -83,7 +85,7 @@ void PhantomWindowController::Show(const gfx::Rect& bounds) { if (bounds == bounds_) return; bounds_ = bounds; - if (!phantom_widget_.get()) { + if (!phantom_widget_) { // Show the phantom at the bounds of the window. We'll animate to the target // bounds. start_bounds_ = window_->GetBoundsInScreen(); @@ -103,11 +105,12 @@ void PhantomWindowController::SetBounds(const gfx::Rect& bounds) { } void PhantomWindowController::Hide() { - phantom_widget_.reset(); + phantom_widget_->Close(); + phantom_widget_ = NULL; } bool PhantomWindowController::IsShowing() const { - return phantom_widget_.get() != NULL; + return phantom_widget_ != NULL; } void PhantomWindowController::AnimationProgressed( @@ -117,11 +120,10 @@ void PhantomWindowController::AnimationProgressed( } void PhantomWindowController::CreatePhantomWidget(const gfx::Rect& bounds) { - DCHECK(!phantom_widget_.get()); - phantom_widget_.reset(new views::Widget); + DCHECK(!phantom_widget_); + phantom_widget_ = new views::Widget; views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP); params.transparent = true; - params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; // PhantomWindowController is used by FrameMaximizeButton to highlight the // launcher button. Put the phantom in the same window as the launcher so that // the phantom is visible. @@ -139,7 +141,10 @@ void PhantomWindowController::CreatePhantomWidget(const gfx::Rect& bounds) { views::Background::CreateBackgroundPainter(true, new EdgePainter)); phantom_widget_->SetContentsView(content_view); phantom_widget_->SetBounds(bounds); - phantom_widget_->StackAbove(window_); + if (phantom_below_window_) + phantom_widget_->StackBelow(phantom_below_window_); + else + phantom_widget_->StackAbove(window_); phantom_widget_->Show(); // Fade the window in. ui::Layer* layer = phantom_widget_->GetNativeWindow()->layer(); diff --git a/ash/wm/workspace/phantom_window_controller.h b/ash/wm/workspace/phantom_window_controller.h index 5510fc2..7ea76c4 100644 --- a/ash/wm/workspace/phantom_window_controller.h +++ b/ash/wm/workspace/phantom_window_controller.h @@ -50,6 +50,12 @@ class ASH_EXPORT PhantomWindowController : public ui::AnimationDelegate { // Returns true if the phantom is showing. bool IsShowing() const; + // If set, the phantom window is stacked below this window, otherwise it + // is stacked above the window passed to the constructor. + void set_phantom_below_window(aura::Window* phantom_below_window) { + phantom_below_window_ = phantom_below_window; + } + // ui::AnimationDelegate overrides: virtual void AnimationProgressed(const ui::Animation* animation) OVERRIDE; @@ -60,6 +66,9 @@ class ASH_EXPORT PhantomWindowController : public ui::AnimationDelegate { // Window the phantom is placed beneath. aura::Window* window_; + // If set, the phantom window should get stacked below this window. + aura::Window* phantom_below_window_; + // Initially the bounds of |window_|. Each time Show() is invoked // |start_bounds_| is then reset to the bounds of |phantom_widget_| and // |bounds_| is set to the value passed into Show(). The animation animates @@ -67,7 +76,7 @@ class ASH_EXPORT PhantomWindowController : public ui::AnimationDelegate { gfx::Rect start_bounds_; gfx::Rect bounds_; - scoped_ptr<views::Widget> phantom_widget_; + views::Widget* phantom_widget_; // Used to transition the bounds. scoped_ptr<ui::SlideAnimation> animation_; diff --git a/ash/wm/workspace/snap_types.h b/ash/wm/workspace/snap_types.h new file mode 100644 index 0000000..482a7dd --- /dev/null +++ b/ash/wm/workspace/snap_types.h @@ -0,0 +1,23 @@ +// 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 ASH_WM_WORKSPACE_SNAP_TYPES_H_ +#define ASH_WM_WORKSPACE_SNAP_TYPES_H_ + +namespace ash { + +// These are the window snap types which can be used for window resizing. +// Their main use case is the class FrameMaximizeButton. +enum SnapType { + SNAP_LEFT, + SNAP_RIGHT, + SNAP_MAXIMIZE, + SNAP_MINIMIZE, + SNAP_RESTORE, + SNAP_NONE +}; + +} // namespace ash + +#endif // ASH_WM_WORKSPACE_SNAP_TYPES_H_ |