Commit ff9d9941 authored by estade's avatar estade Committed by Commit bot

Apply new MD shadows to CrOS tray bubbles.

* affects system menu, message center, palette bubble, ime bubble, etc.
* WM shadow aperture calculation is adjusted to work better for small
  windows (like the message center when it's empty). There's a TODO
  here which will have to wait for Sebastien to get back so he can give
  feedback on what to do in this case.
* fix alignment of NO_ASSETS bubble --- don't account for
  non-existent stroke.
* existing BubbleBorder tests applied to more different kinds of bubble

BUG=608852

Review-Url: https://codereview.chromium.org/2555373004
Cr-Commit-Position: refs/heads/master@{#438196}
parent 479f44fc
......@@ -534,20 +534,12 @@ views::View* TrayBackgroundView::GetBubbleAnchor() const {
gfx::Insets TrayBackgroundView::GetBubbleAnchorInsets() const {
gfx::Insets anchor_insets = GetBubbleAnchor()->GetInsets();
gfx::Insets tray_bg_insets = GetInsets();
// TODO(estade): for reasons I don't understand, BubbleBorder distances the
// bubble by the arrow's "interior" thickness even when the paint type is
// PAINT_NONE.
const int kBigShadowArrowInteriorThickness = 9;
if (GetAnchorAlignment() == TrayBubbleView::ANCHOR_ALIGNMENT_BOTTOM) {
return gfx::Insets(kBigShadowArrowInteriorThickness - tray_bg_insets.top(),
anchor_insets.left(), -tray_bg_insets.bottom(),
anchor_insets.right());
return gfx::Insets(-tray_bg_insets.top(), anchor_insets.left(),
-tray_bg_insets.bottom(), anchor_insets.right());
} else {
return gfx::Insets(
anchor_insets.top(),
kBigShadowArrowInteriorThickness - tray_bg_insets.left(),
anchor_insets.bottom(),
kBigShadowArrowInteriorThickness - tray_bg_insets.right());
return gfx::Insets(anchor_insets.top(), -tray_bg_insets.left(),
anchor_insets.bottom(), -tray_bg_insets.right());
}
}
......
......@@ -255,10 +255,12 @@ gfx::Rect BubbleBorder::GetBounds(const gfx::Rect& anchor_rect,
int h = anchor_rect.height();
const gfx::Size size(GetSizeForContentsSize(contents_size));
const int arrow_offset = GetArrowOffset(size);
const int stroke_width = shadow_ == NO_ASSETS ? 0 : kStroke;
// |arrow_shift| is necessary to visually align the tip of the bubble arrow
// with the anchor point. This shift is an inverse of the shadow thickness.
int arrow_shift = UseMd() ? 0 :
images_->arrow_interior_thickness + kStroke - images_->arrow_thickness;
int arrow_shift = UseMd() ? 0
: images_->arrow_interior_thickness + stroke_width -
images_->arrow_thickness;
// When arrow is painted transparently the visible border of the bubble needs
// to be positioned at the same bounds as when the arrow is shown.
if (arrow_paint_type_ == PAINT_TRANSPARENT)
......@@ -268,12 +270,13 @@ gfx::Rect BubbleBorder::GetBounds(const gfx::Rect& anchor_rect,
// Calculate the bubble coordinates based on the border and arrow settings.
if (is_arrow_on_horizontal(arrow_)) {
if (is_arrow_on_left(arrow_)) {
x += mid_anchor ? w / 2 - arrow_offset : kStroke - GetBorderThickness();
x += mid_anchor ? w / 2 - arrow_offset
: stroke_width - GetBorderThickness();
} else if (is_arrow_at_center(arrow_)) {
x += w / 2 - arrow_offset;
} else {
x += mid_anchor ? w / 2 + arrow_offset - size.width() :
w - size.width() + GetBorderThickness() - kStroke;
x += mid_anchor ? w / 2 + arrow_offset - size.width()
: w - size.width() + GetBorderThickness() - stroke_width;
}
y += is_arrow_on_top(arrow_) ? h + arrow_shift
: -arrow_shift - size.height();
......@@ -281,12 +284,13 @@ gfx::Rect BubbleBorder::GetBounds(const gfx::Rect& anchor_rect,
x += is_arrow_on_left(arrow_) ? w + arrow_shift
: -arrow_shift - size.width();
if (is_arrow_on_top(arrow_)) {
y += mid_anchor ? h / 2 - arrow_offset : kStroke - GetBorderThickness();
y += mid_anchor ? h / 2 - arrow_offset
: stroke_width - GetBorderThickness();
} else if (is_arrow_at_center(arrow_)) {
y += h / 2 - arrow_offset;
} else {
y += mid_anchor ? h / 2 + arrow_offset - size.height() :
h - size.height() + GetBorderThickness() - kStroke;
y += mid_anchor ? h / 2 + arrow_offset - size.height()
: h - size.height() + GetBorderThickness() - stroke_width;
}
} else {
x += (w - size.width()) / 2;
......
......@@ -100,6 +100,8 @@ class VIEWS_EXPORT BubbleBorder : public Border {
NO_SHADOW_OPAQUE_BORDER,
BIG_SHADOW,
SMALL_SHADOW,
// NO_ASSETS borders don't draw a stroke or a shadow. This is used for
// platforms that provide their own shadows.
NO_ASSETS,
SHADOW_COUNT,
};
......
......@@ -320,12 +320,20 @@ TEST_F(BubbleBorderTest, GetSizeForContentsSizeTest) {
}
TEST_F(BubbleBorderTest, GetBoundsOriginTest) {
views::BubbleBorder border(BubbleBorder::TOP_LEFT,
BubbleBorder::NO_SHADOW,
SK_ColorWHITE);
for (int i = 0; i < BubbleBorder::SHADOW_COUNT; ++i) {
const BubbleBorder::Shadow shadow = static_cast<BubbleBorder::Shadow>(i);
SCOPED_TRACE(testing::Message() << "BubbleBorder::Shadow: " << shadow);
views::BubbleBorder border(BubbleBorder::TOP_LEFT, shadow, SK_ColorWHITE);
#if defined(OS_MACOSX)
// Mac always uses NO_ASSETS, no matter what we tell it to use. No point in
// testing any other shadow.
EXPECT_EQ(BubbleBorder::NO_ASSETS, border.shadow());
if (shadow != BubbleBorder::NO_ASSETS)
continue;
#endif
const gfx::Rect kAnchor(100, 100, 20, 30);
const gfx::Size kContentSize(50, 60);
const gfx::Size kContentSize(500, 600);
const views::internal::BorderImages* kImages = border.GetImagesForTest();
......@@ -348,15 +356,27 @@ TEST_F(BubbleBorderTest, GetBoundsOriginTest) {
const int kArrowOffsetForNotCenter =
kImages->border_thickness + (kImages->arrow_width / 2);
const int kStrokeWidth =
shadow == BubbleBorder::NO_ASSETS ? 0 : BubbleBorder::kStroke;
const int kArrowThickness = kImages->arrow_interior_thickness;
const int kArrowShift =
kArrowThickness + BubbleBorder::kStroke - kImages->arrow_thickness;
const int kHeightDifference = kTotalSizeWithHorizArrow.height() -
kTotalSizeWithNoArrow.height();
const int kWidthDifference = kTotalSizeWithVertArrow.width() -
kTotalSizeWithNoArrow.width();
kArrowThickness + kStrokeWidth - kImages->arrow_thickness;
const int kHeightDifference =
kTotalSizeWithHorizArrow.height() - kTotalSizeWithNoArrow.height();
const int kWidthDifference =
kTotalSizeWithVertArrow.width() - kTotalSizeWithNoArrow.width();
EXPECT_EQ(kHeightDifference, kWidthDifference);
EXPECT_EQ(kHeightDifference, kArrowThickness);
// The arrow only makes a difference in height if it is longer than the
// shadow.
const int kExpectedHeightDifference =
std::max(kImages->arrow_thickness + kImages->border_interior_thickness,
kImages->border_thickness) -
std::max(kImages->border_interior_thickness, kImages->border_thickness);
EXPECT_EQ(kExpectedHeightDifference, kHeightDifference)
<< "Size with arrow: " << kTotalSizeWithHorizArrow.ToString()
<< " vs. size without arrow: " << kTotalSizeWithNoArrow.ToString();
const int kTopHorizArrowY = kAnchor.y() + kAnchor.height() + kArrowShift;
const int kBottomHorizArrowY =
......@@ -374,46 +394,52 @@ TEST_F(BubbleBorderTest, GetBoundsOriginTest) {
TestCase cases[] = {
// Horizontal arrow tests.
{ BubbleBorder::TOP_LEFT, BubbleBorder::ALIGN_ARROW_TO_MID_ANCHOR,
kAnchor.CenterPoint().x() - kArrowOffsetForNotCenter, kTopHorizArrowY },
{ BubbleBorder::TOP_LEFT, BubbleBorder::ALIGN_EDGE_TO_ANCHOR_EDGE,
kAnchor.x() + BubbleBorder::kStroke - kBorderThickness, kTopHorizArrowY },
{ BubbleBorder::TOP_CENTER, BubbleBorder::ALIGN_ARROW_TO_MID_ANCHOR,
kAnchor.CenterPoint().x() - kArrowOffsetForHorizCenter, kTopHorizArrowY },
{ BubbleBorder::BOTTOM_RIGHT, BubbleBorder::ALIGN_ARROW_TO_MID_ANCHOR,
{BubbleBorder::TOP_LEFT, BubbleBorder::ALIGN_ARROW_TO_MID_ANCHOR,
kAnchor.CenterPoint().x() - kArrowOffsetForNotCenter, kTopHorizArrowY},
{BubbleBorder::TOP_LEFT, BubbleBorder::ALIGN_EDGE_TO_ANCHOR_EDGE,
kAnchor.x() + kStrokeWidth - kBorderThickness, kTopHorizArrowY},
{BubbleBorder::TOP_CENTER, BubbleBorder::ALIGN_ARROW_TO_MID_ANCHOR,
kAnchor.CenterPoint().x() - kArrowOffsetForHorizCenter,
kTopHorizArrowY},
{BubbleBorder::BOTTOM_RIGHT, BubbleBorder::ALIGN_ARROW_TO_MID_ANCHOR,
kAnchor.CenterPoint().x() + kArrowOffsetForNotCenter -
kTotalSizeWithHorizArrow.width(), kBottomHorizArrowY },
{ BubbleBorder::BOTTOM_RIGHT, BubbleBorder::ALIGN_EDGE_TO_ANCHOR_EDGE,
kTotalSizeWithHorizArrow.width(),
kBottomHorizArrowY},
{BubbleBorder::BOTTOM_RIGHT, BubbleBorder::ALIGN_EDGE_TO_ANCHOR_EDGE,
kAnchor.x() + kAnchor.width() - kTotalSizeWithHorizArrow.width() +
kBorderThickness - BubbleBorder::kStroke, kBottomHorizArrowY },
kBorderThickness - kStrokeWidth,
kBottomHorizArrowY},
// Vertical arrow tests.
{ BubbleBorder::LEFT_TOP, BubbleBorder::ALIGN_ARROW_TO_MID_ANCHOR,
kLeftVertArrowX, kAnchor.CenterPoint().y() - kArrowOffsetForNotCenter },
{ BubbleBorder::LEFT_TOP, BubbleBorder::ALIGN_EDGE_TO_ANCHOR_EDGE,
kLeftVertArrowX, kAnchor.y() + BubbleBorder::kStroke - kBorderThickness },
{ BubbleBorder::LEFT_CENTER, BubbleBorder::ALIGN_ARROW_TO_MID_ANCHOR,
kLeftVertArrowX, kAnchor.CenterPoint().y() - kArrowOffsetForVertCenter },
{ BubbleBorder::RIGHT_BOTTOM, BubbleBorder::ALIGN_ARROW_TO_MID_ANCHOR,
kRightVertArrowX, kAnchor.CenterPoint().y() + kArrowOffsetForNotCenter -
kTotalSizeWithVertArrow.height() },
{ BubbleBorder::RIGHT_BOTTOM, BubbleBorder::ALIGN_EDGE_TO_ANCHOR_EDGE,
kRightVertArrowX, kAnchor.y() + kAnchor.height() -
kTotalSizeWithVertArrow.height() + kBorderThickness -
BubbleBorder::kStroke },
{BubbleBorder::LEFT_TOP, BubbleBorder::ALIGN_ARROW_TO_MID_ANCHOR,
kLeftVertArrowX, kAnchor.CenterPoint().y() - kArrowOffsetForNotCenter},
{BubbleBorder::LEFT_TOP, BubbleBorder::ALIGN_EDGE_TO_ANCHOR_EDGE,
kLeftVertArrowX, kAnchor.y() + kStrokeWidth - kBorderThickness},
{BubbleBorder::LEFT_CENTER, BubbleBorder::ALIGN_ARROW_TO_MID_ANCHOR,
kLeftVertArrowX,
kAnchor.CenterPoint().y() - kArrowOffsetForVertCenter},
{BubbleBorder::RIGHT_BOTTOM, BubbleBorder::ALIGN_ARROW_TO_MID_ANCHOR,
kRightVertArrowX,
kAnchor.CenterPoint().y() + kArrowOffsetForNotCenter -
kTotalSizeWithVertArrow.height()},
{BubbleBorder::RIGHT_BOTTOM, BubbleBorder::ALIGN_EDGE_TO_ANCHOR_EDGE,
kRightVertArrowX,
kAnchor.y() + kAnchor.height() - kTotalSizeWithVertArrow.height() +
kBorderThickness - kStrokeWidth},
// No arrow tests.
{ BubbleBorder::NONE, BubbleBorder::ALIGN_ARROW_TO_MID_ANCHOR,
{BubbleBorder::NONE, BubbleBorder::ALIGN_ARROW_TO_MID_ANCHOR,
kAnchor.x() + (kAnchor.width() - kTotalSizeWithNoArrow.width()) / 2,
kAnchor.y() + kAnchor.height() },
{ BubbleBorder::FLOAT, BubbleBorder::ALIGN_ARROW_TO_MID_ANCHOR,
kAnchor.y() + kAnchor.height()},
{BubbleBorder::FLOAT, BubbleBorder::ALIGN_ARROW_TO_MID_ANCHOR,
kAnchor.x() + (kAnchor.width() - kTotalSizeWithNoArrow.width()) / 2,
kAnchor.y() + (kAnchor.height() - kTotalSizeWithNoArrow.height()) / 2 },
kAnchor.y() + (kAnchor.height() - kTotalSizeWithNoArrow.height()) / 2},
};
for (size_t i = 0; i < arraysize(cases); ++i) {
SCOPED_TRACE(base::StringPrintf("i=%d arrow=%d alignment=%d",
static_cast<int>(i), cases[i].arrow, cases[i].alignment));
static_cast<int>(i), cases[i].arrow,
cases[i].alignment));
const BubbleBorder::Arrow arrow = cases[i].arrow;
border.set_arrow(arrow);
border.set_alignment(cases[i].alignment);
......@@ -429,10 +455,12 @@ TEST_F(BubbleBorderTest, GetBoundsOriginTest) {
origin = border.GetBounds(kAnchor, kContentSize).origin();
if (border.is_arrow_on_horizontal(arrow)) {
expected_y += BubbleBorder::is_arrow_on_top(arrow)
? kArrowThickness : (-kArrowThickness + kHeightDifference);
? kArrowThickness
: (-kArrowThickness + kHeightDifference);
} else if (BubbleBorder::has_arrow(arrow)) {
expected_x += BubbleBorder::is_arrow_on_left(arrow)
? kArrowThickness : (-kArrowThickness + kWidthDifference);
? kArrowThickness
: (-kArrowThickness + kWidthDifference);
}
EXPECT_EQ(expected_x, origin.x());
EXPECT_EQ(expected_y, origin.y());
......@@ -453,6 +481,7 @@ TEST_F(BubbleBorderTest, GetBoundsOriginTest) {
EXPECT_EQ(expected_y, origin.y());
}
}
}
// Ensure all the shadow types pass some size validation and paint sanely.
TEST_F(BubbleBorderTest, ShadowTypes) {
......
......@@ -196,7 +196,7 @@ TrayBubbleView::TrayBubbleView(View* anchor,
delegate_(delegate),
preferred_width_(init_params.min_width),
bubble_border_(new BubbleBorder(arrow(),
BubbleBorder::BIG_SHADOW,
BubbleBorder::NO_ASSETS,
init_params.bg_color)),
owned_bubble_border_(bubble_border_),
is_gesture_dragging_(false),
......@@ -272,6 +272,8 @@ void TrayBubbleView::OnBeforeBubbleWidgetInit(Widget::InitParams* params,
Widget* bubble_widget) const {
if (delegate_)
delegate_->OnBeforeBubbleWidgetInit(anchor_widget(), bubble_widget, params);
// Apply a WM-provided shadow (see ui/wm/core/).
params->shadow_type = Widget::InitParams::SHADOW_TYPE_DROP;
}
NonClientFrameView* TrayBubbleView::CreateNonClientFrameView(Widget* widget) {
......
......@@ -132,15 +132,6 @@ void Shadow::RecreateShadowLayer() {
const ShadowDetails& details = GetDetailsForElevation(ElevationForStyle());
shadow_layer_->UpdateNinePatchLayerImage(details.ninebox_image);
// The ninebox grid is defined in terms of the image size. The shadow blurs in
// both inward and outward directions from the edge of the contents, so the
// aperture goes further inside the image than the shadow margins (which
// represent exterior blur).
gfx::Rect aperture(details.ninebox_image.size());
gfx::Insets blur_region = gfx::ShadowValue::GetBlurRegion(details.values) +
gfx::Insets(kRoundedCornerRadius);
aperture.Inset(blur_region);
shadow_layer_->UpdateNinePatchLayerAperture(aperture);
UpdateLayerBounds();
}
......@@ -194,6 +185,21 @@ void Shadow::UpdateLayerBounds() {
int border_y = border_h * blur_region.top() / blur_region.height();
shadow_layer_->UpdateNinePatchLayerBorder(
gfx::Rect(border_x, border_y, border_w, border_h));
// The ninebox grid is defined in terms of the image size. The shadow blurs in
// both inward and outward directions from the edge of the contents, so the
// aperture goes further inside the image than the shadow margins (which
// represent exterior blur).
gfx::Rect aperture(details.ninebox_image.size());
// The insets for the aperture are nominally |blur_region| but we need to
// resize them if the contents are too small.
// TODO(estade): by cutting out parts of ninebox, we lose the smooth
// horizontal or vertical transition. This isn't very noticeable, but we may
// need to address it by using a separate shadow layer for each ShadowValue,
// by adjusting the shadow for very small windows, or other means.
aperture.Inset(gfx::Insets(border_y, border_x, border_h - border_y,
border_w - border_x));
shadow_layer_->UpdateNinePatchLayerAperture(aperture);
}
int Shadow::ElevationForStyle() {
......
......@@ -21,6 +21,7 @@ namespace wm {
// Simple class that draws a drop shadow around content at given bounds.
class WM_EXPORT Shadow : public ui::ImplicitAnimationObserver {
public:
// TODO(estade): remove this enum and instead set elevation directly.
enum Style {
// Active windows have more opaque shadows, shifted down to make the window
// appear "higher".
......
......@@ -45,20 +45,22 @@ ShadowType GetShadowTypeFromWindow(aura::Window* window) {
return SHADOW_TYPE_NONE;
}
bool ShouldUseSmallShadowForWindow(aura::Window* window) {
Shadow::Style GetShadowStyleForWindow(aura::Window* window) {
switch (window->type()) {
case ui::wm::WINDOW_TYPE_MENU:
case ui::wm::WINDOW_TYPE_TOOLTIP:
return true;
return Shadow::STYLE_SMALL;
// System tray bubbles render like active windows. TODO(estade): this
// mechanism will need to be revisited for applying WM shadows to other
// types of bubbles which don't want to render such large shadows.
case ui::wm::WINDOW_TYPE_POPUP:
return Shadow::STYLE_ACTIVE;
default:
break;
return IsActiveWindow(window) ? Shadow::STYLE_ACTIVE
: Shadow::STYLE_INACTIVE;
}
return false;
}
Shadow::Style GetShadowStyleForWindow(aura::Window* window) {
return ShouldUseSmallShadowForWindow(window) ? Shadow::STYLE_SMALL :
(IsActiveWindow(window) ? Shadow::STYLE_ACTIVE : Shadow::STYLE_INACTIVE);
}
// Returns the shadow style to be applied to |losing_active| when it is losing
......@@ -193,12 +195,13 @@ void ShadowController::Impl::OnWindowActivated(ActivationReason reason,
aura::Window* lost_active) {
if (gained_active) {
Shadow* shadow = GetShadowForWindow(gained_active);
if (shadow && !ShouldUseSmallShadowForWindow(gained_active))
shadow->SetStyle(Shadow::STYLE_ACTIVE);
if (shadow)
shadow->SetStyle(GetShadowStyleForWindow(gained_active));
}
if (lost_active) {
Shadow* shadow = GetShadowForWindow(lost_active);
if (shadow && !ShouldUseSmallShadowForWindow(lost_active)) {
if (shadow &&
GetShadowStyleForWindow(lost_active) == Shadow::STYLE_INACTIVE) {
shadow->SetStyle(GetShadowStyleForWindowLosingActive(lost_active,
gained_active));
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment