1diff --git ui/base/models/menu_model.h ui/base/models/menu_model.h 2index 11f1421cc79c1..c5dbc643ae7c8 100644 3--- ui/base/models/menu_model.h 4+++ ui/base/models/menu_model.h 5@@ -15,8 +15,11 @@ 6 #include "ui/base/models/menu_separator_types.h" 7 #include "ui/gfx/native_widget_types.h" 8 9+#include "third_party/skia/include/core/SkColor.h" 10+ 11 namespace gfx { 12 class FontList; 13+class Point; 14 } 15 16 namespace ui { 17@@ -147,6 +150,27 @@ class COMPONENT_EXPORT(UI_BASE) MenuModel 18 // |event_flags| is a bit mask of ui::EventFlags. 19 virtual void ActivatedAt(int index, int event_flags); 20 21+ // Called when the user moves the mouse outside the menu and over the owning 22+ // window. 23+ virtual void MouseOutsideMenu(const gfx::Point& screen_point) {} 24+ 25+ // Called on unhandled open/close submenu keyboard commands. |is_rtl| will be 26+ // true if the menu is displaying a right-to-left language. 27+ virtual void UnhandledOpenSubmenu(bool is_rtl) {} 28+ virtual void UnhandledCloseSubmenu(bool is_rtl) {} 29+ 30+ // Override the text/background color of a given menu item dependent on the 31+ // |index| and its |is_hovered| state. |is_minor| will be true for accelerator 32+ // text. Returns true if it chooses to override the color. 33+ virtual bool GetTextColor(int index, 34+ bool is_minor, 35+ bool is_hovered, 36+ SkColor* override_color) const { return false; } 37+ virtual bool GetBackgroundColor(int index, 38+ bool is_hovered, 39+ SkColor* override_color) const 40+ { return false; } 41+ 42 // Called when the menu is about to be shown. 43 virtual void MenuWillShow() {} 44 45diff --git ui/gfx/render_text.cc ui/gfx/render_text.cc 46index de5999c5252f3..97d08b4d3b7f9 100644 47--- ui/gfx/render_text.cc 48+++ ui/gfx/render_text.cc 49@@ -659,6 +659,14 @@ void RenderText::SetWhitespaceElision(absl::optional<bool> whitespace_elision) { 50 } 51 } 52 53+void RenderText::SetDrawStringsFlags(int flags) { 54+ if (draw_strings_flags_ == flags) 55+ return; 56+ draw_strings_flags_ = flags; 57+ cached_bounds_and_offset_valid_ = false; 58+ OnTextAttributeChanged(); 59+} 60+ 61 void RenderText::SetDisplayRect(const Rect& r) { 62 if (r != display_rect_) { 63 display_rect_ = r; 64@@ -2003,6 +2011,19 @@ void RenderText::OnTextAttributeChanged() { 65 66 layout_text_up_to_date_ = false; 67 68+ if (draw_strings_flags_ != 0) { 69+ // Compute layout size with the mnemonic character underlined since it might 70+ // be larger than with the underline hidden. 71+ int char_pos = -1; 72+ int char_span = 0; 73+ layout_text_ = 74+ gfx::LocateAndRemoveAcceleratorChar(layout_text_, &char_pos, &char_span); 75+ if (char_pos != -1) { 76+ gfx::Range range(char_pos, char_pos + char_span); 77+ styles_[TEXT_STYLE_UNDERLINE].ApplyValue(true, range); 78+ } 79+ } 80+ 81 OnLayoutTextAttributeChanged(true); 82 } 83 84diff --git ui/gfx/render_text.h ui/gfx/render_text.h 85index ec064d43e5afd..3893339b72cf3 100644 86--- ui/gfx/render_text.h 87+++ ui/gfx/render_text.h 88@@ -343,6 +343,10 @@ class GFX_EXPORT RenderText { 89 return whitespace_elision_; 90 } 91 92+ // Get or set the flags that control display of accelerator characters. 93+ void SetDrawStringsFlags(int flags); 94+ int draw_strings_flags() const { return draw_strings_flags_; } 95+ 96 const Rect& display_rect() const { return display_rect_; } 97 void SetDisplayRect(const Rect& r); 98 99@@ -1052,6 +1056,8 @@ class GFX_EXPORT RenderText { 100 101 // Tell whether or not the |layout_text_| needs an update or is up to date. 102 mutable bool layout_text_up_to_date_ = false; 103+ 104+ int draw_strings_flags_ = 0; 105 }; 106 107 } // namespace gfx 108diff --git ui/views/animation/ink_drop_host_view.h ui/views/animation/ink_drop_host_view.h 109index 7de6bdd643ad1..90994f096a0e0 100644 110--- ui/views/animation/ink_drop_host_view.h 111+++ ui/views/animation/ink_drop_host_view.h 112@@ -175,6 +175,8 @@ class VIEWS_EXPORT InkDropHost { 113 View* host_view() { return host_view_; } 114 const View* host_view() const { return host_view_; } 115 116+ InkDropMode ink_drop_mode() const { return ink_drop_mode_; } 117+ 118 private: 119 friend class test::InkDropHostTestApi; 120 121diff --git ui/views/controls/button/label_button.cc ui/views/controls/button/label_button.cc 122index 57e3a7d7e1d8c..eaccdf1cc50cc 100644 123--- ui/views/controls/button/label_button.cc 124+++ ui/views/controls/button/label_button.cc 125@@ -510,6 +510,12 @@ void LabelButton::OnThemeChanged() { 126 SchedulePaint(); 127 } 128 129+void LabelButton::SetFontList(const gfx::FontList& font_list) { 130+ cached_normal_font_list_ = font_list; 131+ cached_default_button_font_list_ = font_list; 132+ label_->SetFontList(cached_normal_font_list_); 133+} 134+ 135 void LabelButton::StateChanged(ButtonState old_state) { 136 Button::StateChanged(old_state); 137 ResetLabelEnabledColor(); 138diff --git ui/views/controls/button/label_button.h ui/views/controls/button/label_button.h 139index 75d1292ce4d49..e22a73e3771c4 100644 140--- ui/views/controls/button/label_button.h 141+++ ui/views/controls/button/label_button.h 142@@ -133,6 +133,9 @@ class VIEWS_EXPORT LabelButton : public Button, public NativeThemeDelegate { 143 ui::NativeTheme::State GetForegroundThemeState( 144 ui::NativeTheme::ExtraParams* params) const override; 145 146+ // Sets the font list used by this button. 147+ void SetFontList(const gfx::FontList& font_list); 148+ 149 protected: 150 ImageView* image() const { return image_; } 151 Label* label() const { return label_; } 152diff --git ui/views/controls/label.cc ui/views/controls/label.cc 153index 1852dffd41233..7e581af0d4465 100644 154--- ui/views/controls/label.cc 155+++ ui/views/controls/label.cc 156@@ -53,12 +53,27 @@ enum LabelPropertyKey { 157 kLabelLineHeight, 158 kLabelObscured, 159 kLabelAllowCharacterBreak, 160+ kLabelDrawStringsFlags, 161 }; 162 163 bool IsOpaque(SkColor color) { 164 return SkColorGetA(color) == SK_AlphaOPAQUE; 165 } 166 167+// Strips accelerator character prefixes in |text| if needed, based on |flags|. 168+// Returns a range in |text| to underline or Range::InvalidRange() if 169+// underlining is not needed. 170+gfx::Range StripAcceleratorChars(int flags, std::u16string* text) { 171+ if (flags & (gfx::Canvas::SHOW_PREFIX | gfx::Canvas::HIDE_PREFIX)) { 172+ int char_pos = -1; 173+ int char_span = 0; 174+ *text = gfx::LocateAndRemoveAcceleratorChar(*text, &char_pos, &char_span); 175+ if ((flags & gfx::Canvas::SHOW_PREFIX) && char_pos != -1) 176+ return gfx::Range(char_pos, char_pos + char_span); 177+ } 178+ return gfx::Range::InvalidRange(); 179+} 180+ 181 } // namespace 182 183 namespace views { 184@@ -426,6 +441,15 @@ void Label::SetElideBehavior(gfx::ElideBehavior elide_behavior) { 185 OnPropertyChanged(&elide_behavior_, kPropertyEffectsPreferredSizeChanged); 186 } 187 188+void Label::SetDrawStringsFlags(int flags) { 189+ if (draw_strings_flags_ == flags) 190+ return; 191+ draw_strings_flags_ = flags; 192+ full_text_->SetDrawStringsFlags(draw_strings_flags_); 193+ OnPropertyChanged(&full_text_ + kLabelDrawStringsFlags, 194+ kPropertyEffectsPreferredSizeChanged); 195+} 196+ 197 std::u16string Label::GetTooltipText() const { 198 return tooltip_text_; 199 } 200@@ -722,6 +746,16 @@ std::unique_ptr<gfx::RenderText> Label::CreateRenderText() const { 201 render_text->SelectRange(stored_selection_range_); 202 } 203 204+ if (draw_strings_flags_ != 0) { 205+ auto text_str = GetText(); 206+ gfx::Range range = StripAcceleratorChars(draw_strings_flags_, &text_str); 207+ render_text->SetText(text_str); 208+ if (range.IsValid()) { 209+ render_text->SetDisplayRect(bounds()); 210+ render_text->ApplyStyle(gfx::TEXT_STYLE_UNDERLINE, true, range); 211+ } 212+ } 213+ 214 return render_text; 215 } 216 217diff --git ui/views/controls/label.h ui/views/controls/label.h 218index 8132b9b9a1c75..4f78f6f41c77f 100644 219--- ui/views/controls/label.h 220+++ ui/views/controls/label.h 221@@ -234,6 +234,10 @@ class VIEWS_EXPORT Label : public View, 222 gfx::ElideBehavior GetElideBehavior() const; 223 void SetElideBehavior(gfx::ElideBehavior elide_behavior); 224 225+ // Get or set the flags that control display of accelerator characters. 226+ void SetDrawStringsFlags(int flags); 227+ int GetDrawStringsFlags() const { return draw_strings_flags_; } 228+ 229 // Gets/Sets the tooltip text. Default behavior for a label (single-line) is 230 // to show the full text if it is wider than its bounds. Calling this 231 // overrides the default behavior and lets you set a custom tooltip. To 232@@ -480,6 +484,7 @@ class VIEWS_EXPORT Label : public View, 233 int max_width_ = 0; 234 // This is used in single-line mode. 235 int max_width_single_line_ = 0; 236+ int draw_strings_flags_ = 0; 237 238 std::unique_ptr<SelectionController> selection_controller_; 239 240diff --git ui/views/controls/menu/menu_controller.cc ui/views/controls/menu/menu_controller.cc 241index 2571ad8b9cbb9..77ffb9f214b34 100644 242--- ui/views/controls/menu/menu_controller.cc 243+++ ui/views/controls/menu/menu_controller.cc 244@@ -2868,8 +2868,13 @@ MenuItemView* MenuController::FindNextSelectableMenuItem( 245 246 void MenuController::OpenSubmenuChangeSelectionIfCan() { 247 MenuItemView* item = pending_state_.item; 248- if (!item->HasSubmenu() || !item->GetEnabled()) 249+ if (!item->HasSubmenu() || !item->GetEnabled() || !item->GetParentMenuItem()) { 250+ MenuItemView* submenu_item = 251+ item->GetParentMenuItem() ? item->GetParentMenuItem() : item; 252+ submenu_item->GetDelegate()->OnUnhandledOpenSubmenu(submenu_item, 253+ base::i18n::IsRTL()); 254 return; 255+ } 256 MenuItemView* to_select = nullptr; 257 if (!item->GetSubmenu()->GetMenuItems().empty()) 258 to_select = FindInitialSelectableMenuItem(item, INCREMENT_SELECTION_DOWN); 259@@ -2888,8 +2893,10 @@ void MenuController::OpenSubmenuChangeSelectionIfCan() { 260 void MenuController::CloseSubmenu() { 261 MenuItemView* item = state_.item; 262 DCHECK(item); 263- if (!item->GetParentMenuItem()) 264+ if (!item->GetParentMenuItem()) { 265+ item->GetDelegate()->OnUnhandledCloseSubmenu(item, base::i18n::IsRTL()); 266 return; 267+ } 268 if (item->SubmenuIsShowing()) 269 SetSelection(item, SELECTION_UPDATE_IMMEDIATELY); 270 else if (item->GetParentMenuItem()->GetParentMenuItem()) 271diff --git ui/views/controls/menu/menu_delegate.h ui/views/controls/menu/menu_delegate.h 272index 9c1542e1f4f0f..548fc8e87d520 100644 273--- ui/views/controls/menu/menu_delegate.h 274+++ ui/views/controls/menu/menu_delegate.h 275@@ -73,6 +73,22 @@ class VIEWS_EXPORT MenuDelegate { 276 virtual const gfx::FontList* GetLabelFontList(int id) const; 277 virtual absl::optional<SkColor> GetLabelColor(int id) const; 278 279+ // Override the text color of a given menu item dependent on the |command_id| 280+ // and its |is_hovered| state. |is_minor| will be true for accelerator text. 281+ // Returns true if it chooses to override the color. 282+ virtual bool GetTextColor(int command_id, 283+ bool is_minor, 284+ bool is_hovered, 285+ SkColor* override_color) const { return false; } 286+ 287+ // Override the background color of a given menu item dependent on the 288+ // |command_id| and its |is_hovered| state. Returns true if it chooses to 289+ // override the color. 290+ virtual bool GetBackgroundColor(int command_id, 291+ bool is_hovered, 292+ SkColor* override_color) const 293+ { return false; } 294+ 295 // The tooltip shown for the menu item. This is invoked when the user 296 // hovers over the item, and no tooltip text has been set for that item. 297 virtual std::u16string GetTooltipText(int id, 298@@ -211,6 +227,11 @@ class VIEWS_EXPORT MenuDelegate { 299 bool* has_mnemonics, 300 MenuButton** button); 301 302+ // Called on unhandled open/close submenu keyboard commands. |is_rtl| will be 303+ // true if the menu is displaying a right-to-left language. 304+ virtual void OnUnhandledOpenSubmenu(MenuItemView* menu, bool is_rtl) {} 305+ virtual void OnUnhandledCloseSubmenu(MenuItemView* menu, bool is_rtl) {} 306+ 307 // Returns the max width menus can grow to be. 308 virtual int GetMaxWidthForMenu(MenuItemView* menu); 309 310diff --git ui/views/controls/menu/menu_item_view.cc ui/views/controls/menu/menu_item_view.cc 311index 20f678de0dcbf..4668a984ddcea 100644 312--- ui/views/controls/menu/menu_item_view.cc 313+++ ui/views/controls/menu/menu_item_view.cc 314@@ -1084,6 +1084,15 @@ void MenuItemView::PaintBackground(gfx::Canvas* canvas, 315 spilling_rect.set_y(spilling_rect.y() - corner_radius_); 316 spilling_rect.set_height(spilling_rect.height() + corner_radius_); 317 canvas->DrawRoundRect(spilling_rect, corner_radius_, flags); 318+ return; 319+ } 320+ 321+ MenuDelegate *delegate = GetDelegate(); 322+ SkColor override_color; 323+ if (delegate && delegate->GetBackgroundColor(GetCommand(), 324+ paint_as_selected, 325+ &override_color)) { 326+ canvas->DrawColor(override_color); 327 } else if (paint_as_selected) { 328 gfx::Rect item_bounds = GetLocalBounds(); 329 if (type_ == Type::kActionableSubMenu) { 330@@ -1151,6 +1160,13 @@ void MenuItemView::PaintMinorIconAndText(gfx::Canvas* canvas, SkColor color) { 331 } 332 333 SkColor MenuItemView::GetTextColor(bool minor, bool paint_as_selected) const { 334+ SkColor text_color; 335+ const MenuDelegate *delegate = GetDelegate(); 336+ if (delegate && delegate->GetTextColor(GetCommand(), minor, paint_as_selected, 337+ &text_color)) { 338+ return text_color; 339+ } 340+ 341 style::TextContext context = 342 GetMenuController() && GetMenuController()->use_touchable_layout() 343 ? style::CONTEXT_TOUCH_MENU 344diff --git ui/views/controls/menu/menu_model_adapter.cc ui/views/controls/menu/menu_model_adapter.cc 345index fb5d5e6a79a3f..a336b5a74d6a1 100644 346--- ui/views/controls/menu/menu_model_adapter.cc 347+++ ui/views/controls/menu/menu_model_adapter.cc 348@@ -245,6 +245,77 @@ bool MenuModelAdapter::IsItemChecked(int id) const { 349 return false; 350 } 351 352+MenuItemView* MenuModelAdapter::GetSiblingMenu(MenuItemView* menu, 353+ const gfx::Point& screen_point, 354+ MenuAnchorPosition* anchor, 355+ bool* has_mnemonics, 356+ MenuButton** button) { 357+ // Look up the menu model for this menu. 358+ const std::map<MenuItemView*, ui::MenuModel*>::const_iterator map_iterator = 359+ menu_map_.find(menu); 360+ if (map_iterator != menu_map_.end()) { 361+ map_iterator->second->MouseOutsideMenu(screen_point); 362+ return nullptr; 363+ } 364+ 365+ NOTREACHED(); 366+ return nullptr; 367+} 368+ 369+void MenuModelAdapter::OnUnhandledOpenSubmenu(MenuItemView* menu, 370+ bool is_rtl) { 371+ // Look up the menu model for this menu. 372+ const std::map<MenuItemView*, ui::MenuModel*>::const_iterator map_iterator = 373+ menu_map_.find(menu); 374+ if (map_iterator != menu_map_.end()) { 375+ map_iterator->second->UnhandledOpenSubmenu(is_rtl); 376+ return; 377+ } 378+ 379+ NOTREACHED(); 380+} 381+ 382+void MenuModelAdapter::OnUnhandledCloseSubmenu(MenuItemView* menu, 383+ bool is_rtl) { 384+ // Look up the menu model for this menu. 385+ const std::map<MenuItemView*, ui::MenuModel*>::const_iterator map_iterator = 386+ menu_map_.find(menu); 387+ if (map_iterator != menu_map_.end()) { 388+ map_iterator->second->UnhandledCloseSubmenu(is_rtl); 389+ return; 390+ } 391+ 392+ NOTREACHED(); 393+} 394+ 395+bool MenuModelAdapter::GetTextColor(int command_id, 396+ bool is_minor, 397+ bool is_hovered, 398+ SkColor* override_color) const { 399+ ui::MenuModel* model = menu_model_; 400+ int index = 0; 401+ if (ui::MenuModel::GetModelAndIndexForCommandId(command_id, &model, &index)) 402+ return model->GetTextColor(index, is_minor, is_hovered, override_color); 403+ 404+ NOTREACHED(); 405+ return false; 406+} 407+ 408+bool MenuModelAdapter::GetBackgroundColor(int command_id, 409+ bool is_hovered, 410+ SkColor* override_color) const { 411+ if (command_id == -1) 412+ return menu_model_->GetBackgroundColor(-1, is_hovered, override_color); 413+ 414+ ui::MenuModel* model = menu_model_; 415+ int index = 0; 416+ if (ui::MenuModel::GetModelAndIndexForCommandId(command_id, &model, &index)) 417+ return model->GetBackgroundColor(index, is_hovered, override_color); 418+ 419+ NOTREACHED(); 420+ return false; 421+} 422+ 423 void MenuModelAdapter::WillShowMenu(MenuItemView* menu) { 424 // Look up the menu model for this menu. 425 const std::map<MenuItemView*, ui::MenuModel*>::const_iterator map_iterator = 426diff --git ui/views/controls/menu/menu_model_adapter.h ui/views/controls/menu/menu_model_adapter.h 427index b7c7474fb5910..ce3e14071f0c6 100644 428--- ui/views/controls/menu/menu_model_adapter.h 429+++ ui/views/controls/menu/menu_model_adapter.h 430@@ -88,6 +88,20 @@ class VIEWS_EXPORT MenuModelAdapter : public MenuDelegate, 431 bool IsCommandEnabled(int id) const override; 432 bool IsCommandVisible(int id) const override; 433 bool IsItemChecked(int id) const override; 434+ MenuItemView* GetSiblingMenu(MenuItemView* menu, 435+ const gfx::Point& screen_point, 436+ MenuAnchorPosition* anchor, 437+ bool* has_mnemonics, 438+ MenuButton** button) override; 439+ void OnUnhandledOpenSubmenu(MenuItemView* menu, bool is_rtl) override; 440+ void OnUnhandledCloseSubmenu(MenuItemView* menu, bool is_rtl) override; 441+ bool GetTextColor(int command_id, 442+ bool is_minor, 443+ bool is_hovered, 444+ SkColor* override_color) const override; 445+ bool GetBackgroundColor(int command_id, 446+ bool is_hovered, 447+ SkColor* override_color) const override; 448 void WillShowMenu(MenuItemView* menu) override; 449 void WillHideMenu(MenuItemView* menu) override; 450 void OnMenuClosed(MenuItemView* menu) override; 451diff --git ui/views/controls/menu/menu_scroll_view_container.cc ui/views/controls/menu/menu_scroll_view_container.cc 452index 86bb005ff2c81..397d34be23dcb 100644 453--- ui/views/controls/menu/menu_scroll_view_container.cc 454+++ ui/views/controls/menu/menu_scroll_view_container.cc 455@@ -239,6 +239,11 @@ MenuScrollViewContainer::MenuScrollViewContainer(SubmenuView* content_view) 456 scroll_down_button_ = 457 AddChildView(std::make_unique<MenuScrollButton>(content_view, false)); 458 459+ SkColor override_color; 460+ MenuDelegate* delegate = content_view_->GetMenuItem()->GetDelegate(); 461+ if (delegate && delegate->GetBackgroundColor(-1, false, &override_color)) 462+ SetBackground(views::CreateSolidBackground(override_color)); 463+ 464 arrow_ = BubbleBorderTypeFromAnchor( 465 content_view_->GetMenuItem()->GetMenuController()->GetAnchorPosition()); 466 467diff --git ui/views/view.h ui/views/view.h 468index bc6f04c6d2b1b..be4a0c8ad6695 100644 469--- ui/views/view.h 470+++ ui/views/view.h 471@@ -21,6 +21,7 @@ 472 #include "base/memory/ptr_util.h" 473 #include "base/memory/raw_ptr.h" 474 #include "base/strings/string_piece.h" 475+#include "base/supports_user_data.h" 476 #include "build/build_config.h" 477 #include "third_party/abseil-cpp/absl/types/optional.h" 478 #include "third_party/skia/include/core/SkPath.h" 479@@ -269,7 +270,8 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, 480 public ui::EventTarget, 481 public ui::EventHandler, 482 public ui::PropertyHandler, 483- public ui::metadata::MetaDataProvider { 484+ public ui::metadata::MetaDataProvider, 485+ public base::SupportsUserData { 486 public: 487 using Views = std::vector<View*>; 488 489