Unified type popup: explicit TypeEntry model, modifier toggles, section headers

- Replace sentinel-id scheme (kPrimBase) with TypeEntry::Kind discriminant
- Merge showTypeSelectorPopup + showTypePickerPopup into single showTypePopup
- Merge applyTypePickerResult into applyTypePopupResult matching on entryKind
- Add modifier toggle buttons (plain, *, **, [n]) with array count input
- Add section headers (primitives / project types) with dim centered styling
- Root mode shows project types first; non-Root sorts same-size primitives first
- Remove popup outside border, flat "Create new type" button
- Add parseTypeSpec parser, update tests with new TypeEntry API
This commit is contained in:
IChooseYou
2026-02-11 13:39:43 -07:00
committed by sysadmin
parent 33a093ae7d
commit db5d3ae311
6 changed files with 889 additions and 241 deletions

View File

@@ -6,6 +6,7 @@
#include <QLineEdit>
#include <QListView>
#include <QToolButton>
#include <QButtonGroup>
#include <QStringListModel>
#include <QStyledItemDelegate>
#include <QPainter>
@@ -14,11 +15,44 @@
#include <QIcon>
#include <QApplication>
#include <QScreen>
#include <QIntValidator>
#include "themes/thememanager.h"
namespace rcx {
// ── Custom delegate: gutter checkmark + icon + text ──
// ── parseTypeSpec ──
TypeSpec parseTypeSpec(const QString& text) {
TypeSpec spec;
QString s = text.trimmed();
if (s.isEmpty()) return spec;
// Check for pointer suffix: "Ball*" or "Ball**"
if (s.endsWith('*')) {
spec.isPointer = true;
s.chop(1);
if (s.endsWith('*')) s.chop(1); // double pointer
spec.baseName = s.trimmed();
return spec;
}
// Check for array suffix: "int32_t[10]"
int bracket = s.indexOf('[');
if (bracket > 0 && s.endsWith(']')) {
spec.baseName = s.left(bracket).trimmed();
QString countStr = s.mid(bracket + 1, s.size() - bracket - 2);
bool ok;
int count = countStr.toInt(&ok);
if (ok && count > 0)
spec.arrayCount = count;
return spec;
}
spec.baseName = s;
return spec;
}
// ── Custom delegate: gutter checkmark + icon + text + sections ──
class TypeSelectorDelegate : public QStyledItemDelegate {
public:
@@ -26,51 +60,115 @@ public:
: QStyledItemDelegate(parent), m_popup(popup) {}
void setFont(const QFont& f) { m_font = f; }
void setCurrentTypes(const QVector<TypeEntry>* filtered, uint64_t currentId) {
void setFilteredTypes(const QVector<TypeEntry>* filtered, const TypeEntry* current, bool hasCurrent) {
m_filtered = filtered;
m_currentId = currentId;
m_current = current;
m_hasCurrent = hasCurrent;
}
void paint(QPainter* painter, const QStyleOptionViewItem& option,
const QModelIndex& index) const override {
painter->save();
// Background: themed colors
const auto& t = ThemeManager::instance().current();
if (option.state & QStyle::State_Selected)
painter->fillRect(option.rect, t.selected);
else if (option.state & QStyle::State_MouseOver)
painter->fillRect(option.rect, t.hover);
int row = index.row();
bool isSection = (m_filtered && row >= 0 && row < m_filtered->size()
&& (*m_filtered)[row].entryKind == TypeEntry::Section);
bool isDisabled = (m_filtered && row >= 0 && row < m_filtered->size()
&& !(*m_filtered)[row].enabled);
// Background
if (isSection) {
// No background highlight for sections
} else if (isDisabled) {
// Subtle background on hover only
if (option.state & QStyle::State_MouseOver)
painter->fillRect(option.rect, t.surface);
} else {
if (option.state & QStyle::State_Selected)
painter->fillRect(option.rect, t.selected);
else if (option.state & QStyle::State_MouseOver)
painter->fillRect(option.rect, t.hover);
}
int x = option.rect.x();
int y = option.rect.y();
int h = option.rect.height();
int w = option.rect.width();
// Section: centered dim text with horizontal rules
if (isSection) {
painter->setPen(t.textDim);
QFont dimFont = m_font;
dimFont.setPointSize(qMax(7, m_font.pointSize() - 1));
painter->setFont(dimFont);
QFontMetrics fm(dimFont);
QString text = index.data().toString();
int textW = fm.horizontalAdvance(text);
int textX = x + (w - textW) / 2;
int lineY = y + h / 2;
// Left rule
if (textX > x + 8)
painter->drawLine(x + 8, lineY, textX - 6, lineY);
// Text
painter->drawText(QRect(textX, y, textW, h), Qt::AlignVCenter, text);
// Right rule
if (textX + textW + 6 < x + w - 8)
painter->drawLine(textX + textW + 6, lineY, x + w - 8, lineY);
painter->restore();
return;
}
// 18px gutter: side triangle if current
int row = index.row();
if (m_filtered && row >= 0 && row < m_filtered->size()
&& (*m_filtered)[row].id == m_currentId) {
painter->setPen(t.syntaxType);
QFont checkFont = m_font;
painter->setFont(checkFont);
painter->drawText(QRect(x, y, 18, h), Qt::AlignCenter,
QString(QChar(0x25B8)));
if (m_hasCurrent && m_filtered && row >= 0 && row < m_filtered->size()) {
const TypeEntry& entry = (*m_filtered)[row];
bool isCurrent = false;
if (m_current->entryKind == TypeEntry::Primitive && entry.entryKind == TypeEntry::Primitive)
isCurrent = (entry.primitiveKind == m_current->primitiveKind);
else if (m_current->entryKind == TypeEntry::Composite && entry.entryKind == TypeEntry::Composite)
isCurrent = (entry.structId == m_current->structId);
if (isCurrent) {
painter->setPen(t.syntaxType);
painter->setFont(m_font);
painter->drawText(QRect(x, y, 18, h), Qt::AlignCenter,
QString(QChar(0x25B8)));
}
}
x += 18;
// Icon 16x16 — only for struct/class/enum entries (non-empty classKeyword)
// Icon 16x16 — only for composite entries
bool hasIcon = (m_filtered && row >= 0 && row < m_filtered->size()
&& !(*m_filtered)[row].classKeyword.isEmpty());
&& (*m_filtered)[row].entryKind == TypeEntry::Composite);
if (hasIcon) {
static QIcon structIcon(QStringLiteral(":/vsicons/symbol-structure.svg"));
structIcon.paint(painter, x, y + (h - 16) / 2, 16, 16);
QPixmap pm = structIcon.pixmap(16, 16);
if (isDisabled) {
// Paint dimmed
QPixmap dimmed(pm.size());
dimmed.fill(Qt::transparent);
QPainter p(&dimmed);
p.setOpacity(0.35);
p.drawPixmap(0, 0, pm);
p.end();
painter->drawPixmap(x, y + (h - 16) / 2, dimmed);
} else {
structIcon.paint(painter, x, y + (h - 16) / 2, 16, 16);
}
}
x += 20; // reserve space for alignment
x += 20;
// Text
painter->setPen(option.state & QStyle::State_Selected
? option.palette.color(QPalette::HighlightedText)
: option.palette.color(QPalette::Text));
QColor textColor;
if (isDisabled)
textColor = t.textDim;
else if (option.state & QStyle::State_Selected)
textColor = option.palette.color(QPalette::HighlightedText);
else
textColor = option.palette.color(QPalette::Text);
painter->setPen(textColor);
painter->setFont(m_font);
painter->drawText(QRect(x, y, option.rect.right() - x, h),
Qt::AlignVCenter | Qt::AlignLeft,
@@ -80,16 +178,21 @@ public:
}
QSize sizeHint(const QStyleOptionViewItem& /*option*/,
const QModelIndex& /*index*/) const override {
const QModelIndex& index) const override {
QFontMetrics fm(m_font);
return QSize(200, fm.height() + 8);
int row = index.row();
bool isSection = (m_filtered && row >= 0 && row < m_filtered->size()
&& (*m_filtered)[row].entryKind == TypeEntry::Section);
int h = isSection ? fm.height() + 2 : fm.height() + 8;
return QSize(200, h);
}
private:
TypeSelectorPopup* m_popup = nullptr;
QFont m_font;
const QVector<TypeEntry>* m_filtered = nullptr;
uint64_t m_currentId = 0;
const TypeEntry* m_current = nullptr;
bool m_hasCurrent = false;
};
// ── TypeSelectorPopup ──
@@ -99,7 +202,6 @@ TypeSelectorPopup::TypeSelectorPopup(QWidget* parent)
{
setAttribute(Qt::WA_DeleteOnClose, false);
// Theme palette
const auto& theme = ThemeManager::instance().current();
QPalette pal;
pal.setColor(QPalette::Window, theme.backgroundAlt);
@@ -114,9 +216,8 @@ TypeSelectorPopup::TypeSelectorPopup(QWidget* parent)
setPalette(pal);
setAutoFillBackground(true);
// Thin border
setFrameShape(QFrame::Box);
setLineWidth(1);
setFrameShape(QFrame::NoFrame);
setLineWidth(0);
auto* layout = new QVBoxLayout(this);
layout->setContentsMargins(6, 6, 6, 6);
@@ -126,7 +227,7 @@ TypeSelectorPopup::TypeSelectorPopup(QWidget* parent)
{
auto* row = new QHBoxLayout;
row->setContentsMargins(0, 0, 0, 0);
m_titleLabel = new QLabel(QStringLiteral("Change root"));
m_titleLabel = new QLabel(QStringLiteral("Change type"));
m_titleLabel->setPalette(pal);
QFont bold = m_titleLabel->font();
bold.setBold(true);
@@ -151,14 +252,17 @@ TypeSelectorPopup::TypeSelectorPopup(QWidget* parent)
layout->addLayout(row);
}
// Row 2: + Create new type button
// Row 2: + Create new type button (flat, no gradient)
{
m_createBtn = new QToolButton;
m_createBtn->setText(QStringLiteral("+ Create new type\u2026"));
m_createBtn->setToolButtonStyle(Qt::ToolButtonTextOnly);
m_createBtn->setAutoRaise(true);
m_createBtn->setCursor(Qt::PointingHandCursor);
m_createBtn->setPalette(pal);
m_createBtn->setStyleSheet(QStringLiteral(
"QToolButton { color: %1; border: none; padding: 3px 6px; }"
"QToolButton:hover { color: %2; background: %3; }")
.arg(theme.textMuted.name(), theme.text.name(), theme.hover.name()));
connect(m_createBtn, &QToolButton::clicked, this, [this]() {
emit createNewTypeRequested();
hide();
@@ -178,7 +282,65 @@ TypeSelectorPopup::TypeSelectorPopup(QWidget* parent)
layout->addWidget(sep);
}
// Row 3: Filter
// Row 3: Modifier toggles [ plain ] [ * ] [ ** ] [ [n] ]
{
m_modRow = new QWidget;
auto* modLayout = new QHBoxLayout(m_modRow);
modLayout->setContentsMargins(0, 0, 0, 0);
modLayout->setSpacing(3);
m_modGroup = new QButtonGroup(this);
m_modGroup->setExclusive(true);
QString btnStyle = QStringLiteral(
"QToolButton { color: %1; background: %2; border: 1px solid %3;"
" padding: 2px 8px; border-radius: 3px; }"
"QToolButton:checked { color: %4; background: %5; border-color: %5; }"
"QToolButton:hover:!checked { background: %6; }")
.arg(theme.textDim.name(), theme.background.name(), theme.border.name(),
theme.text.name(), theme.selected.name(), theme.hover.name());
auto makeToggle = [&](const QString& label, int id) -> QToolButton* {
auto* btn = new QToolButton;
btn->setText(label);
btn->setCheckable(true);
btn->setCursor(Qt::PointingHandCursor);
btn->setStyleSheet(btnStyle);
m_modGroup->addButton(btn, id);
modLayout->addWidget(btn);
return btn;
};
m_btnPlain = makeToggle(QStringLiteral("plain"), 0);
m_btnPtr = makeToggle(QStringLiteral("*"), 1);
m_btnDblPtr = makeToggle(QStringLiteral("**"), 2);
m_btnArray = makeToggle(QStringLiteral("[n]"), 3);
m_btnPlain->setChecked(true);
// Array count input (shown only when [n] is active)
m_arrayCountEdit = new QLineEdit;
m_arrayCountEdit->setPlaceholderText(QStringLiteral("n"));
m_arrayCountEdit->setValidator(new QIntValidator(1, 99999, m_arrayCountEdit));
m_arrayCountEdit->setFixedWidth(50);
m_arrayCountEdit->setPalette(pal);
m_arrayCountEdit->hide();
modLayout->addWidget(m_arrayCountEdit);
modLayout->addStretch();
layout->addWidget(m_modRow);
connect(m_modGroup, &QButtonGroup::idToggled,
this, [this](int id, bool checked) {
if (!checked) return;
m_arrayCountEdit->setVisible(id == 3);
if (id == 3) m_arrayCountEdit->setFocus();
updateModifierPreview();
});
connect(m_arrayCountEdit, &QLineEdit::textChanged,
this, [this]() { updateModifierPreview(); });
}
// Row 4: Filter + preview
{
m_filterEdit = new QLineEdit;
m_filterEdit->setPlaceholderText(QStringLiteral("Filter types\u2026"));
@@ -188,6 +350,13 @@ TypeSelectorPopup::TypeSelectorPopup(QWidget* parent)
connect(m_filterEdit, &QLineEdit::textChanged,
this, &TypeSelectorPopup::applyFilter);
layout->addWidget(m_filterEdit);
m_previewLabel = new QLabel;
m_previewLabel->setPalette(pal);
m_previewLabel->setStyleSheet(QStringLiteral(
"QLabel { color: %1; padding: 1px 6px; }").arg(theme.syntaxType.name()));
m_previewLabel->hide();
layout->addWidget(m_previewLabel);
}
// Row 4: List
@@ -214,6 +383,17 @@ TypeSelectorPopup::TypeSelectorPopup(QWidget* parent)
}
}
void TypeSelectorPopup::warmUp() {
TypeEntry dummy;
dummy.entryKind = TypeEntry::Primitive;
dummy.primitiveKind = NodeKind::Hex8;
dummy.displayName = "warmup";
setTypes({dummy});
popup(QPoint(-9999, -9999));
hide();
QApplication::processEvents();
}
void TypeSelectorPopup::setFont(const QFont& font) {
m_font = font;
@@ -224,35 +404,80 @@ void TypeSelectorPopup::setFont(const QFont& font) {
m_createBtn->setFont(font);
m_filterEdit->setFont(font);
m_listView->setFont(font);
m_previewLabel->setFont(font);
QFont smallFont = font;
smallFont.setPointSize(qMax(7, font.pointSize() - 1));
m_btnPlain->setFont(smallFont);
m_btnPtr->setFont(smallFont);
m_btnDblPtr->setFont(smallFont);
m_btnArray->setFont(smallFont);
m_arrayCountEdit->setFont(smallFont);
auto* delegate = static_cast<TypeSelectorDelegate*>(m_listView->itemDelegate());
if (delegate)
delegate->setFont(font);
}
void TypeSelectorPopup::setTypes(const QVector<TypeEntry>& types, uint64_t currentId) {
void TypeSelectorPopup::setTitle(const QString& title) {
m_titleLabel->setText(title);
}
void TypeSelectorPopup::setMode(TypePopupMode mode) {
m_mode = mode;
// Show modifier toggles for modes where type modifiers make sense
bool showMods = (mode == TypePopupMode::FieldType
|| mode == TypePopupMode::ArrayElement);
m_modRow->setVisible(showMods);
// Reset to plain when showing
if (showMods) {
m_btnPlain->setChecked(true);
m_arrayCountEdit->clear();
m_arrayCountEdit->hide();
}
}
void TypeSelectorPopup::setCurrentNodeSize(int bytes) {
m_currentNodeSize = bytes;
}
void TypeSelectorPopup::setTypes(const QVector<TypeEntry>& types, const TypeEntry* current) {
m_allTypes = types;
m_currentId = currentId;
if (current) {
m_currentEntry = *current;
m_hasCurrent = true;
} else {
m_currentEntry = TypeEntry{};
m_hasCurrent = false;
}
// Reset modifier toggles
m_btnPlain->setChecked(true);
m_arrayCountEdit->clear();
m_arrayCountEdit->hide();
m_previewLabel->hide();
m_filterEdit->clear();
applyFilter(QString());
}
void TypeSelectorPopup::popup(const QPoint& globalPos) {
// Size: width based on longest entry, height based on count
QFontMetrics fm(m_font);
int maxTextW = fm.horizontalAdvance(QStringLiteral("Choose element type Esc"));
for (const auto& t : m_allTypes) {
QString text = t.classKeyword + QStringLiteral(" ") + t.displayName;
int w = 18 + 20 + fm.horizontalAdvance(text) + 16; // gutter + icon + text + pad
QString text = t.classKeyword.isEmpty()
? t.displayName
: (t.classKeyword + QStringLiteral(" ") + t.displayName);
int w = 18 + 20 + fm.horizontalAdvance(text) + 16;
if (w > maxTextW) maxTextW = w;
}
int popupW = qBound(250, maxTextW + 24, 500); // +margins
int popupW = qBound(280, maxTextW + 24, 500);
int rowH = fm.height() + 8;
int headerH = rowH * 3 + 30; // title + button + filter + separators/margins
int listH = qBound(rowH * 3, rowH * (int)m_allTypes.size(), rowH * 12);
int headerH = rowH * 3 + 30;
if (m_modRow->isVisible())
headerH += rowH + 4; // extra row for modifier toggles
int listH = qBound(rowH * 3, rowH * (int)m_filteredTypes.size(), rowH * 14);
int popupH = headerH + listH;
// Clamp to screen
QScreen* screen = QApplication::screenAt(globalPos);
if (screen) {
QRect avail = screen->availableGeometry();
@@ -270,40 +495,125 @@ void TypeSelectorPopup::popup(const QPoint& globalPos) {
m_filterEdit->setFocus();
// Pre-select current type in list
for (int i = 0; i < m_filteredTypes.size(); i++) {
if (m_filteredTypes[i].id == m_currentId) {
m_listView->setCurrentIndex(m_model->index(i));
break;
if (m_hasCurrent) {
for (int i = 0; i < m_filteredTypes.size(); i++) {
const auto& entry = m_filteredTypes[i];
if (entry.entryKind == TypeEntry::Section) continue;
bool match = false;
if (m_currentEntry.entryKind == TypeEntry::Primitive && entry.entryKind == TypeEntry::Primitive)
match = (entry.primitiveKind == m_currentEntry.primitiveKind);
else if (m_currentEntry.entryKind == TypeEntry::Composite && entry.entryKind == TypeEntry::Composite)
match = (entry.structId == m_currentEntry.structId);
if (match) {
m_listView->setCurrentIndex(m_model->index(i));
break;
}
}
}
}
void TypeSelectorPopup::updateModifierPreview() {
int modId = m_modGroup->checkedId();
if (modId <= 0) {
m_previewLabel->hide();
return;
}
QString suffix;
if (modId == 1) suffix = QStringLiteral("*");
else if (modId == 2) suffix = QStringLiteral("**");
else if (modId == 3) {
QString countText = m_arrayCountEdit->text().trimmed();
suffix = countText.isEmpty()
? QStringLiteral("[n]")
: QStringLiteral("[%1]").arg(countText);
}
m_previewLabel->setText(QStringLiteral("\u2192 <type>%1").arg(suffix));
m_previewLabel->show();
}
void TypeSelectorPopup::applyFilter(const QString& text) {
m_filteredTypes.clear();
QStringList displayStrings;
QString filterBase = text.trimmed();
// Separate primitives and composites
QVector<TypeEntry> primitives, composites;
for (const auto& t : m_allTypes) {
if (text.isEmpty()
|| t.displayName.contains(text, Qt::CaseInsensitive)
|| t.classKeyword.contains(text, Qt::CaseInsensitive)) {
m_filteredTypes.append(t);
if (t.classKeyword.isEmpty())
displayStrings << t.displayName;
if (t.entryKind == TypeEntry::Section) continue; // skip stale sections
bool matchesFilter = filterBase.isEmpty()
|| t.displayName.contains(filterBase, Qt::CaseInsensitive)
|| t.classKeyword.contains(filterBase, Qt::CaseInsensitive);
if (!matchesFilter) continue;
if (t.entryKind == TypeEntry::Primitive)
primitives.append(t);
else if (t.entryKind == TypeEntry::Composite)
composites.append(t);
}
// For non-Root modes, sort primitives: same-size first, then rest
if (m_mode != TypePopupMode::Root && m_currentNodeSize > 0 && !primitives.isEmpty()) {
QVector<TypeEntry> sameSize, other;
for (const auto& p : primitives) {
if (sizeForKind(p.primitiveKind) == m_currentNodeSize)
sameSize.append(p);
else
displayStrings << (t.classKeyword + QStringLiteral(" ") + t.displayName);
other.append(p);
}
primitives = sameSize + other;
}
// Helper lambdas for appending sections
auto appendPrimitives = [&]() {
if (primitives.isEmpty()) return;
TypeEntry sec;
sec.entryKind = TypeEntry::Section;
sec.displayName = QStringLiteral("primitives");
sec.enabled = false;
m_filteredTypes.append(sec);
displayStrings << sec.displayName;
for (const auto& p : primitives) {
m_filteredTypes.append(p);
displayStrings << p.displayName;
}
};
auto appendComposites = [&]() {
if (composites.isEmpty()) return;
TypeEntry sec;
sec.entryKind = TypeEntry::Section;
sec.displayName = QStringLiteral("project types");
sec.enabled = false;
m_filteredTypes.append(sec);
displayStrings << sec.displayName;
for (const auto& c : composites) {
m_filteredTypes.append(c);
QString label = c.classKeyword.isEmpty()
? c.displayName
: (c.classKeyword + QStringLiteral(" ") + c.displayName);
displayStrings << label;
}
};
// Root mode: project types first (composites are the primary selection)
if (m_mode == TypePopupMode::Root) {
appendComposites();
appendPrimitives();
} else {
appendPrimitives();
appendComposites();
}
m_model->setStringList(displayStrings);
// Update delegate data
auto* delegate = static_cast<TypeSelectorDelegate*>(m_listView->itemDelegate());
if (delegate)
delegate->setCurrentTypes(&m_filteredTypes, m_currentId);
delegate->setFilteredTypes(&m_filteredTypes, &m_currentEntry, m_hasCurrent);
// Select first match
if (!m_filteredTypes.isEmpty())
m_listView->setCurrentIndex(m_model->index(0));
// Select first selectable item
int first = nextSelectableRow(0, 1);
if (first >= 0)
m_listView->setCurrentIndex(m_model->index(first));
}
void TypeSelectorPopup::acceptCurrent() {
@@ -312,16 +622,40 @@ void TypeSelectorPopup::acceptCurrent() {
acceptIndex(idx.row());
}
void TypeSelectorPopup::setTitle(const QString& title) {
m_titleLabel->setText(title);
}
void TypeSelectorPopup::acceptIndex(int row) {
if (row < 0 || row >= m_filteredTypes.size()) return;
emit typeSelected(m_filteredTypes[row].id, m_filteredTypes[row].displayName);
const TypeEntry& entry = m_filteredTypes[row];
if (entry.entryKind == TypeEntry::Section) return;
if (!entry.enabled) return;
// Build full text with modifier from toggle buttons
int modId = m_modGroup->checkedId();
QString fullText = entry.displayName;
if (modId == 1)
fullText += QStringLiteral("*");
else if (modId == 2)
fullText += QStringLiteral("**");
else if (modId == 3) {
QString countText = m_arrayCountEdit->text().trimmed();
if (!countText.isEmpty())
fullText += QStringLiteral("[%1]").arg(countText);
}
emit typeSelected(entry, fullText);
hide();
}
int TypeSelectorPopup::nextSelectableRow(int from, int direction) const {
int i = from;
while (i >= 0 && i < m_filteredTypes.size()) {
const auto& e = m_filteredTypes[i];
if (e.entryKind != TypeEntry::Section && e.enabled)
return i;
i += direction;
}
return -1;
}
bool TypeSelectorPopup::eventFilter(QObject* obj, QEvent* event) {
if (event->type() == QEvent::KeyPress) {
auto* ke = static_cast<QKeyEvent*>(event);
@@ -334,8 +668,11 @@ bool TypeSelectorPopup::eventFilter(QObject* obj, QEvent* event) {
if (obj == m_filterEdit) {
if (ke->key() == Qt::Key_Down) {
m_listView->setFocus();
if (!m_listView->currentIndex().isValid() && m_model->rowCount() > 0)
m_listView->setCurrentIndex(m_model->index(0));
QModelIndex cur = m_listView->currentIndex();
int startRow = cur.isValid() ? cur.row() : 0;
int next = nextSelectableRow(startRow, 1);
if (next >= 0)
m_listView->setCurrentIndex(m_model->index(next));
return true;
}
if (ke->key() == Qt::Key_Return || ke->key() == Qt::Key_Enter) {
@@ -351,11 +688,33 @@ bool TypeSelectorPopup::eventFilter(QObject* obj, QEvent* event) {
m_filterEdit->setFocus();
return true;
}
// Skip sections and disabled entries
int prev = nextSelectableRow(cur.row() - 1, -1);
if (prev < 0) {
m_filterEdit->setFocus();
return true;
}
m_listView->setCurrentIndex(m_model->index(prev));
return true;
}
if (ke->key() == Qt::Key_Down) {
QModelIndex cur = m_listView->currentIndex();
int startRow = cur.isValid() ? cur.row() + 1 : 0;
int next = nextSelectableRow(startRow, 1);
if (next >= 0)
m_listView->setCurrentIndex(m_model->index(next));
return true;
}
if (ke->key() == Qt::Key_Return || ke->key() == Qt::Key_Enter) {
acceptCurrent();
return true;
}
// Forward printable keys to filter edit for type-to-filter
if (!ke->text().isEmpty() && ke->text()[0].isPrint()) {
m_filterEdit->setFocus();
m_filterEdit->setText(m_filterEdit->text() + ke->text());
return true;
}
}
}