Skip to content

Commit 95569bc

Browse files
committed
controllers: improve UX with exclusive MIDI options
1 parent 1b819e1 commit 95569bc

3 files changed

Lines changed: 59 additions & 7 deletions

File tree

src/controllers/delegates/midioptionsdelegate.cpp

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -81,13 +81,21 @@ QWidget* MidiOptionsDelegate::createEditor(QWidget* parent,
8181
// * clicking an option or pressing Enter on a selected option toggles it,
8282
// closes the list view and commits the updated data
8383
// * pressing Space on a selected option toggles it, list remains open
84-
// * clicking outside the listview closes it, and another click causing the
84+
// * clicking outside the list view closes it, and another click causing the
8585
// combobox to lose focus closes that and commits pending changes
8686
connect(pComboBox,
8787
QOverload<int>::of(&QComboBox::activated),
8888
this,
8989
&MidiOptionsDelegate::commitAndCloseEditor);
9090

91+
// Uncheck other exclusive options whenever one is checked, whether by
92+
// clicking, pressing Enter, or toggling with Space (which keeps the list
93+
// open and doesn't emit activated()).
94+
connect(pModel,
95+
&QStandardItemModel::itemChanged,
96+
this,
97+
&MidiOptionsDelegate::slotItemChanged);
98+
9199
return pComboBox;
92100
}
93101

@@ -161,21 +169,46 @@ void MidiOptionsDelegate::commitAndCloseEditor(int index) {
161169
DEBUG_ASSERT(pItem);
162170
if (pItem->isCheckable()) {
163171
pItem->setCheckState(pItem->checkState() == Qt::Checked ? Qt::Unchecked : Qt::Checked);
164-
// TODO Concurrent option scan be selected. Implement a compatibility
165-
// matrix and uncheck all options that are incompatible with the last
166-
// checked option. Store initial check state for/in each item and
167-
// hook up to QStandardItemModel::itemChanged()
168172
} else {
169173
// Clear was selected. Uncheck all other items
170174
for (int row = 0; row < pModel->rowCount() - 1; row++) {
171175
if (row == index) { // Actually it's the last item, but this is safer.
172176
continue;
173177
}
174-
pItem = pModel->item(row, 0);
175-
pItem->setCheckState(Qt::Unchecked);
178+
auto* pOtherItem = pModel->item(row, 0);
179+
pOtherItem->setCheckState(Qt::Unchecked);
176180
}
177181
}
178182

179183
emit commitData(pComboBox);
180184
emit closeEditor(pComboBox);
181185
}
186+
187+
void MidiOptionsDelegate::slotItemChanged(QStandardItem* pItem) {
188+
// Return if we unchecked an item
189+
if (!pItem || !pItem->isCheckable() || pItem->checkState() != Qt::Checked) {
190+
return;
191+
}
192+
auto option = static_cast<MidiOption>(pItem->data().toUInt());
193+
// Nothing to do for non-exclusive options
194+
if (!midiOptionIsExclusive(option)) {
195+
return;
196+
}
197+
198+
// This is an exclusive option, so uncheck all other exclusive options,
199+
// but keep state of "modifiers" (Invert, SoftTakeover).
200+
// This happens on the fly so it covers toggling items with Spacebar,
201+
// as well as clicks committed via activated().
202+
const auto* pModel = qobject_cast<QStandardItemModel*>(pItem->model());
203+
DEBUG_ASSERT(pModel);
204+
for (int row = 0; row < pModel->rowCount() - 1; row++) {
205+
auto* pOtherItem = pModel->item(row, 0);
206+
if (pOtherItem == pItem || !pOtherItem->isCheckable()) {
207+
continue;
208+
}
209+
auto otherOption = static_cast<MidiOption>(pOtherItem->data().toUInt());
210+
if (midiOptionIsExclusive(otherOption)) {
211+
pOtherItem->setCheckState(Qt::Unchecked);
212+
}
213+
}
214+
}

src/controllers/delegates/midioptionsdelegate.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
#include <QStyledItemDelegate>
44

5+
class QStandardItem;
6+
57
class MidiOptionsDelegate : public QStyledItemDelegate {
68
Q_OBJECT
79
public:
@@ -20,4 +22,5 @@ class MidiOptionsDelegate : public QStyledItemDelegate {
2022

2123
private slots:
2224
void commitAndCloseEditor(int index);
25+
void slotItemChanged(QStandardItem* pItem);
2326
};

src/controllers/midi/midimessage.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,22 @@ Q_DECLARE_FLAGS(MidiOptions, MidiOption);
139139
Q_DECLARE_OPERATORS_FOR_FLAGS(MidiOptions);
140140
Q_DECLARE_METATYPE(MidiOptions);
141141

142+
inline bool midiOptionIsModifier(MidiOption option) {
143+
// Actually the same as !midiOptionIsExclusive() but kept separate
144+
// for now to allow extension
145+
return option == MidiOption::Invert || option == MidiOption::SoftTakeover;
146+
}
147+
148+
inline bool midiOptionIsExclusive(MidiOption option) {
149+
switch (option) {
150+
case MidiOption::Invert:
151+
case MidiOption::SoftTakeover:
152+
return false;
153+
default:
154+
return true;
155+
}
156+
}
157+
142158
struct MidiOutput {
143159
MidiOutput()
144160
: message(0) {

0 commit comments

Comments
 (0)