• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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