Add collapsible properties drawer
- PropertiesRole returns QVariantMap of key->value - loadProperties() parses KEY=value lines from /properties file - QML drawer shows ':PROPERTIES:' header with ▶/▼ toggle - Properties shown as monospace :KEY: value pairs when expanded - Drawer starts collapsed by default
This commit is contained in:
parent
eb3b174b12
commit
429c896f5a
|
|
@ -51,6 +51,8 @@ ListView {
|
||||||
required property int index
|
required property int index
|
||||||
required property var model
|
required property var model
|
||||||
|
|
||||||
|
property bool drawerOpen: false
|
||||||
|
|
||||||
readonly property string sectionBody: (model.body ?? "").trim()
|
readonly property string sectionBody: (model.body ?? "").trim()
|
||||||
readonly property int sectionLevel: model.level ?? 1
|
readonly property int sectionLevel: model.level ?? 1
|
||||||
readonly property int indent: (sectionLevel - 1) * outline.indentWidth
|
readonly property int indent: (sectionLevel - 1) * outline.indentWidth
|
||||||
|
|
@ -167,6 +169,67 @@ ListView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Properties drawer (collapsible)
|
||||||
|
RowLayout {
|
||||||
|
visible: Object.keys(del.model.properties ?? {}).length > 0
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: 4
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: drawerToggle
|
||||||
|
text: del.drawerOpen ? "▼" : "▶"
|
||||||
|
color: theme.placeholderTextColor
|
||||||
|
font.pointSize: outline.baseFontSize - 2
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: del.drawerOpen = !del.drawerOpen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: ":PROPERTIES:"
|
||||||
|
color: theme.placeholderTextColor
|
||||||
|
font.pointSize: outline.baseFontSize - 1
|
||||||
|
font.family: "monospace"
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: del.drawerOpen = !del.drawerOpen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Properties content (when expanded)
|
||||||
|
ColumnLayout {
|
||||||
|
visible: del.drawerOpen && Object.keys(del.model.properties ?? {}).length > 0
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: 16
|
||||||
|
spacing: 2
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: Object.keys(del.model.properties ?? {})
|
||||||
|
delegate: RowLayout {
|
||||||
|
required property string modelData
|
||||||
|
spacing: 8
|
||||||
|
Label {
|
||||||
|
text: ":" + modelData + ":"
|
||||||
|
color: theme.linkColor
|
||||||
|
font.pointSize: outline.baseFontSize - 1
|
||||||
|
font.family: "monospace"
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
text: del.model.properties[modelData] ?? ""
|
||||||
|
color: theme.textColor
|
||||||
|
font.pointSize: outline.baseFontSize - 1
|
||||||
|
font.family: "monospace"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Body text with left rule
|
// Body text with left rule
|
||||||
RowLayout {
|
RowLayout {
|
||||||
visible: del.sectionBody !== ""
|
visible: del.sectionBody !== ""
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,10 @@ QVariant DocumentModel::data(const QModelIndex &index, int role) const
|
||||||
if (!sec.bodyLoaded)
|
if (!sec.bodyLoaded)
|
||||||
loadBody(sec);
|
loadBody(sec);
|
||||||
return sec.body;
|
return sec.body;
|
||||||
|
case PropertiesRole:
|
||||||
|
if (!sec.propertiesLoaded)
|
||||||
|
loadProperties(sec);
|
||||||
|
return sec.properties;
|
||||||
case HasChildrenRole:
|
case HasChildrenRole:
|
||||||
return !sec.childIds.isEmpty();
|
return !sec.childIds.isEmpty();
|
||||||
case ExpandedRole:
|
case ExpandedRole:
|
||||||
|
|
@ -98,6 +102,7 @@ QHash<int, QByteArray> DocumentModel::roleNames() const
|
||||||
{IsTodoRole, "isTodo"},
|
{IsTodoRole, "isTodo"},
|
||||||
{IsDoneRole, "isDone"},
|
{IsDoneRole, "isDone"},
|
||||||
{BodyRole, "body"},
|
{BodyRole, "body"},
|
||||||
|
{PropertiesRole, "properties"},
|
||||||
{HasChildrenRole, "hasChildren"},
|
{HasChildrenRole, "hasChildren"},
|
||||||
{ExpandedRole, "expanded"},
|
{ExpandedRole, "expanded"},
|
||||||
};
|
};
|
||||||
|
|
@ -224,6 +229,25 @@ void DocumentModel::loadBody(Section &sec) const
|
||||||
sec.bodyLoaded = true;
|
sec.bodyLoaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DocumentModel::loadProperties(Section &sec) const
|
||||||
|
{
|
||||||
|
if (!m_client || sec.propertiesLoaded)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QString propsStr = m_client->read(sec.path + "/properties").trimmed();
|
||||||
|
if (!propsStr.isEmpty()) {
|
||||||
|
for (const QString &line : propsStr.split('\n')) {
|
||||||
|
int eq = line.indexOf('=');
|
||||||
|
if (eq > 0) {
|
||||||
|
QString key = line.left(eq).trimmed();
|
||||||
|
QString value = line.mid(eq + 1).trimmed();
|
||||||
|
sec.properties[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sec.propertiesLoaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
void DocumentModel::rebuildVisibleRows()
|
void DocumentModel::rebuildVisibleRows()
|
||||||
{
|
{
|
||||||
QStringList oldRows = m_visibleRows;
|
QStringList oldRows = m_visibleRows;
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ public:
|
||||||
IsTodoRole, // keyword is non-empty
|
IsTodoRole, // keyword is non-empty
|
||||||
IsDoneRole, // keyword is a "done" state (DONE/CANCELLED)
|
IsDoneRole, // keyword is a "done" state (DONE/CANCELLED)
|
||||||
BodyRole, // section content beneath the headline (lazy)
|
BodyRole, // section content beneath the headline (lazy)
|
||||||
|
PropertiesRole, // QVariantMap of property key -> value
|
||||||
HasChildrenRole, // true if section has children
|
HasChildrenRole, // true if section has children
|
||||||
ExpandedRole, // true if section is expanded (children visible)
|
ExpandedRole, // true if section is expanded (children visible)
|
||||||
};
|
};
|
||||||
|
|
@ -81,6 +82,10 @@ private:
|
||||||
// Section body (lazy)
|
// Section body (lazy)
|
||||||
QString body;
|
QString body;
|
||||||
bool bodyLoaded = false;
|
bool bodyLoaded = false;
|
||||||
|
|
||||||
|
// Properties drawer (lazy)
|
||||||
|
QVariantMap properties;
|
||||||
|
bool propertiesLoaded = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Load all sections for the current document
|
// Load all sections for the current document
|
||||||
|
|
@ -95,6 +100,9 @@ private:
|
||||||
// Load body for a section
|
// Load body for a section
|
||||||
void loadBody(Section &sec) const;
|
void loadBody(Section &sec) const;
|
||||||
|
|
||||||
|
// Load properties for a section
|
||||||
|
void loadProperties(Section &sec) const;
|
||||||
|
|
||||||
// Rebuild the visible row list based on expansion state
|
// Rebuild the visible row list based on expansion state
|
||||||
void rebuildVisibleRows();
|
void rebuildVisibleRows();
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue