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. 4 5 #include "chrome/browser/ui/views/autofill/password_generation_popup_view_views.h" 6 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/base/resource/resource_bundle.h" 12 #include "ui/gfx/canvas.h" 13 #include "ui/views/background.h" 14 #include "ui/views/border.h" 15 #include "ui/views/controls/image_view.h" 16 #include "ui/views/controls/label.h" 17 #include "ui/views/controls/styled_label.h" 18 #include "ui/views/layout/box_layout.h" 19 #include "ui/views/widget/widget.h" 20 21 namespace autofill { 22 23 namespace { 24 25 // The amount of whitespace that is present when there is no padding. Used 26 // to get the proper spacing in the help section. 27 const int kHelpVerticalOffset = 5; 28 29 // Wrapper around just the text portions of the generation UI (password and 30 // prompting text). 31 class PasswordTextBox : public views::View { 32 public: 33 PasswordTextBox() {} 34 virtual ~PasswordTextBox() {} 35 36 // |suggestion_text| prompts the user to select the password, 37 // |generated_password| is the generated password, and |font_list| is the font 38 // used for all text in this class. 39 void Init(const base::string16& suggestion_text, 40 const base::string16& generated_password, 41 const gfx::FontList& font_list) { 42 views::BoxLayout* box_layout = new views::BoxLayout( 43 views::BoxLayout::kVertical, 0, 12, 5); 44 box_layout->set_main_axis_alignment( 45 views::BoxLayout::MAIN_AXIS_ALIGNMENT_START); 46 SetLayoutManager(box_layout); 47 48 views::Label* suggestion_label = new views::Label( 49 suggestion_text, font_list.DeriveWithStyle(gfx::Font::BOLD)); 50 suggestion_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); 51 suggestion_label->SetEnabledColor( 52 PasswordGenerationPopupView::kPasswordTextColor); 53 AddChildView(suggestion_label); 54 55 views::Label* password_label = 56 new views::Label(generated_password, font_list); 57 password_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); 58 password_label->SetEnabledColor( 59 PasswordGenerationPopupView::kPasswordTextColor); 60 AddChildView(password_label); 61 } 62 63 // views::View: 64 virtual bool CanProcessEventsWithinSubtree() const OVERRIDE { 65 // Send events to the parent view for handling. 66 return false; 67 } 68 69 private: 70 DISALLOW_COPY_AND_ASSIGN(PasswordTextBox); 71 }; 72 73 } // namespace 74 75 // Class that shows the generated password and associated UI (currently a key 76 // image and some explanatory text). 77 class PasswordGenerationPopupViewViews::PasswordBox : public views::View { 78 public: 79 PasswordBox() {} 80 virtual ~PasswordBox() {} 81 82 // |password| is the generated password, |suggestion| is the text prompting 83 // the user to select the password, and |font_list| is the font used for all 84 // the text. 85 void Init(const base::string16& password, 86 const base::string16& suggestion, 87 const gfx::FontList& font_list) { 88 views::BoxLayout* box_layout = new views::BoxLayout( 89 views::BoxLayout::kHorizontal, 90 PasswordGenerationPopupController::kHorizontalPadding, 91 0, 92 15); 93 box_layout->set_main_axis_alignment( 94 views::BoxLayout::MAIN_AXIS_ALIGNMENT_START); 95 SetLayoutManager(box_layout); 96 97 views::ImageView* key_image = new views::ImageView(); 98 key_image->SetImage( 99 ui::ResourceBundle::GetSharedInstance().GetImageNamed( 100 IDR_GENERATE_PASSWORD_KEY).ToImageSkia()); 101 AddChildView(key_image); 102 103 PasswordTextBox* password_text_box = new PasswordTextBox(); 104 password_text_box->Init(suggestion, password, font_list); 105 AddChildView(password_text_box); 106 } 107 108 // views::View: 109 virtual bool CanProcessEventsWithinSubtree() const OVERRIDE { 110 // Send events to the parent view for handling. 111 return false; 112 } 113 114 private: 115 DISALLOW_COPY_AND_ASSIGN(PasswordBox); 116 }; 117 118 PasswordGenerationPopupViewViews::PasswordGenerationPopupViewViews( 119 PasswordGenerationPopupController* controller, 120 views::Widget* observing_widget) 121 : AutofillPopupBaseView(controller, observing_widget), 122 password_view_(NULL), 123 font_list_(ResourceBundle::GetSharedInstance().GetFontList( 124 ResourceBundle::SmallFont)), 125 controller_(controller) { 126 if (controller_->display_password()) 127 CreatePasswordView(); 128 129 help_label_ = new views::StyledLabel(controller_->HelpText(), this); 130 help_label_->SetBaseFontList(font_list_); 131 help_label_->SetLineHeight(20); 132 views::StyledLabel::RangeStyleInfo default_style; 133 default_style.color = kExplanatoryTextColor; 134 help_label_->SetDefaultStyle(default_style); 135 136 views::StyledLabel::RangeStyleInfo link_style = 137 views::StyledLabel::RangeStyleInfo::CreateForLink(); 138 link_style.disable_line_wrapping = false; 139 help_label_->AddStyleRange(controller_->HelpTextLinkRange(), link_style); 140 141 help_label_->set_background( 142 views::Background::CreateSolidBackground( 143 kExplanatoryTextBackgroundColor)); 144 help_label_->SetBorder(views::Border::CreateEmptyBorder( 145 PasswordGenerationPopupController::kHelpVerticalPadding - 146 kHelpVerticalOffset, 147 PasswordGenerationPopupController::kHorizontalPadding, 148 PasswordGenerationPopupController::kHelpVerticalPadding - 149 kHelpVerticalOffset, 150 PasswordGenerationPopupController::kHorizontalPadding)); 151 AddChildView(help_label_); 152 153 set_background(views::Background::CreateSolidBackground(kPopupBackground)); 154 } 155 156 PasswordGenerationPopupViewViews::~PasswordGenerationPopupViewViews() {} 157 158 void PasswordGenerationPopupViewViews::CreatePasswordView() { 159 if (password_view_) 160 return; 161 162 password_view_ = new PasswordBox(); 163 password_view_->Init(controller_->password(), 164 controller_->SuggestedText(), 165 font_list_); 166 password_view_->SetPosition(gfx::Point(kPopupBorderThickness, 167 kPopupBorderThickness)); 168 password_view_->SizeToPreferredSize(); 169 AddChildView(password_view_); 170 } 171 172 gfx::Size PasswordGenerationPopupViewViews::GetPreferredSizeOfPasswordView() { 173 int height = kPopupBorderThickness; 174 if (controller_->display_password()) { 175 // Add divider height as well. 176 height += 177 PasswordGenerationPopupController::kPopupPasswordSectionHeight + 1; 178 } 179 int width = controller_->GetMinimumWidth(); 180 int popup_width = width - 2 * kPopupBorderThickness; 181 height += help_label_->GetHeightForWidth(popup_width); 182 return gfx::Size(width, height + kPopupBorderThickness); 183 } 184 185 void PasswordGenerationPopupViewViews::Show() { 186 DoShow(); 187 } 188 189 void PasswordGenerationPopupViewViews::Hide() { 190 // The controller is no longer valid after it hides us. 191 controller_ = NULL; 192 193 DoHide(); 194 } 195 196 void PasswordGenerationPopupViewViews::UpdateBoundsAndRedrawPopup() { 197 DoUpdateBoundsAndRedrawPopup(); 198 } 199 200 void PasswordGenerationPopupViewViews::PasswordSelectionUpdated() { 201 if (!password_view_) 202 return; 203 204 password_view_->set_background( 205 views::Background::CreateSolidBackground( 206 controller_->password_selected() ? 207 kHoveredBackgroundColor : 208 kPopupBackground)); 209 } 210 211 void PasswordGenerationPopupViewViews::Layout() { 212 // Need to leave room for the border. 213 int y = kPopupBorderThickness; 214 int popup_width = bounds().width() - 2 * kPopupBorderThickness; 215 if (controller_->display_password()) { 216 // Currently the UI can change from not offering a password to offering 217 // a password (e.g. the user is editing a generated password and deletes 218 // it), but it can't change the other way around. 219 CreatePasswordView(); 220 password_view_->SetBounds( 221 kPopupBorderThickness, 222 y, 223 popup_width, 224 PasswordGenerationPopupController::kPopupPasswordSectionHeight); 225 divider_bounds_ = 226 gfx::Rect(kPopupBorderThickness, password_view_->bounds().bottom(), 227 popup_width, 1); 228 y = divider_bounds_.bottom(); 229 } 230 231 help_label_->SetBounds(kPopupBorderThickness, y, popup_width, 232 help_label_->GetHeightForWidth(popup_width)); 233 } 234 235 void PasswordGenerationPopupViewViews::OnPaint(gfx::Canvas* canvas) { 236 if (!controller_) 237 return; 238 239 // Draw border and background. 240 views::View::OnPaint(canvas); 241 242 // Divider line needs to be drawn after OnPaint() otherwise the background 243 // will overwrite the divider. 244 if (password_view_) 245 canvas->FillRect(divider_bounds_, kDividerColor); 246 } 247 248 void PasswordGenerationPopupViewViews::StyledLabelLinkClicked( 249 const gfx::Range& range, int event_flags) { 250 controller_->OnSavedPasswordsLinkClicked(); 251 } 252 253 bool PasswordGenerationPopupViewViews::IsPointInPasswordBounds( 254 const gfx::Point& point) { 255 if (password_view_) 256 return password_view_->bounds().Contains(point); 257 return false; 258 } 259 260 PasswordGenerationPopupView* PasswordGenerationPopupView::Create( 261 PasswordGenerationPopupController* controller) { 262 views::Widget* observing_widget = 263 views::Widget::GetTopLevelWidgetForNativeView( 264 controller->container_view()); 265 266 // If the top level widget can't be found, cancel the popup since we can't 267 // fully set it up. 268 if (!observing_widget) 269 return NULL; 270 271 return new PasswordGenerationPopupViewViews(controller, observing_widget); 272 } 273 274 } // namespace autofill 275