| Index: ash/system/tray/tray_bubble_view.cc
|
| diff --git a/ash/system/tray/tray_bubble_view.cc b/ash/system/tray/tray_bubble_view.cc
|
| index 66a9498e45c8251b1af9a15265b366656dc2546d..99a0eb19dc1c38252a7a0323d7c5d74a4e988c91 100644
|
| --- a/ash/system/tray/tray_bubble_view.cc
|
| +++ b/ash/system/tray/tray_bubble_view.cc
|
| @@ -8,6 +8,7 @@
|
| #include "ash/shell_window_ids.h"
|
| #include "ash/system/tray/tray_constants.h"
|
| #include "ash/wm/shelf_layout_manager.h"
|
| +#include "ash/wm/window_animations.h"
|
| #include "grit/ash_strings.h"
|
| #include "third_party/skia/include/core/SkCanvas.h"
|
| #include "third_party/skia/include/core/SkColor.h"
|
| @@ -37,10 +38,13 @@ const int kArrowHeight = 9;
|
| const int kArrowWidth = 19;
|
|
|
| // Inset the arrow a bit from the edge.
|
| -const int kArrowMinOffset = kArrowWidth / 2 + 4;
|
| +const int kArrowEdgeMargin = 12;
|
| +const int kArrowMinOffset = kArrowWidth / 2 + kArrowEdgeMargin;
|
|
|
| const SkColor kShadowColor = SkColorSetARGB(0xff, 0, 0, 0);
|
|
|
| +const int kAnimationDurationForPopupMS = 200;
|
| +
|
| void DrawBlurredShadowAroundView(gfx::Canvas* canvas,
|
| int top,
|
| int bottom,
|
| @@ -72,27 +76,18 @@ class TrayBubbleBorder : public views::BubbleBorder {
|
| TrayBubbleBorder(views::View* owner,
|
| views::View* anchor,
|
| views::BubbleBorder::ArrowLocation arrow_location,
|
| - int arrow_offset)
|
| - : views::BubbleBorder(arrow_location,
|
| - views::BubbleBorder::NO_SHADOW),
|
| + int arrow_offset,
|
| + const SkColor& arrow_color)
|
| + : views::BubbleBorder(arrow_location, views::BubbleBorder::NO_SHADOW),
|
| owner_(owner),
|
| anchor_(anchor),
|
| tray_arrow_offset_(arrow_offset) {
|
| set_alignment(views::BubbleBorder::ALIGN_EDGE_TO_ANCHOR_EDGE);
|
| + set_background_color(arrow_color);
|
| }
|
|
|
| virtual ~TrayBubbleBorder() {}
|
|
|
| - private:
|
| - views::Background* FindAppropriateBackground(views::View* view,
|
| - const gfx::Point& point) const {
|
| - views::Background* background = NULL;
|
| - views::View* target = view->GetEventHandlerForPoint(point);
|
| - for (; target && !background; target = target->parent())
|
| - background = target->background();
|
| - return background;
|
| - }
|
| -
|
| // Overridden from views::BubbleBorder.
|
| // Override views::BubbleBorder to set the bubble on top of the anchor when
|
| // it has no arrow.
|
| @@ -117,11 +112,27 @@ class TrayBubbleBorder : public views::BubbleBorder {
|
| return gfx::Rect(x, y, border_size.width(), border_size.height());
|
| }
|
|
|
| + // TrayBubbleView supports dynamically updated bubbles. This does not
|
| + // behave well with BubbleFrameView which expects arrow_location to be
|
| + // unmirrored during initial layout (when ClientView is constructed),
|
| + // then mirrored after SizeToContents() gets called.
|
| + // So, instead of mirroring the arrow in CreateNonClientFrameView,
|
| + // mirror it here instead.
|
| + // TODO(stevenjb): Fix this in ui/views/bubble: crbug.com/139813
|
| + virtual void GetInsets(gfx::Insets* insets) const OVERRIDE {
|
| + ArrowLocation arrow_loc = arrow_location();
|
| + if (base::i18n::IsRTL())
|
| + arrow_loc = horizontal_mirror(arrow_loc);
|
| + return GetInsetsForArrowLocation(insets, arrow_loc);
|
| + }
|
| +
|
| // Overridden from views::Border.
|
| virtual void Paint(const views::View& view,
|
| gfx::Canvas* canvas) const OVERRIDE {
|
| gfx::Insets inset;
|
| - GetInsets(&inset);
|
| + // Get the unmirrored insets for the arrow location; the tray bubbles are
|
| + // never mirrored for RTL (since that would put them off screen).
|
| + GetInsetsForArrowLocation(&inset, arrow_location());
|
| DrawBlurredShadowAroundView(
|
| canvas, 0, owner_->height(), owner_->width(), inset);
|
|
|
| @@ -137,22 +148,28 @@ class TrayBubbleBorder : public views::BubbleBorder {
|
| gfx::Point arrow_reference;
|
|
|
| // Draw the arrow after drawing child borders, so that the arrow can cover
|
| - // the its overlap section with child border.
|
| + // its overlap section with child border.
|
| SkPath path;
|
| path.incReserve(4);
|
| if (arrow_location() == views::BubbleBorder::BOTTOM_RIGHT ||
|
| arrow_location() == views::BubbleBorder::BOTTOM_LEFT) {
|
| - // Do not let the arrow too close to the edge of the bubble and
|
| - // and the edge of the anchor.
|
| - int tip_x = base::i18n::IsRTL() ?
|
| - std::min(std::max(tray_arrow_offset_, kArrowMinOffset),
|
| - std::min(owner_->width(), anchor_->width())
|
| - - kArrowMinOffset) :
|
| - std::min(std::max(owner_->width() - tray_arrow_offset_,
|
| - owner_->width() -
|
| - std::min(owner_->width(), anchor_->width()) +
|
| - kArrowMinOffset),
|
| - owner_->width() - kArrowMinOffset);
|
| + // Note: tray_arrow_offset_ is relative to the anchor widget.
|
| + int tip_x;
|
| + if (tray_arrow_offset_ ==
|
| + internal::TrayBubbleView::InitParams::kArrowDefaultOffset) {
|
| + if (arrow_location() == views::BubbleBorder::BOTTOM_LEFT)
|
| + tip_x = kArrowMinOffset;
|
| + else
|
| + tip_x = owner_->width() - kArrowMinOffset;
|
| + } else {
|
| + gfx::Point pt(tray_arrow_offset_, 0);
|
| + views::View::ConvertPointToScreen(
|
| + anchor_->GetWidget()->GetRootView(), &pt);
|
| + views::View::ConvertPointFromScreen(
|
| + owner_->GetWidget()->GetRootView(), &pt);
|
| + tip_x = std::min(pt.x(), owner_->width() - kArrowMinOffset);
|
| + tip_x = std::max(tip_x, kArrowMinOffset);
|
| + }
|
| int left_base_x = tip_x - kArrowWidth / 2;
|
| int left_base_y = y;
|
| int tip_y = left_base_y + kArrowHeight;
|
| @@ -162,9 +179,20 @@ class TrayBubbleBorder : public views::BubbleBorder {
|
| SkIntToScalar(left_base_y));
|
| arrow_reference.SetPoint(tip_x, left_base_y - kArrowHeight);
|
| } else {
|
| - int tip_y = y - tray_arrow_offset_;
|
| - tip_y = std::min(std::max(kArrowMinOffset, tip_y),
|
| - owner_->height() - kArrowMinOffset);
|
| + int tip_y;
|
| + if (tray_arrow_offset_ ==
|
| + internal::TrayBubbleView::InitParams::kArrowDefaultOffset) {
|
| + tip_y = owner_->height() - kArrowMinOffset;
|
| + } else {
|
| + int pty = y - tray_arrow_offset_;
|
| + gfx::Point pt(0, pty);
|
| + views::View::ConvertPointToScreen(
|
| + anchor_->GetWidget()->GetRootView(), &pt);
|
| + views::View::ConvertPointFromScreen(
|
| + owner_->GetWidget()->GetRootView(), &pt);
|
| + tip_y = std::min(pt.y(), owner_->height() - kArrowMinOffset);
|
| + tip_y = std::max(tip_y, kArrowMinOffset);
|
| + }
|
| int top_base_y = tip_y - kArrowWidth / 2;
|
| int top_base_x, tip_x;
|
| if (arrow_location() == views::BubbleBorder::LEFT_BOTTOM) {
|
| @@ -184,13 +212,10 @@ class TrayBubbleBorder : public views::BubbleBorder {
|
| SkIntToScalar(top_base_y + kArrowWidth));
|
| }
|
|
|
| - views::Background* background = FindAppropriateBackground(owner_,
|
| - arrow_reference);
|
| -
|
| SkPaint paint;
|
| paint.setAntiAlias(true);
|
| paint.setStyle(SkPaint::kFill_Style);
|
| - paint.setColor(background ? background->get_color() : kBackgroundColor);
|
| + paint.setColor(background_color());
|
| canvas->DrawPath(path, paint);
|
|
|
| // Now draw the arrow border.
|
| @@ -211,17 +236,50 @@ class TrayBubbleBorder : public views::BubbleBorder {
|
|
|
| namespace internal {
|
|
|
| +// static
|
| +const int TrayBubbleView::InitParams::kArrowDefaultOffset = -1;
|
| +
|
| +TrayBubbleView::InitParams::InitParams(AnchorType anchor_type,
|
| + ShelfAlignment shelf_alignment)
|
| + : anchor_type(anchor_type),
|
| + shelf_alignment(shelf_alignment),
|
| + bubble_width(kTrayPopupWidth),
|
| + max_height(0),
|
| + can_activate(false),
|
| + arrow_offset(kArrowDefaultOffset),
|
| + arrow_color(kHeaderBackgroundColorDark) {
|
| +}
|
| +
|
| +TrayBubbleView* TrayBubbleView::Create(views::View* anchor,
|
| + Host* host,
|
| + const InitParams& init_params) {
|
| + // Set arrow_location here so that it can be passed correctly to the
|
| + // BubbleView constructor.
|
| + views::BubbleBorder::ArrowLocation arrow_location;
|
| + if (init_params.anchor_type == ANCHOR_TYPE_TRAY) {
|
| + if (init_params.shelf_alignment == SHELF_ALIGNMENT_BOTTOM) {
|
| + arrow_location = base::i18n::IsRTL() ?
|
| + views::BubbleBorder::BOTTOM_LEFT : views::BubbleBorder::BOTTOM_RIGHT;
|
| + } else if (init_params.shelf_alignment == SHELF_ALIGNMENT_LEFT) {
|
| + arrow_location = views::BubbleBorder::LEFT_BOTTOM;
|
| + } else {
|
| + arrow_location = views::BubbleBorder::RIGHT_BOTTOM;
|
| + }
|
| + } else {
|
| + arrow_location = views::BubbleBorder::NONE;
|
| + }
|
| +
|
| + return new TrayBubbleView(init_params, arrow_location, anchor, host);
|
| +}
|
| +
|
| TrayBubbleView::TrayBubbleView(
|
| - views::View* anchor,
|
| + const InitParams& init_params,
|
| views::BubbleBorder::ArrowLocation arrow_location,
|
| - Host* host,
|
| - bool can_activate,
|
| - int bubble_width)
|
| + views::View* anchor,
|
| + Host* host)
|
| : views::BubbleDelegateView(anchor, arrow_location),
|
| - host_(host),
|
| - can_activate_(can_activate),
|
| - max_height_(0),
|
| - bubble_width_(bubble_width) {
|
| + params_(init_params),
|
| + host_(host) {
|
| set_margins(gfx::Insets());
|
| set_parent_window(Shell::GetContainer(
|
| anchor->GetWidget()->GetNativeWindow()->GetRootWindow(),
|
| @@ -237,22 +295,13 @@ TrayBubbleView::~TrayBubbleView() {
|
| host_->BubbleViewDestroyed();
|
| }
|
|
|
| -void TrayBubbleView::SetBubbleBorder(int arrow_offset) {
|
| - DCHECK(GetWidget());
|
| - TrayBubbleBorder* bubble_border = new TrayBubbleBorder(
|
| - this, anchor_view(), arrow_location(), arrow_offset);
|
| - GetBubbleFrameView()->SetBubbleBorder(bubble_border);
|
| - // Recalculate size with new border.
|
| - SizeToContents();
|
| -}
|
| -
|
| void TrayBubbleView::UpdateBubble() {
|
| SizeToContents();
|
| GetWidget()->GetRootView()->SchedulePaint();
|
| }
|
|
|
| void TrayBubbleView::SetMaxHeight(int height) {
|
| - max_height_ = height;
|
| + params_.max_height = height;
|
| if (GetWidget())
|
| SizeToContents();
|
| }
|
| @@ -267,8 +316,36 @@ void TrayBubbleView::Init() {
|
|
|
| gfx::Rect TrayBubbleView::GetAnchorRect() {
|
| gfx::Rect rect;
|
| - if (host_)
|
| - rect = host_->GetAnchorRect();
|
| +
|
| + if (anchor_widget()->IsVisible()) {
|
| + rect = anchor_widget()->GetWindowBoundsInScreen();
|
| + if (params_.anchor_type == ANCHOR_TYPE_TRAY) {
|
| + if (params_.shelf_alignment == SHELF_ALIGNMENT_BOTTOM) {
|
| + bool rtl = base::i18n::IsRTL();
|
| + rect.Inset(
|
| + rtl ? kPaddingFromRightEdgeOfScreenBottomAlignment : 0,
|
| + 0,
|
| + rtl ? 0 : kPaddingFromRightEdgeOfScreenBottomAlignment,
|
| + kPaddingFromBottomOfScreenBottomAlignment);
|
| + } else if (params_.shelf_alignment == SHELF_ALIGNMENT_LEFT) {
|
| + rect.Inset(0, 0, kPaddingFromInnerEdgeOfLauncherVerticalAlignment,
|
| + kPaddingFromBottomOfScreenVerticalAlignment);
|
| + } else {
|
| + rect.Inset(kPaddingFromInnerEdgeOfLauncherVerticalAlignment,
|
| + 0, 0, kPaddingFromBottomOfScreenVerticalAlignment);
|
| + }
|
| + } else if (params_.anchor_type == ANCHOR_TYPE_BUBBLE) {
|
| + // Invert the offsets to align with the bubble below.
|
| + if (params_.shelf_alignment == SHELF_ALIGNMENT_LEFT) {
|
| + rect.Inset(kPaddingFromInnerEdgeOfLauncherVerticalAlignment,
|
| + 0, 0, kPaddingFromBottomOfScreenVerticalAlignment);
|
| + } else if (params_.shelf_alignment == SHELF_ALIGNMENT_RIGHT) {
|
| + rect.Inset(0, 0, kPaddingFromInnerEdgeOfLauncherVerticalAlignment,
|
| + kPaddingFromBottomOfScreenVerticalAlignment);
|
| + }
|
| + }
|
| + }
|
| +
|
| // TODO(jennyz): May need to add left/right alignment in the following code.
|
| if (rect.IsEmpty()) {
|
| rect = gfx::Screen::GetPrimaryDisplay().bounds();
|
| @@ -288,15 +365,25 @@ gfx::Rect TrayBubbleView::GetBubbleBounds() {
|
| }
|
|
|
| bool TrayBubbleView::CanActivate() const {
|
| - return can_activate_;
|
| + return params_.can_activate;
|
| +}
|
| +
|
| +// Overridden to create BubbleFrameView and set the border to TrayBubbleBorder
|
| +// (instead of creating a default BubbleBorder and replacing it).
|
| +views::NonClientFrameView* TrayBubbleView::CreateNonClientFrameView(
|
| + views::Widget* widget) {
|
| + TrayBubbleBorder* bubble_border = new TrayBubbleBorder(
|
| + this, anchor_view(),
|
| + arrow_location(), params_.arrow_offset, params_.arrow_color);
|
| + return new views::BubbleFrameView(margins(), bubble_border);
|
| }
|
|
|
| gfx::Size TrayBubbleView::GetPreferredSize() {
|
| gfx::Size size = views::BubbleDelegateView::GetPreferredSize();
|
| int height = size.height();
|
| - if (max_height_ != 0 && height > max_height_)
|
| - height = max_height_;
|
| - return gfx::Size(bubble_width_, height);
|
| + if (params_.max_height != 0 && height > params_.max_height)
|
| + height = params_.max_height;
|
| + return gfx::Size(params_.bubble_width, height);
|
| }
|
|
|
| void TrayBubbleView::OnMouseEntered(const views::MouseEvent& event) {
|
| @@ -310,7 +397,7 @@ void TrayBubbleView::OnMouseExited(const views::MouseEvent& event) {
|
| }
|
|
|
| void TrayBubbleView::GetAccessibleState(ui::AccessibleViewState* state) {
|
| - if (can_activate_) {
|
| + if (params_.can_activate) {
|
| state->role = ui::AccessibilityTypes::ROLE_WINDOW;
|
| state->name = l10n_util::GetStringUTF16(
|
| IDS_ASH_STATUS_TRAY_ACCESSIBLE_NAME);
|
| @@ -341,10 +428,28 @@ TrayBubbleView::Host::~Host() {
|
| Shell::GetInstance()->RemoveEnvEventFilter(this);
|
| }
|
|
|
| -void TrayBubbleView::Host::InitializeHost(views::Widget* widget,
|
| - views::View* tray_view) {
|
| +void TrayBubbleView::Host::InitializeAndShowBubble(views::Widget* widget,
|
| + TrayBubbleView* bubble_view,
|
| + views::View* tray_view) {
|
| widget_ = widget;
|
| tray_view_ = tray_view;
|
| +
|
| + // Must occur after call to BubbleDelegateView::CreateBubble().
|
| + bubble_view->SetAlignment(views::BubbleBorder::ALIGN_EDGE_TO_ANCHOR_EDGE);
|
| +
|
| + // Setup animation.
|
| + ash::SetWindowVisibilityAnimationType(
|
| + widget->GetNativeWindow(),
|
| + ash::WINDOW_VISIBILITY_ANIMATION_TYPE_FADE);
|
| + ash::SetWindowVisibilityAnimationTransition(
|
| + widget->GetNativeWindow(),
|
| + ash::ANIMATE_BOTH);
|
| + ash::SetWindowVisibilityAnimationDuration(
|
| + widget->GetNativeWindow(),
|
| + base::TimeDelta::FromMilliseconds(kAnimationDurationForPopupMS));
|
| +
|
| + bubble_view->Show();
|
| + bubble_view->UpdateBubble();
|
| }
|
|
|
| bool TrayBubbleView::Host::PreHandleKeyEvent(aura::Window* target,
|
|
|