1 // Copyright 2014 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.
5 #include "chrome/browser/ui/views/autofill/password_generation_popup_view_views.h"
7 #include "base/strings/string16.h"
8 #include "chrome/browser/ui/autofill/password_generation_popup_controller.h"
9 #include "chrome/browser/ui/autofill/popup_constants.h"
10 #include "grit/theme_resources.h"
11 #include "ui/accessibility/ax_view_state.h"
12 #include "ui/base/resource/resource_bundle.h"
13 #include "ui/gfx/canvas.h"
14 #include "ui/views/background.h"
15 #include "ui/views/border.h"
16 #include "ui/views/controls/image_view.h"
17 #include "ui/views/controls/label.h"
18 #include "ui/views/controls/styled_label.h"
19 #include "ui/views/layout/box_layout.h"
20 #include "ui/views/widget/widget.h"
26 // The amount of whitespace that is present when there is no padding. Used
27 // to get the proper spacing in the help section.
28 const int kHelpVerticalOffset = 5;
30 // Wrapper around just the text portions of the generation UI (password and
32 class PasswordTextBox : public views::View {
35 ~PasswordTextBox() override {}
37 // |suggestion_text| prompts the user to select the password,
38 // |generated_password| is the generated password, and |font_list| is the font
39 // used for all text in this class.
40 void Init(const base::string16& suggestion_text,
41 const base::string16& generated_password,
42 const gfx::FontList& font_list) {
43 views::BoxLayout* box_layout = new views::BoxLayout(
44 views::BoxLayout::kVertical, 0, 12, 5);
45 box_layout->set_main_axis_alignment(
46 views::BoxLayout::MAIN_AXIS_ALIGNMENT_START);
47 SetLayoutManager(box_layout);
49 views::Label* suggestion_label = new views::Label(
50 suggestion_text, font_list.DeriveWithStyle(gfx::Font::BOLD));
51 suggestion_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
52 suggestion_label->SetEnabledColor(
53 PasswordGenerationPopupView::kPasswordTextColor);
54 AddChildView(suggestion_label);
56 views::Label* password_label =
57 new views::Label(generated_password, font_list);
58 password_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
59 password_label->SetEnabledColor(
60 PasswordGenerationPopupView::kPasswordTextColor);
61 AddChildView(password_label);
65 bool CanProcessEventsWithinSubtree() const override {
66 // Send events to the parent view for handling.
71 DISALLOW_COPY_AND_ASSIGN(PasswordTextBox);
76 // Class that shows the generated password and associated UI (currently a key
77 // image and some explanatory text).
78 class PasswordGenerationPopupViewViews::PasswordBox : public views::View {
81 ~PasswordBox() override {}
83 // |password| is the generated password, |suggestion| is the text prompting
84 // the user to select the password, and |font_list| is the font used for all
86 void Init(const base::string16& password,
87 const base::string16& suggestion,
88 const base::string16& accessible_name,
89 const gfx::FontList& font_list) {
90 accessible_name_ = accessible_name;
92 views::BoxLayout* box_layout = new views::BoxLayout(
93 views::BoxLayout::kHorizontal,
94 PasswordGenerationPopupController::kHorizontalPadding,
97 box_layout->set_main_axis_alignment(
98 views::BoxLayout::MAIN_AXIS_ALIGNMENT_START);
99 SetLayoutManager(box_layout);
101 views::ImageView* key_image = new views::ImageView();
103 ui::ResourceBundle::GetSharedInstance().GetImageNamed(
104 IDR_GENERATE_PASSWORD_KEY).ToImageSkia());
105 AddChildView(key_image);
107 PasswordTextBox* password_text_box = new PasswordTextBox();
108 password_text_box->Init(suggestion, password, font_list);
109 AddChildView(password_text_box);
111 NotifyAccessibilityEvent(ui::AX_EVENT_ALERT, true);
115 bool CanProcessEventsWithinSubtree() const override {
116 // Send events to the parent view for handling.
120 void GetAccessibleState(ui::AXViewState* state) override {
121 state->role = ui::AX_ROLE_ALERT;
122 state->name = accessible_name_;
126 base::string16 accessible_name_;
128 DISALLOW_COPY_AND_ASSIGN(PasswordBox);
131 PasswordGenerationPopupViewViews::PasswordGenerationPopupViewViews(
132 PasswordGenerationPopupController* controller,
133 views::Widget* observing_widget)
134 : AutofillPopupBaseView(controller, observing_widget),
135 password_view_(NULL),
136 font_list_(ResourceBundle::GetSharedInstance().GetFontList(
137 ResourceBundle::SmallFont)),
138 controller_(controller) {
139 if (controller_->display_password())
140 CreatePasswordView();
142 help_label_ = new views::StyledLabel(controller_->HelpText(), this);
143 help_label_->SetBaseFontList(font_list_);
144 help_label_->SetLineHeight(20);
145 views::StyledLabel::RangeStyleInfo default_style;
146 default_style.color = kExplanatoryTextColor;
147 help_label_->SetDefaultStyle(default_style);
149 views::StyledLabel::RangeStyleInfo link_style =
150 views::StyledLabel::RangeStyleInfo::CreateForLink();
151 link_style.disable_line_wrapping = false;
152 help_label_->AddStyleRange(controller_->HelpTextLinkRange(), link_style);
154 help_label_->set_background(
155 views::Background::CreateSolidBackground(
156 kExplanatoryTextBackgroundColor));
157 help_label_->SetBorder(views::Border::CreateEmptyBorder(
158 PasswordGenerationPopupController::kHelpVerticalPadding -
160 PasswordGenerationPopupController::kHorizontalPadding,
161 PasswordGenerationPopupController::kHelpVerticalPadding -
163 PasswordGenerationPopupController::kHorizontalPadding));
164 AddChildView(help_label_);
166 set_background(views::Background::CreateSolidBackground(kPopupBackground));
169 PasswordGenerationPopupViewViews::~PasswordGenerationPopupViewViews() {}
171 void PasswordGenerationPopupViewViews::CreatePasswordView() {
175 password_view_ = new PasswordBox();
176 password_view_->Init(controller_->password(),
177 controller_->SuggestedText(),
178 controller_->AccessibleName(),
180 password_view_->SetPosition(gfx::Point(kPopupBorderThickness,
181 kPopupBorderThickness));
182 password_view_->SizeToPreferredSize();
183 AddChildView(password_view_);
186 gfx::Size PasswordGenerationPopupViewViews::GetPreferredSizeOfPasswordView() {
187 int height = kPopupBorderThickness;
188 if (controller_->display_password()) {
189 // Add divider height as well.
191 PasswordGenerationPopupController::kPopupPasswordSectionHeight + 1;
193 int width = controller_->GetMinimumWidth();
194 int popup_width = width - 2 * kPopupBorderThickness;
195 height += help_label_->GetHeightForWidth(popup_width);
196 return gfx::Size(width, height + kPopupBorderThickness);
199 void PasswordGenerationPopupViewViews::Show() {
203 void PasswordGenerationPopupViewViews::Hide() {
204 // The controller is no longer valid after it hides us.
210 void PasswordGenerationPopupViewViews::UpdateBoundsAndRedrawPopup() {
211 DoUpdateBoundsAndRedrawPopup();
214 void PasswordGenerationPopupViewViews::PasswordSelectionUpdated() {
218 password_view_->set_background(
219 views::Background::CreateSolidBackground(
220 controller_->password_selected() ?
221 kHoveredBackgroundColor :
225 void PasswordGenerationPopupViewViews::Layout() {
226 // Need to leave room for the border.
227 int y = kPopupBorderThickness;
228 int popup_width = bounds().width() - 2 * kPopupBorderThickness;
229 if (controller_->display_password()) {
230 // Currently the UI can change from not offering a password to offering
231 // a password (e.g. the user is editing a generated password and deletes
232 // it), but it can't change the other way around.
233 CreatePasswordView();
234 password_view_->SetBounds(
235 kPopupBorderThickness,
238 PasswordGenerationPopupController::kPopupPasswordSectionHeight);
240 gfx::Rect(kPopupBorderThickness, password_view_->bounds().bottom(),
242 y = divider_bounds_.bottom();
245 help_label_->SetBounds(kPopupBorderThickness, y, popup_width,
246 help_label_->GetHeightForWidth(popup_width));
249 void PasswordGenerationPopupViewViews::OnPaint(gfx::Canvas* canvas) {
253 // Draw border and background.
254 views::View::OnPaint(canvas);
256 // Divider line needs to be drawn after OnPaint() otherwise the background
257 // will overwrite the divider.
259 canvas->FillRect(divider_bounds_, kDividerColor);
262 void PasswordGenerationPopupViewViews::StyledLabelLinkClicked(
263 const gfx::Range& range, int event_flags) {
264 controller_->OnSavedPasswordsLinkClicked();
267 bool PasswordGenerationPopupViewViews::IsPointInPasswordBounds(
268 const gfx::Point& point) {
270 return password_view_->bounds().Contains(point);
274 PasswordGenerationPopupView* PasswordGenerationPopupView::Create(
275 PasswordGenerationPopupController* controller) {
276 views::Widget* observing_widget =
277 views::Widget::GetTopLevelWidgetForNativeView(
278 controller->container_view());
280 // If the top level widget can't be found, cancel the popup since we can't
282 if (!observing_widget)
285 return new PasswordGenerationPopupViewViews(controller, observing_widget);
288 } // namespace autofill