Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qcombobox_p.h
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5#ifndef QCOMBOBOX_P_H
6#define QCOMBOBOX_P_H
7
8//
9// W A R N I N G
10// -------------
11//
12// This file is not part of the Qt API. It exists purely as an
13// implementation detail. This header file may change from version to
14// version without notice, or even be removed.
15//
16// We mean it.
17//
18
19#include <QtWidgets/private/qtwidgetsglobal_p.h>
20#include "QtWidgets/qcombobox.h"
21
22#include "QtWidgets/qabstractslider.h"
23#include "QtWidgets/qapplication.h"
24#include "QtWidgets/qstyleditemdelegate.h"
25#include "QtGui/qstandarditemmodel.h"
26#include "QtWidgets/qlineedit.h"
27#include "QtWidgets/qlistview.h"
28#include "QtGui/qpainter.h"
29#include "QtWidgets/qstyle.h"
30#include "QtWidgets/qstyleoption.h"
31#include "QtCore/qtimer.h"
32#include "private/qwidget_p.h"
33#include "QtCore/qpointer.h"
34#if QT_CONFIG(completer)
35#include "QtWidgets/qcompleter.h"
36#endif
37#include "QtGui/qevent.h"
38
39#include <limits.h>
40
42
43QT_BEGIN_NAMESPACE
44
45class QPlatformMenu;
46
48{
50public:
52 {
53 if (cmb)
55 }
56
57protected:
63
71
73 {
74 if (combo) {
79 //we paint the empty menu area to avoid having blank space that can happen when scrolling
81 menuOpt.initFrom(this);
90 }
91 }
93 }
94
95private:
97};
98
99class Q_AUTOTEST_EXPORT QComboBoxPrivateScroller : public QWidget
100{
101 Q_OBJECT
102
103public:
104 QComboBoxPrivateScroller(QAbstractSlider::SliderAction action, QWidget *parent)
105 : QWidget(parent), sliderAction(action)
106 {
107 setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
108 setAttribute(Qt::WA_NoMousePropagation);
109 }
110 QSize sizeHint() const override {
111 return QSize(20, style()->pixelMetric(QStyle::PM_MenuScrollerHeight, nullptr, this));
112 }
113
114protected:
115 inline void stopTimer() {
116 timer.stop();
117 }
118
119 inline void startTimer() {
120 timer.start(100, this);
121 fast = false;
122 }
123
124 void enterEvent(QEnterEvent *) override {
125 startTimer();
126 }
127
128 void leaveEvent(QEvent *) override {
129 stopTimer();
130 }
131 void timerEvent(QTimerEvent *e) override {
132 if (e->timerId() == timer.timerId()) {
133 emit doScroll(sliderAction);
134 if (fast) {
135 emit doScroll(sliderAction);
136 emit doScroll(sliderAction);
137 }
138 }
139 }
140 void hideEvent(QHideEvent *) override {
141 stopTimer();
142 }
143
144 void mouseMoveEvent(QMouseEvent *e) override
145 {
146 // Enable fast scrolling if the cursor is directly above or below the popup.
147 const int mouseX = e->position().toPoint().x();
148 const int mouseY = e->position().toPoint().y();
149 const bool horizontallyInside = pos().x() < mouseX && mouseX < rect().right() + 1;
150 const bool verticallyOutside = (sliderAction == QAbstractSlider::SliderSingleStepAdd) ?
151 rect().bottom() + 1 < mouseY : mouseY < pos().y();
152
153 fast = horizontallyInside && verticallyOutside;
154 }
155
156 void paintEvent(QPaintEvent *) override {
157 QPainter p(this);
158 QStyleOptionMenuItem menuOpt;
159 menuOpt.initFrom(this);
160 menuOpt.checkType = QStyleOptionMenuItem::NotCheckable;
161 menuOpt.menuRect = rect();
162 menuOpt.maxIconWidth = 0;
163 menuOpt.reservedShortcutWidth = 0;
164 menuOpt.menuItemType = QStyleOptionMenuItem::Scroller;
165 if (sliderAction == QAbstractSlider::SliderSingleStepAdd)
166 menuOpt.state |= QStyle::State_DownArrow;
167 p.eraseRect(rect());
168 style()->drawControl(QStyle::CE_MenuScroller, &menuOpt, &p);
169 }
170
171Q_SIGNALS:
172 void doScroll(int action);
173
174private:
175 QAbstractSlider::SliderAction sliderAction;
176 QBasicTimer timer;
177 bool fast = false;
178};
179
180class Q_WIDGETS_EXPORT QComboBoxPrivateContainer : public QFrame
181{
182 Q_OBJECT
183
184public:
185 QComboBoxPrivateContainer(QAbstractItemView *itemView, QComboBox *parent);
186 ~QComboBoxPrivateContainer();
187 QAbstractItemView *itemView() const;
188 void setItemView(QAbstractItemView *itemView);
189 int spacing() const;
190 int topMargin() const;
191 int bottomMargin() const { return topMargin(); }
192 void updateTopBottomMargin();
193 void updateStyleSettings();
194
195 QTimer blockMouseReleaseTimer;
196 QBasicTimer adjustSizeTimer;
197 QPoint initialClickPosition;
198
199public Q_SLOTS:
200 void scrollItemView(int action);
201 void hideScrollers();
202 void updateScrollers();
203 void viewDestroyed();
204
205protected:
206 void changeEvent(QEvent *e) override;
207 bool eventFilter(QObject *o, QEvent *e) override;
208 void mousePressEvent(QMouseEvent *e) override;
209 void mouseReleaseEvent(QMouseEvent *e) override;
210 void showEvent(QShowEvent *e) override;
211 void hideEvent(QHideEvent *e) override;
212 void timerEvent(QTimerEvent *timerEvent) override;
213 void resizeEvent(QResizeEvent *e) override;
214 void paintEvent(QPaintEvent *e) override;
215 QStyleOptionComboBox comboStyleOption() const;
216
217Q_SIGNALS:
218 void itemSelected(const QModelIndex &);
219 void resetButton();
220
221private:
222 QComboBox *combo;
223 QAbstractItemView *view = nullptr;
224 QComboBoxPrivateScroller *top = nullptr;
225 QComboBoxPrivateScroller *bottom = nullptr;
226 QElapsedTimer popupTimer;
227 bool maybeIgnoreMouseButtonRelease = false;
228
229 friend class QComboBox;
230 friend class QComboBoxPrivate;
231};
232
233class Q_AUTOTEST_EXPORT QComboMenuDelegate : public QAbstractItemDelegate
234{
235 Q_OBJECT
236public:
237 QComboMenuDelegate(QObject *parent, QComboBox *cmb)
238 : QAbstractItemDelegate(parent), mCombo(cmb), pressedIndex(-1)
239 {}
240
241protected:
242 void paint(QPainter *painter,
243 const QStyleOptionViewItem &option,
244 const QModelIndex &index) const override {
245 QStyleOptionMenuItem opt = getStyleOption(option, index);
246 painter->fillRect(option.rect, opt.palette.window());
247 mCombo->style()->drawControl(QStyle::CE_MenuItem, &opt, painter, mCombo);
248 }
249 QSize sizeHint(const QStyleOptionViewItem &option,
250 const QModelIndex &index) const override {
251 QStyleOptionMenuItem opt = getStyleOption(option, index);
252 return mCombo->style()->sizeFromContents(
253 QStyle::CT_MenuItem, &opt, option.rect.size(), mCombo);
254 }
255 bool editorEvent(QEvent *event, QAbstractItemModel *model,
256 const QStyleOptionViewItem &option, const QModelIndex &index) override;
257
258private:
259 QStyleOptionMenuItem getStyleOption(const QStyleOptionViewItem &option,
260 const QModelIndex &index) const;
261 QComboBox *mCombo;
262 int pressedIndex;
263};
264
265class Q_AUTOTEST_EXPORT QComboBoxDelegate : public QStyledItemDelegate
266{
267 Q_OBJECT
268public:
269 QComboBoxDelegate(QObject *parent, QComboBox *cmb)
270 : QStyledItemDelegate(parent), mCombo(cmb)
271 {}
272
273 static bool isSeparator(const QModelIndex &index) {
274 return index.data(Qt::AccessibleDescriptionRole).toString()
275 == QLatin1StringView("separator");
276 }
277 static void setSeparator(QAbstractItemModel *model, const QModelIndex &index) {
278 model->setData(index, QString::fromLatin1("separator"), Qt::AccessibleDescriptionRole);
279 if (QStandardItemModel *m = qobject_cast<QStandardItemModel*>(model))
280 if (QStandardItem *item = m->itemFromIndex(index))
281 item->setFlags(item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled));
282 }
283
284protected:
285 void paint(QPainter *painter,
286 const QStyleOptionViewItem &option,
287 const QModelIndex &index) const override {
288 if (isSeparator(index)) {
289 QRect rect = option.rect;
290 if (const QAbstractItemView *view = qobject_cast<const QAbstractItemView*>(option.widget))
291 rect.setWidth(view->viewport()->width());
292 QStyleOption opt;
293 opt.rect = rect;
294 mCombo->style()->drawPrimitive(QStyle::PE_IndicatorToolBarSeparator, &opt, painter, mCombo);
295 } else {
296 QStyledItemDelegate::paint(painter, option, index);
297 }
298 }
299
300 QSize sizeHint(const QStyleOptionViewItem &option,
301 const QModelIndex &index) const override {
302 if (isSeparator(index)) {
303 int pm = mCombo->style()->pixelMetric(QStyle::PM_DefaultFrameWidth, nullptr, mCombo);
304 return QSize(pm, pm);
305 }
306 return QStyledItemDelegate::sizeHint(option, index);
307 }
308private:
309 QComboBox *mCombo;
310};
311
312class Q_AUTOTEST_EXPORT QComboBoxPrivate : public QWidgetPrivate
313{
314 Q_DECLARE_PUBLIC(QComboBox)
315public:
316 QComboBoxPrivate();
317 ~QComboBoxPrivate();
318 void init();
319 QComboBoxPrivateContainer* viewContainer();
320 void updateLineEditGeometry();
321 Qt::MatchFlags matchFlags() const;
322 void editingFinished();
323 void returnPressed();
324 void complete();
325 void itemSelected(const QModelIndex &item);
326 bool contains(const QString &text, int role);
327 void emitActivated(const QModelIndex &index);
328 void emitHighlighted(const QModelIndex &index);
329 void emitCurrentIndexChanged(const QModelIndex &index);
330 void modelDestroyed();
331 void modelReset();
332 void updateMicroFocus() { q_func()->updateMicroFocus(); } // PMF connect doesn't handle default args
333#if QT_CONFIG(completer)
334 void completerActivated(const QModelIndex &index);
335#endif
336 void resetButton();
337 void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
338 void updateIndexBeforeChange();
339 void rowsInserted(const QModelIndex &parent, int start, int end);
340 void rowsRemoved(const QModelIndex &parent, int start, int end);
341 void updateArrow(QStyle::StateFlag state);
342 bool updateHoverControl(const QPoint &pos);
343 void trySetValidIndex();
344 QRect popupGeometry(const QPoint &globalPos) const;
345 QStyle::SubControl newHoverControl(const QPoint &pos);
346 int computeWidthHint() const;
347 QSize recomputeSizeHint(QSize &sh) const;
348 void adjustComboBoxSize();
349 QString itemText(const QModelIndex &index) const;
350 QIcon itemIcon(const QModelIndex &index) const;
351 int itemRole() const;
352 void updateLayoutDirection();
353 void setCurrentIndex(const QModelIndex &index);
354 void updateDelegate(bool force = false);
355 void initViewItemOption(QStyleOptionViewItem *option) const;
356 void keyboardSearchString(const QString &text);
357 void modelChanged();
358 void updateViewContainerPaletteAndOpacity();
359 void updateFocusPolicy();
360 void showPopupFromMouseEvent(QMouseEvent *e);
361 void doHidePopup();
362 void updateCurrentText(const QString &text);
363 void connectModel();
364 void disconnectModel();
365
366#ifdef Q_OS_MAC
367 void cleanupNativePopup();
368 bool showNativePopup();
369 struct IndexSetter {
370 int index;
371 QComboBox *cb;
372
373 void operator()(void)
374 {
375 cb->setCurrentIndex(index);
376 cb->d_func()->emitActivated(cb->d_func()->currentIndex);
377 }
378 };
379#endif
380
381 std::array<QMetaObject::Connection, 8> modelConnections;
382 QAbstractItemModel *model = nullptr;
383 QLineEdit *lineEdit = nullptr;
384 QPointer<QComboBoxPrivateContainer> container;
385#ifdef Q_OS_MAC
386 QPlatformMenu *m_platformMenu = nullptr;
387#endif
388 QPersistentModelIndex currentIndex;
389 QPersistentModelIndex root;
390 QString placeholderText;
391 QString currentText;
392 QRect hoverRect;
393 QSize iconSize;
394 mutable QSize minimumSizeHint;
395 mutable QSize sizeHint;
396 QComboBox::InsertPolicy insertPolicy = QComboBox::InsertAtBottom;
397 QComboBox::SizeAdjustPolicy sizeAdjustPolicy = QComboBox::AdjustToContentsOnFirstShow;
398 QStyle::StateFlag arrowState = QStyle::State_None;
399 QStyle::SubControl hoverControl = QStyle::SC_None;
400 QComboBox::LabelDrawingMode labelDrawingMode = QComboBox::LabelDrawingMode::UseStyle;
401 int minimumContentsLength = 0;
402 int indexBeforeChange = -1;
403 int maxVisibleItems = 10;
404 int maxCount = (std::numeric_limits<int>::max)();
405 int modelColumn = 0;
406 int placeholderIndex = -1;
407 bool shownOnce : 1;
408 bool duplicatesEnabled : 1;
409 bool frame : 1;
410 bool inserting : 1;
411 bool hidingPopup : 1;
412};
413
414QT_END_NAMESPACE
415
416#endif // QCOMBOBOX_P_H
QT_REQUIRE_CONFIG(combobox)