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 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
|
// 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.
#ifndef ASH_STYLE_COMBOBOX_H_
#define ASH_STYLE_COMBOBOX_H_
#include <memory>
#include "ash/ash_export.h"
#include "base/scoped_observation.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/base/models/combobox_model_observer.h"
#include "ui/views/controls/button/button.h"
#include "ui/views/widget/unique_widget_ptr.h"
namespace ui {
class ComboboxModel;
}
namespace views {
class ImageView;
class Label;
} // namespace views
namespace ash {
// A stylized non-editable combobox driven by `ui::ComboboxModel`.
class ASH_EXPORT Combobox : public views::Button,
public ui::ComboboxModelObserver,
public views::WidgetObserver {
METADATA_HEADER(Combobox, views::Button)
public:
static constexpr gfx::Insets kComboboxBorderInsets =
gfx::Insets::TLBR(4, 10, 4, 4);
// `model` is owned by the combobox when using this constructor.
explicit Combobox(std::unique_ptr<ui::ComboboxModel> model);
// `model` is not owned by the combobox when using this constructor.
explicit Combobox(ui::ComboboxModel* model);
Combobox(const Combobox&) = delete;
Combobox& operator=(const Combobox&) = delete;
~Combobox() override;
// Sets the callback that is invoked when the selected item changes. Note that
// this works same as `views::Combobox::SetCallback`.
void SetSelectionChangedCallback(base::RepeatingClosure callback);
// Gets/Sets the selected index.
std::optional<size_t> GetSelectedIndex() const { return selected_index_; }
void SetSelectedIndex(std::optional<size_t> index);
// Looks for the first occurrence of `value` in `model_`. If found, selects
// the found index and returns true. Otherwise simply noops and returns false.
bool SelectValue(const std::u16string& value);
// Returns whether or not the menu is currently running.
bool IsMenuRunning() const;
gfx::Size GetMenuViewSize() const;
views::View* MenuItemAtIndex(int index) const;
views::View* MenuView() const;
// views::Button:
void SetCallback(PressedCallback callback) override;
void OnBoundsChanged(const gfx::Rect& previous_bounds) override;
void OnBlur() override;
void AddedToWidget() override;
void RemovedFromWidget() override;
void Layout(PassKey) override;
// WidgetObserver:
void OnWidgetBoundsChanged(views::Widget* widget,
const gfx::Rect& bounds) override;
std::u16string GetTextForRow(size_t row) const;
// Test method exposing MenuSelectionAt.
void SelectMenuItemForTest(size_t index);
private:
friend class ComboboxTest;
class ComboboxMenuView;
class ComboboxEventHandler;
// Gets expected menu bounds according to combox location.
gfx::Rect GetExpectedMenuBounds() const;
// Called when there has been a selection from the menu.
void MenuSelectionAt(size_t index);
// Called when the combobox is pressed.
void OnComboboxPressed();
// Shows/Closes the drop down menu.
void ShowDropDownMenu();
void CloseDropDownMenu();
// Called when a selection is made.
void OnPerformAction();
// Overridden from ComboboxModelObserver:
void OnComboboxModelChanged(ui::ComboboxModel* model) override;
void OnComboboxModelDestroying(ui::ComboboxModel* model) override;
// views::Button:
bool SkipDefaultKeyEventProcessing(const ui::KeyEvent& e) override;
bool OnKeyPressed(const ui::KeyEvent& e) override;
void OnEnabledChanged() override;
void UpdateExpandedCollapsedAccessibleState() const;
void UpdateAccessibleAccessibleActiveDescendantId();
void UpdateAccessibleDefaultAction();
// Optionally used to tie the lifetime of the model to this combobox. See
// constructor.
std::unique_ptr<ui::ComboboxModel> owned_model_;
// Reference to our model, which may be owned or not.
raw_ptr<ui::ComboboxModel> model_;
const raw_ptr<views::Label> title_ = nullptr;
const raw_ptr<views::ImageView> drop_down_arrow_ = nullptr;
// Callback notified when the selected index changes.
base::RepeatingClosure callback_;
// The current selected index; nullopt means no selection.
std::optional<size_t> selected_index_ = std::nullopt;
// The selection that committed by performing selection changed action.
std::optional<size_t> last_commit_selection_ = std::nullopt;
// A handler handles mouse and touch event happening outside combobox and drop
// down menu. This is mainly used to decide if we should close the drop down
// menu.
std::unique_ptr<ComboboxEventHandler> event_handler_;
// Drop down menu view owned by menu widget.
raw_ptr<ComboboxMenuView> menu_view_ = nullptr;
// Drop down menu widget.
views::UniqueWidgetPtr menu_;
// Like MenuButton, we use a time object in order to keep track of when the
// combobox was closed. The time is used for simulating menu behavior; that
// is, if the menu is shown and the button is pressed, we need to close the
// menu. There is no clean way to get the second click event because the
// menu is displayed using a modal loop and, unlike regular menus in Windows,
// the button is not part of the displayed menu.
base::TimeTicks closed_time_;
base::ScopedObservation<ui::ComboboxModel, ui::ComboboxModelObserver>
observation_{this};
base::ScopedObservation<views::Widget, views::WidgetObserver>
widget_observer_{this};
base::WeakPtrFactory<Combobox> weak_ptr_factory_{this};
};
} // namespace ash
#endif // ASH_STYLE_COMBOBOX_H_
|