2929#include < QRadioButton>
3030#include < QSaveFile>
3131#include < QStandardPaths>
32+ #include < QStyledItemDelegate>
33+ #include < QWheelEvent>
3234#include < QStyle>
3335#include < QToolButton>
3436#include < QVBoxLayout>
@@ -78,33 +80,45 @@ QString css(const QColor &c)
7880 return QStringLiteral (" rgb(%1,%2,%3)" ).arg (c.red ()).arg (c.green ()).arg (c.blue ());
7981}
8082
81- // A checkmark SVG in `color` for QCheckBox::indicator:checked . Style sheets
82- // cannot tint an image, so one file per colour is written to the cache dir
83- // once per process (a few hundred bytes) and reused by every form .
84- QString checkmarkImage ( const QColor &color)
83+ // A 12x12 stroked SVG glyph in `color` for style-sheet `image:` rules . Style
84+ // sheets cannot tint an image, so one file per glyph and colour is written to
85+ // the cache dir once per process (a few hundred bytes) and reused.
86+ QString glyphImage ( const char *name, const char *pathData, const QColor &color)
8587{
86- static QHash<QRgb , QString> cache;
87- const QRgb key = color.rgb ();
88+ static QHash<QString , QString> cache;
89+ const QString key = QLatin1String (name) + color.name ();
8890 if (const auto it = cache.constFind (key); it != cache.constEnd ())
8991 return it.value ();
9092 QString dir = QStandardPaths::writableLocation (QStandardPaths::CacheLocation);
9193 if (dir.isEmpty () || !QDir ().mkpath (dir))
9294 dir = QDir::tempPath ();
93- const QString path = dir + QStringLiteral (" /ask-user-check- " ) + color.name ().mid (1 ) + QStringLiteral ( " .svg " );
95+ const QString path = dir + QStringLiteral (" /ask-user-%1-%2.svg " ). arg ( QLatin1String (name), color.name ().mid (1 ));
9496 if (!QFileInfo::exists (path)) {
9597 QSaveFile file (path);
9698 if (file.open (QIODevice::WriteOnly)) {
9799 file.write (QStringLiteral (
98100 " <svg xmlns=\" http://www.w3.org/2000/svg\" width=\" 12\" height=\" 12\" viewBox=\" 0 0 12 12\" >"
99- " <path d=\" M2.5 6.3 L5 8.8 L9.6 3.4\" fill=\" none\" stroke=\" %1\" stroke-width=\" 1.9\" "
100- " stroke-linecap=\" round\" stroke-linejoin=\" round\" /></svg>" ).arg (color.name ()).toUtf8 ());
101+ " <path d=\" %1\" fill=\" none\" stroke=\" %2\" stroke-width=\" 1.9\" "
102+ " stroke-linecap=\" round\" stroke-linejoin=\" round\" /></svg>" )
103+ .arg (QLatin1String (pathData), color.name ())
104+ .toUtf8 ());
101105 file.commit ();
102106 }
103107 }
104108 cache.insert (key, path);
105109 return path;
106110}
107111
112+ // Scrolling the transcript over a dropdown must scroll, not change the pick.
113+ class NoWheelComboBox : public QComboBox
114+ {
115+ public:
116+ using QComboBox::QComboBox;
117+
118+ protected:
119+ void wheelEvent (QWheelEvent *event) override { event->ignore (); }
120+ };
121+
108122// One choice as a rounded, clickable row: the indicator button and the
109123// description underneath. A click anywhere on the
110124// row toggles the button; the row restyles itself from the button's state.
@@ -312,7 +326,21 @@ void AskUserFormWidget::applyTheme()
312326 " QToolButton#auToggle:hover { color: %TEXT%; }"
313327 " QFrame#askUserForm QLineEdit, QFrame#askUserForm QPlainTextEdit { background: %INPUT%;"
314328 " color: %TEXT%; border: 1px solid %BORDER%; border-radius: 6px; padding: 4px 6px; }"
315- " QFrame#askUserForm QLineEdit:focus, QFrame#askUserForm QPlainTextEdit:focus { border-color: %ACCENT%; }" );
329+ " QFrame#askUserForm QLineEdit:focus, QFrame#askUserForm QPlainTextEdit:focus { border-color: %ACCENT%; }"
330+ " QFrame#askUserForm QComboBox { background: %INPUT%; color: %TEXT%; border: 1px solid %BORDER%;"
331+ " border-radius: 6px; padding: 5px 28px 5px 8px; }"
332+ " QFrame#askUserForm QComboBox:hover { border-color: %BORDERSTRONG%; }"
333+ " QFrame#askUserForm QComboBox:focus, QFrame#askUserForm QComboBox:on { border-color: %ACCENT%; }"
334+ " QFrame#askUserForm QComboBox::drop-down { subcontrol-origin: padding; subcontrol-position: center right;"
335+ " width: 24px; border: none; background: transparent; }"
336+ " QFrame#askUserForm QComboBox::down-arrow { image: url(\" %CHEVRON%\" ); width: 12px; height: 12px; }"
337+ " QFrame#askUserForm QComboBox QAbstractItemView { background: %BASE%; color: %TEXT%;"
338+ " border: 1px solid %BORDER%; padding: 4px; outline: 0; }"
339+ " QFrame#askUserForm QComboBox QAbstractItemView::item { min-height: 26px; padding: 0px 8px;"
340+ " border-radius: 4px; }"
341+ " QFrame#askUserForm QComboBox QAbstractItemView::item:hover,"
342+ " QFrame#askUserForm QComboBox QAbstractItemView::item:selected { background: %ACCENTTINT%;"
343+ " color: %TEXT%; }" );
316344 const std::pair<const char *, QColor> vars[] = {
317345 {" %BASE%" , base}, {" %BORDERSTRONG%" , borderStrong}, {" %BORDER%" , border},
318346 {" %HOVER%" , hover},
@@ -322,7 +350,8 @@ void AskUserFormWidget::applyTheme()
322350 };
323351 for (const auto &[key, color] : vars)
324352 sheet.replace (QLatin1String (key), css (color));
325- sheet.replace (QLatin1String (" %CHECKMARK%" ), checkmarkImage (onAccent));
353+ sheet.replace (QLatin1String (" %CHECKMARK%" ), glyphImage (" check" , " M2.5 6.3 L5 8.8 L9.6 3.4" , onAccent));
354+ sheet.replace (QLatin1String (" %CHEVRON%" ), glyphImage (" chevron" , " M3 4.6 L6 7.6 L9 4.6" , muted));
326355 setStyleSheet (sheet);
327356}
328357
@@ -615,7 +644,9 @@ void AskUserFormWidget::buildChoice(int index, Row &row, QVBoxLayout *layout)
615644void AskUserFormWidget::buildDropdown (int index, Row &row, QVBoxLayout *layout)
616645{
617646 const Question &q = m_spec.questions .at (index);
618- row.combo = new QComboBox (row.container );
647+ row.combo = new NoWheelComboBox (row.container );
648+ // Item rules in the style sheet apply only with a styled delegate.
649+ row.combo ->setItemDelegate (new QStyledItemDelegate (row.combo ));
619650 row.combo ->addItem (tr (" Choose…" ), QString ()); // index 0 = no answer
620651 for (const AskUserForm::Option &opt : q.options ) {
621652 const bool recommended = q.recommended .toString () == opt.value ;
0 commit comments