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

Side by Side Diff: ash/common/system/date/date_view.cc

Issue 2734653002: chromeos: Move files in //ash/common to //ash (Closed)
Patch Set: fix a11y tests, fix docs Created 3 years, 9 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
« no previous file with comments | « ash/common/system/date/date_view.h ('k') | ash/common/system/date/date_view_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ash/common/system/date/date_view.h"
6
7 #include "ash/common/material_design/material_design_controller.h"
8 #include "ash/common/system/tray/system_tray_controller.h"
9 #include "ash/common/system/tray/tray_constants.h"
10 #include "ash/common/system/tray/tray_popup_item_style.h"
11 #include "ash/common/system/tray/tray_popup_utils.h"
12 #include "ash/common/system/tray/tray_utils.h"
13 #include "ash/common/wm_shell.h"
14 #include "ash/strings/grit/ash_strings.h"
15 #include "base/i18n/rtl.h"
16 #include "base/i18n/time_formatting.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "base/time/time.h"
19 #include "third_party/icu/source/i18n/unicode/datefmt.h"
20 #include "third_party/icu/source/i18n/unicode/dtptngen.h"
21 #include "third_party/icu/source/i18n/unicode/smpdtfmt.h"
22 #include "ui/accessibility/ax_node_data.h"
23 #include "ui/base/l10n/l10n_util.h"
24 #include "ui/views/border.h"
25 #include "ui/views/controls/button/button.h"
26 #include "ui/views/controls/label.h"
27 #include "ui/views/layout/box_layout.h"
28 #include "ui/views/layout/fill_layout.h"
29 #include "ui/views/layout/grid_layout.h"
30 #include "ui/views/widget/widget.h"
31
32 namespace ash {
33 namespace tray {
34 namespace {
35
36 // Amount of slop to add into the timer to make sure we're into the next minute
37 // when the timer goes off.
38 const int kTimerSlopSeconds = 1;
39
40 // Text color of the vertical clock minutes.
41 const SkColor kVerticalClockMinuteColor = SkColorSetRGB(0xBA, 0xBA, 0xBA);
42
43 // Padding between the left edge of the shelf and the left edge of the vertical
44 // clock.
45 const int kVerticalClockLeftPadding = 9;
46
47 // Offset used to bring the minutes line closer to the hours line in the
48 // vertical clock.
49 const int kVerticalClockMinutesTopOffset = -4;
50 const int kVerticalClockMinutesTopOffsetMD = -2;
51
52 // Leading padding used to draw the tray background to the left of the clock
53 // when the shelf is vertically aligned.
54 const int kClockLeadingPadding = 8;
55
56 bool UseMd() {
57 return MaterialDesignController::IsSystemTrayMenuMaterial();
58 }
59
60 base::string16 FormatDateWithPattern(const base::Time& time,
61 const char* pattern) {
62 UErrorCode status = U_ZERO_ERROR;
63 std::unique_ptr<icu::DateTimePatternGenerator> generator(
64 icu::DateTimePatternGenerator::createInstance(status));
65 DCHECK(U_SUCCESS(status));
66 icu::UnicodeString generated_pattern =
67 generator->getBestPattern(icu::UnicodeString(pattern), status);
68 DCHECK(U_SUCCESS(status));
69 icu::SimpleDateFormat simple_formatter(generated_pattern, status);
70 DCHECK(U_SUCCESS(status));
71 icu::UnicodeString date_string;
72 simple_formatter.format(static_cast<UDate>(time.ToDoubleT() * 1000),
73 date_string, status);
74 DCHECK(U_SUCCESS(status));
75 return base::string16(date_string.getBuffer(),
76 static_cast<size_t>(date_string.length()));
77 }
78
79 base::string16 FormatDate(const base::Time& time) {
80 if (UseMd()) {
81 // Use 'short' month format (e.g., "Oct") followed by non-padded day of
82 // month (e.g., "2", "10").
83 return FormatDateWithPattern(time, "LLLd");
84 } else {
85 icu::UnicodeString date_string;
86 std::unique_ptr<icu::DateFormat> formatter(
87 icu::DateFormat::createDateInstance(icu::DateFormat::kMedium));
88 formatter->format(static_cast<UDate>(time.ToDoubleT() * 1000), date_string);
89 return base::string16(date_string.getBuffer(),
90 static_cast<size_t>(date_string.length()));
91 }
92 }
93
94 base::string16 FormatDayOfWeek(const base::Time& time) {
95 // Use 'short' day of week format (e.g., "Wed").
96 return FormatDateWithPattern(time, "EEE");
97 }
98
99 } // namespace
100
101 BaseDateTimeView::~BaseDateTimeView() {
102 timer_.Stop();
103 }
104
105 void BaseDateTimeView::UpdateText() {
106 base::Time now = base::Time::Now();
107 UpdateTextInternal(now);
108 SchedulePaint();
109 SetTimer(now);
110 }
111
112 void BaseDateTimeView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
113 ActionableView::GetAccessibleNodeData(node_data);
114 node_data->role = ui::AX_ROLE_TIME;
115 }
116
117 BaseDateTimeView::BaseDateTimeView(SystemTrayItem* owner)
118 : ActionableView(owner, TrayPopupInkDropStyle::INSET_BOUNDS),
119 hour_type_(WmShell::Get()->system_tray_controller()->hour_clock_type()) {
120 SetTimer(base::Time::Now());
121 SetFocusBehavior(FocusBehavior::NEVER);
122 }
123
124 void BaseDateTimeView::SetTimer(const base::Time& now) {
125 // Try to set the timer to go off at the next change of the minute. We don't
126 // want to have the timer go off more than necessary since that will cause
127 // the CPU to wake up and consume power.
128 base::Time::Exploded exploded;
129 now.LocalExplode(&exploded);
130
131 // Often this will be called at minute boundaries, and we'll actually want
132 // 60 seconds from now.
133 int seconds_left = 60 - exploded.second;
134 if (seconds_left == 0)
135 seconds_left = 60;
136
137 // Make sure that the timer fires on the next minute. Without this, if it is
138 // called just a teeny bit early, then it will skip the next minute.
139 seconds_left += kTimerSlopSeconds;
140
141 timer_.Stop();
142 timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(seconds_left), this,
143 &BaseDateTimeView::UpdateText);
144 }
145
146 void BaseDateTimeView::UpdateTextInternal(const base::Time& now) {
147 SetAccessibleName(base::TimeFormatTimeOfDayWithHourClockType(
148 now, hour_type_, base::kKeepAmPm) +
149 base::ASCIIToUTF16(", ") +
150 base::TimeFormatFriendlyDate(now));
151
152 NotifyAccessibilityEvent(ui::AX_EVENT_TEXT_CHANGED, true);
153 }
154
155 void BaseDateTimeView::ChildPreferredSizeChanged(views::View* child) {
156 PreferredSizeChanged();
157 }
158
159 void BaseDateTimeView::OnLocaleChanged() {
160 UpdateText();
161 }
162
163 ///////////////////////////////////////////////////////////////////////////////
164
165 DateView::DateView(SystemTrayItem* owner)
166 : BaseDateTimeView(owner), action_(DateAction::NONE) {
167 if (UseMd()) {
168 // TODO(tdanderson): Tweak spacing and layout for material design.
169 views::BoxLayout* box_layout =
170 new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 0);
171 box_layout->set_inside_border_insets(gfx::Insets(0, 12, 0, 0));
172 box_layout->set_main_axis_alignment(
173 views::BoxLayout::MAIN_AXIS_ALIGNMENT_START);
174 box_layout->set_cross_axis_alignment(
175 views::BoxLayout::CROSS_AXIS_ALIGNMENT_CENTER);
176 SetLayoutManager(box_layout);
177 } else {
178 SetLayoutManager(
179 new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0));
180 }
181 date_label_ = TrayPopupUtils::CreateDefaultLabel();
182 date_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
183 if (!UseMd())
184 date_label_->SetEnabledColor(kHeaderTextColorNormal);
185 UpdateTextInternal(base::Time::Now());
186 TrayPopupItemStyle style(TrayPopupItemStyle::FontStyle::SYSTEM_INFO);
187 style.SetupLabel(date_label_);
188 AddChildView(date_label_);
189 }
190
191 DateView::~DateView() {}
192
193 void DateView::SetAction(DateAction action) {
194 if (action == action_)
195 return;
196 if (IsMouseHovered() && !UseMd()) {
197 date_label_->SetEnabledColor(action == DateAction::NONE
198 ? kHeaderTextColorNormal
199 : kHeaderTextColorHover);
200 SchedulePaint();
201 }
202 action_ = action;
203 SetFocusBehavior(action_ != DateAction::NONE ? FocusBehavior::ALWAYS
204 : FocusBehavior::NEVER);
205
206 // Disable |this| when not clickable so that the material design ripple is
207 // not shown.
208 if (UseMd()) {
209 SetEnabled(action_ != DateAction::NONE);
210 if (action_ != DateAction::NONE)
211 SetInkDropMode(views::InkDropHostView::InkDropMode::ON);
212 }
213 }
214
215 void DateView::UpdateTimeFormat() {
216 hour_type_ = WmShell::Get()->system_tray_controller()->hour_clock_type();
217 UpdateText();
218 }
219
220 base::HourClockType DateView::GetHourTypeForTesting() const {
221 return hour_type_;
222 }
223
224 void DateView::SetActive(bool active) {
225 if (UseMd())
226 return;
227
228 date_label_->SetEnabledColor(active ? kHeaderTextColorHover
229 : kHeaderTextColorNormal);
230 SchedulePaint();
231 }
232
233 void DateView::UpdateTextInternal(const base::Time& now) {
234 BaseDateTimeView::UpdateTextInternal(now);
235 date_label_->SetText(l10n_util::GetStringFUTF16(
236 IDS_ASH_STATUS_TRAY_DATE, FormatDayOfWeek(now), FormatDate(now)));
237 }
238
239 bool DateView::PerformAction(const ui::Event& event) {
240 if (action_ == DateAction::NONE)
241 return false;
242 if (action_ == DateAction::SHOW_DATE_SETTINGS)
243 WmShell::Get()->system_tray_controller()->ShowDateSettings();
244 else if (action_ == DateAction::SET_SYSTEM_TIME)
245 WmShell::Get()->system_tray_controller()->ShowSetTimeDialog();
246 else
247 return false;
248 CloseSystemBubble();
249 return true;
250 }
251
252 void DateView::OnMouseEntered(const ui::MouseEvent& event) {
253 if (action_ == DateAction::NONE)
254 return;
255 SetActive(true);
256 }
257
258 void DateView::OnMouseExited(const ui::MouseEvent& event) {
259 if (action_ == DateAction::NONE)
260 return;
261 SetActive(false);
262 }
263
264 void DateView::OnGestureEvent(ui::GestureEvent* event) {
265 if (event->type() == ui::ET_GESTURE_TAP_DOWN) {
266 SetActive(true);
267 } else if (event->type() == ui::ET_GESTURE_TAP_CANCEL ||
268 event->type() == ui::ET_GESTURE_END) {
269 SetActive(false);
270 }
271 BaseDateTimeView::OnGestureEvent(event);
272 }
273
274 ///////////////////////////////////////////////////////////////////////////////
275
276 TimeView::TimeView(ClockLayout clock_layout) : BaseDateTimeView(nullptr) {
277 SetupLabels();
278 UpdateTextInternal(base::Time::Now());
279 UpdateClockLayout(clock_layout);
280 }
281
282 TimeView::~TimeView() {}
283
284 void TimeView::UpdateTimeFormat() {
285 hour_type_ = WmShell::Get()->system_tray_controller()->hour_clock_type();
286 UpdateText();
287 }
288
289 base::HourClockType TimeView::GetHourTypeForTesting() const {
290 return hour_type_;
291 }
292
293 void TimeView::UpdateTextInternal(const base::Time& now) {
294 // Just in case |now| is null, do NOT update time; otherwise, it will
295 // crash icu code by calling into base::TimeFormatTimeOfDayWithHourClockType,
296 // see details in crbug.com/147570.
297 if (now.is_null()) {
298 LOG(ERROR) << "Received null value from base::Time |now| in argument";
299 return;
300 }
301
302 BaseDateTimeView::UpdateTextInternal(now);
303 base::string16 current_time = base::TimeFormatTimeOfDayWithHourClockType(
304 now, hour_type_, base::kDropAmPm);
305 horizontal_label_->SetText(current_time);
306 horizontal_label_->SetTooltipText(base::TimeFormatFriendlyDate(now));
307
308 // Calculate vertical clock layout labels.
309 size_t colon_pos = current_time.find(base::ASCIIToUTF16(":"));
310 base::string16 hour = current_time.substr(0, colon_pos);
311 base::string16 minute = current_time.substr(colon_pos + 1);
312
313 // Sometimes pad single-digit hours with a zero for aesthetic reasons.
314 if (hour.length() == 1 && hour_type_ == base::k24HourClock &&
315 !base::i18n::IsRTL())
316 hour = base::ASCIIToUTF16("0") + hour;
317
318 vertical_label_hours_->SetText(hour);
319 vertical_label_minutes_->SetText(minute);
320 Layout();
321 }
322
323 bool TimeView::PerformAction(const ui::Event& event) {
324 return false;
325 }
326
327 bool TimeView::OnMousePressed(const ui::MouseEvent& event) {
328 // Let the event fall through.
329 return false;
330 }
331
332 void TimeView::OnGestureEvent(ui::GestureEvent* event) {
333 // Skip gesture handling happening in CustomButton so that the container views
334 // receive and handle them properly.
335 // TODO(mohsen): Refactor TimeView/DateView classes so that they are not
336 // ActionableView anymore. Create an ActionableView as a container for when
337 // needed.
338 }
339
340 void TimeView::UpdateClockLayout(ClockLayout clock_layout) {
341 SetBorderFromLayout(clock_layout);
342 if (clock_layout == ClockLayout::HORIZONTAL_CLOCK) {
343 RemoveChildView(vertical_label_hours_.get());
344 RemoveChildView(vertical_label_minutes_.get());
345 SetLayoutManager(new views::FillLayout());
346 AddChildView(horizontal_label_.get());
347 } else {
348 const bool is_material_design = MaterialDesignController::IsShelfMaterial();
349 RemoveChildView(horizontal_label_.get());
350 views::GridLayout* layout = new views::GridLayout(this);
351 SetLayoutManager(layout);
352 const int kColumnId = 0;
353 views::ColumnSet* columns = layout->AddColumnSet(kColumnId);
354 columns->AddPaddingColumn(0, kVerticalClockLeftPadding);
355 columns->AddColumn(views::GridLayout::TRAILING, views::GridLayout::CENTER,
356 0, views::GridLayout::USE_PREF, 0, 0);
357 layout->AddPaddingRow(
358 0, is_material_design ? kClockLeadingPadding
359 : kTrayLabelItemVerticalPaddingVerticalAlignment);
360 layout->StartRow(0, kColumnId);
361 layout->AddView(vertical_label_hours_.get());
362 layout->StartRow(0, kColumnId);
363 layout->AddView(vertical_label_minutes_.get());
364 layout->AddPaddingRow(
365 0,
366 is_material_design
367 ? kTrayImageItemPadding + kVerticalClockMinutesTopOffsetMD
368 : kTrayLabelItemVerticalPaddingVerticalAlignment);
369 }
370 Layout();
371 }
372
373 void TimeView::SetBorderFromLayout(ClockLayout clock_layout) {
374 if (clock_layout == ClockLayout::HORIZONTAL_CLOCK) {
375 SetBorder(views::CreateEmptyBorder(
376 gfx::Insets(0,
377 UseMd() ? kTrayImageItemPadding
378 : kTrayLabelItemHorizontalPaddingBottomAlignment)));
379 } else {
380 SetBorder(views::NullBorder());
381 }
382 }
383
384 void TimeView::SetupLabels() {
385 horizontal_label_.reset(new views::Label());
386 SetupLabel(horizontal_label_.get());
387 vertical_label_hours_.reset(new views::Label());
388 SetupLabel(vertical_label_hours_.get());
389 vertical_label_minutes_.reset(new views::Label());
390 SetupLabel(vertical_label_minutes_.get());
391 // TODO(estade): this should use the NativeTheme's secondary text color.
392 vertical_label_minutes_->SetEnabledColor(kVerticalClockMinuteColor);
393 // Pull the minutes up closer to the hours by using a negative top border.
394 vertical_label_minutes_->SetBorder(
395 views::CreateEmptyBorder(MaterialDesignController::IsShelfMaterial()
396 ? kVerticalClockMinutesTopOffsetMD
397 : kVerticalClockMinutesTopOffset,
398 0, 0, 0));
399 }
400
401 void TimeView::SetupLabel(views::Label* label) {
402 label->set_owned_by_client();
403 SetupLabelForTray(label);
404 label->SetElideBehavior(gfx::NO_ELIDE);
405 }
406
407 } // namespace tray
408 } // namespace ash
OLDNEW
« no previous file with comments | « ash/common/system/date/date_view.h ('k') | ash/common/system/date/date_view_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698