import QtQuick import QtQuick.Controls import QtQuick.Layouts import qs.Commons import qs.Services.UI import qs.Widgets Rectangle { id: root // Properties property var pluginApi: null property var note: null property int noteIndex: 0 // Color schemes property var colorSchemes: ({ "yellow": { bg: "#FFF9C4", fg: "#000000", header: "#FDD835" }, "pink": { bg: "#FCE4EC", fg: "#000000", header: "#F06292" }, "blue": { bg: "#E3F2FD", fg: "#000000", header: "#42A5F5" }, "green": { bg: "#E8F5E9", fg: "#000000", header: "#66BB6A" }, "purple": { bg: "#F3E5F5", fg: "#000000", header: "#AB47BC" } }) // Constants for sizing readonly property int minHeight: 200 readonly property int maxHeight: 600 readonly property int headerHeight: 40 readonly property int margins: 24 // Position and size from note data x: note ? note.x : 0 y: note ? note.y : 0 width: note ? note.width : 350 height: note ? note.height : minHeight z: note ? note.zIndex : 0 // Color from note data color: { const noteColor = note ? note.color : "yellow"; const scheme = colorSchemes[noteColor]; return scheme ? scheme.bg : "#FFF9C4"; } border.color: Color.mOnSurfaceVariant border.width: 1 radius: Style.radiusM // Main layout ColumnLayout { anchors.fill: parent spacing: 0 // Header Rectangle { id: headerBar Layout.fillWidth: true Layout.preferredHeight: root.headerHeight color: { const noteColor = note ? note.color : "yellow"; const scheme = colorSchemes[noteColor]; return scheme ? scheme.header : "#FDD835"; } topLeftRadius: Style.radiusM topRightRadius: Style.radiusM bottomLeftRadius: 0 bottomRightRadius: 0 RowLayout { id: headerContent anchors.left: parent.left anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter anchors.leftMargin: 10 anchors.rightMargin: 6 spacing: 10 z: 1 // Icon - DRAG HANDLE Item { Layout.preferredWidth: 24 Layout.fillHeight: true NIcon { anchors.centerIn: parent icon: "note" pointSize: 15 color: { const noteColor = note ? note.color : "yellow"; const scheme = colorSchemes[noteColor]; return scheme ? scheme.fg : "#000000"; } } MouseArea { id: dragArea anchors.fill: parent cursorShape: Qt.SizeAllCursor drag.target: root drag.axis: Drag.XAndYAxis drag.minimumX: 0 drag.maximumX: root.parent ? (root.parent.width - root.width) : 1200 drag.minimumY: 0 drag.maximumY: root.parent ? (root.parent.height - root.height) : 700 onPressed: { if (root.pluginApi && root.pluginApi.mainInstance) { root.pluginApi.mainInstance.bringNoteToFront(root.note.id); } } onReleased: { if (root.pluginApi && root.pluginApi.mainInstance) { root.pluginApi.mainInstance.updateNoteCard(root.note.id, { x: root.x, y: root.y }); } } } } // Title Item { Layout.fillWidth: true Layout.fillHeight: true Layout.minimumWidth: 100 TextInput { id: titleInput anchors.fill: parent anchors.leftMargin: 4 anchors.rightMargin: 4 verticalAlignment: TextInput.AlignVCenter horizontalAlignment: TextInput.AlignLeft color: { const noteColor = note ? note.color : "yellow"; const scheme = colorSchemes[noteColor]; return scheme ? scheme.fg : "#000000"; } font.pixelSize: 14 font.bold: false selectByMouse: true clip: true Text { anchors.fill: parent verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignLeft text: pluginApi?.tr("notecards.untitled-placeholder") color: parent.color opacity: 0.5 visible: titleInput.text.length === 0 font: titleInput.font } Component.onCompleted: { if (note) { text = note.title || ""; } } onAccepted: { root.syncChanges(); titleInput.focus = false; } } } NIconButton { icon: note && note.isPrivate ? "eye-off" : "eye" tooltipText: pluginApi?.tr("notecards.toggle-privacy-mode") colorFg: { const noteColor = note ? note.color : "yellow"; const scheme = colorSchemes[noteColor]; return scheme ? scheme.fg : "#000000"; } colorBg: "transparent" colorBgHover: Qt.rgba(0, 0, 0, 0.1) colorBorder: "transparent" colorBorderHover: "transparent" onClicked: { if (root.pluginApi && root.pluginApi.mainInstance) { root.pluginApi.mainInstance.updateNoteCard(root.note.id, { isPrivate: !(root.note && root.note.isPrivate) }); } } } NIconButton { icon: "palette" tooltipText: pluginApi?.tr("notecards.change-color") colorFg: { const noteColor = note ? note.color : "yellow"; const scheme = colorSchemes[noteColor]; return scheme ? scheme.fg : "#000000"; } colorBg: "transparent" colorBgHover: Qt.rgba(0, 0, 0, 0.1) colorBorder: "transparent" colorBorderHover: "transparent" onClicked: { const colors = ["yellow", "pink", "blue", "green", "purple"]; const noteColor = (root.note && root.note.color) ? root.note.color : "yellow"; const currentIndex = colors.indexOf(noteColor); const nextIndex = (currentIndex + 1) % colors.length; const nextColor = colors[nextIndex]; if (root.pluginApi && root.pluginApi.mainInstance) { root.pluginApi.mainInstance.updateNoteCard(root.note.id, { color: nextColor }); } } } NIconButton { icon: "file-export" tooltipText: pluginApi?.tr("notecards.export") colorFg: { const noteColor = note ? note.color : "yellow"; const scheme = colorSchemes[noteColor]; return scheme ? scheme.fg : "#000000"; } colorBg: "transparent" colorBgHover: Qt.rgba(0, 0, 0, 0.1) colorBorder: "transparent" colorBorderHover: "transparent" onClicked: { if (root.pluginApi && root.pluginApi.mainInstance) { root.pluginApi.mainInstance.exportNoteCard(root.note.id); } } } NIconButton { icon: "trash" tooltipText: pluginApi?.tr("notecards.delete") colorFg: { const noteColor = note ? note.color : "yellow"; const scheme = colorSchemes[noteColor]; return scheme ? scheme.fg : "#000000"; } colorBg: "transparent" colorBgHover: Qt.rgba(0, 0, 0, 0.1) colorBorder: "transparent" colorBorderHover: "transparent" onClicked: { if (root.pluginApi && root.pluginApi.mainInstance) { root.pluginApi.mainInstance.deleteNoteCard(root.note.id); } } } } } // Separator Rectangle { width: parent.width - 10 Layout.alignment: Qt.AlignHCenter height: 1 gradient: Gradient { orientation: Gradient.Horizontal GradientStop { position: 0.0 color: "transparent" } GradientStop { position: 0.5 color: { const noteColor = note ? note.color : "yellow"; const scheme = colorSchemes[noteColor]; return scheme ? scheme.header : "#FDD835"; } } GradientStop { position: 1.0 color: "transparent" } } } // Content area with ScrollView Item { Layout.fillWidth: true Layout.fillHeight: true Layout.margins: 12 clip: true ScrollView { anchors.fill: parent clip: true ScrollBar.horizontal.policy: ScrollBar.AlwaysOff ScrollBar.vertical.policy: ScrollBar.AsNeeded TextArea { id: textArea width: parent.width wrapMode: TextArea.Wrap selectByMouse: true color: { if (note.isPrivate && !textArea.activeFocus) { return "transparent" } const noteColor = note ? note.color : "yellow"; const scheme = colorSchemes[noteColor]; return scheme ? scheme.fg : "#000000"; } font.pixelSize: 14 background: Rectangle { color: "transparent" } Component.onCompleted: { if (note) { text = note.content || ""; } // Check if we need to expand card on load Qt.callLater(checkAndExpandHeight); } onTextChanged: { if (note) { note.content = text; } } } } Rectangle { enabled: false opacity: 1.0 visible: note.isPrivate && !textArea.activeFocus anchors.fill: parent color: "transparent" NIcon { anchors.centerIn: parent icon: "password" pointSize: 45 color: { const noteColor = note ? note.color : "yellow"; const scheme = colorSchemes[noteColor]; return scheme ? scheme.fg : "#000000"; } } } } } // Check if card needs to be expanded to fit content function checkAndExpandHeight() { if (!textArea || !note) return; const contentHeight = textArea.contentHeight; const availableHeight = root.height - root.headerHeight - root.margins - 1; // 1 = separator // If content doesn't fit, expand card if (contentHeight > availableHeight) { let newHeight = root.headerHeight + root.margins + contentHeight + 1; newHeight = Math.min(newHeight, root.maxHeight); if (newHeight !== root.height && root.pluginApi && root.pluginApi.mainInstance) { root.pluginApi.mainInstance.updateNoteCard(root.note.id, { height: newHeight }); } } } function syncChanges() { if (root.pluginApi && root.pluginApi.mainInstance && note) { root.pluginApi.mainInstance.updateNoteCard(note.id, { title: titleInput.text, content: textArea.text }); } } Component.onDestruction: { root.syncChanges(); } }