Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 } | |
| OLD | NEW |