Chromium Code Reviews| Index: chrome/browser/ui/views/message_center/message_center_widget_delegate.cc |
| diff --git a/chrome/browser/ui/views/message_center/message_center_widget_delegate.cc b/chrome/browser/ui/views/message_center/message_center_widget_delegate.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..6bcb8d7c54004c46bda9723a001efc7f4e5ee00d |
| --- /dev/null |
| +++ b/chrome/browser/ui/views/message_center/message_center_widget_delegate.cc |
| @@ -0,0 +1,373 @@ |
| +// Copyright 2013 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "chrome/browser/ui/views/message_center/message_center_widget_delegate.h" |
| + |
| +#include "chrome/browser/ui/views/message_center/message_center_frame_view.h" |
| +#include "content/public/browser/user_metrics.h" |
| +#include "ui/base/accessibility/accessible_view_state.h" |
| +#include "ui/gfx/screen.h" |
| +#include "ui/message_center/message_center_style.h" |
| +#include "ui/message_center/message_center_util.h" |
| +#include "ui/message_center/views/message_center_view.h" |
| +#include "ui/native_theme/native_theme.h" |
| +#include "ui/views/border.h" |
| +#include "ui/views/layout/box_layout.h" |
| +#include "ui/views/widget/widget.h" |
| + |
| +namespace { |
| + |
| +const int kBorderWidth = 1; |
| + |
| +} // namespace |
| + |
| +namespace message_center { |
| + |
| +namespace internal { |
| + |
| +// Gets the position of the systray (same as position of taskbar) from the |
| +// work area bounds. Returns ALIGNMENT_NONE if position cannot be found. |
| +Alignment GetSystrayAlignment() { |
| + gfx::Screen* screen = gfx::Screen::GetNativeScreen(); |
| + // TODO(dewittj): It's possible GetPrimaryDisplay is wrong. |
| + gfx::Rect screen_bounds = screen->GetPrimaryDisplay().bounds(); |
| + gfx::Rect work_area = screen->GetPrimaryDisplay().work_area(); |
| + |
| + // Comparing the work area to the screen bounds gives us the location of the |
| + // taskbar. If the work area is exactly the same as the screen bounds, |
| + // we are unable to locate the systray so we say we don't know it's alignment. |
| + if (work_area.height() < screen_bounds.height()) { |
| + if (work_area.y() > screen_bounds.y()) |
| + return ALIGNMENT_TOP; |
| + return ALIGNMENT_BOTTOM; |
| + } |
| + if (work_area.width() < screen_bounds.width()) { |
| + if (work_area.x() > screen_bounds.x()) |
| + return ALIGNMENT_LEFT; |
| + return ALIGNMENT_RIGHT; |
| + } |
| + |
| + return ALIGNMENT_NONE; |
| +} |
| + |
| +gfx::Point GetClosestCorner(const gfx::Rect& rect, const gfx::Point& query) { |
| + gfx::Point center_point = rect.CenterPoint(); |
| + gfx::Point rv; |
| + |
| + if (query.x() > center_point.x()) |
| + rv.set_x(rect.right()); |
| + else |
| + rv.set_x(rect.x()); |
| + |
| + if (query.y() > center_point.y()) |
| + rv.set_y(rect.bottom()); |
| + else |
| + rv.set_y(rect.y()); |
| + |
| + return rv; |
| +} |
| + |
| +// Gets the corner of the screen where the message center should pop up. |
| +Alignment GetAnchorAlignment(const gfx::Rect& work_area, gfx::Point corner) { |
| + gfx::Point center = work_area.CenterPoint(); |
| + |
| + Alignment anchor_alignment = |
| + center.y() > corner.y() ? ALIGNMENT_TOP : ALIGNMENT_BOTTOM; |
| + anchor_alignment = |
| + (Alignment)(anchor_alignment | |
| + (center.x() > corner.x() ? ALIGNMENT_LEFT : ALIGNMENT_RIGHT)); |
| + |
| + return anchor_alignment; |
| +} |
| + |
| +} // namespace internal |
| + |
| +MessageCenterWidgetDelegate::MessageCenterWidgetDelegate( |
| + WebNotificationTray* tray, |
| + MessageCenterTray* mc_tray, |
| + bool initially_settings_visible) |
| + : MessageCenterView(tray->message_center(), |
| + mc_tray, |
| + initially_settings_visible), |
| + color_explicitly_set_(false), |
| + close_on_esc_(true), |
| + close_on_deactivate_(true), |
| + tray_(tray) { |
| + |
| + PositionAnchor(); |
| + MessageCenterView::Init(max_height_); |
| + |
| + 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.
|
| + if (IsRichNotificationEnabled()) { |
| + min_width_ += kMarginBetweenItems * 2; |
| + max_width_ += kMarginBetweenItems * 2; |
| + } |
| + preferred_width_ = min_width_; |
| + |
| + views::BoxLayout* layout = |
| + new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0); |
| + layout->set_spread_blank_space(true); |
| + SetLayoutManager(layout); |
| + |
| + AddAccelerator(ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE)); |
| + UpdateColorsFromTheme(GetNativeTheme()); |
| + |
| + if (get_use_acceleration_when_possible()) { |
| + SetPaintToLayer(true); |
| + SetFillsBoundsOpaquely(true); |
| + } |
| + |
| + InitWidget(); |
| + UpdateNotifications(); |
| +} |
| + |
| +MessageCenterWidgetDelegate::~MessageCenterWidgetDelegate() { |
| + views::Widget* widget = GetWidget(); |
| + if (widget) { |
| + widget->RemoveObserver(this); |
| + } |
| +} |
| + |
| +views::View* MessageCenterWidgetDelegate::GetContentsView() { |
| + return this; |
| +} |
| + |
| +views::NonClientFrameView* |
| +MessageCenterWidgetDelegate::CreateNonClientFrameView(views::Widget* widget) { |
| + return new MessageCenterFrameView(kBorderWidth); |
| +} |
| + |
| +views::Widget* MessageCenterWidgetDelegate::GetWidget() { |
| + return View::GetWidget(); |
| +} |
| + |
| +const views::Widget* MessageCenterWidgetDelegate::GetWidget() const { |
| + return View::GetWidget(); |
| +} |
| + |
| +void MessageCenterWidgetDelegate::OnWidgetActivationChanged( |
| + views::Widget* widget, |
| + bool active) { |
| + if (close_on_deactivate() && !active) |
| + tray_->HideBubble(); |
| +} |
| + |
| +void MessageCenterWidgetDelegate::PreferredSizeChanged() { |
| + GetWidget()->SetBounds(GetBubbleBounds()); |
| + views::View::PreferredSizeChanged(); |
| +} |
| + |
| +gfx::Size MessageCenterWidgetDelegate::GetPreferredSize() { |
| + gfx::Size size = |
| + gfx::Size(preferred_width_, GetHeightForWidth(preferred_width_)); |
| + |
| + // Make space for borders on sides. |
| + size.Enlarge(2 * kBorderWidth, 2 * kBorderWidth); |
| + return size; |
| +} |
| + |
| +gfx::Size MessageCenterWidgetDelegate::GetMaximumSize() { |
| + gfx::Size size = GetPreferredSize(); |
| + size.set_width(max_width_); |
| + return size; |
| +} |
| + |
| +int MessageCenterWidgetDelegate::GetHeightForWidth(int width) { |
| + int height = MessageCenterView::GetHeightForWidth(width); |
| + return (max_height_ != 0) ? std::min(height, max_height_) : height; |
| +} |
| +bool MessageCenterWidgetDelegate::AcceleratorPressed( |
| + const ui::Accelerator& accelerator) { |
| + if (!close_on_esc() || accelerator.key_code() != ui::VKEY_ESCAPE) |
| + return false; |
| + if (fade_animation_.get()) |
| + fade_animation_->Reset(); |
| + GetWidget()->Close(); |
| + return true; |
| +} |
| + |
| +void MessageCenterWidgetDelegate::OnNativeThemeChanged( |
| + const ui::NativeTheme* theme) { |
| + UpdateColorsFromTheme(theme); |
| +} |
| + |
| +void MessageCenterWidgetDelegate::InitWidget() { |
| + views::Widget* widget = new views::Widget(); |
| + views::Widget::InitParams params(views::Widget::InitParams::TYPE_BUBBLE); |
| + params.delegate = this; |
| + params.keep_on_top = true; |
| + params.top_level = true; |
| +#if defined(OS_WIN) && !defined(USE_AURA) |
| + params.type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS; |
| +#endif |
| + widget->Init(params); |
| + |
| + widget->AddObserver(this); |
| + widget->StackAtTop(); |
| + |
| + // Popups should appear on top of everything, but not disturb the user's |
| + // focus since they could appear at any time. Message Center is always |
| + // shown as a result of user action so it can be activated here. |
| + widget->SetAlwaysOnTop(true); |
| + widget->Activate(); |
| +} |
| + |
| +void MessageCenterWidgetDelegate::CloseWidget() { |
| + GetWidget()->Close(); |
| +} |
| + |
| +void MessageCenterWidgetDelegate::UpdateColorsFromTheme( |
| + const ui::NativeTheme* theme) { |
| + if (!color_explicitly_set_) { |
| + color_ = GetNativeTheme()->GetSystemColor( |
| + ui::NativeTheme::kColorId_WindowBackground); |
| + } |
| + set_background(views::Background::CreateSolidBackground(color())); |
| +} |
| + |
| +void MessageCenterWidgetDelegate::UpdateNotifications() { |
| + const NotificationList::Notifications& notifications = |
| + tray_->message_center()->GetNotifications(); |
| + SetNotifications(notifications); |
| + GetWidget()->Show(); |
| + GetWidget()->SetBounds(GetBubbleBounds()); |
| + GetWidget()->GetRootView()->SchedulePaint(); |
| +} |
| + |
| +MessageCenterFrameView* MessageCenterWidgetDelegate::GetBubbleFrameView() |
| + const { |
| + const views::Widget* widget = GetWidget(); |
| + const views::NonClientView* view = widget ? widget->non_client_view() : NULL; |
| + return view ? static_cast<MessageCenterFrameView*>(view->frame_view()) : NULL; |
| +} |
| + |
| +gfx::Rect MessageCenterWidgetDelegate::GetBubbleBounds() { |
| + const gfx::Size size = GetPreferredSize(); |
| + gfx::Rect bounds(size); |
| + |
| + gfx::Point corrected_anchor = GetCorrectedAnchor(size); |
| + |
| + if (bubble_alignment_ & ALIGNMENT_TOP) |
| + bounds.set_y(corrected_anchor.y()); |
| + if (bubble_alignment_ & ALIGNMENT_BOTTOM) |
| + bounds.set_y(corrected_anchor.y() - size.height()); |
| + if (bubble_alignment_ & ALIGNMENT_LEFT) |
| + bounds.set_x(corrected_anchor.x()); |
| + if (bubble_alignment_ & ALIGNMENT_RIGHT) |
| + bounds.set_x(corrected_anchor.x() - size.width()); |
| + |
| + return bounds; |
| +} |
| + |
| +void MessageCenterWidgetDelegate::AnimationEnded( |
| + const ui::Animation* animation) { |
| + if (animation == fade_animation_.get()) { |
| + bool closed = fade_animation_->GetCurrentValue() == 0; |
| + fade_animation_->Reset(); |
| + if (closed) |
| + GetWidget()->Close(); |
| + } |
| + MessageCenterView::AnimationEnded(animation); |
| +} |
| + |
| +void MessageCenterWidgetDelegate::AnimationProgressed( |
| + const ui::Animation* animation) { |
| + if (animation == fade_animation_.get()) { |
| + DCHECK(fade_animation_->is_animating()); |
| + unsigned char opacity = fade_animation_->GetCurrentValue() * 255; |
| +#if defined(OS_WIN) && !defined(USE_AURA) |
| + // Explicitly set the content Widget's layered style and set transparency |
| + // via SetLayeredWindowAttributes. This is done because initializing the |
| + // Widget as transparent and setting opacity via UpdateLayeredWindow doesn't |
| + // support hosting child native Windows controls. |
| + const HWND hwnd = GetWidget()->GetNativeView(); |
| + const DWORD style = GetWindowLong(hwnd, GWL_EXSTYLE); |
| + if ((opacity == 255) == !!(style & WS_EX_LAYERED)) |
| + SetWindowLong(hwnd, GWL_EXSTYLE, style ^ WS_EX_LAYERED); |
| + SetLayeredWindowAttributes(hwnd, 0, opacity, LWA_ALPHA); |
| +#endif |
| + GetWidget()->SetOpacity(opacity); |
| + } |
| + MessageCenterView::AnimationProgressed(animation); |
| +} |
| + |
| +gfx::Point MessageCenterWidgetDelegate::GetCorrectedAnchor( |
| + gfx::Size calculated_size) { |
| + gfx::Point corrected_anchor = inital_anchor_point_; |
| + |
| + // Inset the width slightly so that the click point is not exactly on the edge |
| + // of the message center but somewhere within the middle 60 %. |
| + int insetted_width = (calculated_size.width() * 4) / 5; |
| + |
| + if (systray_alignment_ == ALIGNMENT_TOP || |
| + systray_alignment_ == ALIGNMENT_BOTTOM) { |
| + int click_point_x = tray_->mouse_click_point().x(); |
| + |
| + if (bubble_alignment_ & ALIGNMENT_RIGHT) { |
| + int opposite_x_corner = inital_anchor_point_.x() - insetted_width; |
| + |
| + // If the click point is outside the x axis length of the message center, |
| + // push the message center towards the LEFT to align with the click point. |
| + if (opposite_x_corner > click_point_x) |
| + corrected_anchor.set_x(inital_anchor_point_.x() - |
| + (opposite_x_corner - click_point_x)); |
| + } else { |
| + int opposite_x_corner = inital_anchor_point_.x() + insetted_width; |
| + |
| + if (opposite_x_corner < click_point_x) |
| + corrected_anchor.set_x(inital_anchor_point_.x() + |
| + (click_point_x - opposite_x_corner)); |
| + } |
| + } else if (systray_alignment_ == ALIGNMENT_LEFT || |
| + systray_alignment_ == ALIGNMENT_RIGHT) { |
| + int click_point_y = tray_->mouse_click_point().y(); |
| + |
| + if (bubble_alignment_ & ALIGNMENT_BOTTOM) { |
| + int opposite_y_corner = inital_anchor_point_.y() - insetted_width; |
| + |
| + // If the click point is outside the y axis length of the message center, |
| + // push the message center UPWARDS to align with the click point. |
| + if (opposite_y_corner > click_point_y) |
| + corrected_anchor.set_y(inital_anchor_point_.y() - |
| + (opposite_y_corner - click_point_y)); |
| + } else { |
| + int opposite_y_corner = inital_anchor_point_.y() + insetted_width; |
| + |
| + if (opposite_y_corner < click_point_y) |
| + corrected_anchor.set_y(inital_anchor_point_.y() + |
| + (click_point_y - opposite_y_corner)); |
| + } |
| + } |
| + return corrected_anchor; |
| +} |
| + |
| +void MessageCenterWidgetDelegate::PositionAnchor() { |
| + gfx::Screen* screen = gfx::Screen::GetNativeScreen(); |
| + gfx::Rect work_area = screen->GetPrimaryDisplay().work_area(); |
| + gfx::Point mouse_click_point = tray_->mouse_click_point(); |
| + gfx::Point corner = internal::GetClosestCorner(work_area, mouse_click_point); |
| + |
| + systray_alignment_ = internal::GetSystrayAlignment(); |
| + |
| + // We assume the systray and taskbar are either at the top or at the bottom |
| + // if we are not able to find it. |
| + if (systray_alignment_ == ALIGNMENT_NONE) { |
| + if (mouse_click_point.y() > corner.y()) |
| + systray_alignment_ = ALIGNMENT_TOP; |
| + else |
| + systray_alignment_ = ALIGNMENT_BOTTOM; |
| + } |
| + |
| + bubble_alignment_ = internal::GetAnchorAlignment(work_area, corner); |
| + |
| + inital_anchor_point_ = corner; |
| + max_height_ = work_area.height(); |
| + |
| + if (work_area.Contains(mouse_click_point)) { |
| + inital_anchor_point_.set_y(mouse_click_point.y()); |
| + 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.
|
| + } |
| +} |
| + |
| +} // namespace message_center |