Commit a13607a5 authored by dewittj@chromium.org's avatar dewittj@chromium.org

Retain popup bubble mouse status even through updates.

Currently rapid updates completely block the user from
interacting with the view even for completely static
content such as the close button.

BUG=368025

Review URL: https://codereview.chromium.org/271773002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@269458 0039d316-1c4b-4281-b951-d872f2087c98
parent 6c8d40b3
......@@ -43,6 +43,9 @@ class InnerBoundedLabel : public views::Label {
gfx::Size GetSizeForWidthAndLines(int width, int lines);
std::vector<base::string16> GetWrappedText(int width, int lines);
// Overridden from views::Label.
virtual void SetText(const base::string16& text) OVERRIDE;
protected:
// Overridden from views::Label.
virtual void OnBoundsChanged(const gfx::Rect& previous_bounds) OVERRIDE;
......@@ -190,6 +193,11 @@ void InnerBoundedLabel::OnPaint(gfx::Canvas* canvas) {
}
}
void InnerBoundedLabel::SetText(const base::string16& text) {
views::Label::SetText(text);
ClearCaches();
}
int InnerBoundedLabel::GetTextFlags() {
int flags = gfx::Canvas::MULTI_LINE | gfx::Canvas::CHARACTER_BREAK;
......@@ -293,6 +301,10 @@ void BoundedLabel::SetLineLimit(int lines) {
line_limit_ = std::max(lines, -1);
}
void BoundedLabel::SetText(const base::string16& text) {
label_->SetText(text);
}
int BoundedLabel::GetLineHeight() const {
return label_->line_height();
}
......
......@@ -40,6 +40,7 @@ class MESSAGE_CENTER_EXPORT BoundedLabel : public views::View {
void SetColors(SkColor textColor, SkColor backgroundColor);
void SetLineHeight(int height); // Pass in 0 for default height.
void SetLineLimit(int lines); // Pass in -1 for no limit.
void SetText(const base::string16& text); // Additionally clears caches.
int GetLineHeight() const;
int GetLineLimit() const;
......
......@@ -113,7 +113,7 @@ class MessageListView : public views::View,
void AddNotificationAt(MessageView* view, int i);
void RemoveNotification(MessageView* view);
void UpdateNotification(MessageView* view, MessageView* new_view);
void UpdateNotification(MessageView* view, const Notification& notification);
void SetRepositionTarget(const gfx::Rect& target_rect);
void ResetRepositionSession();
void ClearAllNotifications(const gfx::Rect& visible_scroll_rect);
......@@ -261,23 +261,17 @@ void MessageListView::RemoveNotification(MessageView* view) {
}
void MessageListView::UpdateNotification(MessageView* view,
MessageView* new_view) {
const Notification& notification) {
int index = GetIndexOf(view);
DCHECK_LE(0, index); // GetIndexOf is negative if not a child.
if (animator_.get())
animator_->StopAnimatingView(view);
gfx::Rect old_bounds = view->bounds();
if (deleting_views_.find(view) != deleting_views_.end())
deleting_views_.erase(view);
if (deleted_when_done_.find(view) != deleted_when_done_.end())
deleted_when_done_.erase(view);
delete view;
AddChildViewAt(new_view, index);
new_view->SetBounds(old_bounds.x(),
old_bounds.y(),
old_bounds.width(),
new_view->GetHeightForWidth(old_bounds.width()));
view->UpdateWithNotification(notification);
DoUpdateIfPossible();
}
......@@ -886,16 +880,11 @@ void MessageCenterView::OnNotificationUpdated(const std::string& id) {
for (NotificationList::Notifications::const_iterator iter =
notifications.begin(); iter != notifications.end(); ++iter) {
if ((*iter)->id() == id) {
NotificationView* new_view =
NotificationView::Create(this,
*(*iter),
false); // Not creating a top-level
// notification.
new_view->set_context_menu_controller(context_menu_controller_.get());
new_view->set_scroller(scroller_);
message_list_view_->UpdateNotification(view, new_view);
notification_views_[id] = new_view;
NotificationsChanged();
int old_width = view->width();
int old_height = view->GetHeightForWidth(old_width);
message_list_view_->UpdateNotification(view, **iter);
if (view->GetHeightForWidth(old_width) != old_height)
NotificationsChanged();
break;
}
}
......
......@@ -450,20 +450,22 @@ void MessagePopupCollection::OnNotificationUpdated(
for (NotificationList::PopupNotifications::iterator iter =
notifications.begin(); iter != notifications.end(); ++iter) {
if ((*iter)->id() != notification_id)
Notification* notification = *iter;
DCHECK(notification);
ToastContentsView* toast_contents_view = *toast_iter;
DCHECK(toast_contents_view);
if (notification->id() != notification_id)
continue;
const RichNotificationData& optional_fields =
(*iter)->rich_notification_data();
notification->rich_notification_data();
bool a11y_feedback_for_updates =
optional_fields.should_make_spoken_feedback_for_popup_updates;
NotificationView* view =
NotificationView::Create(*toast_iter,
*(*iter),
true); // Create top-level notification.
view->set_context_menu_controller(context_menu_controller_.get());
(*toast_iter)->SetContents(view, a11y_feedback_for_updates);
toast_contents_view->UpdateContents(*notification,
a11y_feedback_for_updates);
updated = true;
}
......
......@@ -83,6 +83,11 @@ MessageView::MessageView(MessageViewController* controller,
MessageView::~MessageView() {
}
void MessageView::UpdateWithNotification(const Notification& notification) {
small_image_view_->SetImage(notification.small_image().AsImageSkia());
display_source_ = notification.display_source();
}
// static
gfx::Insets MessageView::GetShadowInsets() {
return gfx::Insets(kShadowBlur / 2 - kShadowOffset,
......
......@@ -53,6 +53,9 @@ class MESSAGE_CENTER_EXPORT MessageView : public views::SlideOutView,
const base::string16& display_source);
virtual ~MessageView();
// Updates this view with the new data contained in the notification.
virtual void UpdateWithNotification(const Notification& notification);
// Returns the insets for the shadow it will have for rich notification.
static gfx::Insets GetShadowInsets();
......
......@@ -51,6 +51,8 @@ class MESSAGE_CENTER_EXPORT NotificationView : public MessageView,
virtual gfx::NativeCursor GetCursor(const ui::MouseEvent& event) OVERRIDE;
// Overridden from MessageView:
virtual void UpdateWithNotification(
const Notification& notification) OVERRIDE;
virtual void ButtonPressed(views::Button* sender,
const ui::Event& event) OVERRIDE;
......@@ -68,7 +70,21 @@ class MESSAGE_CENTER_EXPORT NotificationView : public MessageView,
const Notification& notification);
private:
FRIEND_TEST_ALL_PREFIXES(NotificationViewTest, CreateOrUpdateTest);
FRIEND_TEST_ALL_PREFIXES(NotificationViewTest, TestLineLimits);
void CreateOrUpdateViews(const Notification& notification);
void SetAccessibleName(const Notification& notification);
void CreateOrUpdateTitleView(const Notification& notification);
void CreateOrUpdateMessageView(const Notification& notification);
void CreateOrUpdateContextMessageView(const Notification& notification);
void CreateOrUpdateProgressBarView(const Notification& notification);
void CreateOrUpdateListItemViews(const Notification& notification);
void CreateOrUpdateIconView(const Notification& notification);
void CreateOrUpdateImageView(const Notification& notification);
void CreateOrUpdateActionButtonViews(const Notification& notification);
int GetMessageLineLimit(int title_lines, int width);
int GetMessageHeight(int width, int limit);
......@@ -88,6 +104,7 @@ class MESSAGE_CENTER_EXPORT NotificationView : public MessageView,
views::View* image_view_;
views::ProgressBar* progress_bar_view_;
std::vector<views::View*> action_buttons_;
std::vector<views::View*> separators_;
DISALLOW_COPY_AND_ASSIGN(NotificationView);
};
......
......@@ -10,68 +10,169 @@
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/gfx/image/image.h"
#include "ui/message_center/notification.h"
#include "ui/message_center/notification_list.h"
#include "ui/message_center/notification_types.h"
#include "ui/message_center/views/message_center_controller.h"
namespace message_center {
/* Test fixture ***************************************************************/
typedef testing::Test NotificationViewTest;
class NotificationViewTest : public testing::Test,
public MessageCenterController {
public:
NotificationViewTest();
virtual ~NotificationViewTest();
TEST_F(NotificationViewTest, TestLineLimits) {
message_center::RichNotificationData data;
std::string id("id");
NotifierId notifier_id(NotifierId::APPLICATION, "notifier");
scoped_ptr<Notification> notification(
new Notification(NOTIFICATION_TYPE_BASE_FORMAT,
id,
base::UTF8ToUTF16("test title"),
base::UTF8ToUTF16("test message"),
gfx::Image(),
base::string16() /* display_source */,
notifier_id,
data,
NULL /* delegate */));
scoped_ptr<NotificationView> view(new NotificationView(NULL, *notification));
EXPECT_EQ(5, view->GetMessageLineLimit(0, 360));
EXPECT_EQ(5, view->GetMessageLineLimit(1, 360));
EXPECT_EQ(3, view->GetMessageLineLimit(2, 360));
virtual void SetUp() OVERRIDE;
virtual void TearDown() OVERRIDE;
NotificationView* notification_view() { return notification_view_.get(); }
Notification* notification() { return notification_.get(); }
RichNotificationData* data() { return data_.get(); }
// Overridden from MessageCenterController:
virtual void ClickOnNotification(const std::string& notification_id) OVERRIDE;
virtual void RemoveNotification(const std::string& notification_id,
bool by_user) OVERRIDE;
virtual scoped_ptr<ui::MenuModel> CreateMenuModel(
const NotifierId& notifier_id,
const base::string16& display_source) OVERRIDE;
virtual bool HasClickedListener(const std::string& notification_id) OVERRIDE;
virtual void ClickOnNotificationButton(const std::string& notification_id,
int button_index) OVERRIDE;
protected:
const gfx::Image CreateTestImage(int width, int height) {
return gfx::Image::CreateFrom1xBitmap(CreateBitmap(width, height));
}
const SkBitmap CreateBitmap(int width, int height) {
SkBitmap bitmap;
bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
bitmap.allocPixels();
bitmap.eraseRGB(0, 255, 0);
return bitmap;
}
private:
scoped_ptr<RichNotificationData> data_;
scoped_ptr<Notification> notification_;
scoped_ptr<NotificationView> notification_view_;
DISALLOW_COPY_AND_ASSIGN(NotificationViewTest);
};
NotificationViewTest::NotificationViewTest() {
}
NotificationViewTest::~NotificationViewTest() {
}
void NotificationViewTest::SetUp() {
// Create a dummy notification.
SkBitmap bitmap;
bitmap.setConfig(SkBitmap::kARGB_8888_Config, 2, 2);
bitmap.allocPixels();
bitmap.eraseColor(SK_ColorGREEN);
data.image = gfx::Image::CreateFrom1xBitmap(bitmap);
notification.reset(new Notification(NOTIFICATION_TYPE_BASE_FORMAT,
id,
base::UTF8ToUTF16("test title"),
base::UTF8ToUTF16("test message"),
gfx::Image(),
base::string16() /* display_source */,
notifier_id,
data,
NULL /* delegate */));
view.reset(new NotificationView(NULL, *notification));
EXPECT_EQ(2, view->GetMessageLineLimit(0, 360));
EXPECT_EQ(2, view->GetMessageLineLimit(1, 360));
EXPECT_EQ(1, view->GetMessageLineLimit(2, 360));
data.context_message = base::UTF8ToUTF16("foo");
notification.reset(new Notification(NOTIFICATION_TYPE_BASE_FORMAT,
id,
base::UTF8ToUTF16("test title"),
base::UTF8ToUTF16("test message"),
gfx::Image(),
base::string16() /* display_source */,
notifier_id,
data,
NULL /* delegate */));
view.reset(new NotificationView(NULL, *notification));
EXPECT_EQ(1, view->GetMessageLineLimit(0, 360));
EXPECT_EQ(1, view->GetMessageLineLimit(1, 360));
EXPECT_EQ(0, view->GetMessageLineLimit(2, 360));
data_.reset(new RichNotificationData());
notification_.reset(
new Notification(NOTIFICATION_TYPE_BASE_FORMAT,
std::string("notification id"),
base::UTF8ToUTF16("title"),
base::UTF8ToUTF16("message"),
CreateTestImage(80, 80),
base::UTF8ToUTF16("display source"),
NotifierId(NotifierId::APPLICATION, "extension_id"),
*data_,
NULL));
notification_->set_small_image(CreateTestImage(16, 16));
notification_->set_image(CreateTestImage(320, 240));
// Then create a new NotificationView with that single notification.
notification_view_.reset(
NotificationView::Create(this, *notification_, true));
}
void NotificationViewTest::TearDown() {
notification_view_.reset();
}
void NotificationViewTest::ClickOnNotification(
const std::string& notification_id) {
// For this test, this method should not be invoked.
NOTREACHED();
}
void NotificationViewTest::RemoveNotification(
const std::string& notification_id,
bool by_user) {
// For this test, this method should not be invoked.
NOTREACHED();
}
scoped_ptr<ui::MenuModel> NotificationViewTest::CreateMenuModel(
const NotifierId& notifier_id,
const base::string16& display_source) {
// For this test, this method should not be invoked.
NOTREACHED();
return scoped_ptr<ui::MenuModel>();
}
bool NotificationViewTest::HasClickedListener(
const std::string& notification_id) {
return true;
}
void NotificationViewTest::ClickOnNotificationButton(
const std::string& notification_id,
int button_index) {
// For this test, this method should not be invoked.
NOTREACHED();
}
/* Unit tests *****************************************************************/
TEST_F(NotificationViewTest, CreateOrUpdateTest) {
EXPECT_TRUE(NULL != notification_view()->title_view_);
EXPECT_TRUE(NULL != notification_view()->message_view_);
EXPECT_TRUE(NULL != notification_view()->icon_view_);
EXPECT_TRUE(NULL != notification_view()->image_view_);
notification()->set_image(gfx::Image());
notification()->set_title(base::ASCIIToUTF16(""));
notification()->set_message(base::ASCIIToUTF16(""));
notification()->set_icon(gfx::Image());
notification_view()->CreateOrUpdateViews(*notification());
EXPECT_TRUE(NULL == notification_view()->title_view_);
EXPECT_TRUE(NULL == notification_view()->message_view_);
EXPECT_TRUE(NULL == notification_view()->image_view_);
// We still expect an icon view for all layouts.
EXPECT_TRUE(NULL != notification_view()->icon_view_);
}
TEST_F(NotificationViewTest, TestLineLimits) {
notification()->set_image(CreateTestImage(0, 0));
notification()->set_context_message(base::ASCIIToUTF16(""));
notification_view()->CreateOrUpdateViews(*notification());
EXPECT_EQ(5, notification_view()->GetMessageLineLimit(0, 360));
EXPECT_EQ(5, notification_view()->GetMessageLineLimit(1, 360));
EXPECT_EQ(3, notification_view()->GetMessageLineLimit(2, 360));
notification()->set_image(CreateTestImage(2, 2));
notification_view()->CreateOrUpdateViews(*notification());
EXPECT_EQ(2, notification_view()->GetMessageLineLimit(0, 360));
EXPECT_EQ(2, notification_view()->GetMessageLineLimit(1, 360));
EXPECT_EQ(1, notification_view()->GetMessageLineLimit(2, 360));
notification()->set_context_message(base::UTF8ToUTF16("foo"));
notification_view()->CreateOrUpdateViews(*notification());
EXPECT_TRUE(notification_view()->context_message_view_ != NULL);
EXPECT_EQ(1, notification_view()->GetMessageLineLimit(0, 360));
EXPECT_EQ(1, notification_view()->GetMessageLineLimit(1, 360));
EXPECT_EQ(0, notification_view()->GetMessageLineLimit(2, 360));
}
} // namespace message_center
......@@ -87,6 +87,15 @@ void ToastContentsView::SetContents(MessageView* view,
NotifyAccessibilityEvent(ui::AX_EVENT_ALERT, false);
}
void ToastContentsView::UpdateContents(const Notification& notification,
bool a11y_feedback_for_updates) {
DCHECK_GT(child_count(), 0);
MessageView* message_view = static_cast<MessageView*>(child_at(0));
message_view->UpdateWithNotification(notification);
if (a11y_feedback_for_updates)
NotifyAccessibilityEvent(ui::AX_EVENT_ALERT, false);
}
void ToastContentsView::RevealWithAnimation(gfx::Point origin) {
// Place/move the toast widgets. Currently it stacks the widgets from the
// right-bottom of the work area.
......
......@@ -50,6 +50,9 @@ class ToastContentsView : public views::WidgetDelegateView,
// accessibility message should be read after this update.
void SetContents(MessageView* view, bool a11y_feedback_for_updates);
void UpdateContents(const Notification& notification,
bool a11y_feedback_for_updates);
// Shows the new toast for the first time, animated.
// |origin| is the right-bottom corner of the toast.
void RevealWithAnimation(gfx::Point origin);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment