410 lines
14 KiB
QML
410 lines
14 KiB
QML
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();
|
|
}
|
|
}
|