diff options
author | skuhne@chromium.org <skuhne@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-05-22 23:06:56 +0000 |
---|---|---|
committer | skuhne@chromium.org <skuhne@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-05-22 23:06:56 +0000 |
commit | 45e0ff3b78fb3fea23b512b4e5632f86c302510a (patch) | |
tree | c9a674242661c4aee6965ca97cde925b01830215 /ash | |
parent | f654244048abd59f49f1b6af64050fc80b34c55d (diff) | |
download | chromium_src-45e0ff3b78fb3fea23b512b4e5632f86c302510a.zip chromium_src-45e0ff3b78fb3fea23b512b4e5632f86c302510a.tar.gz chromium_src-45e0ff3b78fb3fea23b512b4e5632f86c302510a.tar.bz2 |
Adding new general bubble error message consisting of icon, message and caption. Using this message for the multi signin user error case.
There is still more to do: Unit tests & removal of test code. Beside that feature wise it is now fully functional as requested.
BUG=239201
TEST=visual, multiple scenarios
Review URL: https://chromiumcodereview.appspot.com/15329005
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@201627 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ash')
-rw-r--r-- | ash/ash.gyp | 2 | ||||
-rw-r--r-- | ash/popup_message.cc | 226 | ||||
-rw-r--r-- | ash/popup_message.h | 83 | ||||
-rw-r--r-- | ash/resources/ash_resources.grd | 2 | ||||
-rw-r--r-- | ash/system/user/tray_user.cc | 43 |
5 files changed, 344 insertions, 12 deletions
diff --git a/ash/ash.gyp b/ash/ash.gyp index eab24da..1f19dfd 100644 --- a/ash/ash.gyp +++ b/ash/ash.gyp @@ -161,6 +161,8 @@ 'magnifier/magnifier_constants.h', 'magnifier/partial_magnification_controller.cc', 'magnifier/partial_magnification_controller.h', + 'popup_message.cc', + 'popup_message.h', 'root_window_controller.cc', 'root_window_controller.h', 'rotator/screen_rotation.cc', diff --git a/ash/popup_message.cc b/ash/popup_message.cc new file mode 100644 index 0000000..bd51938 --- /dev/null +++ b/ash/popup_message.cc @@ -0,0 +1,226 @@ +// Copyright (c) 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 "ash/popup_message.h" + +#include "ash/wm/window_animations.h" +#include "grit/ash_resources.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/gfx/insets.h" +#include "ui/views/bubble/bubble_delegate.h" +#include "ui/views/bubble/bubble_frame_view.h" +#include "ui/views/controls/image_view.h" +#include "ui/views/controls/label.h" +#include "ui/views/layout/box_layout.h" +#include "ui/views/widget/widget.h" + +namespace ash { +namespace { +const int kMessageTopBottomMargin = 10; +const int kMessageLeftRightMargin = 10; +const int kMessageAppearanceDelayMs = 200; +const int kMessageMinHeight = 29 - 2 * kMessageTopBottomMargin; +const SkColor kMessageTextColor = SkColorSetRGB(0x22, 0x22, 0x22); + +// The maximum width of the Message bubble. Borrowed the value from +// ash/message/message_controller.cc +const int kMessageMaxWidth = 250; + +// The offset for the Message bubble - making sure that the bubble is flush +// with the shelf. The offset includes the arrow size in pixels as well as +// the activation bar and other spacing elements. +const int kArrowOffsetLeftRight = 11; +const int kArrowOffsetTopBottom = 7; + +// The number of pixels between the icon and the text. +const int kHorizontalPopupPaddingBetweenItems = 10; + +// The number of pixels between the text items. +const int kVerticalPopupPaddingBetweenItems = 10; +} // namespace + +// The implementation of Message of the launcher. +class PopupMessage::MessageBubble : public views::BubbleDelegateView { + public: + MessageBubble(const base::string16& caption, + const base::string16& message, + IconType message_type, + views::View* anchor, + views::BubbleBorder::Arrow arrow_orientation, + const gfx::Size& size_override, + int arrow_offset); + + void Close(); + + private: + // views::View overrides: + virtual gfx::Size GetPreferredSize() OVERRIDE; + + // Each component (width/height) can force a size override for that component + // if not 0. + gfx::Size size_override_; + + DISALLOW_COPY_AND_ASSIGN(MessageBubble); +}; + +PopupMessage::MessageBubble::MessageBubble(const base::string16& caption, + const base::string16& message, + IconType message_type, + views::View* anchor, + views::BubbleBorder::Arrow arrow, + const gfx::Size& size_override, + int arrow_offset) + : views::BubbleDelegateView(anchor, arrow), + size_override_(size_override) { + gfx::Insets insets = gfx::Insets(kArrowOffsetTopBottom, + kArrowOffsetLeftRight, + kArrowOffsetTopBottom, + kArrowOffsetLeftRight); + // An anchor can have an asymmetrical border for spacing reasons. Adjust the + // anchor location for this. + if (anchor->border()) + insets += anchor->border()->GetInsets(); + + set_anchor_view_insets(insets); + set_close_on_esc(false); + set_close_on_deactivate(false); + set_use_focusless(true); + set_accept_events(false); + + set_margins(gfx::Insets(kMessageTopBottomMargin, kMessageLeftRightMargin, + kMessageTopBottomMargin, kMessageLeftRightMargin)); + set_shadow(views::BubbleBorder::SMALL_SHADOW); + + ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); + SetLayoutManager(new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, + kHorizontalPopupPaddingBetweenItems)); + + // Here is the layout: + // arrow_offset (if not 0) + // |-------------| + // | ^ + // +-------------------------------------------------+ + // -| |- + // icon | [!] Caption in bold which can be multi line | caption_label + // -| |- + // | Message text which can be multi line | message_label + // | as well. | + // | |- + // +-------------------------------------------------+ + // |------------details container--------------| + // Note that the icon, caption and massage are optional. + + // Add the icon to the first column (if there is one). + if (message_type != ICON_NONE) { + views::ImageView* icon = new views::ImageView(); + icon->SetImage( + bundle.GetImageNamed(IDR_AURA_WARNING_ICON).ToImageSkia()); + icon->SetVerticalAlignment(views::ImageView::LEADING); + AddChildView(icon); + } + + // Create a container for the text items and use it as second column. + views::View* details = new views::View(); + AddChildView(details); + details->SetLayoutManager(new views::BoxLayout( + views::BoxLayout::kVertical, 0, 0, kVerticalPopupPaddingBetweenItems)); + + // The caption label. + if (!caption.empty()) { + views::Label* caption_label = new views::Label(caption); + caption_label->SetMultiLine(true); + caption_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); + caption_label->SetFont(bundle.GetFont(ui::ResourceBundle::BoldFont)); + caption_label->SetEnabledColor(kMessageTextColor); + details->AddChildView(caption_label); + } + + // The message label. + if (!message.empty()) { + views::Label* message_label = new views::Label(message); + message_label->SetMultiLine(true); + message_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); + message_label->SetEnabledColor(kMessageTextColor); + details->AddChildView(message_label); + } + views::BubbleDelegateView::CreateBubble(this); + + // Change the arrow offset if needed. + if (arrow_offset) { + // With the creation of the bubble, the bubble got already placed (and + // possibly re-oriented to fit on the screen). Since it is not possible to + // set the arrow offset before the creation, we need to set the offset, + // and the orientation variables again and force a re-placement. + GetBubbleFrameView()->bubble_border()->set_arrow_offset(arrow_offset); + GetBubbleFrameView()->bubble_border()->set_arrow(arrow); + SetAlignment(views::BubbleBorder::ALIGN_ARROW_TO_MID_ANCHOR); + } +} + +void PopupMessage::MessageBubble::Close() { + if (GetWidget()) + GetWidget()->Close(); +} + +gfx::Size PopupMessage::MessageBubble::GetPreferredSize() { + gfx::Size pref_size = views::BubbleDelegateView::GetPreferredSize(); + // Override the size with either the provided size or adjust it to not + // violate our minimum / maximum sizes. + if (size_override_.height()) + pref_size.set_height(size_override_.height()); + else if (pref_size.height() < kMessageMinHeight) + pref_size.set_height(kMessageMinHeight); + + if (size_override_.width()) + pref_size.set_width(size_override_.width()); + else if (pref_size.width() > kMessageMaxWidth) + pref_size.set_width(kMessageMaxWidth); + + return pref_size; +} + +PopupMessage::PopupMessage(const base::string16& caption, + const base::string16& message, + IconType message_type, + views::View* anchor, + views::BubbleBorder::Arrow arrow, + const gfx::Size& size_override, + int arrow_offset) + : view_(NULL) { + view_ = new MessageBubble( + caption, message, message_type, anchor, arrow, size_override, + arrow_offset); + widget_ = view_->GetWidget(); + + gfx::NativeView native_view = widget_->GetNativeView(); + views::corewm::SetWindowVisibilityAnimationType( + native_view, views::corewm::WINDOW_VISIBILITY_ANIMATION_TYPE_VERTICAL); + views::corewm::SetWindowVisibilityAnimationTransition( + native_view, views::corewm::ANIMATE_HIDE); + view_->GetWidget()->Show(); +} + +PopupMessage::~PopupMessage() { + CancelHidingAnimation(); + Close(); +} + +void PopupMessage::Close() { + if (view_) { + view_->Close(); + view_ = NULL; + widget_ = NULL; + } +} + +void PopupMessage::CancelHidingAnimation() { + if (!widget_ || !widget_->GetNativeView()) + return; + + gfx::NativeView native_view = widget_->GetNativeView(); + views::corewm::SetWindowVisibilityAnimationTransition( + native_view, views::corewm::ANIMATE_NONE); +} + +} // namespace ash diff --git a/ash/popup_message.h b/ash/popup_message.h new file mode 100644 index 0000000..76257cb --- /dev/null +++ b/ash/popup_message.h @@ -0,0 +1,83 @@ +// 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. + +#ifndef ASH_POPUP_MESSAGE_H_ +#define ASH_POPUP_MESSAGE_H_ + +#include "ash/ash_export.h" +#include "base/basictypes.h" +#include "base/string16.h" +#include "ui/gfx/rect.h" +#include "ui/views/bubble/bubble_border.h" + +namespace views { +class BubbleDelegateView; +} + +namespace ash { + +// PopupMessage shows a message to the user. Since the user is not able to +// dismiss it, the calling code needs to explictly close and destroy it. +class ASH_EXPORT PopupMessage { + public: + enum IconType { + ICON_WARNING, + ICON_NONE + }; + + // Creates a message pointing towards |anchor| with the requested + // |arrow_orientation|. The message contains an optional |caption| which is + // drawn in bold and an optional |message| together with an optional icon of + // shape |message_type|. If a component in |size_override| is not 0 the value + // is the used as output size. If |arrow_offset| is not 0, the number is the + // arrow offset in pixels from the border. + // + // Here is the layout (arrow given as TOP_LEFT): + // |--------| + // | Anchor | + // |--------| + // |-arrow_offset---^ + // +-------------------------------------------------+ + // -| |- + // icon | [!] Caption in bold which can be multi line | caption_label + // -| |- + // | Message text which can be multi line | message_label + // | as well. | + // | |- + // +-------------------------------------------------+ + PopupMessage(const base::string16& caption, + const base::string16& message, + IconType message_type, + views::View* anchor, + views::BubbleBorder::Arrow arrow, + const gfx::Size& size_override, + int arrow_offset); + // If the message was not explicitly closed before, it closes the message + // without animation. + virtual ~PopupMessage(); + + // Closes the message with a fade out animation. + void Close(); + + private: + class MessageBubble; + + void CancelHidingAnimation(); + + MessageBubble* view_; + views::Widget* widget_; + + // Variables of the construction time. + views::View* anchor_; + base::string16 caption_; + base::string16 message_; + IconType message_type_; + views::BubbleBorder::Arrow arrow_orientation_; + + DISALLOW_COPY_AND_ASSIGN(PopupMessage); +}; + +} // namespace ash + +#endif // ASH_POPUP_MESSAGE_H_ diff --git a/ash/resources/ash_resources.grd b/ash/resources/ash_resources.grd index 38cbdeb..4e00cc5 100644 --- a/ash/resources/ash_resources.grd +++ b/ash/resources/ash_resources.grd @@ -47,6 +47,8 @@ <structure type="chrome_scaled_image" name="IDR_AURA_CROS_DEFAULT_THROBBER" file="cros/common/default_throbber.png" /> + <structure type="chrome_scaled_image" name="IDR_AURA_WARNING_ICON" file="common/alert_small.png" /> + <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_ACCESSIBILITY" file="cros/status/status_accessibility_mode.png" /> <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_ACCESSIBILITY_DARK" file="cros/status/status_accessibility_dark.png" /> <structure type="chrome_scaled_image" name="IDR_AURA_UBER_TRAY_BLUETOOTH" file="cros/status/status_bluetooth.png" /> diff --git a/ash/system/user/tray_user.cc b/ash/system/user/tray_user.cc index 51d0901..5c56e90 100644 --- a/ash/system/user/tray_user.cc +++ b/ash/system/user/tray_user.cc @@ -8,6 +8,7 @@ #include <climits> #include <vector> +#include "ash/popup_message.h" #include "ash/session_state_delegate.h" #include "ash/shell.h" #include "ash/shell_delegate.h" @@ -105,6 +106,9 @@ const int kPublicAccountLogoutButtonBorderImagesHovered[] = { IDR_AURA_TRAY_POPUP_PUBLIC_ACCOUNT_LOGOUT_BUTTON_BORDER, }; +// Offsetting the popup message relative to the tray menu. +const int kPopupMessageOffset = 25; + } // namespace namespace ash { @@ -262,6 +266,7 @@ class UserView : public views::View, MultiProfileIndex multiprofile_index_; views::View* user_card_; views::View* logout_button_; + scoped_ptr<ash::PopupMessage> popup_message_; scoped_ptr<views::Widget> add_menu_option_; // The mouse watcher which takes care of out of window hover events. @@ -280,6 +285,9 @@ class AddUserView : public views::CustomButton, AddUserView(UserCard* owner, views::ButtonListener* listener); virtual ~AddUserView(); + // Get the anchor view for a message. + views::View* anchor() { return anchor_; } + // Overridden from views::ButtonListener. virtual void ButtonPressed(views::Button* sender, const ui::Event& event) OVERRIDE; @@ -302,6 +310,9 @@ class AddUserView : public views::CustomButton, // This is the owner view of this item. UserCard* owner_; + // The anchor view for targetted bubble messages. + views::View* anchor_; + DISALLOW_COPY_AND_ASSIGN(AddUserView); }; @@ -591,8 +602,7 @@ UserView::UserView(SystemTrayItem* owner, UserView::~UserView() {} void UserView::MouseMovedOutOfHost() { - // Make sure that the MouseWatcher does not outlive our add menu option. - DCHECK(!add_menu_option_.get()); + popup_message_.reset(); mouse_watcher_.reset(); add_menu_option_.reset(); } @@ -832,6 +842,7 @@ void UserView::AddLoggedInPublicModeUserCardContent(SystemTrayItem* owner) { void UserView::ToggleAddUserMenuOption() { if (add_menu_option_.get()) { + popup_message_.reset(); mouse_watcher_.reset(); add_menu_option_.reset(); return; @@ -865,14 +876,21 @@ void UserView::ToggleAddUserMenuOption() { add_menu_option_->SetBounds(bounds); // Show the content. - add_menu_option_->SetContentsView(new AddUserView( - static_cast<UserCard*>(user_card_), this)); + AddUserView* add_user_view = new AddUserView( + static_cast<UserCard*>(user_card_), this); + add_menu_option_->SetContentsView(add_user_view); add_menu_option_->SetAlwaysOnTop(true); add_menu_option_->Show(); if (cannot_add_more_users) { - // TODO(skuhne): Use IDS_ASH_STATUS_TRAY_CAPTION_CANNOT_ADD_USER and - // IDS_ASH_STATUS_TRAY_MESSAGE_CANNOT_ADD_USER when showing the error - // message that no more users can be added. + ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); + popup_message_.reset(new PopupMessage( + bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_CAPTION_CANNOT_ADD_USER), + bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_MESSAGE_CANNOT_ADD_USER), + PopupMessage::ICON_WARNING, + add_user_view->anchor(), + views::BubbleBorder::TOP_LEFT, + gfx::Size(parent()->bounds().width() - kPopupMessageOffset, 0), + 2 * kPopupMessageOffset)); } // Find the screen area which encloses both elements and sets then a mouse // watcher which will close the "menu". @@ -888,7 +906,8 @@ AddUserView::AddUserView(UserCard* owner, views::ButtonListener* listener) : CustomButton(listener_), add_user_(NULL), listener_(listener), - owner_(owner) { + owner_(owner), + anchor_(NULL) { AddContent(); owner_->ForceBorderVisible(true); } @@ -942,13 +961,13 @@ void AddUserView::AddContent() { views::BoxLayout::kHorizontal, 0, 0 , kTrayPopupPaddingBetweenItems)); AddChildViewAt(add_user_, 0); - // Add the [+] icon. + // Add the [+] icon which is also the anchor for messages. ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); RoundedImageView* icon = new RoundedImageView(kProfileRoundedCornerRadius, true); - // TODO(skuhne): Add the resource and load the proper icon. + anchor_ = icon; icon->SetImage(*ui::ResourceBundle::GetSharedInstance(). - GetImageNamed(IDR_AURA_UBER_TRAY_GUEST_ICON).ToImageSkia(), + GetImageNamed(IDR_AURA_UBER_TRAY_ADD_MULTIPROFILE_USER).ToImageSkia(), gfx::Size(kUserIconSize, kUserIconSize)); add_user_->AddChildView(icon); @@ -1010,7 +1029,7 @@ views::View* TrayUser::CreateDefaultView(user::LoginStatus status) { // Do not show more UserView's then there are logged in users. if (multiprofile_index_ >= logged_in_users) - return NULL;; + return NULL; user_ = new tray::UserView(this, status, multiprofile_index_); return user_; |