mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-10-14 15:24:00 -04:00
228 lines
7.7 KiB
QML
228 lines
7.7 KiB
QML
import QtQuick
|
|
import qs.Common
|
|
import qs.Services
|
|
import qs.DCommon.Widgets
|
|
import qs.Widgets
|
|
import qs.Modules.Notifications
|
|
import qs.Modules.Notifications.Center
|
|
|
|
FocusScope {
|
|
id: root
|
|
|
|
property bool live: false
|
|
property bool interactive: true
|
|
property bool nested: false
|
|
property string entryId: "notifications"
|
|
property int currentTab: 0
|
|
property var transientSurfaceTracker: null
|
|
property real swipeBleed: 0
|
|
readonly property var surfaces: transientSurfaceTracker ?? localSurfaces
|
|
readonly property bool hasNotifications: list.count > 0
|
|
readonly property Item focusTarget: root
|
|
property bool blocksTabNavigation: false
|
|
activeFocusOnTab: interactive
|
|
|
|
function handleKeyEvent(event) {
|
|
event.accepted = false;
|
|
if (!interactive || !enabled || !activeFocus)
|
|
return false;
|
|
if (event.key === Qt.Key_Tab || event.key === Qt.Key_Backtab)
|
|
return focusNextControl(event.key === Qt.Key_Backtab || !!(event.modifiers & Qt.ShiftModifier));
|
|
if (event.key === Qt.Key_Escape) {
|
|
if (surfaces.active) {
|
|
surfaces.closeAll();
|
|
return true;
|
|
}
|
|
keyboard.reset();
|
|
if (historyLoader.item)
|
|
historyLoader.item.keyboardActive = false;
|
|
return false;
|
|
}
|
|
if (currentTab === 1) {
|
|
historyLoader.item?.handleKey(event);
|
|
return event.accepted;
|
|
}
|
|
keyboard.handleKey(event);
|
|
return event.accepted;
|
|
}
|
|
|
|
function focusNextControl(backwards) {
|
|
const current = root.Window.window?.activeFocusItem ?? root;
|
|
let next = current.nextItemInFocusChain(!backwards);
|
|
while (next && next !== current) {
|
|
let ancestor = next;
|
|
while (ancestor && ancestor !== root)
|
|
ancestor = ancestor.parent;
|
|
if (!ancestor)
|
|
return false;
|
|
if (next.activeFocusOnTab && next.visible && next.enabled) {
|
|
revealControl(next);
|
|
next.forceActiveFocus(backwards ? Qt.BacktabFocusReason : Qt.TabFocusReason);
|
|
return true;
|
|
}
|
|
next = next.nextItemInFocusChain(!backwards);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function revealControl(item) {
|
|
let ancestor = item.parent;
|
|
while (ancestor && ancestor !== root) {
|
|
if (ancestor.contentItem && ancestor.contentY !== undefined && ancestor.contentHeight !== undefined)
|
|
revealInViewport(item, ancestor);
|
|
ancestor = ancestor.parent;
|
|
}
|
|
}
|
|
|
|
function revealInViewport(item, viewport) {
|
|
const point = item.mapToItem(viewport.contentItem, 0, 0);
|
|
if (typeof viewport.revealRange === "function") {
|
|
viewport.revealRange(point.y, point.y + item.height);
|
|
return;
|
|
}
|
|
if (point.y < viewport.contentY) {
|
|
viewport.contentY = Math.max(0, point.y);
|
|
return;
|
|
}
|
|
const bottom = point.y + item.height - viewport.height;
|
|
if (bottom > viewport.contentY)
|
|
viewport.contentY = Math.max(0, Math.min(point.y, bottom));
|
|
}
|
|
|
|
implicitHeight: DashMetrics.tabMinHeight
|
|
enabled: interactive
|
|
LayoutMirroring.enabled: I18n.isRtl
|
|
LayoutMirroring.childrenInherit: true
|
|
|
|
onLiveChanged: {
|
|
if (!live) {
|
|
keyboard.reset();
|
|
surfaces.closeAll();
|
|
return;
|
|
}
|
|
NotificationService.markNotificationsSeen();
|
|
keyboard.rebuildFlatNavigation();
|
|
}
|
|
|
|
readonly property bool settingsNotificationHistoryEnabled: SettingsData.notificationHistoryEnabled
|
|
|
|
onSettingsNotificationHistoryEnabledChanged: {
|
|
if (!settingsNotificationHistoryEnabled)
|
|
currentTab = 0;
|
|
}
|
|
|
|
TransientSurfaceTracker {
|
|
id: localSurfaces
|
|
}
|
|
|
|
NotificationKeyboardController {
|
|
id: keyboard
|
|
listView: list
|
|
isOpen: root.live
|
|
}
|
|
|
|
Keys.onPressed: event => event.accepted = handleKeyEvent(event)
|
|
|
|
TapHandler {
|
|
onPressedChanged: {
|
|
if (pressed)
|
|
root.forceActiveFocus(Qt.MouseFocusReason);
|
|
}
|
|
}
|
|
|
|
KeyboardNavigatedNotificationList {
|
|
id: list
|
|
anchors.fill: parent
|
|
anchors.leftMargin: -root.swipeBleed
|
|
anchors.rightMargin: -root.swipeBleed
|
|
nested: root.nested
|
|
anchors.bottomMargin: footer.height + Theme.spacingM
|
|
swipeBleed: root.swipeBleed
|
|
visible: root.currentTab === 0
|
|
showScrollBar: false
|
|
keyboardController: keyboard
|
|
focusAllowed: root.activeFocus
|
|
transientSurfaceTracker: root.surfaces
|
|
trackStableContentHeight: false
|
|
}
|
|
|
|
Loader {
|
|
id: historyLoader
|
|
anchors.fill: parent
|
|
anchors.bottomMargin: footer.height + Theme.spacingM
|
|
active: root.currentTab === 1
|
|
visible: active
|
|
sourceComponent: HistoryNotificationList {
|
|
focusAllowed: root.activeFocus
|
|
showScrollBar: false
|
|
nested: root.nested
|
|
swipeBleed: root.swipeBleed
|
|
}
|
|
}
|
|
|
|
Row {
|
|
id: footer
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
anchors.bottom: parent.bottom
|
|
spacing: Theme.spacingS
|
|
height: Theme.iconButtonSize
|
|
|
|
DActionButton {
|
|
id: historyButton
|
|
visible: SettingsData.notificationHistoryEnabled
|
|
buttonSize: Theme.iconButtonSize
|
|
iconName: root.currentTab === 1 ? "inbox" : "history"
|
|
backgroundColor: Theme.secondaryContainer
|
|
iconColor: Theme.onSecondaryContainer
|
|
tooltipText: root.currentTab === 1 ? I18n.tr("Current", "notification center tab") : I18n.tr("History", "notification center tab")
|
|
onClicked: root.currentTab = root.currentTab === 0 ? 1 : 0
|
|
}
|
|
|
|
DButton {
|
|
width: Math.max(0, footer.width - dndButton.width - (historyButton.visible ? historyButton.width + footer.spacing : 0) - footer.spacing)
|
|
text: I18n.tr("Clear All")
|
|
buttonHeight: Theme.iconButtonSize
|
|
backgroundColor: Theme.secondaryContainer
|
|
textColor: Theme.onSecondaryContainer
|
|
enabled: root.currentTab === 0 ? NotificationService.notifications.length > 0 : NotificationService.historyList.length > 0
|
|
onClicked: {
|
|
if (root.currentTab === 0) {
|
|
NotificationService.clearAllNotifications();
|
|
return;
|
|
}
|
|
NotificationService.clearHistory();
|
|
}
|
|
}
|
|
|
|
DActionButton {
|
|
id: dndButton
|
|
buttonSize: Theme.iconButtonSize
|
|
iconName: SessionData.doNotDisturb ? "notifications_off" : "notifications"
|
|
backgroundColor: SessionData.doNotDisturb ? Theme.primaryContainer : Theme.secondaryContainer
|
|
iconColor: SessionData.doNotDisturb ? Theme.onPrimaryContainer : Theme.onSecondaryContainer
|
|
tooltipText: I18n.tr("Do not disturb")
|
|
onClicked: duration.showDropdownMenu()
|
|
}
|
|
}
|
|
|
|
DDropdown {
|
|
id: duration
|
|
showTrigger: false
|
|
popupAnchorItem: dndButton
|
|
popupWidth: NotificationMetrics.menuWidth
|
|
openUpwards: true
|
|
transientSurfaceTracker: root.surfaces
|
|
options: [I18n.tr("Off")].concat(DndPresets.presetOptions.map(option => option.label))
|
|
onValueChanged: value => {
|
|
if (value === I18n.tr("Off")) {
|
|
SessionData.setDoNotDisturb(false);
|
|
return;
|
|
}
|
|
const option = DndPresets.presetOptions.find(option => option.label === value);
|
|
if (option)
|
|
DndPresets.selectPreset(option);
|
|
}
|
|
}
|
|
}
|