1f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved. 2f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 3f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// found in the LICENSE file. 4f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 5f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "chrome/browser/ui/views/autofill/info_bubble.h" 6f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 7f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "base/i18n/rtl.h" 8f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "ui/gfx/point.h" 9f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "ui/gfx/rect.h" 10f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "ui/gfx/size.h" 11f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "ui/gfx/text_constants.h" 12f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "ui/views/bubble/bubble_border.h" 13f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "ui/views/bubble/bubble_frame_view.h" 14f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "ui/views/controls/combobox/combobox.h" 15f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "ui/views/controls/label.h" 16f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "ui/views/layout/fill_layout.h" 17f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "ui/views/layout/layout_constants.h" 18f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "ui/views/widget/widget.h" 19f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 20f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)namespace autofill { 21f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 22f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)namespace { 23f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 24f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// The visible width of bubble borders (differs from the actual width) in px. 25f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)const int kBubbleBorderVisibleWidth = 1; 26f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 27f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// The margin between the content of the error bubble and its border. 28f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)const int kInfoBubbleHorizontalMargin = 14; 29f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)const int kInfoBubbleVerticalMargin = 12; 30f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 31f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)} // namespace 32f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)class InfoBubbleFrame : public views::BubbleFrameView { 345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) public: 355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) explicit InfoBubbleFrame(const gfx::Insets& content_margins) 365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) : views::BubbleFrameView(content_margins) {} 375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) virtual ~InfoBubbleFrame() {} 385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) virtual gfx::Rect GetAvailableScreenBounds(const gfx::Rect& rect) OVERRIDE { 405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return available_bounds_; 415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) void set_available_bounds(const gfx::Rect& available_bounds) { 445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) available_bounds_ = available_bounds; 455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) private: 485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Bounds that this frame should try to keep bubbles within (screen coords). 495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) gfx::Rect available_bounds_; 505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) DISALLOW_COPY_AND_ASSIGN(InfoBubbleFrame); 525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}; 535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 54a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)InfoBubble::InfoBubble(views::View* anchor, 55a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) const base::string16& message) 56f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) : anchor_(anchor), 575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) frame_(NULL), 58f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) align_to_anchor_edge_(false), 59f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) preferred_width_(233), 60f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) show_above_anchor_(false) { 61f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) DCHECK(anchor_); 62f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) SetAnchorView(anchor_); 63f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 64f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) set_margins(gfx::Insets(kInfoBubbleVerticalMargin, 65f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) kInfoBubbleHorizontalMargin, 66f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) kInfoBubbleVerticalMargin, 67f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) kInfoBubbleHorizontalMargin)); 685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) set_can_activate(false); 69f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 70f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) SetLayoutManager(new views::FillLayout); 71f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) views::Label* label = new views::Label(message); 72f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) label->SetHorizontalAlignment(gfx::ALIGN_LEFT); 73f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) label->SetMultiLine(true); 74f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) AddChildView(label); 75f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)} 76f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 77f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)InfoBubble::~InfoBubble() {} 78f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 79f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void InfoBubble::Show() { 80f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // TODO(dbeam): currently we assume that combobox menus always show downward 81f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // (which isn't true). If the invalid combobox is low enough on the screen, 82f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // its menu will actually show upward and obscure the bubble. Figure out when 83f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // this might happen and adjust |show_above_anchor_| accordingly. This is not 84f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // that big of deal because it rarely happens in practice. 855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (show_above_anchor_) 865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) set_arrow(views::BubbleBorder::vertical_mirror(arrow())); 87f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 88f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) widget_ = views::BubbleDelegateView::CreateBubble(this); 895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (align_to_anchor_edge_) { 915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // The frame adjusts its arrow before the bubble's alignment can be changed. 925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Set the created bubble border back to the original arrow and re-adjust. 935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) frame_->bubble_border()->set_arrow(arrow()); 945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) SetAlignment(views::BubbleBorder::ALIGN_EDGE_TO_ANCHOR_EDGE); 955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 97f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) UpdatePosition(); 98f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)} 99f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 100f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void InfoBubble::Hide() { 101f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) views::Widget* widget = GetWidget(); 102f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if (widget && !widget->IsClosed()) 103f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) widget->Close(); 104f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)} 105f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 106f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void InfoBubble::UpdatePosition() { 107f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if (!widget_) 108f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return; 109f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 110f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if (!anchor_->GetVisibleBounds().IsEmpty()) { 111f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) SizeToContents(); 112f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) widget_->SetVisibilityChangedAnimationsEnabled(true); 113f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) widget_->ShowInactive(); 114f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } else { 115f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) widget_->SetVisibilityChangedAnimationsEnabled(false); 116f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) widget_->Hide(); 117f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) } 118f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)} 119f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 1205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)views::NonClientFrameView* InfoBubble::CreateNonClientFrameView( 1215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) views::Widget* widget) { 1225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) DCHECK(!frame_); 1235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) frame_ = new InfoBubbleFrame(margins()); 1245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) frame_->set_available_bounds(anchor_widget()->GetWindowBoundsInScreen()); 1255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) frame_->SetBubbleBorder(scoped_ptr<views::BubbleBorder>( 1265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) new views::BubbleBorder(arrow(), shadow(), color()))); 1275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return frame_; 1285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 1295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 130cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)gfx::Size InfoBubble::GetPreferredSize() const { 131f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) int pref_width = preferred_width_; 1325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) pref_width -= frame_->GetInsets().width(); 133f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) pref_width -= 2 * kBubbleBorderVisibleWidth; 134f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return gfx::Size(pref_width, GetHeightForWidth(pref_width)); 135f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)} 136f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 1375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void InfoBubble::OnWidgetDestroyed(views::Widget* widget) { 138f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if (widget == widget_) 139f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) widget_ = NULL; 140f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)} 141f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 1425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void InfoBubble::OnWidgetBoundsChanged(views::Widget* widget, 1435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const gfx::Rect& new_bounds) { 1445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) views::BubbleDelegateView::OnWidgetBoundsChanged(widget, new_bounds); 1455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (anchor_widget() == widget) 1465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) frame_->set_available_bounds(widget->GetWindowBoundsInScreen()); 147f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)} 148f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 149f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)} // namespace autofill 150