Commit b5d76027 authored by Dana Fried's avatar Dana Fried Committed by Commit Bot

Add "New" badging for menu items as a user education feature.

Behind a flag (nothing happens if flag is not set).
This is part 1. It introduces the flag which enables the badging, as
well as the ability for menu items to be badged.

However, the pipeline to hook up badging is not included. Follow-up CLs
will include:
1. hook-up from SimpleMenuModel
2. specific items to be badged for release

See: 
https: //docs.google.com/document/d/1vczPpUCnYOKvSQtSWMYX8K1mK-vhrjwsAyRKFv22eYk
Change-Id: If8900433015e71802b5deb0e32bebb9477ea8d62
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2208794Reviewed-by: default avatarMichael Wasserman <msw@chromium.org>
Commit-Queue: Dana Fried <dfried@chromium.org>
Cr-Commit-Position: refs/heads/master@{#782090}
parent ca19197c
...@@ -4691,6 +4691,11 @@ const FeatureEntry kFeatureEntries[] = { ...@@ -4691,6 +4691,11 @@ const FeatureEntry kFeatureEntries[] = {
flag_descriptions::kEnableMDRoundedCornersOnDialogsName, flag_descriptions::kEnableMDRoundedCornersOnDialogsName,
flag_descriptions::kEnableMDRoundedCornersOnDialogsDescription, kOsDesktop, flag_descriptions::kEnableMDRoundedCornersOnDialogsDescription, kOsDesktop,
FEATURE_VALUE_TYPE(views::features::kEnableMDRoundedCornersOnDialogs)}, FEATURE_VALUE_TYPE(views::features::kEnableMDRoundedCornersOnDialogs)},
{"enable-new-badge-on-menu-items",
flag_descriptions::kEnableNewBadgeOnMenuItemsName,
flag_descriptions::kEnableNewBadgeOnMenuItemsDescription, kOsDesktop,
FEATURE_VALUE_TYPE(views::features::kEnableNewBadgeOnMenuItems)},
#endif // defined(TOOLKIT_VIEWS) #endif // defined(TOOLKIT_VIEWS)
{"strict-origin-isolation", flag_descriptions::kStrictOriginIsolationName, {"strict-origin-isolation", flag_descriptions::kStrictOriginIsolationName,
......
This diff is collapsed.
...@@ -4052,6 +4052,12 @@ const char kReopenTabInProductHelpDescription[] = ...@@ -4052,6 +4052,12 @@ const char kReopenTabInProductHelpDescription[] =
"Enable in-product help that guides a user to reopen a tab if it looks " "Enable in-product help that guides a user to reopen a tab if it looks "
"like they accidentally closed it."; "like they accidentally closed it.";
const char kEnableNewBadgeOnMenuItemsName[] =
"Enable 'New' badge on menu items";
const char kEnableNewBadgeOnMenuItemsDescription[] =
"When enabled, allows 'New' badge to help users identify menu items which "
"access new functionality.";
#endif // defined(TOOLKIT_VIEWS) #endif // defined(TOOLKIT_VIEWS)
// Random platform combinations ----------------------------------------------- // Random platform combinations -----------------------------------------------
......
...@@ -2374,6 +2374,9 @@ extern const char kTextfieldFocusOnTapUpDescription[]; ...@@ -2374,6 +2374,9 @@ extern const char kTextfieldFocusOnTapUpDescription[];
extern const char kReopenTabInProductHelpName[]; extern const char kReopenTabInProductHelpName[];
extern const char kReopenTabInProductHelpDescription[]; extern const char kReopenTabInProductHelpDescription[];
extern const char kEnableNewBadgeOnMenuItemsName[];
extern const char kEnableNewBadgeOnMenuItemsDescription[];
#endif // defined(TOOLKIT_VIEWS) #endif // defined(TOOLKIT_VIEWS)
// Random platform combinations ----------------------------------------------- // Random platform combinations -----------------------------------------------
......
...@@ -41741,6 +41741,7 @@ from previous Chrome versions. ...@@ -41741,6 +41741,7 @@ from previous Chrome versions.
<int value="667643314" label="LitePageServerPreviews:enabled"/> <int value="667643314" label="LitePageServerPreviews:enabled"/>
<int value="669097106" label="NtpRealboxMatchOmniboxTheme:disabled"/> <int value="669097106" label="NtpRealboxMatchOmniboxTheme:disabled"/>
<int value="673588373" label="OmniboxPedalSuggestions:disabled"/> <int value="673588373" label="OmniboxPedalSuggestions:disabled"/>
<int value="674788251" label="EnableNewBadgeOnMenuItems:enabled"/>
<int value="677866592" label="ClickToCallUI:disabled"/> <int value="677866592" label="ClickToCallUI:disabled"/>
<int value="679251577" label="AccessibilityExposeARIAAnnotations:disabled"/> <int value="679251577" label="AccessibilityExposeARIAAnnotations:disabled"/>
<int value="679931272" label="DcheckIsFatal:enabled"/> <int value="679931272" label="DcheckIsFatal:enabled"/>
...@@ -42777,6 +42778,7 @@ from previous Chrome versions. ...@@ -42777,6 +42778,7 @@ from previous Chrome versions.
<int value="1823337908" label="SmartDim20190221:enabled"/> <int value="1823337908" label="SmartDim20190221:enabled"/>
<int value="1824931483" <int value="1824931483"
label="EnableHistoryFaviconsGoogleServerQuery:disabled"/> label="EnableHistoryFaviconsGoogleServerQuery:disabled"/>
<int value="1825369164" label="EnableNewBadgeOnMenuItems:disabled"/>
<int value="1825940786" label="ChromeHomePromo:disabled"/> <int value="1825940786" label="ChromeHomePromo:disabled"/>
<int value="1827369558" label="AndroidPayIntegrationV1:disabled"/> <int value="1827369558" label="AndroidPayIntegrationV1:disabled"/>
<int value="1828660283" label="enable-webfonts-intervention-trigger"/> <int value="1828660283" label="enable-webfonts-intervention-trigger"/>
...@@ -367,6 +367,10 @@ need to be translated for each locale.--> ...@@ -367,6 +367,10 @@ need to be translated for each locale.-->
(empty) (empty)
</message> </message>
<message name="IDS_MENU_ITEM_NEW_BADGE" desc="Appears as a badge on menu items denoting new features">
New
</message>
<!-- General application strings --> <!-- General application strings -->
<message name="IDS_SENTENCE_END" desc="The symbol that is used to end a sentence."> <message name="IDS_SENTENCE_END" desc="The symbol that is used to end a sentence.">
. .
......
c88f3adea4b633341c30b3220292ae3cfa4dd50f
\ No newline at end of file
...@@ -46,12 +46,19 @@ ...@@ -46,12 +46,19 @@
#include "ui/views/style/typography.h" #include "ui/views/style/typography.h"
#include "ui/views/vector_icons.h" #include "ui/views/vector_icons.h"
#include "ui/views/view_class_properties.h" #include "ui/views/view_class_properties.h"
#include "ui/views/views_features.h"
#include "ui/views/widget/widget.h" #include "ui/views/widget/widget.h"
namespace views { namespace views {
namespace { namespace {
// Difference in the font size (in pixels) between menu label font and "new"
// badge font size.
constexpr int kNewBadgeFontSizeAdjustment = -2;
constexpr int kNewBadgeHorizontalMargin = 6;
constexpr int kNewBadgeInternalPadding = 2;
// EmptyMenuMenuItem --------------------------------------------------------- // EmptyMenuMenuItem ---------------------------------------------------------
// EmptyMenuMenuItem is used when a menu has no menu items. EmptyMenuMenuItem // EmptyMenuMenuItem is used when a menu has no menu items. EmptyMenuMenuItem
...@@ -954,13 +961,26 @@ void MenuItemView::PaintButton(gfx::Canvas* canvas, PaintButtonMode mode) { ...@@ -954,13 +961,26 @@ void MenuItemView::PaintButton(gfx::Canvas* canvas, PaintButtonMode mode) {
// The rest should be drawn with the minor foreground color. // The rest should be drawn with the minor foreground color.
style.foreground = GetTextColor(/*minor=*/true, render_selection); style.foreground = GetTextColor(/*minor=*/true, render_selection);
if (!secondary_title().empty()) { if (!secondary_title().empty()) {
text_bounds.set_y(text_bounds.y() + text_height); gfx::Rect secondary_bounds = text_bounds;
secondary_bounds.set_y(secondary_bounds.y() + text_height);
canvas->DrawStringRectWithFlags(secondary_title(), style.font_list, canvas->DrawStringRectWithFlags(secondary_title(), style.font_list,
style.foreground, text_bounds, flags); style.foreground, secondary_bounds, flags);
} }
PaintMinorIconAndText(canvas, style); PaintMinorIconAndText(canvas, style);
if (ShouldShowNewBadge()) {
const int title_width = gfx::GetStringWidth(title(), style.font_list);
gfx::Rect new_bounds = text_bounds;
if (base::i18n::IsRTL())
new_bounds.Inset(kNewBadgeHorizontalMargin, 0,
title_width + kNewBadgeHorizontalMargin, 0);
else
new_bounds.Inset(title_width + kNewBadgeHorizontalMargin, 0,
kNewBadgeHorizontalMargin, 0);
DrawNewBadge(canvas, new_bounds, flags, style.font_list);
}
// Set the submenu indicator (arrow) image and color. // Set the submenu indicator (arrow) image and color.
if (HasSubmenu()) if (HasSubmenu())
submenu_arrow_image_view_->SetImage(GetSubmenuArrowImage(icon_color)); submenu_arrow_image_view_->SetImage(GetSubmenuArrowImage(icon_color));
...@@ -1215,7 +1235,11 @@ MenuItemView::MenuItemDimensions MenuItemView::CalculateDimensions() const { ...@@ -1215,7 +1235,11 @@ MenuItemView::MenuItemDimensions MenuItemView::CalculateDimensions() const {
dimensions.standard_width = string_width + label_start + item_right_margin_; dimensions.standard_width = string_width + label_start + item_right_margin_;
// Determine the length of the right-side text. // Determine the length of the right-side text.
dimensions.minor_text_width = dimensions.minor_text_width =
minor_text.empty() ? 0 : gfx::GetStringWidth(minor_text, style.font_list); (minor_text.empty() ? 0
: gfx::GetStringWidth(minor_text, style.font_list));
if (ShouldShowNewBadge())
dimensions.minor_text_width += GetNewBadgeRequiredWidth(style.font_list);
// Determine the height to use. // Determine the height to use.
int label_text_height = secondary_title().empty() int label_text_height = secondary_title().empty()
...@@ -1357,6 +1381,58 @@ const View* MenuItemView::GetFirstVisibleChild() const { ...@@ -1357,6 +1381,58 @@ const View* MenuItemView::GetFirstVisibleChild() const {
return (it == children().cend()) ? nullptr : *it; return (it == children().cend()) ? nullptr : *it;
} }
bool MenuItemView::ShouldShowNewBadge() const {
static const bool feature_enabled =
base::FeatureList::IsEnabled(features::kEnableNewBadgeOnMenuItems);
return feature_enabled && is_new_;
}
void MenuItemView::DrawNewBadge(gfx::Canvas* canvas,
gfx::Rect badge_bounds,
int render_flags,
const gfx::FontList& font_list) {
const base::string16 new_text =
l10n_util::GetStringUTF16(IDS_MENU_ITEM_NEW_BADGE);
gfx::FontList badge_font =
font_list.DeriveWithSizeDelta(kNewBadgeFontSizeAdjustment);
const int text_width = gfx::GetStringWidth(new_text, badge_font);
const int badge_width = text_width + 2 * kNewBadgeInternalPadding;
const int width_diff = badge_bounds.width() - badge_width;
if (base::i18n::IsRTL())
badge_bounds.Inset(width_diff, 0, 0, 0);
else
badge_bounds.Inset(0, 0, width_diff, 0);
cc::PaintFlags new_flags;
const SkColor background_color = GetNativeTheme()->GetSystemColor(
ui::NativeTheme::kColorId_ProminentButtonColor);
new_flags.setColor(background_color);
constexpr int kBadgeRadius = 4;
canvas->DrawRoundRect(badge_bounds, kBadgeRadius, new_flags);
const int height_diff = badge_bounds.height() - badge_font.GetHeight();
badge_bounds.Inset(kNewBadgeInternalPadding, height_diff / 2,
kNewBadgeInternalPadding, (height_diff + 1) / 2);
const SkColor foreground_color = GetNativeTheme()->GetSystemColor(
ui::NativeTheme::kColorId_TextOnProminentButtonColor);
canvas->DrawStringRectWithFlags(new_text, badge_font, foreground_color,
badge_bounds, render_flags);
}
int MenuItemView::GetNewBadgeRequiredWidth(
const gfx::FontList& font_list) const {
const base::string16 new_text =
l10n_util::GetStringUTF16(IDS_MENU_ITEM_NEW_BADGE);
gfx::FontList badge_font =
font_list.DeriveWithSizeDelta(kNewBadgeFontSizeAdjustment);
// Reserve space on either side of the label text for the badge's internal
// padding and margin between it and other elements.
return gfx::GetStringWidth(new_text, badge_font) +
2 * (kNewBadgeInternalPadding + kNewBadgeHorizontalMargin);
}
BEGIN_METADATA(MenuItemView) BEGIN_METADATA(MenuItemView)
METADATA_PARENT_CLASS(View) METADATA_PARENT_CLASS(View)
END_METADATA() END_METADATA()
......
...@@ -268,6 +268,9 @@ class VIEWS_EXPORT MenuItemView : public View { ...@@ -268,6 +268,9 @@ class VIEWS_EXPORT MenuItemView : public View {
// Returns the command id of this item. // Returns the command id of this item.
int GetCommand() const { return command_; } int GetCommand() const { return command_; }
void set_is_new(bool is_new) { is_new_ = is_new; }
bool is_new() const { return is_new_; }
// Paints the menu item. // Paints the menu item.
void OnPaint(gfx::Canvas* canvas) override; void OnPaint(gfx::Canvas* canvas) override;
...@@ -487,6 +490,20 @@ class VIEWS_EXPORT MenuItemView : public View { ...@@ -487,6 +490,20 @@ class VIEWS_EXPORT MenuItemView : public View {
static_cast<const MenuItemView*>(this)->GetFirstVisibleChild()); static_cast<const MenuItemView*>(this)->GetFirstVisibleChild());
} }
// Returns whether or not a "new" badge should be shown on this menu item.
// Takes into account whether the badging feature is enabled.
bool ShouldShowNewBadge() const;
// Renders a "New" badge on |canvas| in the given |badge_bounds|, which should
// be roughly adjacent to the menu item label.
void DrawNewBadge(gfx::Canvas* canvas,
gfx::Rect badge_bounds,
int render_flags,
const gfx::FontList& font_list);
// Returns the additional width required for a "New" badge.
int GetNewBadgeRequiredWidth(const gfx::FontList& font_list) const;
void invalidate_dimensions() { dimensions_.height = 0; } void invalidate_dimensions() { dimensions_.height = 0; }
bool is_dimensions_valid() const { return dimensions_.height > 0; } bool is_dimensions_valid() const { return dimensions_.height > 0; }
...@@ -517,6 +534,10 @@ class VIEWS_EXPORT MenuItemView : public View { ...@@ -517,6 +534,10 @@ class VIEWS_EXPORT MenuItemView : public View {
// Command id. // Command id.
int command_ = 0; int command_ = 0;
// Whether the menu item should be badged as "New" (if badging is enabled) as
// a way to highlight a new feature for users.
bool is_new_ = false;
// Submenu, created via CreateSubmenu. // Submenu, created via CreateSubmenu.
SubmenuView* submenu_ = nullptr; SubmenuView* submenu_ = nullptr;
......
...@@ -34,5 +34,10 @@ const base::Feature kEnableViewPaintOptimization{ ...@@ -34,5 +34,10 @@ const base::Feature kEnableViewPaintOptimization{
const base::Feature kTextfieldFocusOnTapUp{"TextfieldFocusOnTapUp", const base::Feature kTextfieldFocusOnTapUp{"TextfieldFocusOnTapUp",
base::FEATURE_DISABLED_BY_DEFAULT}; base::FEATURE_DISABLED_BY_DEFAULT};
// Allows a "New" badge to be displayed on menu items that provide access to new
// features.
const base::Feature kEnableNewBadgeOnMenuItems{
"EnableNewBadgeOnMenuItems", base::FEATURE_DISABLED_BY_DEFAULT};
} // namespace features } // namespace features
} // namespace views } // namespace views
...@@ -18,6 +18,7 @@ VIEWS_EXPORT extern const base::Feature kEnableMDRoundedCornersOnDialogs; ...@@ -18,6 +18,7 @@ VIEWS_EXPORT extern const base::Feature kEnableMDRoundedCornersOnDialogs;
VIEWS_EXPORT extern const base::Feature kEnablePlatformHighContrastInkDrop; VIEWS_EXPORT extern const base::Feature kEnablePlatformHighContrastInkDrop;
VIEWS_EXPORT extern const base::Feature kEnableViewPaintOptimization; VIEWS_EXPORT extern const base::Feature kEnableViewPaintOptimization;
VIEWS_EXPORT extern const base::Feature kTextfieldFocusOnTapUp; VIEWS_EXPORT extern const base::Feature kTextfieldFocusOnTapUp;
VIEWS_EXPORT extern const base::Feature kEnableNewBadgeOnMenuItems;
} // namespace features } // namespace features
} // namespace views } // namespace views
......
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