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
ash / style / combobox.h [blame]
// 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_