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 var model
|
||||
|
||||
property bool drawerOpen: false
|
||||
|
||||
readonly property string sectionBody: (model.body ?? "").trim()
|
||||
readonly property int sectionLevel: model.level ?? 1
|
||||
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
|
||||
RowLayout {
|
||||
visible: del.sectionBody !== ""
|
||||
|
|
|
|||
|
|
@ -74,6 +74,10 @@ QVariant DocumentModel::data(const QModelIndex &index, int role) const
|
|||
if (!sec.bodyLoaded)
|
||||
loadBody(sec);
|
||||
return sec.body;
|
||||
case PropertiesRole:
|
||||
if (!sec.propertiesLoaded)
|
||||
loadProperties(sec);
|
||||
return sec.properties;
|
||||
case HasChildrenRole:
|
||||
return !sec.childIds.isEmpty();
|
||||
case ExpandedRole:
|
||||
|
|
@ -98,6 +102,7 @@ QHash<int, QByteArray> DocumentModel::roleNames() const
|
|||
{IsTodoRole, "isTodo"},
|
||||
{IsDoneRole, "isDone"},
|
||||
{BodyRole, "body"},
|
||||
{PropertiesRole, "properties"},
|
||||
{HasChildrenRole, "hasChildren"},
|
||||
{ExpandedRole, "expanded"},
|
||||
};
|
||||
|
|
@ -224,6 +229,25 @@ void DocumentModel::loadBody(Section &sec) const
|
|||
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()
|
||||
{
|
||||
QStringList oldRows = m_visibleRows;
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ public:
|
|||
IsTodoRole, // keyword is non-empty
|
||||
IsDoneRole, // keyword is a "done" state (DONE/CANCELLED)
|
||||
BodyRole, // section content beneath the headline (lazy)
|
||||
PropertiesRole, // QVariantMap of property key -> value
|
||||
HasChildrenRole, // true if section has children
|
||||
ExpandedRole, // true if section is expanded (children visible)
|
||||
};
|
||||
|
|
@ -81,6 +82,10 @@ private:
|
|||
// Section body (lazy)
|
||||
QString body;
|
||||
bool bodyLoaded = false;
|
||||
|
||||
// Properties drawer (lazy)
|
||||
QVariantMap properties;
|
||||
bool propertiesLoaded = false;
|
||||
};
|
||||
|
||||
// Load all sections for the current document
|
||||
|
|
@ -95,6 +100,9 @@ private:
|
|||
// Load body for a section
|
||||
void loadBody(Section &sec) const;
|
||||
|
||||
// Load properties for a section
|
||||
void loadProperties(Section &sec) const;
|
||||
|
||||
// Rebuild the visible row list based on expansion state
|
||||
void rebuildVisibleRows();
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue