Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(777)

Side by Side Diff: chrome/browser/ui/views/subtle_notification_view.cc

Issue 2010493005: a11y/Mac: Add screenreader support for SubtleNotificationView announcements. Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase. Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/ui/views/subtle_notification_view.h" 5 #include "chrome/browser/ui/views/subtle_notification_view.h"
6 6
7 #include <memory> 7 #include <memory>
8 8
9 #include "base/strings/string_split.h" 9 #include "base/strings/string_split.h"
10 #include "base/strings/string_util.h"
10 #include "base/strings/utf_string_conversions.h" 11 #include "base/strings/utf_string_conversions.h"
11 #include "third_party/skia/include/core/SkColor.h" 12 #include "third_party/skia/include/core/SkColor.h"
13 #include "ui/accessibility/ax_view_state.h"
12 #include "ui/base/resource/resource_bundle.h" 14 #include "ui/base/resource/resource_bundle.h"
13 #include "ui/gfx/font_list.h" 15 #include "ui/gfx/font_list.h"
14 #include "ui/views/bubble/bubble_border.h" 16 #include "ui/views/bubble/bubble_border.h"
15 #include "ui/views/controls/label.h" 17 #include "ui/views/controls/label.h"
16 #include "ui/views/controls/link.h" 18 #include "ui/views/controls/link.h"
17 #include "ui/views/layout/box_layout.h" 19 #include "ui/views/layout/box_layout.h"
18 #include "ui/views/widget/widget.h" 20 #include "ui/views/widget/widget.h"
21 #include "ui/views/widget/widget_observer.h"
19 22
20 namespace { 23 namespace {
21 24
22 // Space between the site info label and the link. 25 // Space between the site info label and the link.
23 const int kMiddlePaddingPx = 30; 26 const int kMiddlePaddingPx = 30;
24 27
25 const int kOuterPaddingHorizPx = 40; 28 const int kOuterPaddingHorizPx = 40;
26 const int kOuterPaddingVertPx = 8; 29 const int kOuterPaddingVertPx = 8;
27 30
28 // Partially-transparent background color. 31 // Partially-transparent background color.
29 const SkColor kBackgroundColor = SkColorSetARGB(0xcc, 0x28, 0x2c, 0x32); 32 const SkColor kBackgroundColor = SkColorSetARGB(0xcc, 0x28, 0x2c, 0x32);
30 33
31 // Spacing around the key name. 34 // Spacing around the key name.
32 const int kKeyNameMarginHorizPx = 7; 35 const int kKeyNameMarginHorizPx = 7;
33 const int kKeyNameBorderPx = 1; 36 const int kKeyNameBorderPx = 1;
34 const int kKeyNameCornerRadius = 2; 37 const int kKeyNameCornerRadius = 2;
35 const int kKeyNamePaddingPx = 5; 38 const int kKeyNamePaddingPx = 5;
36 39
40 // Delimiter indicating there should be a segment displayed as a keyboard key.
41 const char kKeyNameDelimiter[] = "|";
tapted 2016/07/11 03:46:47 hum - this should probably be a const char16 []. T
Patti Lor 2016/07/12 00:04:36 Done.
Matt Giuca 2016/07/14 01:16:02 Hmm, I don't really like this (I wrote a whole com
tapted 2016/07/14 01:48:40 my brain sees arrays and string literals as interc
Patti Lor 2016/07/19 01:31:51 Have taken Matt's suggestion to keep it as a char
42
37 } // namespace 43 } // namespace
38 44
39 // Class containing the instruction text. Contains fancy styling on the keyboard 45 // Class containing the instruction text. Contains fancy styling on the keyboard
40 // key (not just a simple label). 46 // key (not just a simple label).
41 class SubtleNotificationView::InstructionView : public views::View { 47 class SubtleNotificationView::InstructionView : public views::View {
42 public: 48 public:
43 // Creates an InstructionView with specific text. |text| may contain one or 49 // Creates an InstructionView with specific text. |text| may contain one or
44 // more segments delimited by a pair of pipes ('|'); each of these segments 50 // more segments delimited by a pair of pipes ('|'); each of these segments
45 // will be displayed as a keyboard key. e.g., "Press |Alt|+|Q| to exit" will 51 // will be displayed as a keyboard key. e.g. "Press |Alt|+|Q| to exit" will
tapted 2016/07/11 03:46:47 (ubernit: technically, a comma should "always" com
Patti Lor 2016/07/12 00:04:36 Oops, didn't notice that! Noted for future referen
Matt Giuca 2016/07/14 01:16:02 Also I had a comma there in the first place :)
Patti Lor 2016/07/19 01:31:51 Haha, yup, that's what I meant D: Didn't copy and
46 // have "Alt" and "Q" rendered as keys. 52 // have "Alt" and "Q" rendered as keys.
47 InstructionView(const base::string16& text, 53 InstructionView(const base::string16& text,
48 const gfx::FontList& font_list, 54 const gfx::FontList& font_list,
49 SkColor foreground_color, 55 SkColor foreground_color,
50 SkColor background_color); 56 SkColor background_color);
51 57
58 base::string16 accessible_name() { return accessible_name_; }
tapted 2016/07/11 03:46:47 nit: const method
Patti Lor 2016/07/12 00:04:36 Done.
59 void SetAccessibleName(const base::string16& accessible_name);
60
61 base::string16 text() { return text_; }
tapted 2016/07/11 03:46:47 nit: const
Patti Lor 2016/07/12 00:04:36 Done.
52 void SetText(const base::string16& text); 62 void SetText(const base::string16& text);
53 63
54 private: 64 private:
55 // Adds a label to the end of the notification text. If |format_as_key|, 65 // Adds a label to the end of the notification text. If |format_as_key|,
56 // surrounds the label in a rounded-rect border to indicate that it is a 66 // surrounds the label in a rounded-rect border to indicate that it is a
57 // keyboard key. 67 // keyboard key.
58 void AddTextSegment(const base::string16& text, bool format_as_key); 68 void AddTextSegment(const base::string16& text, bool format_as_key);
59 69
60 const gfx::FontList& font_list_; 70 const gfx::FontList& font_list_;
61 SkColor foreground_color_; 71 SkColor foreground_color_;
62 SkColor background_color_; 72 SkColor background_color_;
63 73
74 // Text to read out on displaying this notification for screenreaders, with
75 // unicode characters expanded.
76 base::string16 accessible_name_;
tapted 2016/07/11 03:46:47 If it achieves the same thing, I think it makes mo
Patti Lor 2016/07/12 00:04:36 Done.
77
64 base::string16 text_; 78 base::string16 text_;
65 79
66 DISALLOW_COPY_AND_ASSIGN(InstructionView); 80 DISALLOW_COPY_AND_ASSIGN(InstructionView);
67 }; 81 };
68 82
69 SubtleNotificationView::InstructionView::InstructionView( 83 SubtleNotificationView::InstructionView::InstructionView(
70 const base::string16& text, 84 const base::string16& text,
71 const gfx::FontList& font_list, 85 const gfx::FontList& font_list,
72 SkColor foreground_color, 86 SkColor foreground_color,
73 SkColor background_color) 87 SkColor background_color)
74 : font_list_(font_list), 88 : font_list_(font_list),
75 foreground_color_(foreground_color), 89 foreground_color_(foreground_color),
76 background_color_(background_color) { 90 background_color_(background_color) {
77 // The |between_child_spacing| is the horizontal margin of the key name. 91 // The |between_child_spacing| is the horizontal margin of the key name.
78 views::BoxLayout* layout = new views::BoxLayout(views::BoxLayout::kHorizontal, 92 views::BoxLayout* layout = new views::BoxLayout(views::BoxLayout::kHorizontal,
79 0, 0, kKeyNameMarginHorizPx); 93 0, 0, kKeyNameMarginHorizPx);
80 SetLayoutManager(layout); 94 SetLayoutManager(layout);
81 95
82 SetText(text); 96 SetText(text);
83 } 97 }
84 98
99 void SubtleNotificationView::InstructionView::SetAccessibleName(
100 const base::string16& accessible_name) {
101 base::RemoveChars(accessible_name, base::ASCIIToUTF16(kKeyNameDelimiter),
102 &accessible_name_);
103 }
104
85 void SubtleNotificationView::InstructionView::SetText( 105 void SubtleNotificationView::InstructionView::SetText(
86 const base::string16& text) { 106 const base::string16& text) {
87 // Avoid replacing the contents with the same text. 107 // Avoid replacing the contents with the same text.
88 if (text == text_) 108 if (text == text_)
89 return; 109 return;
90 110
91 RemoveAllChildViews(true); 111 RemoveAllChildViews(true);
92 112
93 // Parse |text|, looking for pipe-delimited segment. 113 // Parse |text|, looking for pipe-delimited segment.
94 std::vector<base::string16> segments = 114 std::vector<base::string16> segments =
95 base::SplitString(text, base::ASCIIToUTF16("|"), base::TRIM_WHITESPACE, 115 base::SplitString(text, base::ASCIIToUTF16(kKeyNameDelimiter),
96 base::SPLIT_WANT_ALL); 116 base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
97 // SplitString() returns empty strings for zero-length segments, so given an 117 // SplitString() returns empty strings for zero-length segments, so given an
98 // even number of pipes, there should always be an odd number of segments. 118 // even number of pipes, there should always be an odd number of segments.
99 // The exception is if |text| is entirely empty, in which case the returned 119 // The exception is if |text| is entirely empty, in which case the returned
100 // list is also empty (rather than containing a single empty string). 120 // list is also empty (rather than containing a single empty string).
101 DCHECK(segments.empty() || segments.size() % 2 == 1); 121 DCHECK(segments.empty() || segments.size() % 2 == 1);
102 122
103 // Add text segment, alternating between non-key (no border) and key (border) 123 // Add text segment, alternating between non-key (no border) and key (border)
104 // formatting. 124 // formatting.
105 bool format_as_key = false; 125 bool format_as_key = false;
106 for (const auto& segment : segments) { 126 for (const auto& segment : segments) {
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after
175 195
176 SubtleNotificationView::~SubtleNotificationView() {} 196 SubtleNotificationView::~SubtleNotificationView() {}
177 197
178 void SubtleNotificationView::UpdateContent( 198 void SubtleNotificationView::UpdateContent(
179 const base::string16& instruction_text, 199 const base::string16& instruction_text,
180 const base::string16& link_text) { 200 const base::string16& link_text) {
181 instruction_view_->SetText(instruction_text); 201 instruction_view_->SetText(instruction_text);
182 instruction_view_->SetVisible(!instruction_text.empty()); 202 instruction_view_->SetVisible(!instruction_text.empty());
183 link_->SetText(link_text); 203 link_->SetText(link_text);
184 link_->SetVisible(!link_text.empty()); 204 link_->SetVisible(!link_text.empty());
205
206 NotifyAccessibilityEvent(ui::AX_EVENT_TEXT_CHANGED, true);
207 NotifyAccessibilityEvent(ui::AX_EVENT_ALERT, true);
185 } 208 }
186 209
187 // static 210 // static
188 views::Widget* SubtleNotificationView::CreatePopupWidget( 211 views::Widget* SubtleNotificationView::CreatePopupWidget(
189 gfx::NativeView parent_view, 212 gfx::NativeView parent_view,
190 SubtleNotificationView* view, 213 SubtleNotificationView* view,
191 bool accept_events) { 214 bool accept_events) {
192 // Initialize the popup. 215 // Initialize the popup.
193 views::Widget* popup = new views::Widget; 216 views::Widget* popup = new views::Widget;
194 views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP); 217 views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
195 params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; 218 params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
196 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 219 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
197 params.parent = parent_view; 220 params.parent = parent_view;
198 params.accept_events = accept_events; 221 params.accept_events = accept_events;
199 popup->Init(params); 222 popup->Init(params);
200 popup->SetContentsView(view); 223 popup->SetContentsView(view);
201 // We set layout manager to nullptr to prevent the widget from sizing its 224 // We set layout manager to nullptr to prevent the widget from sizing its
202 // contents to the same size as itself. This prevents the widget contents from 225 // contents to the same size as itself. This prevents the widget contents from
203 // shrinking while we animate the height of the popup to give the impression 226 // shrinking while we animate the height of the popup to give the impression
204 // that it is sliding off the top of the screen. 227 // that it is sliding off the top of the screen.
205 // TODO(mgiuca): This probably isn't necessary now that there is no slide 228 // TODO(mgiuca): This probably isn't necessary now that there is no slide
206 // animation. Remove it. 229 // animation. Remove it.
207 popup->GetRootView()->SetLayoutManager(nullptr); 230 popup->GetRootView()->SetLayoutManager(nullptr);
208 231
232 // Allow accessibility events to be sent when the view is shown.
233 popup->AddObserver(view);
234
209 return popup; 235 return popup;
210 } 236 }
237
238 void SubtleNotificationView::SetAccessibleName(
239 const base::string16& accessible_name) {
240 instruction_view_->SetAccessibleName(accessible_name);
241 }
242
243 void SubtleNotificationView::GetAccessibleState(ui::AXViewState* state) {
244 state->role = ui::AX_ROLE_LABEL_TEXT;
245
246 // If there is no accessible name set, fall back to the text displayed.
247 if (instruction_view_->accessible_name().empty())
tapted 2016/07/11 03:46:47 Can this happen?
Patti Lor 2016/07/12 00:04:36 Yes, currently the new backspace shortcut bubble i
tapted 2016/07/12 00:51:39 Ah. So, it's "generally" good to avoid side-effect
Matt Giuca 2016/07/14 01:16:02 This is a good point. How about we *pretend* that
Patti Lor 2016/07/19 01:31:51 A point Matt brought up in one of his other commen
248 SetAccessibleName(instruction_view_->text());
249 state->name = instruction_view_->accessible_name();
250 }
251
252 void SubtleNotificationView::OnWidgetClosing(views::Widget* widget) {
253 if (widget && widget == GetWidget())
254 widget->RemoveObserver(this);
255 }
256
257 void SubtleNotificationView::OnWidgetVisibilityChanged(views::Widget* widget,
258 bool visible) {
259 if (visible)
260 NotifyAccessibilityEvent(ui::AX_EVENT_ALERT, true);
261 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698