Cycle checkboxes with the mouse wheel
Scrolling over a checkbox glyph cycles its state unchecked -> partial -> checked (and back), respecting scroll direction: wheel up steps forward, wheel down steps in reverse. Left-click still toggles checked/unchecked. The shared rewriteCheckbox helper applies a state-mapping function to the Nth cookie; toggleCheckbox and the new cycleCheckbox are thin callers.
This commit is contained in:
parent
3d3ccd1e11
commit
39ab7108ff
|
|
@ -473,7 +473,8 @@ ListView {
|
|||
font.pointSize: outline.baseFontSize + 1
|
||||
font.bold: cb !== ""
|
||||
|
||||
// Clickable only for checkbox items; toggles via the model.
|
||||
// Checkbox interaction: left-click toggles,
|
||||
// mouse wheel cycles ( ) -> (-) -> (X).
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
enabled: cbGlyph.cb !== ""
|
||||
|
|
@ -481,6 +482,15 @@ ListView {
|
|||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: docModel.toggleCheckbox(
|
||||
del.index, modelData.checkboxOrdinal ?? -1)
|
||||
onWheel: (wheel) => {
|
||||
// Scroll up = forward, down = reverse.
|
||||
let forward = wheel.angleDelta.y > 0
|
||||
docModel.cycleCheckbox(
|
||||
del.index,
|
||||
modelData.checkboxOrdinal ?? -1,
|
||||
forward)
|
||||
wheel.accepted = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -187,7 +187,9 @@ bool DocumentModel::isLanguageSupported(const QString &lang)
|
|||
return m_supportedLanguages.contains(lang.trimmed().toLower());
|
||||
}
|
||||
|
||||
bool DocumentModel::toggleCheckbox(int row, int checkboxOrdinal)
|
||||
bool DocumentModel::rewriteCheckbox(
|
||||
int row, int checkboxOrdinal,
|
||||
const std::function<QString(const QString &)> &nextState)
|
||||
{
|
||||
if (!m_client || row < 0 || row >= m_visibleRows.size())
|
||||
return false;
|
||||
|
|
@ -210,28 +212,24 @@ bool DocumentModel::toggleCheckbox(int row, int checkboxOrdinal)
|
|||
out.reserve(lines.size());
|
||||
|
||||
int seen = -1;
|
||||
bool toggled = false;
|
||||
bool changed = false;
|
||||
for (const QString &line : lines) {
|
||||
QRegularExpressionMatch m = cookieRe.match(line);
|
||||
if (m.hasMatch()) {
|
||||
++seen;
|
||||
if (seen == checkboxOrdinal) {
|
||||
const QString state = m.captured(2);
|
||||
const QString next = (state == QLatin1String("X") ||
|
||||
state == QLatin1String("x"))
|
||||
? QStringLiteral(" ")
|
||||
: QStringLiteral("X");
|
||||
const QString next = nextState(m.captured(2));
|
||||
QString updated = line;
|
||||
updated.replace(m.capturedStart(2), m.capturedLength(2), next);
|
||||
out.append(updated);
|
||||
toggled = true;
|
||||
changed = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
out.append(line);
|
||||
}
|
||||
|
||||
if (!toggled)
|
||||
if (!changed)
|
||||
return false;
|
||||
|
||||
const QString newBody = out.join('\n');
|
||||
|
|
@ -247,6 +245,37 @@ bool DocumentModel::toggleCheckbox(int row, int checkboxOrdinal)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool DocumentModel::toggleCheckbox(int row, int checkboxOrdinal)
|
||||
{
|
||||
// Checked -> unchecked; unchecked/partial -> checked.
|
||||
return rewriteCheckbox(row, checkboxOrdinal, [](const QString &state) {
|
||||
const bool checked = (state == QLatin1String("X") ||
|
||||
state == QLatin1String("x"));
|
||||
return checked ? QStringLiteral(" ") : QStringLiteral("X");
|
||||
});
|
||||
}
|
||||
|
||||
bool DocumentModel::cycleCheckbox(int row, int checkboxOrdinal, bool forward)
|
||||
{
|
||||
// Cycle order: unchecked ( ) -> partial (-) -> checked (X) -> wrap.
|
||||
return rewriteCheckbox(row, checkboxOrdinal, [forward](const QString &state) {
|
||||
// Normalize current state to an index in the cycle.
|
||||
int cur;
|
||||
if (state == QLatin1String("-"))
|
||||
cur = 1; // partial
|
||||
else if (state == QLatin1String("X") || state == QLatin1String("x"))
|
||||
cur = 2; // checked
|
||||
else
|
||||
cur = 0; // unchecked
|
||||
const int next = forward ? (cur + 1) % 3 : (cur + 2) % 3;
|
||||
switch (next) {
|
||||
case 1: return QStringLiteral("-");
|
||||
case 2: return QStringLiteral("X");
|
||||
default: return QStringLiteral(" ");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void DocumentModel::loadDocument()
|
||||
{
|
||||
beginResetModel();
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@
|
|||
#include <QVector>
|
||||
#include <QSet>
|
||||
|
||||
#include <functional>
|
||||
|
||||
class OrkClient;
|
||||
|
||||
class DocumentModel : public QAbstractListModel {
|
||||
|
|
@ -76,6 +78,11 @@ public:
|
|||
// back via /body, and refreshes the row. Returns true on success.
|
||||
Q_INVOKABLE bool toggleCheckbox(int row, int checkboxOrdinal);
|
||||
|
||||
// Cycle the Nth checkbox through unchecked -> checked -> partial and back.
|
||||
// `forward` steps unchecked->checked->partial; otherwise the reverse. Writes
|
||||
// the section body back via /body and refreshes the row. Returns success.
|
||||
Q_INVOKABLE bool cycleCheckbox(int row, int checkboxOrdinal, bool forward);
|
||||
|
||||
signals:
|
||||
void documentChanged();
|
||||
|
||||
|
|
@ -123,6 +130,12 @@ private:
|
|||
// Rebuild the visible row list based on expansion state
|
||||
void rebuildVisibleRows();
|
||||
|
||||
// Rewrite the Nth checkbox cookie (document order) in the given row's
|
||||
// section body, mapping the current state char (' '/'x'/'X'/'-') to a new
|
||||
// one via `nextState`. Writes back via /body and refreshes. Returns success.
|
||||
bool rewriteCheckbox(int row, int checkboxOrdinal,
|
||||
const std::function<QString(const QString &)> &nextState);
|
||||
|
||||
OrkClient *m_client = nullptr;
|
||||
QString m_document;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue