Windows should animate when they are about to get docked at screen edges.
[chromium-blink-merge.git] / ash / system / date / date_view.cc
blobcc1552d92465ec0013111077a1764738f584736d
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.
5 #include "ash/system/date/date_view.h"
7 #include "ash/shell.h"
8 #include "ash/system/tray/system_tray_delegate.h"
9 #include "ash/system/tray/tray_constants.h"
10 #include "ash/system/tray/tray_utils.h"
11 #include "base/i18n/time_formatting.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "base/time/time.h"
14 #include "grit/ash_strings.h"
15 #include "third_party/icu/source/i18n/unicode/datefmt.h"
16 #include "third_party/icu/source/i18n/unicode/dtptngen.h"
17 #include "third_party/icu/source/i18n/unicode/smpdtfmt.h"
18 #include "ui/base/l10n/l10n_util.h"
19 #include "ui/views/controls/label.h"
20 #include "ui/views/layout/box_layout.h"
21 #include "ui/views/layout/grid_layout.h"
22 #include "ui/views/widget/widget.h"
24 namespace ash {
25 namespace internal {
26 namespace tray {
28 namespace {
30 // Amount of slop to add into the timer to make sure we're into the next minute
31 // when the timer goes off.
32 const int kTimerSlopSeconds = 1;
34 // Top number text color of vertical clock.
35 const SkColor kVerticalClockHourColor = SkColorSetRGB(0xBA, 0xBA, 0xBA);
37 base::string16 FormatDate(const base::Time& time) {
38 icu::UnicodeString date_string;
39 scoped_ptr<icu::DateFormat> formatter(
40 icu::DateFormat::createDateInstance(icu::DateFormat::kMedium));
41 formatter->format(static_cast<UDate>(time.ToDoubleT() * 1000), date_string);
42 return base::string16(date_string.getBuffer(),
43 static_cast<size_t>(date_string.length()));
46 base::string16 FormatDayOfWeek(const base::Time& time) {
47 UErrorCode status = U_ZERO_ERROR;
48 scoped_ptr<icu::DateTimePatternGenerator> generator(
49 icu::DateTimePatternGenerator::createInstance(status));
50 DCHECK(U_SUCCESS(status));
51 const char kBasePattern[] = "EEE";
52 icu::UnicodeString generated_pattern =
53 generator->getBestPattern(icu::UnicodeString(kBasePattern), status);
54 DCHECK(U_SUCCESS(status));
55 icu::SimpleDateFormat simple_formatter(generated_pattern, status);
56 DCHECK(U_SUCCESS(status));
57 icu::UnicodeString date_string;
58 simple_formatter.format(
59 static_cast<UDate>(time.ToDoubleT() * 1000), date_string, status);
60 DCHECK(U_SUCCESS(status));
61 return base::string16(
62 date_string.getBuffer(), static_cast<size_t>(date_string.length()));
65 views::Label* CreateLabel() {
66 views::Label* label = new views::Label;
67 label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
68 label->SetBackgroundColor(SkColorSetARGB(0, 255, 255, 255));
69 return label;
72 } // namespace
74 BaseDateTimeView::~BaseDateTimeView() {
75 timer_.Stop();
78 void BaseDateTimeView::UpdateText() {
79 base::Time now = base::Time::Now();
80 UpdateTextInternal(now);
81 SchedulePaint();
82 SetTimer(now);
85 BaseDateTimeView::BaseDateTimeView() {
86 SetTimer(base::Time::Now());
89 void BaseDateTimeView::SetTimer(const base::Time& now) {
90 // Try to set the timer to go off at the next change of the minute. We don't
91 // want to have the timer go off more than necessary since that will cause
92 // the CPU to wake up and consume power.
93 base::Time::Exploded exploded;
94 now.LocalExplode(&exploded);
96 // Often this will be called at minute boundaries, and we'll actually want
97 // 60 seconds from now.
98 int seconds_left = 60 - exploded.second;
99 if (seconds_left == 0)
100 seconds_left = 60;
102 // Make sure that the timer fires on the next minute. Without this, if it is
103 // called just a teeny bit early, then it will skip the next minute.
104 seconds_left += kTimerSlopSeconds;
106 timer_.Stop();
107 timer_.Start(
108 FROM_HERE, base::TimeDelta::FromSeconds(seconds_left),
109 this, &BaseDateTimeView::UpdateText);
112 void BaseDateTimeView::ChildPreferredSizeChanged(views::View* child) {
113 PreferredSizeChanged();
116 void BaseDateTimeView::OnLocaleChanged() {
117 UpdateText();
120 DateView::DateView() : actionable_(false) {
121 SetLayoutManager(
122 new views::BoxLayout(
123 views::BoxLayout::kVertical, 0, 0, 0));
124 date_label_ = CreateLabel();
125 date_label_->SetEnabledColor(kHeaderTextColorNormal);
126 UpdateTextInternal(base::Time::Now());
127 AddChildView(date_label_);
128 set_focusable(actionable_);
131 DateView::~DateView() {
134 void DateView::SetActionable(bool actionable) {
135 actionable_ = actionable;
136 set_focusable(actionable_);
139 void DateView::UpdateTextInternal(const base::Time& now) {
140 SetAccessibleName(
141 base::TimeFormatFriendlyDate(now) +
142 ASCIIToUTF16(",") +
143 base::TimeFormatTimeOfDayWithHourClockType(
144 now, base::k12HourClock, base:: kKeepAmPm));
145 date_label_->SetText(
146 l10n_util::GetStringFUTF16(
147 IDS_ASH_STATUS_TRAY_DATE, FormatDayOfWeek(now), FormatDate(now)));
150 bool DateView::PerformAction(const ui::Event& event) {
151 if (!actionable_)
152 return false;
154 ash::Shell::GetInstance()->system_tray_delegate()->ShowDateSettings();
155 return true;
158 void DateView::OnMouseEntered(const ui::MouseEvent& event) {
159 if (!actionable_)
160 return;
161 date_label_->SetEnabledColor(kHeaderTextColorHover);
162 SchedulePaint();
165 void DateView::OnMouseExited(const ui::MouseEvent& event) {
166 if (!actionable_)
167 return;
168 date_label_->SetEnabledColor(kHeaderTextColorNormal);
169 SchedulePaint();
172 TimeView::TimeView(TrayDate::ClockLayout clock_layout)
173 : hour_type_(ash::Shell::GetInstance()->system_tray_delegate()->
174 GetHourClockType()) {
175 SetupLabels();
176 UpdateTextInternal(base::Time::Now());
177 UpdateClockLayout(clock_layout);
178 set_focusable(false);
181 TimeView::~TimeView() {
184 void TimeView::UpdateTimeFormat() {
185 hour_type_ =
186 ash::Shell::GetInstance()->system_tray_delegate()->GetHourClockType();
187 UpdateText();
190 void TimeView::UpdateTextInternal(const base::Time& now) {
191 // Just in case |now| is null, do NOT update time; otherwise, it will
192 // crash icu code by calling into base::TimeFormatTimeOfDayWithHourClockType,
193 // see details in crbug.com/147570.
194 if (now.is_null()) {
195 LOG(ERROR) << "Received null value from base::Time |now| in argument";
196 return;
199 base::string16 current_time = base::TimeFormatTimeOfDayWithHourClockType(
200 now, hour_type_, base::kDropAmPm);
201 label_->SetText(current_time);
202 label_->SetTooltipText(base::TimeFormatFriendlyDate(now));
204 // Calculate vertical clock layout labels.
205 size_t colon_pos = current_time.find(ASCIIToUTF16(":"));
206 base::string16 hour = current_time.substr(0, colon_pos);
207 base::string16 minute = current_time.substr(colon_pos + 1);
208 label_hour_left_->SetText(hour.substr(0, 1));
209 label_hour_right_->SetText(hour.length() == 2 ?
210 hour.substr(1,1) : ASCIIToUTF16(":"));
211 label_minute_left_->SetText(minute.substr(0, 1));
212 label_minute_right_->SetText(minute.substr(1, 1));
214 Layout();
217 bool TimeView::PerformAction(const ui::Event& event) {
218 return false;
221 bool TimeView::OnMousePressed(const ui::MouseEvent& event) {
222 // Let the event fall through.
223 return false;
226 void TimeView::UpdateClockLayout(TrayDate::ClockLayout clock_layout){
227 SetBorder(clock_layout);
228 if (clock_layout == TrayDate::HORIZONTAL_CLOCK) {
229 RemoveChildView(label_hour_left_.get());
230 RemoveChildView(label_hour_right_.get());
231 RemoveChildView(label_minute_left_.get());
232 RemoveChildView(label_minute_right_.get());
233 SetLayoutManager(
234 new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 0));
235 AddChildView(label_.get());
236 } else {
237 RemoveChildView(label_.get());
238 views::GridLayout* layout = new views::GridLayout(this);
239 SetLayoutManager(layout);
240 views::ColumnSet* columns = layout->AddColumnSet(0);
241 columns->AddPaddingColumn(0, 6);
242 columns->AddColumn(views::GridLayout::CENTER, views::GridLayout::CENTER,
243 0, views::GridLayout::USE_PREF, 0, 0);
244 columns->AddColumn(views::GridLayout::CENTER, views::GridLayout::CENTER,
245 0, views::GridLayout::USE_PREF, 0, 0);
246 layout->AddPaddingRow(0, kTrayLabelItemVerticalPaddingVeriticalAlignment);
247 layout->StartRow(0, 0);
248 layout->AddView(label_hour_left_.get());
249 layout->AddView(label_hour_right_.get());
250 layout->StartRow(0, 0);
251 layout->AddView(label_minute_left_.get());
252 layout->AddView(label_minute_right_.get());
253 layout->AddPaddingRow(0, kTrayLabelItemVerticalPaddingVeriticalAlignment);
255 Layout();
258 void TimeView::SetBorder(TrayDate::ClockLayout clock_layout) {
259 if (clock_layout == TrayDate::HORIZONTAL_CLOCK)
260 set_border(views::Border::CreateEmptyBorder(
261 0, kTrayLabelItemHorizontalPaddingBottomAlignment,
262 0, kTrayLabelItemHorizontalPaddingBottomAlignment));
263 else
264 set_border(NULL);
267 void TimeView::SetupLabels() {
268 label_.reset(CreateLabel());
269 SetupLabel(label_.get());
270 label_hour_left_.reset(CreateLabel());
271 SetupLabel(label_hour_left_.get());
272 label_hour_right_.reset(CreateLabel());
273 SetupLabel(label_hour_right_.get());
274 label_minute_left_.reset(CreateLabel());
275 SetupLabel(label_minute_left_.get());
276 label_minute_right_.reset(CreateLabel());
277 SetupLabel(label_minute_right_.get());
278 label_hour_left_->SetEnabledColor(kVerticalClockHourColor);
279 label_hour_right_->SetEnabledColor(kVerticalClockHourColor);
282 void TimeView::SetupLabel(views::Label* label) {
283 label->set_owned_by_client();
284 SetupLabelForTray(label);
285 gfx::Font font = label->font();
286 label->SetFont(font.DeriveFont(0, font.GetStyle() & ~gfx::Font::BOLD));
289 } // namespace tray
290 } // namespace internal
291 } // namespace ash