| // Copyright 2019 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef UI_VIEWS_CONTROLS_EDITABLE_COMBOBOX_EDITABLE_COMBOBOX_H_ |
| #define UI_VIEWS_CONTROLS_EDITABLE_COMBOBOX_EDITABLE_COMBOBOX_H_ |
| |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| #include "base/functional/callback.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/scoped_observation.h" |
| #include "build/build_config.h" |
| #include "ui/base/metadata/metadata_header_macros.h" |
| #include "ui/base/models/image_model.h" |
| #include "ui/base/ui_base_types.h" |
| #include "ui/views/controls/textfield/textfield_controller.h" |
| #include "ui/views/layout/animating_layout_manager.h" |
| #include "ui/views/layout/box_layout_view.h" |
| #include "ui/views/style/typography.h" |
| #include "ui/views/view.h" |
| #include "ui/views/view_observer.h" |
| #include "ui/views/views_export.h" |
| |
| namespace gfx { |
| class FontList; |
| class Range; |
| } // namespace gfx |
| |
| namespace ui { |
| class ComboboxModel; |
| class Event; |
| class MenuModel; |
| } // namespace ui |
| |
| namespace views { |
| class Button; |
| class EditableComboboxMenuModel; |
| class EditableComboboxPreTargetHandler; |
| class MenuRunner; |
| class Textfield; |
| |
| namespace test { |
| class InteractionTestUtilSimulatorViews; |
| } // namespace test |
| |
| // Textfield that also shows a drop-down list with suggestions. |
| class VIEWS_EXPORT EditableCombobox |
| : public View, |
| public TextfieldController, |
| public ViewObserver, |
| public views::AnimatingLayoutManager::Observer { |
| public: |
| METADATA_HEADER(EditableCombobox); |
| |
| // A strategy that can be used to customize the display of the drop-down menu. |
| // It is only intended to be used by classes that extend `EditableCombobox`. |
| class MenuDecorationStrategy { |
| public: |
| virtual ~MenuDecorationStrategy() = default; |
| |
| virtual std::u16string DecorateItemText(std::u16string text) const; |
| }; |
| |
| static constexpr int kDefaultTextContext = style::CONTEXT_BUTTON; |
| static constexpr int kDefaultTextStyle = style::STYLE_PRIMARY; |
| |
| EditableCombobox(); |
| |
| // |combobox_model|: The ComboboxModel that gives us the items to show in the |
| // menu. |
| // |filter_on_edit|: Whether to only show the items that are case-insensitive |
| // completions of the current textfield content. |
| // |show_on_empty|: Whether to show the drop-down list when there is no |
| // textfield content. |
| // |text_context| and |text_style|: Together these indicate the font to use. |
| // |display_arrow|: Whether to display an arrow in the combobox to indicate |
| // that there is a drop-down list. |
| explicit EditableCombobox(std::unique_ptr<ui::ComboboxModel> combobox_model, |
| bool filter_on_edit = false, |
| bool show_on_empty = true, |
| int text_context = kDefaultTextContext, |
| int text_style = kDefaultTextStyle, |
| bool display_arrow = true); |
| |
| EditableCombobox(const EditableCombobox&) = delete; |
| EditableCombobox& operator=(const EditableCombobox&) = delete; |
| |
| ~EditableCombobox() override; |
| |
| void SetModel(std::unique_ptr<ui::ComboboxModel> model); |
| |
| const std::u16string& GetText() const; |
| void SetText(const std::u16string& text); |
| |
| std::u16string GetPlaceholderText() const; |
| void SetPlaceholderText(const std::u16string& text); |
| |
| const gfx::FontList& GetFontList() const; |
| |
| void SetCallback(base::RepeatingClosure callback) { |
| content_changed_callback_ = std::move(callback); |
| } |
| |
| // Selects the specified logical text range for the textfield. |
| void SelectRange(const gfx::Range& range); |
| |
| protected: |
| // Sets the menu decoration strategy. Setting it triggers an update to the |
| // menu. |
| void SetMenuDecorationStrategy( |
| std::unique_ptr<MenuDecorationStrategy> strategy); |
| |
| // Forces an update of the drop-down menu. |
| void UpdateMenu(); |
| |
| // Adds `view` to the set of controls. The ordering is such that views are |
| // added to the front (i.e. to the left in LTR set-ups). |
| template <typename T> |
| T* AddControlElement(std::unique_ptr<T> view) { |
| T* raw_view = |
| control_elements_container_->AddChildViewAt(std::move(view), 0); |
| UpdateTextfieldInsets(); |
| return raw_view; |
| } |
| |
| Textfield& GetTextfield() { return *textfield_; } |
| |
| Button* GetArrowButtonForTesting() { return arrow_; } |
| |
| private: |
| friend class EditableComboboxTest; |
| friend class EditablePasswordComboboxTest; |
| friend class test::InteractionTestUtilSimulatorViews; |
| FRIEND_TEST_ALL_PREFIXES(EditableComboboxTest, AccessibleNameAndRole); |
| |
| class EditableComboboxMenuModel; |
| class EditableComboboxPreTargetHandler; |
| |
| void CloseMenu(); |
| |
| // Called when an item is selected from the menu. |
| void OnItemSelected(size_t index); |
| |
| // Notifies listener of new content and updates the menu items to show. |
| void HandleNewContent(const std::u16string& new_content); |
| |
| // Toggles the dropdown menu in response to |event|. |
| void ArrowButtonPressed(const ui::Event& event); |
| |
| // Shows the drop-down menu. |
| void ShowDropDownMenu(ui::MenuSourceType source_type = ui::MENU_SOURCE_NONE); |
| |
| // Recalculates the extra insets of the textfield based on the size of the |
| // controls container. |
| void UpdateTextfieldInsets(); |
| |
| // These are for unit tests to get data from private implementation classes. |
| const ui::MenuModel* GetMenuModelForTesting() const; |
| std::u16string GetItemTextForTesting(size_t index) const; |
| |
| // Returns the underlying combobox model. Used only by |
| // `ui::test::InteractionTestUtil`. |
| const ui::ComboboxModel* GetComboboxModel() const; |
| |
| // Overridden from View: |
| void Layout() override; |
| void GetAccessibleNodeData(ui::AXNodeData* node_data) override; |
| void RequestFocus() override; |
| bool GetNeedsNotificationWhenVisibleBoundsChange() const override; |
| void OnVisibleBoundsChanged() override; |
| void OnAccessibleNameChanged(const std::u16string& new_name) override; |
| |
| // Overridden from TextfieldController: |
| void ContentsChanged(Textfield* sender, |
| const std::u16string& new_contents) override; |
| bool HandleKeyEvent(Textfield* sender, |
| const ui::KeyEvent& key_event) override; |
| |
| // Overridden from ViewObserver: |
| void OnViewBlurred(View* observed_view) override; |
| |
| // Overridden from views::AnimatingLayoutManager::Observer: |
| void OnLayoutIsAnimatingChanged(views::AnimatingLayoutManager* source, |
| bool is_animating) override; |
| |
| bool ShouldApplyInkDropEffects(); |
| |
| raw_ptr<Textfield> textfield_; |
| raw_ptr<BoxLayoutView> control_elements_container_ = nullptr; |
| raw_ptr<Button> arrow_ = nullptr; |
| |
| // The EditableComboboxMenuModel used by |menu_runner_|. |
| std::unique_ptr<EditableComboboxMenuModel> menu_model_; |
| |
| // Pre-target handler that closes the menu when press events happen in the |
| // root view (outside of the open menu's boundaries) but not inside the |
| // textfield. |
| std::unique_ptr<EditableComboboxPreTargetHandler> pre_target_handler_; |
| |
| // Typography context for the text written in the textfield and the options |
| // shown in the drop-down menu. |
| const int text_context_; |
| |
| // Typography style for the text written in the textfield and the options |
| // shown in the drop-down menu. |
| const int text_style_; |
| |
| // Whether to adapt the items shown to the textfield content. |
| const bool filter_on_edit_; |
| |
| // Whether to show options when the textfield is empty. |
| const bool show_on_empty_; |
| |
| // Set while the drop-down is showing. |
| std::unique_ptr<MenuRunner> menu_runner_; |
| |
| base::RepeatingClosure content_changed_callback_; |
| |
| bool dropdown_blocked_for_animation_ = false; |
| |
| base::ScopedObservation<View, ViewObserver> observation_{this}; |
| }; |
| |
| } // namespace views |
| |
| #endif // UI_VIEWS_CONTROLS_EDITABLE_COMBOBOX_EDITABLE_COMBOBOX_H_ |