Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(202)

Unified Diff: chrome/browser/ui/views/autofill/autofill_dialog_views.cc

Issue 12225095: Interactive autofill: Adds footnote view to accept legal documents in the UI. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: sky@ review Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « chrome/browser/ui/views/autofill/autofill_dialog_views.h ('k') | ui/views/window/dialog_client_view.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/browser/ui/views/autofill/autofill_dialog_views.cc
diff --git a/chrome/browser/ui/views/autofill/autofill_dialog_views.cc b/chrome/browser/ui/views/autofill/autofill_dialog_views.cc
index cee04ad7a45ba4cba4a6e2282f9507dba92c9e09..d8ddaf7de68617bce676f825b8dc3f95d0e8118b 100644
--- a/chrome/browser/ui/views/autofill/autofill_dialog_views.cc
+++ b/chrome/browser/ui/views/autofill/autofill_dialog_views.cc
@@ -6,6 +6,8 @@
#include <utility>
+#include "base/i18n/break_iterator.h"
+#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/autofill/wallet/wallet_service_url.h"
#include "chrome/browser/profiles/profile.h"
@@ -52,6 +54,7 @@ const size_t kArrowWidth = 2 * kArrowHeight;
const char kDecoratedTextfieldClassName[] = "autofill/DecoratedTextfield";
const char kNotificationAreaClassName[] = "autofill/NotificationArea";
+const char kFootnoteViewClassName[] = "autofill/FootnoteView";
// Returns a label that describes a details section.
views::Label* CreateDetailsSectionLabel(const string16& text) {
@@ -332,6 +335,7 @@ void AutofillDialogViews::SuggestionView::ShowTextfield(
}
// AutofilDialogViews::AutocheckoutProgressBar ---------------------------------
+
AutofillDialogViews::AutocheckoutProgressBar::AutocheckoutProgressBar() {}
gfx::Size AutofillDialogViews::AutocheckoutProgressBar::GetPreferredSize() {
@@ -339,6 +343,209 @@ gfx::Size AutofillDialogViews::AutocheckoutProgressBar::GetPreferredSize() {
kAutocheckoutProgressBarHeight);
}
+// AutofillDialogViews::FootnoteView -------------------------------------------
+
+AutofillDialogViews::FootnoteView::FootnoteView()
+ : current_width_(0),
+ layout_(NULL),
+ previous_parent_width_(-1),
+ single_column_set_(0) {
+ set_border(views::Border::CreateEmptyBorder(
+ views::kUnrelatedControlVerticalSpacing, 0,
+ views::kUnrelatedControlVerticalSpacing, 0));
+}
+
+AutofillDialogViews::FootnoteView::~FootnoteView() {}
+
+std::string AutofillDialogViews::FootnoteView::GetClassName() const {
+ return kFootnoteViewClassName;
+}
+
+void AutofillDialogViews::FootnoteView::AddLabel(views::Label* label,
+ bool is_link) {
+ FootnoteLabel* footnote_label = new FootnoteLabel();
+ footnote_label->label.reset(label);
+ footnote_label->is_link = is_link;
+ footnote_labels_.push_back(footnote_label);
+ previous_parent_width_ = -1;
+}
+
+int AutofillDialogViews::FootnoteView::IndexOfLink(views::Label* link) {
+ size_t distance = std::distance(
+ links_.begin(), std::find(links_.begin(), links_.end(), link));
+ return distance < links_.size() ? distance : -1;
+}
+
+void AutofillDialogViews::FootnoteView::StartRow() {
+ layout_->StartRow(0, single_column_set_);
+ views::View* row = new views::View();
+ row->SetLayoutManager(
+ new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 0));
+ layout_->AddView(row);
+}
+
+views::View* AutofillDialogViews::FootnoteView::CurrentRow() {
+ return child_at(child_count() - 1);
+}
+
+void AutofillDialogViews::FootnoteView::ResetRows() {
+ ResetLayoutManager();
+ RemoveAllChildViews(true);
+}
+
+void AutofillDialogViews::FootnoteView::ResetLayoutManager() {
+ views::GridLayout* layout = new views::GridLayout(this);
+ SetLayoutManager(layout);
+
+ layout_ = layout;
+ layout_->AddColumnSet(single_column_set_)->AddColumn(
+ views::GridLayout::FILL,
+ views::GridLayout::FILL,
+ 1,
+ views::GridLayout::USE_PREF,
+ 0,
+ 0);
+}
+
+void AutofillDialogViews::FootnoteView::Reset() {
+ ResetRows();
+ ResetText();
+ ResetWidth();
+ StartRow();
+}
+
+void AutofillDialogViews::FootnoteView::AppendText(const string16& text_part) {
+ current_text_.append(text_part);
+}
+
+const string16& AutofillDialogViews::FootnoteView::CurrentText() const {
+ return current_text_;
+}
+
+void AutofillDialogViews::FootnoteView::ResetWidth() {
+ current_width_ = 0;
+}
+
+size_t AutofillDialogViews::FootnoteView::CurrentWidth() const {
+ return current_width_;
+}
+
+void AutofillDialogViews::FootnoteView::AddWidth(size_t width) {
+ current_width_ += width;
+}
+
+void AutofillDialogViews::FootnoteView::ResetText() {
+ current_text_.clear();
+}
+
+int AutofillDialogViews::FootnoteView::ParentWidth() {
+ DCHECK(parent());
+ return parent()->GetPreferredSize().width();
+}
+
+int AutofillDialogViews::FootnoteView::WidthRemaining() {
+ return ParentWidth() - CurrentWidth();
+}
+
+void AutofillDialogViews::FootnoteView::AddCloneWithWidth(views::Label* label,
+ size_t width) {
+ AddWidth(width);
+ CurrentRow()->AddChildView(label);
+}
+
+views::Label* AutofillDialogViews::FootnoteView::CloneLabel(
+ const string16& text, const gfx::Font& font, bool is_link) {
+ views::Label* label = is_link ? new views::Link(text) :
+ new views::Label(text);
+ label->SetFont(font);
+ return label;
+}
+
+void AutofillDialogViews::FootnoteView::TransferListener(
+ views::Label* from_label, views::Label* to_label) {
+ views::Link* from = static_cast<views::Link*>(from_label);
+ views::Link* to = static_cast<views::Link*>(to_label);
+ to->set_listener(const_cast<views::LinkListener*>(from->listener()));
+}
+
+void AutofillDialogViews::FootnoteView::Layout() {
+ views::View::Layout();
+
+ if (!parent() || previous_parent_width_ == ParentWidth())
+ return;
+
+ Reset();
+
+ for (size_t i = 0; i < footnote_labels_.size(); ++i) {
+ views::Label* label = footnote_labels_[i]->label.get();
+ if (!label->visible())
+ continue;
+
+ // The label is smaller than the available space. Add it wholesale.
+ int tw = gfx::Canvas::GetStringWidth(label->text(), label->font());
+ if (tw < WidthRemaining()) {
+ views::Label* clone = CloneLabel(label->text(),
+ label->font(),
+ footnote_labels_[i]->is_link);
+ if (footnote_labels_[i]->is_link) {
+ TransferListener(label, clone);
+ links_.push_back(clone);
+ }
+ AddCloneWithWidth(clone, tw);
+ continue;
+ }
+
+ // Don't wrap links mid-line. This one's too big, so make a new row.
+ if (footnote_labels_[i]->is_link) {
+ StartRow();
+ ResetWidth();
+ links_.push_back(CloneLabel(label->text(), label->font(), true));
+ TransferListener(label, links_.back());
+ AddCloneWithWidth(links_.back(), tw);
+ if (tw > ParentWidth()) {
+ StartRow();
+ ResetWidth();
+ }
+ continue;
+ }
+
+ // Go through the text word by word, seeing if each one fits.
+ DCHECK(CurrentText().empty());
+ base::i18n::BreakIterator iter(label->text(),
+ base::i18n::BreakIterator::BREAK_SPACE);
+ for (bool first_word = true, more_words = iter.Init() && iter.Advance();
+ more_words; ) {
+ int width = 0;
+ // Iterate until no more words or space left.
+ do {
+ string16 text = iter.GetString();
+ if (first_word) {
+ TrimWhitespace(text, TRIM_LEADING, &text);
+ first_word = false;
+ }
+
+ const int w = gfx::Canvas::GetStringWidth(text, label->font());
+ if (width + w > WidthRemaining())
+ break;
+
+ width += w;
+ AppendText(text);
+ } while ((more_words = iter.Advance()));
+
+ // If we didn't run out of words, the space is full. Start a new row.
+ if (more_words) {
+ StartRow();
+ ResetWidth();
+ }
+
+ AddCloneWithWidth(CloneLabel(CurrentText(), label->font(), false), width);
+ ResetText();
+ }
+ }
+
+ previous_parent_width_ = ParentWidth();
+}
+
// AutofillDialogView ----------------------------------------------------------
// static
@@ -365,6 +572,7 @@ AutofillDialogViews::AutofillDialogViews(AutofillDialogController* controller)
save_in_chrome_checkbox_(NULL),
autocheckout_progress_bar_view_(NULL),
autocheckout_progress_bar_(NULL),
+ footnote_view_(NULL),
focus_manager_(NULL) {
DCHECK(controller);
detail_groups_.insert(std::make_pair(SECTION_EMAIL,
@@ -389,6 +597,7 @@ void AutofillDialogViews::Show() {
InitChildViews();
UpdateAccountChooser();
UpdateNotificationArea();
+ UpdateFootnote();
// Ownership of |contents_| is handed off by this call. The
// WebContentsModalDialog will take care of deleting itself after calling
@@ -413,11 +622,7 @@ void AutofillDialogViews::UpdateAccountChooser() {
void AutofillDialogViews::UpdateNotificationArea() {
DCHECK(notification_area_);
notification_area_->SetNotification(controller_->CurrentNotification());
-
- if (GetWidget())
- GetWidget()->SetSize(GetWidget()->non_client_view()->GetPreferredSize());
-
- contents_->Layout();
+ ContentsResized();
}
void AutofillDialogViews::UpdateSection(DialogSection section) {
@@ -484,6 +689,11 @@ void AutofillDialogViews::UpdateProgressBar(double value) {
autocheckout_progress_bar_->SetValue(value);
}
+void AutofillDialogViews::UpdateFootnote() {
+ PopulateFootnoteLinks();
+ ContentsResized();
+}
+
string16 AutofillDialogViews::GetWindowTitle() const {
return controller_->DialogTitle();
}
@@ -525,8 +735,7 @@ views::View* AutofillDialogViews::GetExtraView() {
}
views::View* AutofillDialogViews::GetFootnoteView() {
- // TODO(estade): add a view to contain the terms of service.
- return NULL;
+ return footnote_view_;
}
bool AutofillDialogViews::Cancel() {
@@ -607,22 +816,22 @@ void AutofillDialogViews::OnDidChangeFocus(
views::View* focused_now) {}
void AutofillDialogViews::LinkClicked(views::Link* source, int event_flags) {
- // Sign in link.
if (source == account_chooser_link_) {
if (controller_->SignedInState() != SIGNED_IN) {
DCHECK(controller_->CanPayWithWallet());
controller_->StartSignInFlow();
}
// TODO(dbeam): handle other clicks on the account chooser (i.e. combobox).
- return;
- }
-
- // Edit links.
- for (DetailGroupMap::iterator iter = detail_groups_.begin();
- iter != detail_groups_.end(); ++iter) {
- if (iter->second.suggested_info->Contains(source)) {
- controller_->EditClickedForSection(iter->first);
- return;
+ } else if (footnote_view_->Contains(source)) {
+ controller_->LegalDocumentLinkClicked(footnote_view_->IndexOfLink(source));
+ } else {
+ // Edit links.
+ for (DetailGroupMap::iterator iter = detail_groups_.begin();
+ iter != detail_groups_.end(); ++iter) {
+ if (iter->second.suggested_info->Contains(source)) {
+ controller_->EditClickedForSection(iter->first);
+ return;
+ }
}
}
}
@@ -656,19 +865,9 @@ void AutofillDialogViews::InitChildViews() {
new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 0));
contents_->AddChildView(CreateMainContainer());
contents_->AddChildView(CreateSignInContainer());
-}
-views::View* AutofillDialogViews::CreateSignInContainer() {
- sign_in_container_ = new views::View();
- sign_in_container_->SetLayoutManager(
- new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0));
- sign_in_container_->SetVisible(false);
- sign_in_webview_ = new views::WebView(controller_->profile());
- cancel_sign_in_ = new views::TextButton(this,
- controller_->CancelSignInText());
- sign_in_container_->AddChildView(cancel_sign_in_);
- sign_in_container_->AddChildView(sign_in_webview_);
- return sign_in_container_;
+ // |footnote_view_| is added to View hierarchy by |GetFootnoteView()|.
+ footnote_view_ = new FootnoteView();
}
views::View* AutofillDialogViews::CreateMainContainer() {
@@ -705,6 +904,37 @@ views::View* AutofillDialogViews::CreateMainContainer() {
return main_container_;
}
+views::View* AutofillDialogViews::CreateSignInContainer() {
+ sign_in_container_ = new views::View();
+ sign_in_container_->SetLayoutManager(
+ new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0));
+ sign_in_container_->SetVisible(false);
+ sign_in_webview_ = new views::WebView(controller_->profile());
+ cancel_sign_in_ = new views::TextButton(this,
+ controller_->CancelSignInText());
+ sign_in_container_->AddChildView(cancel_sign_in_);
+ sign_in_container_->AddChildView(sign_in_webview_);
+ return sign_in_container_;
+}
+
+void AutofillDialogViews::PopulateFootnoteLinks() {
+ const std::vector<string16>& link_parts = controller_->FootnoteLinkParts();
+ for (size_t i = 0; i < link_parts.size(); ++i) {
+ if (i % 2 == 0) {
+ // Text between links.
+ footnote_view_->AddLabel(new views::Label(link_parts[i]), false);
+ } else {
+ // Link to a legal document (i.e. Terms Of Service, Privacy Policy).
+ views::Link* link = new views::Link(link_parts[i]);
+ link->SetEnabledColor(SkColorSetRGB(0x64, 0x64, 0x64));
+ link->set_listener(this);
+ footnote_view_->AddLabel(link, true);
+ }
+ }
+ footnote_view_->Layout();
+ ContentsResized();
+}
+
views::View* AutofillDialogViews::CreateDetailsContainer() {
views::View* view = new views::View();
// A box layout is used because it respects widget visibility.
@@ -738,7 +968,7 @@ views::View* AutofillDialogViews::CreateInputsContainer(DialogSection section) {
views::GridLayout* layout = new views::GridLayout(inputs_container);
inputs_container->SetLayoutManager(layout);
- int kColumnSetId = 0;
+ const int kColumnSetId = 0;
views::ColumnSet* column_set = layout->AddColumnSet(kColumnSetId);
column_set->AddColumn(views::GridLayout::FILL,
views::GridLayout::LEADING,
@@ -892,8 +1122,7 @@ void AutofillDialogViews::UpdateDetailsGroupState(const DetailsGroup& group) {
if (group.container)
group.container->SetForwardMouseEvents(show_suggestions);
- if (GetWidget())
- GetWidget()->SetSize(GetWidget()->non_client_view()->GetPreferredSize());
+ ContentsResized();
}
bool AutofillDialogViews::AtLeastOneSectionIsEditing() {
@@ -981,6 +1210,11 @@ void AutofillDialogViews::TextfieldEditedOrActivated(
decorated->SetInvalid(!controller_->InputIsValid(type, textfield->text()));
}
+void AutofillDialogViews::ContentsResized() {
+ if (GetWidget())
+ GetWidget()->SetSize(GetWidget()->non_client_view()->GetPreferredSize());
+}
+
AutofillDialogViews::DetailsGroup* AutofillDialogViews::GroupForSection(
DialogSection section) {
return &detail_groups_.find(section)->second;
« no previous file with comments | « chrome/browser/ui/views/autofill/autofill_dialog_views.h ('k') | ui/views/window/dialog_client_view.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698