Skip to content

Commit 608242b

Browse files
fix(acp): styled dropdown in question forms; wheel no longer changes it
Scrolling the transcript over a dropdown changed its selection (and could clear the recommended pick). The combo now ignores the wheel so the transcript scrolls. It is also styled like the form's text inputs: rounded border, palette-tinted chevron, styled popup items. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 55cc7fb commit 608242b

1 file changed

Lines changed: 43 additions & 12 deletions

File tree

‎src/widgets/AskUserFormWidget.cpp‎

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
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)
615644
void 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

Comments
 (0)