1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
|
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/views/controls/editable_combobox/editable_password_combobox.h"
#include <algorithm>
#include <memory>
#include <string>
#include <utility>
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "ui/base/ime/text_input_type.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/base/models/combobox_model.h"
#include "ui/compositor/layer.h"
#include "ui/gfx/render_text.h"
#include "ui/views/border.h"
#include "ui/views/controls/button/image_button.h"
#include "ui/views/controls/button/image_button_factory.h"
#include "ui/views/controls/combobox/combobox_util.h"
#include "ui/views/controls/editable_combobox/editable_combobox.h"
#include "ui/views/controls/textfield/textfield.h"
#include "ui/views/layout/box_layout_view.h"
#include "ui/views/layout/layout_provider.h"
#include "ui/views/vector_icons.h"
namespace views {
namespace {
constexpr int kEyePaddingWidth = 4;
// Creates the eye-styled icon that serves as a button to toggle the password
// visibility.
std::unique_ptr<ToggleImageButton> CreateEye(
ImageButton::PressedCallback callback) {
auto button = Builder<ToggleImageButton>()
.SetInstallFocusRingOnFocus(true)
.SetRequestFocusOnPress(true)
.SetImageVerticalAlignment(ImageButton::ALIGN_MIDDLE)
.SetImageHorizontalAlignment(ImageButton::ALIGN_CENTER)
.SetCallback(std::move(callback))
.SetBorder(CreateEmptyBorder(kEyePaddingWidth))
.Build();
// Add the outset for the focus ring to match the behavior of the `Arrow`
// element in `EditableCombobox`.
views::FocusRing::Get(button.get())->SetOutsetFocusRingDisabled(false);
SetImageFromVectorIconWithColorId(button.get(), kEyeIcon, ui::kColorIcon,
ui::kColorIconDisabled);
SetToggledImageFromVectorIconWithColorId(
button.get(), kEyeCrossedIcon, ui::kColorIcon, ui::kColorIconDisabled);
ConfigureComboboxButtonInkDrop(button.get());
// We need this so the eye icon is not covered when the combo box view is
// hovered
button->SetPaintToLayer();
button->layer()->SetFillsBoundsOpaquely(false);
return button;
}
class PasswordMenuDecorationStrategy
: public EditableCombobox::MenuDecorationStrategy {
public:
explicit PasswordMenuDecorationStrategy(
const EditablePasswordCombobox* parent)
: parent_(parent) {
DCHECK(parent);
}
std::u16string DecorateItemText(std::u16string text) const override {
return parent_->ArePasswordsRevealed()
? text
: std::u16string(text.length(),
gfx::RenderText::kPasswordReplacementChar);
}
private:
const raw_ptr<const EditablePasswordCombobox> parent_;
};
} // namespace
EditablePasswordCombobox::EditablePasswordCombobox() = default;
EditablePasswordCombobox::EditablePasswordCombobox(
std::unique_ptr<ui::ComboboxModel> combobox_model,
int text_context,
int text_style,
bool display_arrow,
Button::PressedCallback eye_callback)
: EditableCombobox(std::move(combobox_model),
/*filter_on_edit=*/false,
/*show_on_empty=*/true,
text_context,
text_style,
display_arrow) {
// By default, clicking on the eye reveals/hides passwords.
if (!eye_callback) {
eye_callback = base::BindRepeating(
[](views::EditablePasswordCombobox* combobox_ptr) {
combobox_ptr->RevealPasswords(!combobox_ptr->ArePasswordsRevealed());
},
base::Unretained(this));
}
eye_ = AddControlElement(CreateEye(std::move(eye_callback)));
GetTextfield().SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD);
SetMenuDecorationStrategy(
std::make_unique<PasswordMenuDecorationStrategy>(this));
}
EditablePasswordCombobox::~EditablePasswordCombobox() = default;
void EditablePasswordCombobox::SetPasswordIconTooltips(
const std::u16string& tooltip_text,
const std::u16string& toggled_tooltip_text) {
eye_->SetTooltipText(tooltip_text);
eye_->SetToggledTooltipText(toggled_tooltip_text);
// The eye is implemented as a `ToggleImageButton`. Screen readers typically
// announce whether the toggle is selected, and as a result the accessible
// name should not change when selected. The state is conveyed by the
// "selected" or "unselected" status instead.
eye_->SetToggledAccessibleName(tooltip_text);
}
void EditablePasswordCombobox::RevealPasswords(bool revealed) {
if (revealed == are_passwords_revealed_) {
return;
}
are_passwords_revealed_ = revealed;
GetTextfield().SetTextInputType(revealed ? ui::TEXT_INPUT_TYPE_TEXT
: ui::TEXT_INPUT_TYPE_PASSWORD);
eye_->SetToggled(revealed);
UpdateMenu();
}
bool EditablePasswordCombobox::ArePasswordsRevealed() const {
return are_passwords_revealed_;
}
BEGIN_METADATA(EditablePasswordCombobox)
END_METADATA
} // namespace views
|