qs: add notecard
This commit is contained in:
@@ -4,10 +4,10 @@ import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Bluetooth
|
||||
import Quickshell.Wayland
|
||||
import qs.Constants
|
||||
import qs.Services
|
||||
import qs.Components
|
||||
import qs.Constants
|
||||
import qs.Modules.Sidebar.Misc
|
||||
import qs.Services
|
||||
|
||||
ColumnLayout {
|
||||
spacing: Style.marginM
|
||||
@@ -119,7 +119,6 @@ ColumnLayout {
|
||||
ColumnLayout {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
spacing: Style.marginL
|
||||
|
||||
visible: {
|
||||
if (!BluetoothService.adapter || !BluetoothService.adapter.discovering || !Bluetooth.devices)
|
||||
return false;
|
||||
@@ -138,7 +137,7 @@ ColumnLayout {
|
||||
}
|
||||
|
||||
UText {
|
||||
text: "Pairing Mode"
|
||||
text: "Scanning for devices..."
|
||||
pointSize: Style.fontSizeM
|
||||
color: Colors.mOnSurfaceVariant
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
@@ -8,7 +8,7 @@ import qs.Services
|
||||
UBox {
|
||||
id: root
|
||||
|
||||
property string currentPanel: "bluetooth" // "bluetooth", "wifi"
|
||||
property string currentPanel: ShellState.leftSiderbarTab // "bluetooth", "wifi"
|
||||
|
||||
implicitHeight: contentLoader.implicitHeight + toggleGroup.implicitHeight + Style.marginXS * 2 + Style.marginS * 2
|
||||
|
||||
@@ -21,13 +21,14 @@ UBox {
|
||||
Layout.fillWidth: true
|
||||
|
||||
Rectangle {
|
||||
// border.color: Colors.mOutline
|
||||
|
||||
id: toggleGroup
|
||||
|
||||
Layout.preferredWidth: Style.baseWidgetSize * 2.8
|
||||
Layout.preferredHeight: Style.baseWidgetSize
|
||||
radius: Math.min(Style.radiusS, height / 2)
|
||||
color: Colors.mSurface
|
||||
// border.color: Colors.mOutline
|
||||
|
||||
Row {
|
||||
anchors.fill: parent
|
||||
@@ -41,18 +42,6 @@ UBox {
|
||||
radius: Math.min(Style.radiusS, height / 2)
|
||||
color: root.currentPanel === "bluetooth" ? Colors.mPrimary : "transparent"
|
||||
|
||||
Behavior on width {
|
||||
NumberAnimation {
|
||||
duration: 250
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: 200
|
||||
}
|
||||
}
|
||||
|
||||
UIcon {
|
||||
anchors.centerIn: parent
|
||||
iconName: "bluetooth"
|
||||
@@ -63,14 +52,32 @@ UBox {
|
||||
ColorAnimation {
|
||||
duration: 200
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: root.currentPanel = "bluetooth"
|
||||
onClicked: ShellState.leftSiderbarTab = "bluetooth"
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
|
||||
Behavior on width {
|
||||
NumberAnimation {
|
||||
duration: 250
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: 200
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
@@ -81,18 +88,6 @@ UBox {
|
||||
radius: Math.min(Style.radiusS, height / 2)
|
||||
color: root.currentPanel === "wifi" ? Colors.mPrimary : "transparent"
|
||||
|
||||
Behavior on width {
|
||||
NumberAnimation {
|
||||
duration: 250
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: 200
|
||||
}
|
||||
}
|
||||
|
||||
UIcon {
|
||||
anchors.centerIn: parent
|
||||
iconName: "wifi"
|
||||
@@ -103,16 +98,36 @@ UBox {
|
||||
ColorAnimation {
|
||||
duration: 200
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: root.currentPanel = "wifi"
|
||||
onClicked: ShellState.leftSiderbarTab = "wifi"
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
|
||||
Behavior on width {
|
||||
NumberAnimation {
|
||||
duration: 250
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: 200
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Item {
|
||||
@@ -147,7 +162,9 @@ UBox {
|
||||
}
|
||||
colorFg: Colors.mGreen
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Component {
|
||||
@@ -171,8 +188,11 @@ UBox {
|
||||
onClicked: NetworkService.scan()
|
||||
colorFg: Colors.mGreen
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -186,7 +206,6 @@ UBox {
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
sourceComponent: currentPanel === "bluetooth" ? bluetoothComponent : wifiComponent
|
||||
|
||||
Component {
|
||||
@@ -196,6 +215,7 @@ UBox {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginS
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Component {
|
||||
@@ -205,7 +225,11 @@ UBox {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginS
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import qs.Components
|
||||
import qs.Constants
|
||||
import qs.Services
|
||||
import qs.Utils
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property real calculatedHeight: headerBox.implicitHeight + scrollView.implicitHeight + Style.marginL * 2 + Style.marginM
|
||||
property real contentPreferredHeight: Math.min(root.height, Math.ceil(calculatedHeight))
|
||||
property real layoutWidth: Math.max(1, root.width - Style.marginL * 2)
|
||||
|
||||
color: "transparent"
|
||||
|
||||
ColumnLayout {
|
||||
id: mainColumn
|
||||
|
||||
anchors.fill: parent
|
||||
spacing: Style.marginM
|
||||
|
||||
// Header
|
||||
UBox {
|
||||
id: headerBox
|
||||
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: header.implicitHeight + Style.marginM * 2
|
||||
|
||||
ColumnLayout {
|
||||
id: header
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginM
|
||||
spacing: Style.marginM
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
UIcon {
|
||||
iconName: "notes"
|
||||
iconSize: Style.fontSizeXXL
|
||||
color: Colors.mPrimary
|
||||
}
|
||||
|
||||
UText {
|
||||
text: "Notes" + " (" + NotesService.notesModel.count + ")"
|
||||
pointSize: Style.fontSizeL
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Colors.mOnSurface
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
UIconButton {
|
||||
iconName: "plus"
|
||||
baseSize: Style.baseWidgetSize * 0.8
|
||||
colorFg: Colors.mGreen
|
||||
onClicked: NotesService.createNote()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
NScrollView {
|
||||
id: scrollView
|
||||
|
||||
anchors.fill: parent
|
||||
implicitHeight: notesColumn.implicitHeight
|
||||
|
||||
ColumnLayout {
|
||||
id: notesColumn
|
||||
|
||||
width: scrollView.width
|
||||
spacing: Style.marginM
|
||||
|
||||
Repeater {
|
||||
model: NotesService.notesModel
|
||||
|
||||
delegate: UBox {
|
||||
property color accentColor: Colors.cavaList[model.colorIdx % Colors.cavaList.length]
|
||||
|
||||
width: notesColumn.width
|
||||
implicitHeight: noteLayout.implicitHeight + Style.marginM * 2
|
||||
|
||||
Rectangle {
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
width: Style.marginS
|
||||
color: parent.accentColor
|
||||
radius: Style.radiusS
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onClicked: NotesService.openNote(model.noteId)
|
||||
}
|
||||
|
||||
FileView {
|
||||
id: fileView
|
||||
|
||||
path: NotesService.notesDir + "/" + model.noteId + ".txt"
|
||||
watchChanges: true
|
||||
onFileChanged: reload()
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: noteLayout
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginM
|
||||
anchors.leftMargin: Style.marginM + Style.marginS
|
||||
|
||||
UText {
|
||||
Layout.fillWidth: true
|
||||
text: {
|
||||
var t = fileView.text();
|
||||
if (!t)
|
||||
return "(empty note)";
|
||||
|
||||
return t.trim().split('\n').slice(0, 5).join('\n');
|
||||
}
|
||||
wrapMode: Text.Wrap
|
||||
elide: Text.ElideRight
|
||||
maximumLineCount: 5
|
||||
}
|
||||
|
||||
UIconButton {
|
||||
iconName: "trash"
|
||||
baseSize: Style.baseWidgetSize * 0.8
|
||||
colorFg: Colors.mError
|
||||
onClicked: NotesService.deleteNote(model.noteId)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -491,7 +491,7 @@ Rectangle {
|
||||
readonly property real swipeDismissThreshold: Math.max(110, width * 0.3)
|
||||
readonly property int removeAnimationDuration: Style.animationNormal
|
||||
readonly property int notificationTextFormat: notificationDelegate.isExpanded ? Text.MarkdownText : Text.StyledText
|
||||
readonly property real actionButtonSize: Style.baseWidgetSize * 0.7
|
||||
readonly property real actionButtonSize: Style.baseWidgetSize * 0.8
|
||||
readonly property real buttonClusterWidth: notificationDelegate.actionButtonSize * 2 + Style.marginXS
|
||||
readonly property real iconSize: 40
|
||||
// Parse actions safely
|
||||
|
||||
+144
@@ -0,0 +1,144 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs.Components
|
||||
import qs.Constants
|
||||
import qs.Services
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property string currentPanel: ShellState.rightSiderbarTab // "notifications", "notes"
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
spacing: Style.marginM
|
||||
|
||||
Rectangle {
|
||||
id: toggleGroup
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.preferredWidth: Style.baseWidgetSize * 2.8
|
||||
Layout.preferredHeight: Style.baseWidgetSize
|
||||
radius: Math.min(Style.radiusS, height / 2)
|
||||
color: Colors.mSurface
|
||||
|
||||
Row {
|
||||
anchors.fill: parent
|
||||
spacing: Style.marginS / 2
|
||||
|
||||
Rectangle {
|
||||
id: btnNotifications
|
||||
|
||||
width: root.currentPanel === "notifications" ? (parent.width - parent.spacing) * 0.65 : (parent.width - parent.spacing) * 0.35
|
||||
height: parent.height
|
||||
radius: Math.min(Style.radiusS, height / 2)
|
||||
color: root.currentPanel === "notifications" ? Colors.mPrimary : "transparent"
|
||||
|
||||
UIcon {
|
||||
anchors.centerIn: parent
|
||||
iconName: "bell"
|
||||
iconSize: Style.fontSizeL
|
||||
color: root.currentPanel === "notifications" ? Colors.mOnPrimary : Colors.mOnSurface
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: 200
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: ShellState.rightSiderbarTab = "notifications"
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
|
||||
Behavior on width {
|
||||
NumberAnimation {
|
||||
duration: 250
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: 200
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: btnNotes
|
||||
|
||||
width: root.currentPanel === "notes" ? (parent.width - parent.spacing) * 0.65 : (parent.width - parent.spacing) * 0.35
|
||||
height: parent.height
|
||||
radius: Math.min(Style.radiusS, height / 2)
|
||||
color: root.currentPanel === "notes" ? Colors.mPrimary : "transparent"
|
||||
|
||||
UIcon {
|
||||
anchors.centerIn: parent
|
||||
iconName: "notes"
|
||||
iconSize: Style.fontSizeL
|
||||
color: root.currentPanel === "notes" ? Colors.mOnPrimary : Colors.mOnSurface
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: 200
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: ShellState.rightSiderbarTab = "notes"
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
|
||||
Behavior on width {
|
||||
NumberAnimation {
|
||||
duration: 250
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: 200
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
NotificationHistoryCard {
|
||||
anchors.fill: parent
|
||||
visible: root.currentPanel === "notifications"
|
||||
}
|
||||
|
||||
NoteCard {
|
||||
anchors.fill: parent
|
||||
visible: root.currentPanel === "notes"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -69,7 +69,7 @@ Variants {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
NotificationHistoryCard {
|
||||
NotificationNoteToggleCard {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import qs.Constants
|
||||
import qs.Utils
|
||||
pragma Singleton
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
property string notesDir: Paths.cacheDir + "/notes"
|
||||
property var notes: []
|
||||
property ListModel notesModel
|
||||
|
||||
function loadNotes() {
|
||||
listProcess.running = true;
|
||||
}
|
||||
|
||||
function createNote() {
|
||||
var id = new Date().getTime().toString();
|
||||
var filePath = notesDir + "/" + id + ".txt";
|
||||
// Random color index from 0 to 7
|
||||
var colorIdx = Math.floor(Math.random() * 8);
|
||||
createProcess.command = ["sh", "-c", "mkdir -p " + notesDir + " && echo 'New Note' > " + filePath + " && echo " + colorIdx + " > " + filePath + ".color"];
|
||||
createProcess.running = true;
|
||||
}
|
||||
|
||||
function deleteNote(id) {
|
||||
var filePath = notesDir + "/" + id + ".txt";
|
||||
var colorPath = notesDir + "/" + id + ".txt.color";
|
||||
deleteProcess.command = ["rm", "-f", filePath, colorPath];
|
||||
deleteProcess.running = true;
|
||||
}
|
||||
|
||||
function openNote(id) {
|
||||
var filePath = notesDir + "/" + id + ".txt";
|
||||
openProcess.command = ["gnome-text-editor", filePath];
|
||||
openProcess.running = true;
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
loadNotes();
|
||||
}
|
||||
|
||||
Process {
|
||||
id: openProcess
|
||||
}
|
||||
|
||||
Process {
|
||||
id: createProcess
|
||||
|
||||
onExited: root.loadNotes()
|
||||
}
|
||||
|
||||
Process {
|
||||
id: deleteProcess
|
||||
|
||||
onExited: root.loadNotes()
|
||||
}
|
||||
|
||||
Process {
|
||||
id: listProcess
|
||||
|
||||
command: ["sh", "-c", "mkdir -p " + notesDir + " && ls -1 " + notesDir + " | grep '\\.txt$' || true"]
|
||||
|
||||
stdout: StdioCollector {
|
||||
id: listCollector
|
||||
|
||||
onStreamFinished: {
|
||||
var files = listCollector.text.split('\n');
|
||||
notesModel.clear();
|
||||
for (var i = 0; i < files.length; i++) {
|
||||
if (files[i] === "")
|
||||
continue;
|
||||
|
||||
var id = files[i].replace(".txt", "");
|
||||
var contentFile = notesDir + "/" + files[i];
|
||||
var colorFile = notesDir + "/" + files[i] + ".color";
|
||||
// create an intermediate reader process
|
||||
readProcessComponent.createObject(root, {
|
||||
"noteId": id,
|
||||
"contentFile": contentFile,
|
||||
"colorFile": colorFile
|
||||
}).run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Component {
|
||||
id: readProcessComponent
|
||||
|
||||
Process {
|
||||
id: p
|
||||
|
||||
property string noteId
|
||||
property string contentFile
|
||||
property string colorFile
|
||||
|
||||
function run() {
|
||||
running = true;
|
||||
}
|
||||
|
||||
command: ["sh", "-c", "cat " + colorFile + " 2>/dev/null || echo 0; head -n 5 " + contentFile]
|
||||
|
||||
stdout: StdioCollector {
|
||||
id: readCollector
|
||||
|
||||
onStreamFinished: {
|
||||
var lines = readCollector.text.split('\n');
|
||||
var colorIdx = parseInt(lines[0] || "0");
|
||||
lines.shift();
|
||||
var contentLines = lines.join('\n').trim();
|
||||
notesModel.append({
|
||||
"noteId": p.noteId,
|
||||
"title": contentLines,
|
||||
"colorIdx": colorIdx
|
||||
});
|
||||
p.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
notesModel: ListModel {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,6 +13,8 @@ Singleton {
|
||||
property alias notificationsState: adapter.notificationsState
|
||||
property alias lyricsState: adapter.lyricsState
|
||||
property alias sunsetState: adapter.sunsetState
|
||||
property alias leftSiderbarTab: adapter.leftSiderbarTab
|
||||
property alias rightSiderbarTab: adapter.rightSiderbarTab
|
||||
|
||||
function save() {
|
||||
saveTimer.restart();
|
||||
@@ -21,6 +23,8 @@ Singleton {
|
||||
onNotificationsStateChanged: save()
|
||||
onLyricsStateChanged: save()
|
||||
onSunsetStateChanged: save()
|
||||
onLeftSiderbarTabChanged: save()
|
||||
onRightSiderbarTabChanged: save()
|
||||
Component.onCompleted: {
|
||||
stateFileView.path = stateFile;
|
||||
}
|
||||
@@ -51,6 +55,8 @@ Singleton {
|
||||
property var sunsetState: ({
|
||||
"enabled": true
|
||||
})
|
||||
property string leftSiderbarTab: "bluetooth"
|
||||
property string rightSiderbarTab: "notes"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user