| // Copyright 2019 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 "chrome/browser/autofill/autofill_keyboard_accessory_adapter.h" |
| |
| #include <numeric> |
| #include <vector> |
| |
| #include "base/bind.h" |
| #include "base/callback.h" |
| #include "base/memory/weak_ptr.h" |
| #include "chrome/browser/ui/autofill/autofill_popup_controller.h" |
| #include "chrome/browser/ui/autofill/autofill_popup_layout_model.h" |
| #include "components/autofill/core/browser/ui/popup_item_ids.h" |
| #include "components/autofill/core/browser/ui/suggestion.h" |
| |
| namespace autofill { |
| |
| AutofillKeyboardAccessoryAdapter::AutofillKeyboardAccessoryAdapter( |
| AutofillPopupController* controller, |
| unsigned int animation_duration_millis, |
| bool should_limit_label_width) |
| : controller_(controller), |
| animation_duration_millis_(animation_duration_millis), |
| should_limit_label_width_(should_limit_label_width), |
| weak_ptr_factory_(this) {} |
| |
| AutofillKeyboardAccessoryAdapter::~AutofillKeyboardAccessoryAdapter() = default; |
| |
| // AutofillPopupView implementation. |
| |
| void AutofillKeyboardAccessoryAdapter::Show() { |
| DCHECK(view_) << "Show called before a View was set!"; |
| view_->Initialize(animation_duration_millis_, should_limit_label_width_); |
| OnSuggestionsChanged(); |
| } |
| |
| void AutofillKeyboardAccessoryAdapter::Hide() { |
| DCHECK(view_) << "Hide called before a View was set!"; |
| view_->Hide(); |
| } |
| |
| void AutofillKeyboardAccessoryAdapter::OnSelectedRowChanged( |
| base::Optional<int> previous_row_selection, |
| base::Optional<int> current_row_selection) {} |
| |
| void AutofillKeyboardAccessoryAdapter::OnSuggestionsChanged() { |
| DCHECK(controller_) << "Call OnSuggestionsChanged only from its owner!"; |
| DCHECK(view_) << "OnSuggestionsChanged called before a View was set!"; |
| |
| front_element_ = base::nullopt; |
| for (int i = 0; i < GetLineCount(); ++i) { |
| const Suggestion& suggestion = controller_->GetSuggestionAt(i); |
| if (suggestion.frontend_id != POPUP_ITEM_ID_CLEAR_FORM && |
| suggestion.frontend_id != POPUP_ITEM_ID_CREATE_HINT) |
| continue; |
| DCHECK(!front_element_.has_value()) << "Additional front item at: " << i; |
| front_element_ = base::Optional<int>(i); |
| } |
| |
| view_->Show(); |
| } |
| |
| // AutofillPopupController implementation. |
| |
| void AutofillKeyboardAccessoryAdapter::AcceptSuggestion(int index) { |
| if (controller_) |
| controller_->AcceptSuggestion(OffsetIndexFor(index)); |
| } |
| |
| int AutofillKeyboardAccessoryAdapter::GetLineCount() const { |
| return controller_ ? controller_->GetLineCount() : 0; |
| } |
| |
| const autofill::Suggestion& AutofillKeyboardAccessoryAdapter::GetSuggestionAt( |
| int row) const { |
| DCHECK(controller_) << "Call OnSuggestionsChanged only from its owner!"; |
| return controller_->GetSuggestionAt(OffsetIndexFor(row)); |
| } |
| |
| const base::string16& AutofillKeyboardAccessoryAdapter::GetElidedValueAt( |
| int row) const { |
| DCHECK(controller_) << "Call OnSuggestionsChanged only from its owner!"; |
| return controller_->GetElidedValueAt(OffsetIndexFor(row)); |
| } |
| |
| const base::string16& AutofillKeyboardAccessoryAdapter::GetElidedLabelAt( |
| int row) const { |
| DCHECK(controller_) << "Call OnSuggestionsChanged only from its owner!"; |
| const base::string16& label = |
| controller_->GetElidedLabelAt(OffsetIndexFor(row)); |
| if (label.empty()) |
| return GetSuggestionAt(row).additional_label; |
| return label; |
| } |
| |
| bool AutofillKeyboardAccessoryAdapter::GetRemovalConfirmationText( |
| int index, |
| base::string16* title, |
| base::string16* body) { |
| return controller_ && controller_->GetRemovalConfirmationText( |
| OffsetIndexFor(index), title, body); |
| } |
| |
| bool AutofillKeyboardAccessoryAdapter::RemoveSuggestion(int index) { |
| DCHECK(view_) << "RemoveSuggestion called before a View was set!"; |
| base::string16 title, body; |
| if (!GetRemovalConfirmationText(index, &title, &body)) |
| return false; |
| |
| view_->ConfirmDeletion( |
| title, body, |
| base::BindOnce(&AutofillKeyboardAccessoryAdapter::OnDeletionConfirmed, |
| weak_ptr_factory_.GetWeakPtr(), index)); |
| return true; |
| } |
| |
| void AutofillKeyboardAccessoryAdapter::SetSelectedLine( |
| base::Optional<int> selected_line) { |
| if (!controller_) |
| return; |
| if (!selected_line.has_value()) { |
| controller_->SetSelectedLine(base::nullopt); |
| return; |
| } |
| controller_->SetSelectedLine(OffsetIndexFor(selected_line.value())); |
| } |
| |
| base::Optional<int> AutofillKeyboardAccessoryAdapter::selected_line() const { |
| if (!controller_ || !controller_->selected_line().has_value()) |
| return base::nullopt; |
| for (int i = 0; i < GetLineCount(); ++i) { |
| if (OffsetIndexFor(i) == controller_->selected_line().value()) { |
| return i; |
| } |
| } |
| return base::nullopt; |
| } |
| |
| const AutofillPopupLayoutModel& AutofillKeyboardAccessoryAdapter::layout_model() |
| const { |
| DCHECK(controller_) << "Call OnSuggestionsChanged only from its owner!"; |
| return controller_->layout_model(); |
| } |
| |
| // AutofillPopupViewDelegate implementation |
| |
| void AutofillKeyboardAccessoryAdapter::ViewDestroyed() { |
| if (controller_) |
| controller_->ViewDestroyed(); |
| |
| view_.reset(); |
| } |
| |
| void AutofillKeyboardAccessoryAdapter::SetSelectionAtPoint( |
| const gfx::Point& point) { |
| if (controller_) |
| controller_->SetSelectionAtPoint(point); |
| } |
| |
| bool AutofillKeyboardAccessoryAdapter::AcceptSelectedLine() { |
| return controller_ && controller_->AcceptSelectedLine(); |
| } |
| |
| void AutofillKeyboardAccessoryAdapter::SelectionCleared() { |
| if (controller_) |
| controller_->SelectionCleared(); |
| } |
| |
| bool AutofillKeyboardAccessoryAdapter::HasSelection() const { |
| return controller_ && controller_->HasSelection(); |
| } |
| |
| gfx::Rect AutofillKeyboardAccessoryAdapter::popup_bounds() const { |
| DCHECK(controller_) << "Call OnSuggestionsChanged only from its owner!"; |
| return controller_->popup_bounds(); |
| } |
| |
| gfx::NativeView AutofillKeyboardAccessoryAdapter::container_view() const { |
| DCHECK(controller_) << "Call OnSuggestionsChanged only from its owner!"; |
| return controller_->container_view(); |
| } |
| |
| const gfx::RectF& AutofillKeyboardAccessoryAdapter::element_bounds() const { |
| DCHECK(controller_) << "Call OnSuggestionsChanged only from its owner!"; |
| return controller_->element_bounds(); |
| } |
| |
| bool AutofillKeyboardAccessoryAdapter::IsRTL() const { |
| return controller_ && controller_->IsRTL(); |
| } |
| |
| const std::vector<autofill::Suggestion> |
| AutofillKeyboardAccessoryAdapter::GetSuggestions() { |
| if (!controller_) |
| return std::vector<autofill::Suggestion>(); |
| std::vector<autofill::Suggestion> suggestions = controller_->GetSuggestions(); |
| if (front_element_.has_value()) { |
| std::rotate(suggestions.begin(), |
| suggestions.begin() + front_element_.value(), |
| suggestions.begin() + front_element_.value() + 1); |
| } |
| return suggestions; |
| } |
| |
| void AutofillKeyboardAccessoryAdapter::OnDeletionConfirmed(int index) { |
| if (controller_) |
| controller_->RemoveSuggestion(OffsetIndexFor(index)); |
| } |
| |
| int AutofillKeyboardAccessoryAdapter::OffsetIndexFor(int element_index) const { |
| if (!front_element_.has_value()) |
| return element_index; |
| if (0 == element_index) |
| return front_element_.value(); |
| return element_index - (element_index <= front_element_.value() ? 1 : 0); |
| } |
| |
| } // namespace autofill |