tangled
alpha
login
or
join now
dzming.li
/
noctalia-plugins
0
fork
atom
Personal noctalia plugins collection
0
fork
atom
overview
issues
pulls
pipelines
Improve todo rendering
Dzming Li
2 weeks ago
d2438c23
58f5ba81
+475
-28
5 changed files
expand all
collapse all
unified
split
weekly-calendar
Main.qml
Panel.qml
i18n
en.json
zh-CN.json
scripts
update-todo.py
+33
-1
weekly-calendar/Main.qml
···
294
294
if (!showCompletedTodos && todo.status === "COMPLETED") continue
295
295
296
296
var isDueAllDay = (dueDate.getHours() === 0 && dueDate.getMinutes() === 0)
297
297
-
// Render timed todos as short deadline markers (~8–10 px tall)
297
297
+
// Render timed todos as horizontal line markers (keep a short span for ordering/click hitbox)
298
298
var endDate = isDueAllDay ? new Date(dueDate.getTime() + 86400000)
299
299
: new Date(dueDate.getTime() + 30 * 60000)
300
300
···
313
313
calendarUid: todo.calendarUid,
314
314
todoStatus: todo.status,
315
315
todoPriority: todo.priority,
316
316
+
isLineTodo: !isDueAllDay,
316
317
// Helper flags for compact rendering in Panel.qml
317
318
isDeadlineMarker: !isDueAllDay
318
319
}
···
470
471
var events = []
471
472
for (var i = 0; i < eventsModel.count; i++) {
472
473
var e = eventsModel.get(i)
474
474
+
if (e.isTodo) continue // Timed todos render as overlay lines; don't occupy lanes
473
475
if (getDisplayDayIndexForDate(e.startTime) === day) {
474
476
events.push({index: i, start: e.startTime.getTime(), end: e.endTime.getTime()})
475
477
}
···
637
639
property var selectedEvent: null
638
640
property bool showEventDetail: false
639
641
642
642
+
// Todo detail popup state
643
643
+
property var selectedTodo: null
644
644
+
property bool showTodoDetail: false
645
645
+
640
646
function handleEventClick(event) {
641
647
selectedEvent = {
642
648
title: event.title || "",
···
672
678
if (endTs > 0) { args.push("--end"); args.push(String(endTs)) }
673
679
updateEventProcess.command = args
674
680
updateEventProcess.running = true
681
681
+
}
682
682
+
683
683
+
function handleTodoClick(todoData) {
684
684
+
selectedTodo = {
685
685
+
summary: todoData.title || todoData.summary || "",
686
686
+
description: todoData.description || "",
687
687
+
todoUid: todoData.todoUid || "",
688
688
+
calendarUid: todoData.calendarUid || "",
689
689
+
status: todoData.todoStatus || "",
690
690
+
priority: todoData.todoPriority || 0,
691
691
+
due: todoData.startTime || null
692
692
+
}
693
693
+
showTodoDetail = true
694
694
+
}
695
695
+
696
696
+
function updateTodoFields(taskListUid, todoUid, summary, description, dueTs, priority) {
697
697
+
if (!pluginApi) return
698
698
+
var scriptPath = pluginApi.pluginDir + "/scripts/update-todo.py"
699
699
+
var args = ["python3", scriptPath,
700
700
+
"--task-list", taskListUid, "--uid", todoUid, "--action", "update"]
701
701
+
if (summary !== undefined && summary !== null) { args.push("--summary"); args.push(summary) }
702
702
+
if (description !== undefined && description !== null) { args.push("--description"); args.push(description) }
703
703
+
if (dueTs > 0) { args.push("--due"); args.push(String(dueTs)) }
704
704
+
if (priority >= 0) { args.push("--priority"); args.push(String(priority)) }
705
705
+
updateTodoProcess.command = args
706
706
+
updateTodoProcess.running = true
675
707
}
676
708
677
709
function goToToday() { currentDate = new Date() }
+400
-24
weekly-calendar/Panel.qml
···
25
25
property bool showEventDetailDialog: false
26
26
property bool eventDetailEditMode: false
27
27
property bool showDeleteConfirmation: false
28
28
+
property bool showTodoDetailDialog: false
29
29
+
property bool todoDetailEditMode: false
30
30
+
property bool showTodoDeleteConfirmation: false
28
31
29
32
property real defaultHourHeight: 50 * Style.uiScaleRatio
30
33
property real minHourHeight: 32 * Style.uiScaleRatio
···
711
714
}
712
715
}
713
716
717
717
+
// Todo detail/edit popup
718
718
+
Rectangle {
719
719
+
id: todoDetailOverlay
720
720
+
anchors.fill: parent
721
721
+
color: Qt.rgba(0, 0, 0, 0.5)
722
722
+
visible: showTodoDetailDialog
723
723
+
z: 2000
724
724
+
725
725
+
MouseArea { anchors.fill: parent; onClicked: { showTodoDetailDialog = false; todoDetailEditMode = false; showTodoDeleteConfirmation = false } }
726
726
+
727
727
+
Rectangle {
728
728
+
anchors.centerIn: parent
729
729
+
width: 420 * Style.uiScaleRatio
730
730
+
height: todoDetailColumn.implicitHeight + 2 * Style.marginM
731
731
+
color: Color.mSurface
732
732
+
radius: Style.radiusM
733
733
+
734
734
+
MouseArea { anchors.fill: parent }
735
735
+
736
736
+
ColumnLayout {
737
737
+
id: todoDetailColumn
738
738
+
anchors.fill: parent
739
739
+
anchors.margins: Style.marginM
740
740
+
spacing: Style.marginS
741
741
+
742
742
+
property var todo: mainInstance?.selectedTodo || {}
743
743
+
744
744
+
// View mode
745
745
+
ColumnLayout {
746
746
+
visible: !todoDetailEditMode && !showTodoDeleteConfirmation
747
747
+
spacing: Style.marginS
748
748
+
Layout.fillWidth: true
749
749
+
750
750
+
NText {
751
751
+
text: (todoDetailColumn.todo.status === "COMPLETED" ? "\u2611 " : "\u2610 ") + (todoDetailColumn.todo.summary || "")
752
752
+
font.pointSize: Style.fontSizeL; font.weight: Font.Bold
753
753
+
color: Color.mOnSurface
754
754
+
wrapMode: Text.Wrap; Layout.fillWidth: true
755
755
+
}
756
756
+
757
757
+
RowLayout {
758
758
+
visible: todoDetailColumn.todo.due != null
759
759
+
spacing: Style.marginS
760
760
+
NIcon { icon: "clock"; pointSize: Style.fontSizeS; color: Color.mOnSurfaceVariant }
761
761
+
NText {
762
762
+
text: todoDetailColumn.todo.due ? mainInstance?.formatDateTime(new Date(todoDetailColumn.todo.due)) || "" : ""
763
763
+
font.pointSize: Style.fontSizeS; color: Color.mOnSurfaceVariant
764
764
+
}
765
765
+
}
766
766
+
767
767
+
RowLayout {
768
768
+
visible: (todoDetailColumn.todo.priority || 0) > 0
769
769
+
spacing: Style.marginS
770
770
+
NIcon { icon: "flag"; pointSize: Style.fontSizeS; color: Color.mOnSurfaceVariant }
771
771
+
NText {
772
772
+
text: {
773
773
+
var p = todoDetailColumn.todo.priority || 0
774
774
+
return p <= 4 ? (pluginApi.tr("panel.priority") + ": " + pluginApi.tr("panel.priority_high")) :
775
775
+
p <= 6 ? (pluginApi.tr("panel.priority") + ": " + pluginApi.tr("panel.priority_medium")) :
776
776
+
(pluginApi.tr("panel.priority") + ": " + pluginApi.tr("panel.priority_low"))
777
777
+
}
778
778
+
font.pointSize: Style.fontSizeS; color: Color.mOnSurfaceVariant
779
779
+
}
780
780
+
}
781
781
+
782
782
+
NText {
783
783
+
visible: (todoDetailColumn.todo.description || "") !== ""
784
784
+
text: todoDetailColumn.todo.description || ""
785
785
+
font.pointSize: Style.fontSizeS; color: Color.mOnSurfaceVariant
786
786
+
wrapMode: Text.Wrap; Layout.fillWidth: true
787
787
+
}
788
788
+
789
789
+
RowLayout {
790
790
+
Layout.fillWidth: true
791
791
+
spacing: Style.marginS
792
792
+
793
793
+
// Toggle complete button
794
794
+
Rectangle {
795
795
+
Layout.preferredWidth: toggleTodoBtn.implicitWidth + 2 * Style.marginM
796
796
+
Layout.preferredHeight: toggleTodoBtn.implicitHeight + Style.marginS
797
797
+
color: Color.mSecondary; radius: Style.radiusS
798
798
+
NText {
799
799
+
id: toggleTodoBtn; anchors.centerIn: parent
800
800
+
text: todoDetailColumn.todo.status === "COMPLETED" ? "\u2610" : "\u2611"
801
801
+
color: Color.mOnSecondary; font.weight: Font.Bold
802
802
+
}
803
803
+
MouseArea {
804
804
+
anchors.fill: parent; cursorShape: Qt.PointingHandCursor
805
805
+
onClicked: {
806
806
+
var t = todoDetailColumn.todo
807
807
+
if (t.status === "COMPLETED")
808
808
+
mainInstance?.uncompleteTodo(t.calendarUid, t.todoUid)
809
809
+
else
810
810
+
mainInstance?.completeTodo(t.calendarUid, t.todoUid)
811
811
+
showTodoDetailDialog = false
812
812
+
}
813
813
+
}
814
814
+
}
815
815
+
816
816
+
Item { Layout.fillWidth: true }
817
817
+
818
818
+
Rectangle {
819
819
+
Layout.preferredWidth: editTodoBtn.implicitWidth + 2 * Style.marginM
820
820
+
Layout.preferredHeight: editTodoBtn.implicitHeight + Style.marginS
821
821
+
color: Color.mPrimary; radius: Style.radiusS
822
822
+
NText {
823
823
+
id: editTodoBtn; anchors.centerIn: parent
824
824
+
text: pluginApi.tr("panel.edit"); color: Color.mOnPrimary; font.weight: Font.Bold
825
825
+
}
826
826
+
MouseArea {
827
827
+
anchors.fill: parent; cursorShape: Qt.PointingHandCursor
828
828
+
onClicked: {
829
829
+
var t = todoDetailColumn.todo
830
830
+
editTodoSummary.text = t.summary || ""
831
831
+
editTodoDescription.text = t.description || ""
832
832
+
if (t.due) {
833
833
+
var d = new Date(t.due)
834
834
+
editTodoDueDate.text = d.getFullYear() + "-" + String(d.getMonth()+1).padStart(2,'0') + "-" + String(d.getDate()).padStart(2,'0')
835
835
+
editTodoDueTime.text = String(d.getHours()).padStart(2,'0') + ":" + String(d.getMinutes()).padStart(2,'0')
836
836
+
} else {
837
837
+
editTodoDueDate.text = ""
838
838
+
editTodoDueTime.text = ""
839
839
+
}
840
840
+
editTodoPriority = t.priority || 0
841
841
+
todoDetailEditMode = true
842
842
+
}
843
843
+
}
844
844
+
}
845
845
+
846
846
+
Rectangle {
847
847
+
Layout.preferredWidth: deleteTodoBtn.implicitWidth + 2 * Style.marginM
848
848
+
Layout.preferredHeight: deleteTodoBtn.implicitHeight + Style.marginS
849
849
+
color: Color.mError; radius: Style.radiusS
850
850
+
NText {
851
851
+
id: deleteTodoBtn; anchors.centerIn: parent
852
852
+
text: pluginApi.tr("panel.delete"); color: Color.mOnError; font.weight: Font.Bold
853
853
+
}
854
854
+
MouseArea {
855
855
+
anchors.fill: parent; cursorShape: Qt.PointingHandCursor
856
856
+
onClicked: showTodoDeleteConfirmation = true
857
857
+
}
858
858
+
}
859
859
+
860
860
+
Rectangle {
861
861
+
Layout.preferredWidth: closeTodoBtn.implicitWidth + 2 * Style.marginM
862
862
+
Layout.preferredHeight: closeTodoBtn.implicitHeight + Style.marginS
863
863
+
color: Color.mSurfaceVariant; radius: Style.radiusS
864
864
+
NText {
865
865
+
id: closeTodoBtn; anchors.centerIn: parent
866
866
+
text: pluginApi.tr("panel.close"); color: Color.mOnSurfaceVariant
867
867
+
}
868
868
+
MouseArea {
869
869
+
anchors.fill: parent; cursorShape: Qt.PointingHandCursor
870
870
+
onClicked: { showTodoDetailDialog = false; todoDetailEditMode = false }
871
871
+
}
872
872
+
}
873
873
+
}
874
874
+
}
875
875
+
876
876
+
// Delete confirmation
877
877
+
ColumnLayout {
878
878
+
visible: showTodoDeleteConfirmation
879
879
+
spacing: Style.marginS
880
880
+
Layout.fillWidth: true
881
881
+
882
882
+
NText {
883
883
+
text: pluginApi.tr("panel.delete_task_confirm")
884
884
+
font.pointSize: Style.fontSizeM; font.weight: Font.Bold; color: Color.mOnSurface
885
885
+
}
886
886
+
NText { text: todoDetailColumn.todo.summary || ""; font.pointSize: Style.fontSizeS; color: Color.mOnSurfaceVariant }
887
887
+
888
888
+
RowLayout {
889
889
+
Layout.fillWidth: true; spacing: Style.marginS
890
890
+
Item { Layout.fillWidth: true }
891
891
+
Rectangle {
892
892
+
Layout.preferredWidth: confirmDeleteTodoBtn.implicitWidth + 2 * Style.marginM
893
893
+
Layout.preferredHeight: confirmDeleteTodoBtn.implicitHeight + Style.marginS
894
894
+
color: Color.mError; radius: Style.radiusS
895
895
+
NText { id: confirmDeleteTodoBtn; anchors.centerIn: parent; text: pluginApi.tr("panel.delete"); color: Color.mOnError; font.weight: Font.Bold }
896
896
+
MouseArea {
897
897
+
anchors.fill: parent; cursorShape: Qt.PointingHandCursor
898
898
+
onClicked: {
899
899
+
var t = todoDetailColumn.todo
900
900
+
mainInstance?.deleteTodo(t.calendarUid, t.todoUid)
901
901
+
showTodoDetailDialog = false; showTodoDeleteConfirmation = false; todoDetailEditMode = false
902
902
+
}
903
903
+
}
904
904
+
}
905
905
+
Rectangle {
906
906
+
Layout.preferredWidth: cancelDeleteTodoBtn.implicitWidth + 2 * Style.marginM
907
907
+
Layout.preferredHeight: cancelDeleteTodoBtn.implicitHeight + Style.marginS
908
908
+
color: Color.mSurfaceVariant; radius: Style.radiusS
909
909
+
NText { id: cancelDeleteTodoBtn; anchors.centerIn: parent; text: pluginApi.tr("panel.cancel"); color: Color.mOnSurfaceVariant }
910
910
+
MouseArea { anchors.fill: parent; cursorShape: Qt.PointingHandCursor; onClicked: showTodoDeleteConfirmation = false }
911
911
+
}
912
912
+
}
913
913
+
}
914
914
+
915
915
+
// Edit mode
916
916
+
property int editTodoPriority: 0
917
917
+
ColumnLayout {
918
918
+
visible: todoDetailEditMode && !showTodoDeleteConfirmation
919
919
+
spacing: Style.marginS
920
920
+
Layout.fillWidth: true
921
921
+
922
922
+
NText { text: pluginApi.tr("panel.edit_task"); font.pointSize: Style.fontSizeL; font.weight: Font.Bold; color: Color.mOnSurface }
923
923
+
924
924
+
NText { text: pluginApi.tr("panel.task_summary"); color: Color.mOnSurfaceVariant; font.pointSize: Style.fontSizeS }
925
925
+
TextField {
926
926
+
id: editTodoSummary; Layout.fillWidth: true; color: Color.mOnSurface
927
927
+
background: Rectangle { color: Color.mSurfaceVariant; radius: Style.radiusS }
928
928
+
}
929
929
+
930
930
+
NText { text: pluginApi.tr("panel.due_date"); color: Color.mOnSurfaceVariant; font.pointSize: Style.fontSizeS }
931
931
+
TextField {
932
932
+
id: editTodoDueDate; Layout.fillWidth: true; placeholderText: "YYYY-MM-DD"; color: Color.mOnSurface
933
933
+
background: Rectangle { color: Color.mSurfaceVariant; radius: Style.radiusS }
934
934
+
}
935
935
+
936
936
+
NText { text: pluginApi.tr("panel.end_time"); color: Color.mOnSurfaceVariant; font.pointSize: Style.fontSizeS }
937
937
+
TextField {
938
938
+
id: editTodoDueTime; Layout.fillWidth: true; placeholderText: "HH:MM"; color: Color.mOnSurface
939
939
+
background: Rectangle { color: Color.mSurfaceVariant; radius: Style.radiusS }
940
940
+
}
941
941
+
942
942
+
NText { text: pluginApi.tr("panel.description"); color: Color.mOnSurfaceVariant; font.pointSize: Style.fontSizeS }
943
943
+
TextField {
944
944
+
id: editTodoDescription; Layout.fillWidth: true; color: Color.mOnSurface
945
945
+
background: Rectangle { color: Color.mSurfaceVariant; radius: Style.radiusS }
946
946
+
}
947
947
+
948
948
+
NText { text: pluginApi.tr("panel.priority"); color: Color.mOnSurfaceVariant; font.pointSize: Style.fontSizeS }
949
949
+
RowLayout {
950
950
+
spacing: Style.marginS
951
951
+
Repeater {
952
952
+
model: [
953
953
+
{ label: pluginApi.tr("panel.priority_high"), value: 1 },
954
954
+
{ label: pluginApi.tr("panel.priority_medium"), value: 5 },
955
955
+
{ label: pluginApi.tr("panel.priority_low"), value: 9 }
956
956
+
]
957
957
+
Rectangle {
958
958
+
Layout.preferredWidth: editPriLabel.implicitWidth + 2 * Style.marginM
959
959
+
Layout.preferredHeight: editPriLabel.implicitHeight + Style.marginS
960
960
+
color: todoDetailColumn.editTodoPriority === modelData.value ? Color.mPrimary : Color.mSurfaceVariant
961
961
+
radius: Style.radiusS
962
962
+
NText {
963
963
+
id: editPriLabel; anchors.centerIn: parent
964
964
+
text: modelData.label
965
965
+
color: todoDetailColumn.editTodoPriority === modelData.value ? Color.mOnPrimary : Color.mOnSurfaceVariant
966
966
+
font.weight: Font.Medium
967
967
+
}
968
968
+
MouseArea {
969
969
+
anchors.fill: parent; cursorShape: Qt.PointingHandCursor
970
970
+
onClicked: todoDetailColumn.editTodoPriority =
971
971
+
todoDetailColumn.editTodoPriority === modelData.value ? 0 : modelData.value
972
972
+
}
973
973
+
}
974
974
+
}
975
975
+
}
976
976
+
977
977
+
RowLayout {
978
978
+
Layout.fillWidth: true; spacing: Style.marginS
979
979
+
Item { Layout.fillWidth: true }
980
980
+
Rectangle {
981
981
+
Layout.preferredWidth: saveTodoBtn.implicitWidth + 2 * Style.marginM
982
982
+
Layout.preferredHeight: saveTodoBtn.implicitHeight + Style.marginS
983
983
+
color: Color.mPrimary; radius: Style.radiusS
984
984
+
NText { id: saveTodoBtn; anchors.centerIn: parent; text: pluginApi.tr("panel.save"); color: Color.mOnPrimary; font.weight: Font.Bold }
985
985
+
MouseArea {
986
986
+
anchors.fill: parent; cursorShape: Qt.PointingHandCursor
987
987
+
onClicked: {
988
988
+
var t = todoDetailColumn.todo
989
989
+
var dueTs = 0
990
990
+
if (editTodoDueDate.text.trim() !== "") {
991
991
+
var dateParts = editTodoDueDate.text.split("-")
992
992
+
var timeParts = editTodoDueTime.text.split(":")
993
993
+
var h = editTodoDueTime.text.trim() === "" ? 0 : parseInt(timeParts[0])
994
994
+
var m = editTodoDueTime.text.trim() === "" ? 0 : parseInt(timeParts[1] || "0")
995
995
+
var d = new Date(parseInt(dateParts[0]), parseInt(dateParts[1]) - 1, parseInt(dateParts[2]), h, m, 0)
996
996
+
if (!isNaN(d.getTime())) dueTs = Math.floor(d.getTime() / 1000)
997
997
+
}
998
998
+
mainInstance?.updateTodoFields(
999
999
+
t.calendarUid, t.todoUid,
1000
1000
+
editTodoSummary.text.trim(),
1001
1001
+
editTodoDescription.text.trim(),
1002
1002
+
dueTs, todoDetailColumn.editTodoPriority)
1003
1003
+
showTodoDetailDialog = false; todoDetailEditMode = false
1004
1004
+
}
1005
1005
+
}
1006
1006
+
}
1007
1007
+
Rectangle {
1008
1008
+
Layout.preferredWidth: editTodoCancelBtn.implicitWidth + 2 * Style.marginM
1009
1009
+
Layout.preferredHeight: editTodoCancelBtn.implicitHeight + Style.marginS
1010
1010
+
color: Color.mSurfaceVariant; radius: Style.radiusS
1011
1011
+
NText { id: editTodoCancelBtn; anchors.centerIn: parent; text: pluginApi.tr("panel.cancel"); color: Color.mOnSurfaceVariant }
1012
1012
+
MouseArea { anchors.fill: parent; cursorShape: Qt.PointingHandCursor; onClicked: todoDetailEditMode = false }
1013
1013
+
}
1014
1014
+
}
1015
1015
+
}
1016
1016
+
}
1017
1017
+
}
1018
1018
+
}
1019
1019
+
714
1020
// UI
715
1021
Rectangle {
716
1022
id: panelContainer
···
950
1256
}
951
1257
onClicked: {
952
1258
if (isTodoItem) {
953
953
-
if (eventData.todoStatus === "COMPLETED")
954
954
-
mainInstance?.uncompleteTodo(eventData.calendarUid, eventData.todoUid)
955
955
-
else
956
956
-
mainInstance?.completeTodo(eventData.calendarUid, eventData.todoUid)
1259
1259
+
mainInstance?.handleTodoClick(eventData)
1260
1260
+
showTodoDetailDialog = true
1261
1261
+
todoDetailEditMode = false
1262
1262
+
showTodoDeleteConfirmation = false
957
1263
} else {
958
1264
mainInstance?.handleEventClick(eventData)
959
1265
showEventDetailDialog = true
···
1103
1409
property real eventXOffset: overlapInfo.xOffset
1104
1410
1105
1411
property bool isTodoItem: model.isTodo || false
1106
1106
-
property bool isDeadline: model.isDeadlineMarker || false
1412
1412
+
property bool isTodoLine: isTodoItem && (model.isDeadlineMarker || model.isLineTodo || false)
1413
1413
+
property bool isDeadline: (model.isDeadlineMarker || false) && !isTodoLine
1107
1414
property color eventColor: isDeadline ? Color.mSecondary : (isTodoItem ? Color.mSecondary : Color.mPrimary)
1108
1415
property color eventTextColor: isDeadline ? Color.mOnSecondary : (isTodoItem ? Color.mOnSecondary : Color.mOnPrimary)
1416
1416
+
property real todoLineThickness: Math.max(2, 2 * Style.uiScaleRatio)
1417
1417
+
property real todoLabelGutter: {
1418
1418
+
var available = Math.max(eventWidth - 12, 0)
1419
1419
+
var base = Math.min(Math.max(eventWidth * 0.5, 80 * Style.uiScaleRatio), 150 * Style.uiScaleRatio)
1420
1420
+
var gutter = Math.min(base, available)
1421
1421
+
if (gutter <= 0 && available > 0) gutter = Math.min(available, 40 * Style.uiScaleRatio)
1422
1422
+
return gutter
1423
1423
+
}
1109
1424
1110
1425
visible: dayIndex >= 0 && dayIndex < 7 && duration > 0
1111
1426
width: eventWidth
1112
1112
-
height: isDeadline ? Math.max(8, Math.min(12, exactHeight)) : exactHeight
1427
1427
+
height: isTodoLine ? Math.max(todoLineThickness + 10, 18 * Style.uiScaleRatio)
1428
1428
+
: (isDeadline ? Math.max(8, Math.min(12, exactHeight)) : exactHeight)
1113
1429
x: dayIndex * ((mainInstance?.dayColumnWidth) + (root.daySpacing)) + eventXOffset
1114
1430
y: startHour * (root.hourHeight)
1115
1115
-
z: 100 + overlapInfo.lane
1431
1431
+
z: (isTodoLine ? 300 : 100) + overlapInfo.lane
1116
1432
1117
1117
-
Rectangle {
1433
1433
+
Item {
1118
1434
anchors.fill: parent
1119
1119
-
color: eventColor
1120
1120
-
radius: Style.radiusS
1121
1121
-
opacity: isDeadline ? 0.95 : (isTodoItem && model.todoStatus === "COMPLETED" ? 0.5 : 0.9)
1122
1122
-
clip: true
1435
1435
+
clip: false
1436
1436
+
1123
1437
Rectangle {
1124
1124
-
visible: exactHeight < 5 && overlapInfo.lane > 0
1125
1438
anchors.fill: parent
1126
1126
-
color: "transparent"
1127
1127
-
radius: parent.radius
1128
1128
-
border.width: 1
1129
1129
-
border.color: eventColor
1439
1439
+
visible: !isTodoLine
1440
1440
+
color: eventColor
1441
1441
+
radius: Style.radiusS
1442
1442
+
opacity: isDeadline ? 0.95 : (isTodoItem && model.todoStatus === "COMPLETED" ? 0.5 : 0.9)
1443
1443
+
clip: true
1444
1444
+
Rectangle {
1445
1445
+
visible: exactHeight < 5 && overlapInfo.lane > 0
1446
1446
+
anchors.fill: parent
1447
1447
+
color: "transparent"
1448
1448
+
radius: parent.radius
1449
1449
+
border.width: 1
1450
1450
+
border.color: eventColor
1451
1451
+
}
1130
1452
}
1453
1453
+
1131
1454
Loader {
1132
1455
anchors.fill: parent
1133
1133
-
anchors.margins: exactHeight < 10 ? 1 : Style.marginS
1134
1134
-
anchors.leftMargin: exactHeight < 10 ? 1 : Style.marginS + 3
1135
1135
-
sourceComponent: isDeadline ? deadlineLayout : (isCompact ? compactLayout : normalLayout)
1456
1456
+
anchors.margins: isTodoLine ? 0 : (exactHeight < 10 ? 1 : Style.marginS)
1457
1457
+
anchors.leftMargin: isTodoLine ? 0 : (exactHeight < 10 ? 1 : Style.marginS + 3)
1458
1458
+
sourceComponent: isTodoLine ? todoLineLayout : (isDeadline ? deadlineLayout : (isCompact ? compactLayout : normalLayout))
1136
1459
}
1137
1460
}
1138
1461
···
1185
1508
}
1186
1509
1187
1510
Component {
1511
1511
+
id: todoLineLayout
1512
1512
+
Item {
1513
1513
+
anchors.fill: parent
1514
1514
+
clip: false
1515
1515
+
1516
1516
+
Rectangle {
1517
1517
+
id: line
1518
1518
+
anchors.left: parent.left
1519
1519
+
anchors.right: parent.right
1520
1520
+
anchors.rightMargin: todoLabelGutter
1521
1521
+
anchors.verticalCenter: parent.verticalCenter
1522
1522
+
height: todoLineThickness
1523
1523
+
radius: todoLineThickness / 2
1524
1524
+
color: eventColor
1525
1525
+
opacity: model.todoStatus === "COMPLETED" ? 0.45 : 1.0
1526
1526
+
}
1527
1527
+
1528
1528
+
Rectangle {
1529
1529
+
id: leader
1530
1530
+
width: 12 * Style.uiScaleRatio
1531
1531
+
height: todoLineThickness
1532
1532
+
anchors.left: line.right
1533
1533
+
anchors.verticalCenter: line.verticalCenter
1534
1534
+
color: eventColor
1535
1535
+
opacity: line.opacity
1536
1536
+
}
1537
1537
+
1538
1538
+
Rectangle {
1539
1539
+
id: labelBox
1540
1540
+
anchors.left: leader.right
1541
1541
+
anchors.verticalCenter: line.verticalCenter
1542
1542
+
width: Math.max(todoLabelGutter - leader.width, 70 * Style.uiScaleRatio)
1543
1543
+
height: Math.max(todoLineThickness + Style.marginS, 18 * Style.uiScaleRatio)
1544
1544
+
radius: Style.radiusS
1545
1545
+
color: Color.mSurface
1546
1546
+
border.color: Qt.alpha(eventColor, 0.9)
1547
1547
+
border.width: 1
1548
1548
+
NText {
1549
1549
+
anchors.fill: parent
1550
1550
+
anchors.margins: Style.marginS / 2
1551
1551
+
text: (model.todoStatus === "COMPLETED" ? "\u2611 " : "\u2610 ") + (model.title || "")
1552
1552
+
color: eventColor
1553
1553
+
font.pointSize: Style.fontSizeXXS
1554
1554
+
font.weight: Font.Medium
1555
1555
+
font.strikeout: model.todoStatus === "COMPLETED"
1556
1556
+
elide: Text.ElideRight
1557
1557
+
verticalAlignment: Text.AlignVCenter
1558
1558
+
}
1559
1559
+
}
1560
1560
+
}
1561
1561
+
}
1562
1562
+
1563
1563
+
Component {
1188
1564
id: deadlineLayout
1189
1565
Rectangle {
1190
1566
anchors.fill: parent
···
1204
1580
}
1205
1581
onClicked: {
1206
1582
if (isTodoItem) {
1207
1207
-
if (model.todoStatus === "COMPLETED")
1208
1208
-
mainInstance?.uncompleteTodo(model.calendarUid, model.todoUid)
1209
1209
-
else
1210
1210
-
mainInstance?.completeTodo(model.calendarUid, model.todoUid)
1583
1583
+
mainInstance?.handleTodoClick(model)
1584
1584
+
showTodoDetailDialog = true
1585
1585
+
todoDetailEditMode = false
1586
1586
+
showTodoDeleteConfirmation = false
1211
1587
} else {
1212
1588
mainInstance?.handleEventClick(eventData)
1213
1589
showEventDetailDialog = true
+9
-1
weekly-calendar/i18n/en.json
···
39
39
"task_created": "Task created",
40
40
"task_error": "Failed to create task",
41
41
"show_completed": "Show completed",
42
42
-
"overdue": "Overdue"
42
42
+
"overdue": "Overdue",
43
43
+
"edit": "Edit",
44
44
+
"delete": "Delete",
45
45
+
"close": "Close",
46
46
+
"save": "Save",
47
47
+
"edit_event": "Edit Event",
48
48
+
"edit_task": "Edit Task",
49
49
+
"delete_confirm": "Delete this event?",
50
50
+
"delete_task_confirm": "Delete this task?"
43
51
},
44
52
"settings": {
45
53
"weekStart": "First day of week",
+9
-1
weekly-calendar/i18n/zh-CN.json
···
39
39
"task_created": "任务已创建",
40
40
"task_error": "创建任务失败",
41
41
"show_completed": "显示已完成",
42
42
-
"overdue": "已逾期"
42
42
+
"overdue": "已逾期",
43
43
+
"edit": "编辑",
44
44
+
"delete": "删除",
45
45
+
"close": "关闭",
46
46
+
"save": "保存",
47
47
+
"edit_event": "编辑活动",
48
48
+
"edit_task": "编辑任务",
49
49
+
"delete_confirm": "确定删除此活动?",
50
50
+
"delete_task_confirm": "确定删除此任务?"
43
51
},
44
52
"settings": {
45
53
"weekStart": "一周起始日",
+24
-1
weekly-calendar/scripts/update-todo.py
···
35
35
parser = argparse.ArgumentParser(description="Update/delete EDS VTODO item")
36
36
parser.add_argument("--task-list", required=True, help="Task list UID")
37
37
parser.add_argument("--uid", required=True, help="VTODO UID")
38
38
-
parser.add_argument("--action", required=True, choices=["complete", "uncomplete", "delete"],
38
38
+
parser.add_argument("--action", required=True, choices=["complete", "uncomplete", "delete", "update"],
39
39
help="Action to perform")
40
40
+
parser.add_argument("--summary", help="New task summary (for update)")
41
41
+
parser.add_argument("--description", help="New task description (for update)")
42
42
+
parser.add_argument("--due", type=int, help="New due date as unix timestamp (for update)")
43
43
+
parser.add_argument("--priority", type=int, help="New priority 0-9 (for update)")
40
44
args = parser.parse_args()
41
45
42
46
try:
···
91
95
92
96
# Remove COMPLETED timestamp
93
97
remove_property(ical, ICalGLib.PropertyKind.COMPLETED_PROPERTY)
98
98
+
99
99
+
elif args.action == "update":
100
100
+
if args.summary is not None:
101
101
+
ical.set_summary(args.summary)
102
102
+
if args.description is not None:
103
103
+
ical.set_description(args.description)
104
104
+
if args.due is not None:
105
105
+
dt = datetime.fromtimestamp(args.due, tz=timezone.utc)
106
106
+
t = ICalGLib.Time.new_null_time()
107
107
+
t.set_date(dt.year, dt.month, dt.day)
108
108
+
t.set_time(dt.hour, dt.minute, dt.second)
109
109
+
t.set_timezone(ICalGLib.Timezone.get_utc_timezone())
110
110
+
remove_property(ical, ICalGLib.PropertyKind.DUE_PROPERTY)
111
111
+
prop = ICalGLib.Property.new_due(t)
112
112
+
ical.add_property(prop)
113
113
+
if args.priority is not None:
114
114
+
remove_property(ical, ICalGLib.PropertyKind.PRIORITY_PROPERTY)
115
115
+
prop = ICalGLib.Property.new_priority(args.priority)
116
116
+
ical.add_property(prop)
94
117
95
118
client.modify_object_sync(comp, ECal.ObjModType.ALL, ECal.OperationFlags.NONE, None)
96
119
print(json.dumps({"success": True}))