Commit a3df3751 authored by Alex Newcomer's avatar Alex Newcomer Committed by Commit Bot

cros: Make touchable app context menus use proper shadows

- Add MD shadows by...
  - Adding a member variable to BubbleBorder: std::Optional<int> md_shadow_elevation.
  - Modify static function GetBorderAndShadowFlags to support
    custom shadow elevation.
  - Adding a static function to cache ShadowValues.
    - I created a static function (GetShadowValues) here because other static
      functions assume that the default shadows are used.
  - Make GetBorderAndShadowInsets use gfx::ShadowValues::GetMargins when an elevation
    is set.

Bug: 826912
Change-Id: I93837da730fd5344aa026f67f1bff5ce3c766a98
Reviewed-on: https://chromium-review.googlesource.com/993275
Commit-Queue: Alex Newcomer <newcomer@chromium.org>
Reviewed-by: default avatarMichael Wasserman <msw@chromium.org>
Cr-Commit-Position: refs/heads/master@{#549379}
parent 8d99c3fa
......@@ -182,7 +182,13 @@ BubbleBorder::BubbleBorder(Arrow arrow, Shadow shadow, SkColor color)
BubbleBorder::~BubbleBorder() {}
// static
gfx::Insets BubbleBorder::GetBorderAndShadowInsets() {
gfx::Insets BubbleBorder::GetBorderAndShadowInsets(
base::Optional<int> elevation) {
if (elevation.has_value()) {
return -gfx::ShadowValue::GetMargin(GetShadowValues(elevation)) +
gfx::Insets(kBorderThicknessDip);
}
constexpr gfx::Insets blur(kShadowBlur + kBorderThicknessDip);
constexpr gfx::Insets offset(-kShadowVerticalOffset, 0, kShadowVerticalOffset,
0);
......@@ -378,8 +384,11 @@ void BubbleBorder::Paint(const views::View& view, gfx::Canvas* canvas) {
}
gfx::Insets BubbleBorder::GetInsets() const {
if (UseMaterialDesign())
return (shadow_ == NO_ASSETS) ? gfx::Insets() : GetBorderAndShadowInsets();
if (UseMaterialDesign()) {
return (shadow_ == NO_ASSETS)
? gfx::Insets()
: GetBorderAndShadowInsets(md_shadow_elevation_);
}
// The insets contain the stroke and shadow pixels outside the bubble fill.
const int inset = GetBorderThickness();
......@@ -401,14 +410,20 @@ gfx::Size BubbleBorder::GetMinimumSize() const {
}
// static
const cc::PaintFlags& BubbleBorder::GetBorderAndShadowFlags() {
// This object is always the same, so construct it once and cache.
static const base::NoDestructor<cc::PaintFlags> flags([] {
cc::PaintFlags f;
constexpr SkColor kBorderColor = SkColorSetA(SK_ColorBLACK, 0x26);
f.setColor(kBorderColor);
f.setAntiAlias(true);
const gfx::ShadowValues& BubbleBorder::GetShadowValues(
base::Optional<int> elevation) {
// The shadows are always the same for any elevation, so construct them once
// and cache.
static base::NoDestructor<std::map<int, gfx::ShadowValues>> shadow_map;
if (shadow_map->find(elevation.value_or(-1)) != shadow_map->end())
return shadow_map->find(elevation.value_or(-1))->second;
gfx::ShadowValues shadows;
if (elevation.has_value()) {
DCHECK(elevation.value() >= 0);
shadows = gfx::ShadowValues(
gfx::ShadowValue::MakeMdShadowValues(elevation.value()));
} else {
constexpr int kSmallShadowVerticalOffset = 2;
constexpr int kSmallShadowBlur = 4;
constexpr SkColor kSmallShadowColor = SkColorSetA(SK_ColorBLACK, 0x33);
......@@ -416,15 +431,38 @@ const cc::PaintFlags& BubbleBorder::GetBorderAndShadowFlags() {
// gfx::ShadowValue counts blur pixels both inside and outside the shape,
// whereas these blur values only describe the outside portion, hence they
// must be doubled.
f.setLooper(gfx::CreateShadowDrawLooper({
shadows = gfx::ShadowValues({
{gfx::Vector2d(0, kSmallShadowVerticalOffset), 2 * kSmallShadowBlur,
kSmallShadowColor},
{gfx::Vector2d(0, kShadowVerticalOffset), 2 * kShadowBlur,
kLargeShadowColor},
}));
return f;
}());
return *flags;
});
}
shadow_map->insert(
std::pair<int, gfx::ShadowValues>(elevation.value_or(-1), shadows));
return shadow_map->find(elevation.value_or(-1))->second;
}
// static
const cc::PaintFlags& BubbleBorder::GetBorderAndShadowFlags(
base::Optional<int> elevation) {
// The flags are always the same for any elevation, so construct them once and
// cache.
static base::NoDestructor<std::map<int, cc::PaintFlags>> flag_map;
if (flag_map->find(elevation.value_or(-1)) != flag_map->end())
return flag_map->find(elevation.value_or(-1))->second;
cc::PaintFlags flags;
constexpr SkColor kBorderColor = SkColorSetA(SK_ColorBLACK, 0x26);
flags.setColor(kBorderColor);
flags.setAntiAlias(true);
flags.setLooper(gfx::CreateShadowDrawLooper(GetShadowValues(elevation)));
flag_map->insert(
std::pair<int, cc::PaintFlags>(elevation.value_or(-1), flags));
return flag_map->find(elevation.value_or(-1))->second;
}
gfx::Size BubbleBorder::GetSizeForContentsSize(
......@@ -560,7 +598,8 @@ void BubbleBorder::PaintMd(const View& view, gfx::Canvas* canvas) {
canvas->sk_canvas()->clipRRect(r_rect, SkClipOp::kDifference,
true /*doAntiAlias*/);
DrawBorderAndShadow(std::move(r_rect), &cc::PaintCanvas::drawRRect, canvas);
DrawBorderAndShadow(std::move(r_rect), &cc::PaintCanvas::drawRRect, canvas,
md_shadow_elevation_);
}
void BubbleBorder::PaintNoAssets(const View& view, gfx::Canvas* canvas) {
......
......@@ -13,6 +13,7 @@
#include "build/build_config.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/shadow_value.h"
#include "ui/views/background.h"
#include "ui/views/border.h"
......@@ -172,24 +173,29 @@ class VIEWS_EXPORT BubbleBorder : public Border {
a : static_cast<Arrow>(a ^ BOTTOM);
}
// Returns the insets required by a border and shadow. This is only used for
// MD bubbles.
static gfx::Insets GetBorderAndShadowInsets();
// Returns the insets required by a border and shadow based on
// |shadow_elevation|. This is only used for MD bubbles. A null
// |shadow_elevation| will yield the default BubbleBorder MD insets.
static gfx::Insets GetBorderAndShadowInsets(
base::Optional<int> shadow_elevation = base::nullopt);
// Draws a border and shadow outside the |rect| on |canvas|, using |draw| as
// the draw function. Templated so as to accept either SkRect or SkRRect.
// Draws a border and shadow based on |shadow_elevation| outside the |rect| on
// |canvas|, using |draw| as the draw function. Templated so as to accept
// either SkRect or SkRRect.
template <typename T>
static void DrawBorderAndShadow(
T rect,
void (cc::PaintCanvas::*draw)(const T&, const cc::PaintFlags&),
gfx::Canvas* canvas) {
gfx::Canvas* canvas,
base::Optional<int> shadow_elevation = base::nullopt) {
// Provide a 1 px border outside the bounds.
const int kBorderStrokeThicknessPx = 1;
const SkScalar one_pixel =
SkFloatToScalar(kBorderStrokeThicknessPx / canvas->image_scale());
rect.outset(one_pixel, one_pixel);
(canvas->sk_canvas()->*draw)(rect, GetBorderAndShadowFlags());
(canvas->sk_canvas()->*draw)(rect,
GetBorderAndShadowFlags(shadow_elevation));
}
// Set the corner radius, enables Material Design.
......@@ -227,6 +233,14 @@ class VIEWS_EXPORT BubbleBorder : public Border {
// Sets the way the arrow is actually painted. Default is PAINT_NORMAL.
void set_paint_arrow(ArrowPaintType value);
// Sets the shadow elevation for MD shadows. A null |shadow_elevation| will
// yield the default BubbleBorder MD shadow.
void set_md_shadow_elevation(int shadow_elevation) {
DCHECK(UseMaterialDesign()) << "Setting a non-default MD shadow elevation "
"requires that the BubbleBorder is using MD";
md_shadow_elevation_ = shadow_elevation;
}
// Get the desired widget bounds (in screen coordinates) given the anchor rect
// and bubble content size; calculated from shadow and arrow image dimensions.
virtual gfx::Rect GetBounds(const gfx::Rect& anchor_rect,
......@@ -262,9 +276,18 @@ class VIEWS_EXPORT BubbleBorder : public Border {
FRIEND_TEST_ALL_PREFIXES(BubbleBorderTest, GetBoundsOriginTest);
FRIEND_TEST_ALL_PREFIXES(BubbleBorderTest, ShadowTypes);
// Returns the paint flags to use for painting the border and shadow. This is
// only used for MD bubbles.
static const cc::PaintFlags& GetBorderAndShadowFlags();
// Returns the shadows based on |shadow_elevation| to use for painting the
// border and shadow, and for getting insets. This is only used for MD
// bubbles. A null |shadow_elevation| will yield the default BubbleBorder MD
// ShadowValues.
static const gfx::ShadowValues& GetShadowValues(
base::Optional<int> shadow_elevation = base::nullopt);
// Returns the paint flags to use for painting the border and shadow based on
// |shadow_elevation|. This is only used for MD bubbles. A null
// |shadow_elevation| will yield the default BubbleBorder MD PaintFlags.
static const cc::PaintFlags& GetBorderAndShadowFlags(
base::Optional<int> shadow_elevation = base::nullopt);
// The border and arrow stroke size used in image assets, in pixels.
static const int kStroke;
......@@ -301,6 +324,8 @@ class VIEWS_EXPORT BubbleBorder : public Border {
// Corner radius for the bubble border. If supplied the border will use
// material design.
base::Optional<int> corner_radius_;
// Elevation for the MD shadow. Requires material design.
base::Optional<SkColor> md_shadow_elevation_;
ArrowPaintType arrow_paint_type_;
BubbleAlignment alignment_;
Shadow shadow_;
......
......@@ -50,8 +50,10 @@ MenuConfig::MenuConfig()
show_delay(400),
corner_radius(0),
touchable_corner_radius(8),
touchable_anchor_offset(8),
touchable_menu_height(36),
touchable_menu_width(256),
touchable_menu_shadow_elevation(12),
vertical_touchable_menu_item_padding(8) {
Init();
}
......
......@@ -143,12 +143,18 @@ struct VIEWS_EXPORT MenuConfig {
// Radius of the rounded corners of the touchable menu border
int touchable_corner_radius;
// Anchor offset for touchable menus created by a touch event.
int touchable_anchor_offset;
// Height of child MenuItemViews for touchable menus.
int touchable_menu_height;
// Width of touchable menus.
int touchable_menu_width;
// Shadow elevation of touchable menus.
int touchable_menu_shadow_elevation;
// Vertical padding for touchable menus.
int vertical_touchable_menu_item_padding;
......
......@@ -2144,6 +2144,12 @@ gfx::Rect MenuController::CalculateBubbleMenuBounds(MenuItemView* item,
pref.set_width(std::min(pref.width(),
item->GetDelegate()->GetMaxWidthForMenu(item)));
const MenuConfig& menu_config = MenuConfig::instance();
// Shadow insets are built into MenuScrollView's preferred size so it must be
// compensated for when determining the bounds of touchable menus.
gfx::Insets shadow_insets = BubbleBorder::GetBorderAndShadowInsets(
menu_config.touchable_menu_shadow_elevation);
int x, y;
if (state_.anchor == MENU_ANCHOR_BUBBLE_ABOVE ||
state_.anchor == MENU_ANCHOR_BUBBLE_BELOW) {
......@@ -2164,25 +2170,31 @@ gfx::Rect MenuController::CalculateBubbleMenuBounds(MenuItemView* item,
} else if (state_.anchor == MENU_ANCHOR_BUBBLE_TOUCHABLE_ABOVE) {
// Align the left edges of the menu and anchor, and the bottom of the menu
// with the top of the anchor.
x = owner_bounds.origin().x();
y = owner_bounds.origin().y() - pref.height();
x = owner_bounds.origin().x() - shadow_insets.left();
y = owner_bounds.origin().y() - pref.height() + shadow_insets.bottom() -
menu_config.touchable_anchor_offset;
// Align the right of the container with the right of the app icon.
if (x + pref.width() > state_.monitor_bounds.width())
x = owner_bounds.right() - pref.width();
x = owner_bounds.right() - pref.width() + shadow_insets.right();
// Align the top of the menu with the bottom of the anchor.
if (y < 0)
y = owner_bounds.bottom();
if (y < 0) {
y = owner_bounds.bottom() - shadow_insets.top() +
menu_config.touchable_anchor_offset;
}
} else if (state_.anchor == MENU_ANCHOR_BUBBLE_TOUCHABLE_LEFT) {
// Align the right of the menu with the left of the anchor, and the top of
// the menu with the top of the anchor.
x = owner_bounds.origin().x() - pref.width();
y = owner_bounds.origin().y();
x = owner_bounds.origin().x() - pref.width() + shadow_insets.right() -
menu_config.touchable_anchor_offset;
y = owner_bounds.origin().y() - shadow_insets.top();
// Align the left of the menu with the right of the anchor.
if (x < 0)
x = owner_bounds.right();
if (x < 0) {
x = owner_bounds.right() + shadow_insets.left() +
menu_config.touchable_anchor_offset;
}
// Align the bottom of the menu to the bottom of the anchor.
if (y + pref.height() > state_.monitor_bounds.height())
y = owner_bounds.bottom() - pref.height();
y = owner_bounds.bottom() - pref.height() + shadow_insets.bottom();
} else {
if (state_.anchor == MENU_ANCHOR_BUBBLE_RIGHT)
x = owner_bounds.right() - kBubbleTipSizeLeftRight;
......
......@@ -310,6 +310,8 @@ void MenuScrollViewContainer::CreateBubbleBorder() {
->use_touchable_layout()) {
const MenuConfig& menu_config = MenuConfig::instance();
bubble_border_->SetCornerRadius(menu_config.touchable_corner_radius);
bubble_border_->set_md_shadow_elevation(
menu_config.touchable_menu_shadow_elevation);
scroll_view_->GetContents()->SetBorder(CreateEmptyBorder(
gfx::Insets(menu_config.vertical_touchable_menu_item_padding, 0)));
}
......
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