Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2013 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 "chrome/browser/ui/views/message_center/message_center_widget_delegate. h" | |
| 6 | |
| 7 #include "chrome/browser/ui/views/message_center/message_center_frame_view.h" | |
| 8 #include "content/public/browser/user_metrics.h" | |
| 9 #include "ui/base/accessibility/accessible_view_state.h" | |
| 10 #include "ui/gfx/screen.h" | |
| 11 #include "ui/message_center/message_center_style.h" | |
| 12 #include "ui/message_center/message_center_util.h" | |
| 13 #include "ui/message_center/views/message_center_view.h" | |
| 14 #include "ui/native_theme/native_theme.h" | |
| 15 #include "ui/views/border.h" | |
| 16 #include "ui/views/layout/box_layout.h" | |
| 17 #include "ui/views/widget/widget.h" | |
| 18 | |
| 19 namespace { | |
| 20 | |
| 21 const int kBorderWidth = 1; | |
| 22 | |
| 23 } // namespace | |
| 24 | |
| 25 namespace message_center { | |
| 26 | |
| 27 namespace internal { | |
| 28 | |
| 29 // Gets the position of the systray (same as position of taskbar) from the | |
| 30 // work area bounds. Returns ALIGNMENT_NONE if position cannot be found. | |
| 31 Alignment GetSystrayAlignment() { | |
| 32 gfx::Screen* screen = gfx::Screen::GetNativeScreen(); | |
| 33 // TODO(dewittj): It's possible GetPrimaryDisplay is wrong. | |
| 34 gfx::Rect screen_bounds = screen->GetPrimaryDisplay().bounds(); | |
| 35 gfx::Rect work_area = screen->GetPrimaryDisplay().work_area(); | |
| 36 | |
| 37 // Comparing the work area to the screen bounds gives us the location of the | |
| 38 // taskbar. If the work area is exactly the same as the screen bounds, | |
| 39 // we are unable to locate the systray so we say we don't know it's alignment. | |
| 40 if (work_area.height() < screen_bounds.height()) { | |
| 41 if (work_area.y() > screen_bounds.y()) | |
| 42 return ALIGNMENT_TOP; | |
| 43 return ALIGNMENT_BOTTOM; | |
| 44 } | |
| 45 if (work_area.width() < screen_bounds.width()) { | |
| 46 if (work_area.x() > screen_bounds.x()) | |
| 47 return ALIGNMENT_LEFT; | |
| 48 return ALIGNMENT_RIGHT; | |
| 49 } | |
| 50 | |
| 51 return ALIGNMENT_NONE; | |
| 52 } | |
| 53 | |
| 54 gfx::Point GetClosestCorner(const gfx::Rect& rect, const gfx::Point& query) { | |
| 55 gfx::Point center_point = rect.CenterPoint(); | |
| 56 gfx::Point rv; | |
| 57 | |
| 58 if (query.x() > center_point.x()) | |
| 59 rv.set_x(rect.right()); | |
| 60 else | |
| 61 rv.set_x(rect.x()); | |
| 62 | |
| 63 if (query.y() > center_point.y()) | |
| 64 rv.set_y(rect.bottom()); | |
| 65 else | |
| 66 rv.set_y(rect.y()); | |
| 67 | |
| 68 return rv; | |
| 69 } | |
| 70 | |
| 71 // Gets the corner of the screen where the message center should pop up. | |
| 72 Alignment GetAnchorAlignment(const gfx::Rect& work_area, gfx::Point corner) { | |
| 73 gfx::Point center = work_area.CenterPoint(); | |
| 74 | |
| 75 Alignment anchor_alignment = | |
| 76 center.y() > corner.y() ? ALIGNMENT_TOP : ALIGNMENT_BOTTOM; | |
| 77 anchor_alignment = | |
| 78 (Alignment)(anchor_alignment | | |
| 79 (center.x() > corner.x() ? ALIGNMENT_LEFT : ALIGNMENT_RIGHT)); | |
| 80 | |
| 81 return anchor_alignment; | |
| 82 } | |
| 83 | |
| 84 } // namespace internal | |
| 85 | |
| 86 MessageCenterWidgetDelegate::MessageCenterWidgetDelegate( | |
| 87 WebNotificationTray* tray, | |
| 88 MessageCenterTray* mc_tray, | |
| 89 bool initially_settings_visible) | |
| 90 : MessageCenterView(tray->message_center(), | |
| 91 mc_tray, | |
| 92 initially_settings_visible), | |
| 93 color_explicitly_set_(false), | |
| 94 close_on_esc_(true), | |
| 95 close_on_deactivate_(true), | |
| 96 tray_(tray) { | |
| 97 | |
| 98 PositionAnchor(); | |
| 99 MessageCenterView::Init(max_height_); | |
| 100 | |
| 101 min_width_ = max_width_ = kNotificationWidth; | |
|
dewittj
2013/06/28 01:13:18
Why not just have 'width_'?
sidharthms
2013/07/02 18:09:51
Done.
| |
| 102 if (IsRichNotificationEnabled()) { | |
| 103 min_width_ += kMarginBetweenItems * 2; | |
| 104 max_width_ += kMarginBetweenItems * 2; | |
| 105 } | |
| 106 preferred_width_ = min_width_; | |
| 107 | |
| 108 views::BoxLayout* layout = | |
| 109 new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0); | |
| 110 layout->set_spread_blank_space(true); | |
| 111 SetLayoutManager(layout); | |
| 112 | |
| 113 AddAccelerator(ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE)); | |
| 114 UpdateColorsFromTheme(GetNativeTheme()); | |
| 115 | |
| 116 if (get_use_acceleration_when_possible()) { | |
| 117 SetPaintToLayer(true); | |
| 118 SetFillsBoundsOpaquely(true); | |
| 119 } | |
| 120 | |
| 121 InitWidget(); | |
| 122 UpdateNotifications(); | |
| 123 } | |
| 124 | |
| 125 MessageCenterWidgetDelegate::~MessageCenterWidgetDelegate() { | |
| 126 views::Widget* widget = GetWidget(); | |
| 127 if (widget) { | |
| 128 widget->RemoveObserver(this); | |
| 129 } | |
| 130 } | |
| 131 | |
| 132 views::View* MessageCenterWidgetDelegate::GetContentsView() { | |
| 133 return this; | |
| 134 } | |
| 135 | |
| 136 views::NonClientFrameView* | |
| 137 MessageCenterWidgetDelegate::CreateNonClientFrameView(views::Widget* widget) { | |
| 138 return new MessageCenterFrameView(kBorderWidth); | |
| 139 } | |
| 140 | |
| 141 views::Widget* MessageCenterWidgetDelegate::GetWidget() { | |
| 142 return View::GetWidget(); | |
| 143 } | |
| 144 | |
| 145 const views::Widget* MessageCenterWidgetDelegate::GetWidget() const { | |
| 146 return View::GetWidget(); | |
| 147 } | |
| 148 | |
| 149 void MessageCenterWidgetDelegate::OnWidgetActivationChanged( | |
| 150 views::Widget* widget, | |
| 151 bool active) { | |
| 152 if (close_on_deactivate() && !active) | |
| 153 tray_->HideBubble(); | |
| 154 } | |
| 155 | |
| 156 void MessageCenterWidgetDelegate::PreferredSizeChanged() { | |
| 157 GetWidget()->SetBounds(GetBubbleBounds()); | |
| 158 views::View::PreferredSizeChanged(); | |
| 159 } | |
| 160 | |
| 161 gfx::Size MessageCenterWidgetDelegate::GetPreferredSize() { | |
| 162 gfx::Size size = | |
| 163 gfx::Size(preferred_width_, GetHeightForWidth(preferred_width_)); | |
| 164 | |
| 165 // Make space for borders on sides. | |
| 166 size.Enlarge(2 * kBorderWidth, 2 * kBorderWidth); | |
| 167 return size; | |
| 168 } | |
| 169 | |
| 170 gfx::Size MessageCenterWidgetDelegate::GetMaximumSize() { | |
| 171 gfx::Size size = GetPreferredSize(); | |
| 172 size.set_width(max_width_); | |
| 173 return size; | |
| 174 } | |
| 175 | |
| 176 int MessageCenterWidgetDelegate::GetHeightForWidth(int width) { | |
| 177 int height = MessageCenterView::GetHeightForWidth(width); | |
| 178 return (max_height_ != 0) ? std::min(height, max_height_) : height; | |
| 179 } | |
| 180 bool MessageCenterWidgetDelegate::AcceleratorPressed( | |
| 181 const ui::Accelerator& accelerator) { | |
| 182 if (!close_on_esc() || accelerator.key_code() != ui::VKEY_ESCAPE) | |
| 183 return false; | |
| 184 if (fade_animation_.get()) | |
| 185 fade_animation_->Reset(); | |
| 186 GetWidget()->Close(); | |
| 187 return true; | |
| 188 } | |
| 189 | |
| 190 void MessageCenterWidgetDelegate::OnNativeThemeChanged( | |
| 191 const ui::NativeTheme* theme) { | |
| 192 UpdateColorsFromTheme(theme); | |
| 193 } | |
| 194 | |
| 195 void MessageCenterWidgetDelegate::InitWidget() { | |
| 196 views::Widget* widget = new views::Widget(); | |
| 197 views::Widget::InitParams params(views::Widget::InitParams::TYPE_BUBBLE); | |
| 198 params.delegate = this; | |
| 199 params.keep_on_top = true; | |
| 200 params.top_level = true; | |
| 201 #if defined(OS_WIN) && !defined(USE_AURA) | |
| 202 params.type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS; | |
| 203 #endif | |
| 204 widget->Init(params); | |
| 205 | |
| 206 widget->AddObserver(this); | |
| 207 widget->StackAtTop(); | |
| 208 | |
| 209 // Popups should appear on top of everything, but not disturb the user's | |
| 210 // focus since they could appear at any time. Message Center is always | |
| 211 // shown as a result of user action so it can be activated here. | |
| 212 widget->SetAlwaysOnTop(true); | |
| 213 widget->Activate(); | |
| 214 } | |
| 215 | |
| 216 void MessageCenterWidgetDelegate::CloseWidget() { | |
| 217 GetWidget()->Close(); | |
| 218 } | |
| 219 | |
| 220 void MessageCenterWidgetDelegate::UpdateColorsFromTheme( | |
| 221 const ui::NativeTheme* theme) { | |
| 222 if (!color_explicitly_set_) { | |
| 223 color_ = GetNativeTheme()->GetSystemColor( | |
| 224 ui::NativeTheme::kColorId_WindowBackground); | |
| 225 } | |
| 226 set_background(views::Background::CreateSolidBackground(color())); | |
| 227 } | |
| 228 | |
| 229 void MessageCenterWidgetDelegate::UpdateNotifications() { | |
| 230 const NotificationList::Notifications& notifications = | |
| 231 tray_->message_center()->GetNotifications(); | |
| 232 SetNotifications(notifications); | |
| 233 GetWidget()->Show(); | |
| 234 GetWidget()->SetBounds(GetBubbleBounds()); | |
| 235 GetWidget()->GetRootView()->SchedulePaint(); | |
| 236 } | |
| 237 | |
| 238 MessageCenterFrameView* MessageCenterWidgetDelegate::GetBubbleFrameView() | |
| 239 const { | |
| 240 const views::Widget* widget = GetWidget(); | |
| 241 const views::NonClientView* view = widget ? widget->non_client_view() : NULL; | |
| 242 return view ? static_cast<MessageCenterFrameView*>(view->frame_view()) : NULL; | |
| 243 } | |
| 244 | |
| 245 gfx::Rect MessageCenterWidgetDelegate::GetBubbleBounds() { | |
| 246 const gfx::Size size = GetPreferredSize(); | |
| 247 gfx::Rect bounds(size); | |
| 248 | |
| 249 gfx::Point corrected_anchor = GetCorrectedAnchor(size); | |
| 250 | |
| 251 if (bubble_alignment_ & ALIGNMENT_TOP) | |
| 252 bounds.set_y(corrected_anchor.y()); | |
| 253 if (bubble_alignment_ & ALIGNMENT_BOTTOM) | |
| 254 bounds.set_y(corrected_anchor.y() - size.height()); | |
| 255 if (bubble_alignment_ & ALIGNMENT_LEFT) | |
| 256 bounds.set_x(corrected_anchor.x()); | |
| 257 if (bubble_alignment_ & ALIGNMENT_RIGHT) | |
| 258 bounds.set_x(corrected_anchor.x() - size.width()); | |
| 259 | |
| 260 return bounds; | |
| 261 } | |
| 262 | |
| 263 void MessageCenterWidgetDelegate::AnimationEnded( | |
| 264 const ui::Animation* animation) { | |
| 265 if (animation == fade_animation_.get()) { | |
| 266 bool closed = fade_animation_->GetCurrentValue() == 0; | |
| 267 fade_animation_->Reset(); | |
| 268 if (closed) | |
| 269 GetWidget()->Close(); | |
| 270 } | |
| 271 MessageCenterView::AnimationEnded(animation); | |
| 272 } | |
| 273 | |
| 274 void MessageCenterWidgetDelegate::AnimationProgressed( | |
| 275 const ui::Animation* animation) { | |
| 276 if (animation == fade_animation_.get()) { | |
| 277 DCHECK(fade_animation_->is_animating()); | |
| 278 unsigned char opacity = fade_animation_->GetCurrentValue() * 255; | |
| 279 #if defined(OS_WIN) && !defined(USE_AURA) | |
| 280 // Explicitly set the content Widget's layered style and set transparency | |
| 281 // via SetLayeredWindowAttributes. This is done because initializing the | |
| 282 // Widget as transparent and setting opacity via UpdateLayeredWindow doesn't | |
| 283 // support hosting child native Windows controls. | |
| 284 const HWND hwnd = GetWidget()->GetNativeView(); | |
| 285 const DWORD style = GetWindowLong(hwnd, GWL_EXSTYLE); | |
| 286 if ((opacity == 255) == !!(style & WS_EX_LAYERED)) | |
| 287 SetWindowLong(hwnd, GWL_EXSTYLE, style ^ WS_EX_LAYERED); | |
| 288 SetLayeredWindowAttributes(hwnd, 0, opacity, LWA_ALPHA); | |
| 289 #endif | |
| 290 GetWidget()->SetOpacity(opacity); | |
| 291 } | |
| 292 MessageCenterView::AnimationProgressed(animation); | |
| 293 } | |
| 294 | |
| 295 gfx::Point MessageCenterWidgetDelegate::GetCorrectedAnchor( | |
| 296 gfx::Size calculated_size) { | |
| 297 gfx::Point corrected_anchor = inital_anchor_point_; | |
| 298 | |
| 299 // Inset the width slightly so that the click point is not exactly on the edge | |
| 300 // of the message center but somewhere within the middle 60 %. | |
| 301 int insetted_width = (calculated_size.width() * 4) / 5; | |
| 302 | |
| 303 if (systray_alignment_ == ALIGNMENT_TOP || | |
| 304 systray_alignment_ == ALIGNMENT_BOTTOM) { | |
| 305 int click_point_x = tray_->mouse_click_point().x(); | |
| 306 | |
| 307 if (bubble_alignment_ & ALIGNMENT_RIGHT) { | |
| 308 int opposite_x_corner = inital_anchor_point_.x() - insetted_width; | |
| 309 | |
| 310 // If the click point is outside the x axis length of the message center, | |
| 311 // push the message center towards the LEFT to align with the click point. | |
| 312 if (opposite_x_corner > click_point_x) | |
| 313 corrected_anchor.set_x(inital_anchor_point_.x() - | |
| 314 (opposite_x_corner - click_point_x)); | |
| 315 } else { | |
| 316 int opposite_x_corner = inital_anchor_point_.x() + insetted_width; | |
| 317 | |
| 318 if (opposite_x_corner < click_point_x) | |
| 319 corrected_anchor.set_x(inital_anchor_point_.x() + | |
| 320 (click_point_x - opposite_x_corner)); | |
| 321 } | |
| 322 } else if (systray_alignment_ == ALIGNMENT_LEFT || | |
| 323 systray_alignment_ == ALIGNMENT_RIGHT) { | |
| 324 int click_point_y = tray_->mouse_click_point().y(); | |
| 325 | |
| 326 if (bubble_alignment_ & ALIGNMENT_BOTTOM) { | |
| 327 int opposite_y_corner = inital_anchor_point_.y() - insetted_width; | |
| 328 | |
| 329 // If the click point is outside the y axis length of the message center, | |
| 330 // push the message center UPWARDS to align with the click point. | |
| 331 if (opposite_y_corner > click_point_y) | |
| 332 corrected_anchor.set_y(inital_anchor_point_.y() - | |
| 333 (opposite_y_corner - click_point_y)); | |
| 334 } else { | |
| 335 int opposite_y_corner = inital_anchor_point_.y() + insetted_width; | |
| 336 | |
| 337 if (opposite_y_corner < click_point_y) | |
| 338 corrected_anchor.set_y(inital_anchor_point_.y() + | |
| 339 (click_point_y - opposite_y_corner)); | |
| 340 } | |
| 341 } | |
| 342 return corrected_anchor; | |
| 343 } | |
| 344 | |
| 345 void MessageCenterWidgetDelegate::PositionAnchor() { | |
| 346 gfx::Screen* screen = gfx::Screen::GetNativeScreen(); | |
| 347 gfx::Rect work_area = screen->GetPrimaryDisplay().work_area(); | |
| 348 gfx::Point mouse_click_point = tray_->mouse_click_point(); | |
| 349 gfx::Point corner = internal::GetClosestCorner(work_area, mouse_click_point); | |
| 350 | |
| 351 systray_alignment_ = internal::GetSystrayAlignment(); | |
| 352 | |
| 353 // We assume the systray and taskbar are either at the top or at the bottom | |
| 354 // if we are not able to find it. | |
| 355 if (systray_alignment_ == ALIGNMENT_NONE) { | |
| 356 if (mouse_click_point.y() > corner.y()) | |
| 357 systray_alignment_ = ALIGNMENT_TOP; | |
| 358 else | |
| 359 systray_alignment_ = ALIGNMENT_BOTTOM; | |
| 360 } | |
| 361 | |
| 362 bubble_alignment_ = internal::GetAnchorAlignment(work_area, corner); | |
| 363 | |
| 364 inital_anchor_point_ = corner; | |
| 365 max_height_ = work_area.height(); | |
| 366 | |
| 367 if (work_area.Contains(mouse_click_point)) { | |
| 368 inital_anchor_point_.set_y(mouse_click_point.y()); | |
| 369 max_height_ -= std::abs(mouse_click_point.y() - corner.y()); | |
|
dewittj
2013/06/28 01:13:18
#include <complex>
sidharthms
2013/07/02 18:09:51
Done.
| |
| 370 } | |
| 371 } | |
| 372 | |
| 373 } // namespace message_center | |
| OLD | NEW |