summaryrefslogtreecommitdiff
path: root/raveos-hyprland-theme/theme-data/dms/Modules/Notifications/Center/NotificationKeyboardController.qml
diff options
context:
space:
mode:
Diffstat (limited to 'raveos-hyprland-theme/theme-data/dms/Modules/Notifications/Center/NotificationKeyboardController.qml')
-rw-r--r--raveos-hyprland-theme/theme-data/dms/Modules/Notifications/Center/NotificationKeyboardController.qml556
1 files changed, 556 insertions, 0 deletions
diff --git a/raveos-hyprland-theme/theme-data/dms/Modules/Notifications/Center/NotificationKeyboardController.qml b/raveos-hyprland-theme/theme-data/dms/Modules/Notifications/Center/NotificationKeyboardController.qml
new file mode 100644
index 0000000..eda12c0
--- /dev/null
+++ b/raveos-hyprland-theme/theme-data/dms/Modules/Notifications/Center/NotificationKeyboardController.qml
@@ -0,0 +1,556 @@
+import QtQuick
+import qs.Services
+
+QtObject {
+ id: controller
+
+ property var listView: null
+ property bool isOpen: false
+ property var onClose: null
+
+ property int selectionVersion: 0
+
+ property bool keyboardNavigationActive: false
+ property int selectedFlatIndex: 0
+ property var flatNavigation: []
+ property bool showKeyboardHints: false
+
+ property string selectedNotificationId: ""
+ property string selectedGroupKey: ""
+ property string selectedItemType: ""
+ property bool isTogglingGroup: false
+ property bool isRebuilding: false
+
+ function rebuildFlatNavigation() {
+ isRebuilding = true;
+
+ const nav = [];
+ const groups = NotificationService.groupedNotifications;
+
+ for (var i = 0; i < groups.length; i++) {
+ const group = groups[i];
+ const isExpanded = NotificationService.expandedGroups[group.key] || false;
+
+ nav.push({
+ "type": "group",
+ "groupIndex": i,
+ "notificationIndex": -1,
+ "groupKey": group.key,
+ "notificationId": ""
+ });
+
+ if (isExpanded) {
+ const notifications = group.notifications || [];
+ const maxNotifications = Math.min(notifications.length, 10);
+ for (var j = 0; j < maxNotifications; j++) {
+ const notifId = String(notifications[j] && notifications[j].notification && notifications[j].notification.id ? notifications[j].notification.id : "");
+ nav.push({
+ "type": "notification",
+ "groupIndex": i,
+ "notificationIndex": j,
+ "groupKey": group.key,
+ "notificationId": notifId
+ });
+ }
+ }
+ }
+
+ flatNavigation = nav;
+ updateSelectedIndexFromId();
+ isRebuilding = false;
+ }
+
+ function updateSelectedIndexFromId() {
+ if (!keyboardNavigationActive) {
+ return;
+ }
+
+ for (var i = 0; i < flatNavigation.length; i++) {
+ const item = flatNavigation[i];
+
+ if (selectedItemType === "group" && item.type === "group" && item.groupKey === selectedGroupKey) {
+ selectedFlatIndex = i;
+ selectionVersion++; // Trigger UI update
+ return;
+ } else if (selectedItemType === "notification" && item.type === "notification" && String(item.notificationId) === String(selectedNotificationId)) {
+ selectedFlatIndex = i;
+ selectionVersion++; // Trigger UI update
+ return;
+ }
+ }
+
+ // If not found, try to find the same group but select the group header instead
+ if (selectedItemType === "notification") {
+ for (var j = 0; j < flatNavigation.length; j++) {
+ const groupItem = flatNavigation[j];
+ if (groupItem.type === "group" && groupItem.groupKey === selectedGroupKey) {
+ selectedFlatIndex = j;
+ selectedItemType = "group";
+ selectedNotificationId = "";
+ selectionVersion++; // Trigger UI update
+ return;
+ }
+ }
+ }
+
+ // If still not found, clamp to valid range and update
+ if (flatNavigation.length > 0) {
+ selectedFlatIndex = Math.min(selectedFlatIndex, flatNavigation.length - 1);
+ selectedFlatIndex = Math.max(selectedFlatIndex, 0);
+ updateSelectedIdFromIndex();
+ selectionVersion++; // Trigger UI update
+ }
+ }
+
+ function updateSelectedIdFromIndex() {
+ if (selectedFlatIndex >= 0 && selectedFlatIndex < flatNavigation.length) {
+ const item = flatNavigation[selectedFlatIndex];
+ selectedItemType = item.type;
+ selectedGroupKey = item.groupKey;
+ selectedNotificationId = item.notificationId;
+ }
+ }
+
+ function reset() {
+ selectedFlatIndex = 0;
+ keyboardNavigationActive = false;
+ showKeyboardHints = false;
+ // Reset keyboardActive when modal is reset
+ if (listView) {
+ listView.keyboardActive = false;
+ }
+ rebuildFlatNavigation();
+ }
+
+ function selectNext() {
+ keyboardNavigationActive = true;
+ if (flatNavigation.length === 0)
+ return;
+
+ // Re-enable auto-scrolling when arrow keys are used
+ if (listView && listView.enableAutoScroll) {
+ listView.enableAutoScroll();
+ }
+
+ selectedFlatIndex = Math.min(selectedFlatIndex + 1, flatNavigation.length - 1);
+ updateSelectedIdFromIndex();
+ selectionVersion++;
+ ensureVisible();
+ }
+
+ function selectNextWrapping() {
+ keyboardNavigationActive = true;
+ if (flatNavigation.length === 0)
+ return;
+
+ // Re-enable auto-scrolling when arrow keys are used
+ if (listView && listView.enableAutoScroll) {
+ listView.enableAutoScroll();
+ }
+
+ selectedFlatIndex = (selectedFlatIndex + 1) % flatNavigation.length;
+ updateSelectedIdFromIndex();
+ selectionVersion++;
+ ensureVisible();
+ }
+
+ function selectPrevious() {
+ keyboardNavigationActive = true;
+ if (flatNavigation.length === 0)
+ return;
+
+ // Re-enable auto-scrolling when arrow keys are used
+ if (listView && listView.enableAutoScroll) {
+ listView.enableAutoScroll();
+ }
+
+ selectedFlatIndex = Math.max(selectedFlatIndex - 1, 0);
+ updateSelectedIdFromIndex();
+ selectionVersion++;
+ ensureVisible();
+ }
+
+ function toggleGroupExpanded() {
+ if (flatNavigation.length === 0 || selectedFlatIndex >= flatNavigation.length)
+ return;
+ const currentItem = flatNavigation[selectedFlatIndex];
+ const groups = NotificationService.groupedNotifications;
+ const group = groups[currentItem.groupIndex];
+ if (!group)
+ return;
+
+ // Prevent expanding groups with < 2 notifications
+ const notificationCount = group.notifications ? group.notifications.length : 0;
+ if (notificationCount < 2)
+ return;
+ const wasExpanded = NotificationService.expandedGroups[group.key] || false;
+ const groupIndex = currentItem.groupIndex;
+
+ isTogglingGroup = true;
+ NotificationService.toggleGroupExpansion(group.key);
+ rebuildFlatNavigation();
+
+ // Smart selection after toggle
+ if (!wasExpanded) {
+ // Just expanded - move to first notification in the group
+ for (var i = 0; i < flatNavigation.length; i++) {
+ if (flatNavigation[i].type === "notification" && flatNavigation[i].groupIndex === groupIndex) {
+ selectedFlatIndex = i;
+ break;
+ }
+ }
+ } else {
+ // Just collapsed - stay on the group header
+ for (var i = 0; i < flatNavigation.length; i++) {
+ if (flatNavigation[i].type === "group" && flatNavigation[i].groupIndex === groupIndex) {
+ selectedFlatIndex = i;
+ break;
+ }
+ }
+ }
+
+ isTogglingGroup = false;
+ }
+
+ function handleEnterKey() {
+ if (flatNavigation.length === 0 || selectedFlatIndex >= flatNavigation.length)
+ return;
+ const currentItem = flatNavigation[selectedFlatIndex];
+ const groups = NotificationService.groupedNotifications;
+ const group = groups[currentItem.groupIndex];
+ if (!group)
+ return;
+ if (currentItem.type === "group") {
+ const notificationCount = group.notifications ? group.notifications.length : 0;
+ if (notificationCount >= 2) {
+ toggleGroupExpanded();
+ } else {
+ executeAction(0);
+ }
+ } else if (currentItem.type === "notification") {
+ executeAction(0);
+ }
+ }
+
+ function toggleTextExpanded() {
+ if (flatNavigation.length === 0 || selectedFlatIndex >= flatNavigation.length)
+ return;
+ const currentItem = flatNavigation[selectedFlatIndex];
+ const groups = NotificationService.groupedNotifications;
+ const group = groups[currentItem.groupIndex];
+ if (!group)
+ return;
+ let messageId = "";
+
+ if (currentItem.type === "group") {
+ messageId = group.latestNotification?.notification?.id + "_desc";
+ } else if (currentItem.type === "notification" && currentItem.notificationIndex >= 0 && currentItem.notificationIndex < group.notifications.length) {
+ messageId = group.notifications[currentItem.notificationIndex]?.notification?.id + "_desc";
+ }
+
+ if (messageId) {
+ NotificationService.toggleMessageExpansion(messageId);
+ }
+ }
+
+ function executeAction(actionIndex) {
+ if (flatNavigation.length === 0 || selectedFlatIndex >= flatNavigation.length)
+ return;
+ const currentItem = flatNavigation[selectedFlatIndex];
+ const groups = NotificationService.groupedNotifications;
+ const group = groups[currentItem.groupIndex];
+ if (!group)
+ return;
+ let actions = [];
+
+ if (currentItem.type === "group") {
+ actions = group.latestNotification?.actions || [];
+ } else if (currentItem.type === "notification" && currentItem.notificationIndex >= 0 && currentItem.notificationIndex < group.notifications.length) {
+ actions = group.notifications[currentItem.notificationIndex]?.actions || [];
+ }
+
+ if (actionIndex >= 0 && actionIndex < actions.length) {
+ const action = actions[actionIndex];
+ if (action.invoke) {
+ action.invoke();
+ if (onClose)
+ onClose();
+ }
+ }
+ }
+
+ function clearSelected() {
+ if (flatNavigation.length === 0 || selectedFlatIndex >= flatNavigation.length)
+ return;
+ const currentItem = flatNavigation[selectedFlatIndex];
+ const groups = NotificationService.groupedNotifications;
+ const group = groups[currentItem.groupIndex];
+ if (!group)
+ return;
+ if (currentItem.type === "group") {
+ NotificationService.dismissGroup(group.key);
+ } else if (currentItem.type === "notification") {
+ const notification = group.notifications[currentItem.notificationIndex];
+ NotificationService.dismissNotification(notification);
+ }
+
+ rebuildFlatNavigation();
+
+ if (flatNavigation.length === 0) {
+ keyboardNavigationActive = false;
+ if (listView) {
+ listView.keyboardActive = false;
+ }
+ } else {
+ selectedFlatIndex = Math.min(selectedFlatIndex, flatNavigation.length - 1);
+ updateSelectedIdFromIndex();
+ ensureVisible();
+ }
+ }
+
+ function findRepeater(parent) {
+ if (!parent || !parent.children) {
+ return null;
+ }
+ for (var i = 0; i < parent.children.length; i++) {
+ const child = parent.children[i];
+ if (child.objectName === "notificationRepeater") {
+ return child;
+ }
+ const found = findRepeater(child);
+ if (found) {
+ return found;
+ }
+ }
+ return null;
+ }
+
+ function ensureVisible() {
+ if (flatNavigation.length === 0 || selectedFlatIndex >= flatNavigation.length || !listView)
+ return;
+ const currentItem = flatNavigation[selectedFlatIndex];
+
+ if (keyboardNavigationActive && currentItem && currentItem.groupIndex >= 0) {
+ if (currentItem.type === "notification") {
+ const groupDelegate = listView.itemAtIndex(currentItem.groupIndex);
+ if (groupDelegate && groupDelegate.children && groupDelegate.children.length > 0) {
+ const notificationCard = groupDelegate.children[0];
+ const repeater = findRepeater(notificationCard);
+
+ if (repeater && currentItem.notificationIndex < repeater.count) {
+ const notificationItem = repeater.itemAt(currentItem.notificationIndex);
+ if (notificationItem) {
+ const itemPos = notificationItem.mapToItem(listView.contentItem, 0, 0);
+ const itemY = itemPos.y;
+ const itemHeight = notificationItem.height;
+
+ const viewportTop = listView.contentY;
+ const viewportBottom = listView.contentY + listView.height;
+
+ if (itemY < viewportTop) {
+ listView.contentY = itemY - 20;
+ } else if (itemY + itemHeight > viewportBottom) {
+ listView.contentY = itemY + itemHeight - listView.height + 20;
+ }
+ }
+ }
+ }
+ } else {
+ listView.positionViewAtIndex(currentItem.groupIndex, ListView.Contain);
+ }
+
+ listView.forceLayout();
+ }
+ }
+
+ function handleKey(event) {
+ if ((event.key === Qt.Key_Delete || event.key === Qt.Key_Backspace) && (event.modifiers & Qt.ShiftModifier)) {
+ NotificationService.clearAllNotifications();
+ rebuildFlatNavigation();
+ if (flatNavigation.length === 0) {
+ keyboardNavigationActive = false;
+ if (listView) {
+ listView.keyboardActive = false;
+ }
+ } else {
+ selectedFlatIndex = 0;
+ updateSelectedIdFromIndex();
+ }
+ selectionVersion++;
+ event.accepted = true;
+ return;
+ }
+
+ if (event.key === Qt.Key_Escape) {
+ if (keyboardNavigationActive) {
+ keyboardNavigationActive = false;
+ event.accepted = true;
+ } else {
+ if (onClose)
+ onClose();
+ event.accepted = true;
+ }
+ } else if (event.key === Qt.Key_Down || event.key === 16777237) {
+ if (!keyboardNavigationActive) {
+ keyboardNavigationActive = true;
+ rebuildFlatNavigation(); // Ensure we have fresh navigation data
+ selectedFlatIndex = 0;
+ updateSelectedIdFromIndex();
+ // Set keyboardActive on listView to show highlight
+ if (listView) {
+ listView.keyboardActive = true;
+ }
+ selectionVersion++;
+ ensureVisible();
+ event.accepted = true;
+ } else {
+ selectNext();
+ event.accepted = true;
+ }
+ } else if (event.key === Qt.Key_Up || event.key === 16777235) {
+ if (!keyboardNavigationActive) {
+ keyboardNavigationActive = true;
+ rebuildFlatNavigation(); // Ensure we have fresh navigation data
+ selectedFlatIndex = 0;
+ updateSelectedIdFromIndex();
+ // Set keyboardActive on listView to show highlight
+ if (listView) {
+ listView.keyboardActive = true;
+ }
+ selectionVersion++;
+ ensureVisible();
+ event.accepted = true;
+ } else if (selectedFlatIndex === 0) {
+ keyboardNavigationActive = false;
+ // Reset keyboardActive when navigation is disabled
+ if (listView) {
+ listView.keyboardActive = false;
+ }
+ selectionVersion++;
+ event.accepted = true;
+ return;
+ } else {
+ selectPrevious();
+ event.accepted = true;
+ }
+ } else if (event.key === Qt.Key_N && event.modifiers & Qt.ControlModifier) {
+ if (!keyboardNavigationActive) {
+ keyboardNavigationActive = true;
+ rebuildFlatNavigation();
+ selectedFlatIndex = 0;
+ updateSelectedIdFromIndex();
+ if (listView) {
+ listView.keyboardActive = true;
+ }
+ selectionVersion++;
+ ensureVisible();
+ } else {
+ selectNext();
+ }
+ event.accepted = true;
+ } else if (event.key === Qt.Key_P && event.modifiers & Qt.ControlModifier) {
+ if (!keyboardNavigationActive) {
+ keyboardNavigationActive = true;
+ rebuildFlatNavigation();
+ selectedFlatIndex = 0;
+ updateSelectedIdFromIndex();
+ if (listView) {
+ listView.keyboardActive = true;
+ }
+ selectionVersion++;
+ ensureVisible();
+ } else if (selectedFlatIndex === 0) {
+ keyboardNavigationActive = false;
+ if (listView) {
+ listView.keyboardActive = false;
+ }
+ selectionVersion++;
+ } else {
+ selectPrevious();
+ }
+ event.accepted = true;
+ } else if (event.key === Qt.Key_J && event.modifiers & Qt.ControlModifier) {
+ if (!keyboardNavigationActive) {
+ keyboardNavigationActive = true;
+ rebuildFlatNavigation();
+ selectedFlatIndex = 0;
+ updateSelectedIdFromIndex();
+ if (listView) {
+ listView.keyboardActive = true;
+ }
+ selectionVersion++;
+ ensureVisible();
+ } else {
+ selectNext();
+ }
+ event.accepted = true;
+ } else if (event.key === Qt.Key_K && event.modifiers & Qt.ControlModifier) {
+ if (!keyboardNavigationActive) {
+ keyboardNavigationActive = true;
+ rebuildFlatNavigation();
+ selectedFlatIndex = 0;
+ updateSelectedIdFromIndex();
+ if (listView) {
+ listView.keyboardActive = true;
+ }
+ selectionVersion++;
+ ensureVisible();
+ } else if (selectedFlatIndex === 0) {
+ keyboardNavigationActive = false;
+ if (listView) {
+ listView.keyboardActive = false;
+ }
+ selectionVersion++;
+ } else {
+ selectPrevious();
+ }
+ event.accepted = true;
+ } else if (keyboardNavigationActive) {
+ if (event.key === Qt.Key_Space) {
+ toggleGroupExpanded();
+ event.accepted = true;
+ } else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
+ handleEnterKey();
+ event.accepted = true;
+ } else if (event.key === Qt.Key_E) {
+ toggleTextExpanded();
+ event.accepted = true;
+ } else if (event.key === Qt.Key_Delete || event.key === Qt.Key_Backspace) {
+ clearSelected();
+ event.accepted = true;
+ } else if (event.key === Qt.Key_Tab) {
+ selectNext();
+ event.accepted = true;
+ } else if (event.key === Qt.Key_Backtab) {
+ selectPrevious();
+ event.accepted = true;
+ } else if (event.key >= Qt.Key_1 && event.key <= Qt.Key_9) {
+ const actionIndex = event.key - Qt.Key_1;
+ executeAction(actionIndex);
+ event.accepted = true;
+ }
+ }
+
+ if (event.key === Qt.Key_F10) {
+ showKeyboardHints = !showKeyboardHints;
+ event.accepted = true;
+ }
+ }
+
+ // Get current selection info for UI
+ function getCurrentSelection() {
+ if (!keyboardNavigationActive || selectedFlatIndex < 0 || selectedFlatIndex >= flatNavigation.length) {
+ return {
+ "type": "",
+ "groupIndex": -1,
+ "notificationIndex": -1
+ };
+ }
+ const result = flatNavigation[selectedFlatIndex] || {
+ "type": "",
+ "groupIndex": -1,
+ "notificationIndex": -1
+ };
+ return result;
+ }
+}