mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-04-17 19:22:04 -04:00
notifications: add configurable durations for do not disturb
fixes #1481
This commit is contained in:
@@ -29,9 +29,33 @@ Singleton {
|
|||||||
|
|
||||||
property bool isLightMode: false
|
property bool isLightMode: false
|
||||||
property bool doNotDisturb: false
|
property bool doNotDisturb: false
|
||||||
|
property real doNotDisturbUntil: 0
|
||||||
property bool isSwitchingMode: false
|
property bool isSwitchingMode: false
|
||||||
property bool suppressOSD: true
|
property bool suppressOSD: true
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: dndExpireTimer
|
||||||
|
repeat: false
|
||||||
|
running: false
|
||||||
|
onTriggered: root.setDoNotDisturb(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
function _armDndExpireTimer() {
|
||||||
|
dndExpireTimer.stop();
|
||||||
|
if (!doNotDisturb || doNotDisturbUntil <= 0)
|
||||||
|
return;
|
||||||
|
const remaining = doNotDisturbUntil - Date.now();
|
||||||
|
if (remaining <= 0) {
|
||||||
|
setDoNotDisturb(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dndExpireTimer.interval = remaining;
|
||||||
|
dndExpireTimer.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
onDoNotDisturbChanged: _armDndExpireTimer()
|
||||||
|
onDoNotDisturbUntilChanged: _armDndExpireTimer()
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: osdSuppressTimer
|
id: osdSuppressTimer
|
||||||
interval: 2000
|
interval: 2000
|
||||||
@@ -49,6 +73,7 @@ Singleton {
|
|||||||
function onSessionResumed() {
|
function onSessionResumed() {
|
||||||
root.suppressOSD = true;
|
root.suppressOSD = true;
|
||||||
osdSuppressTimer.restart();
|
osdSuppressTimer.restart();
|
||||||
|
root._applyDndExpirySanity();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,6 +215,7 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Store.parse(root, obj);
|
Store.parse(root, obj);
|
||||||
|
_applyDndExpirySanity();
|
||||||
|
|
||||||
_loadedSessionSnapshot = getCurrentSessionJson();
|
_loadedSessionSnapshot = getCurrentSessionJson();
|
||||||
_hasLoaded = true;
|
_hasLoaded = true;
|
||||||
@@ -271,6 +297,7 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Store.parse(root, obj);
|
Store.parse(root, obj);
|
||||||
|
_applyDndExpirySanity();
|
||||||
|
|
||||||
_loadedSessionSnapshot = getCurrentSessionJson();
|
_loadedSessionSnapshot = getCurrentSessionJson();
|
||||||
_hasLoaded = true;
|
_hasLoaded = true;
|
||||||
@@ -288,6 +315,16 @@ Singleton {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _applyDndExpirySanity() {
|
||||||
|
if (doNotDisturb && doNotDisturbUntil > 0 && Date.now() >= doNotDisturbUntil) {
|
||||||
|
doNotDisturb = false;
|
||||||
|
doNotDisturbUntil = 0;
|
||||||
|
} else if (!doNotDisturb && doNotDisturbUntil !== 0) {
|
||||||
|
doNotDisturbUntil = 0;
|
||||||
|
}
|
||||||
|
_armDndExpireTimer();
|
||||||
|
}
|
||||||
|
|
||||||
function saveSettings() {
|
function saveSettings() {
|
||||||
if (isGreeterMode || _parseError || !_hasLoaded)
|
if (isGreeterMode || _parseError || !_hasLoaded)
|
||||||
return;
|
return;
|
||||||
@@ -357,8 +394,21 @@ Singleton {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function setDoNotDisturb(enabled) {
|
function setDoNotDisturb(enabled, durationMinutes) {
|
||||||
|
const minutes = Number(durationMinutes) || 0;
|
||||||
doNotDisturb = enabled;
|
doNotDisturb = enabled;
|
||||||
|
doNotDisturbUntil = (enabled && minutes > 0) ? Date.now() + minutes * 60 * 1000 : 0;
|
||||||
|
saveSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setDoNotDisturbUntilTimestamp(timestampMs) {
|
||||||
|
const target = Number(timestampMs) || 0;
|
||||||
|
if (target <= Date.now()) {
|
||||||
|
setDoNotDisturb(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
doNotDisturb = true;
|
||||||
|
doNotDisturbUntil = target;
|
||||||
saveSettings();
|
saveSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
var SPEC = {
|
var SPEC = {
|
||||||
isLightMode: { def: false },
|
isLightMode: { def: false },
|
||||||
doNotDisturb: { def: false },
|
doNotDisturb: { def: false },
|
||||||
|
doNotDisturbUntil: { def: 0 },
|
||||||
|
|
||||||
wallpaperPath: { def: "" },
|
wallpaperPath: { def: "" },
|
||||||
perMonitorWallpaper: { def: false },
|
perMonitorWallpaper: { def: false },
|
||||||
|
|||||||
@@ -372,10 +372,10 @@ Popup {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
implicitWidth: Math.max(180, menuColumn.implicitWidth + Theme.spacingS * 2)
|
implicitWidth: Math.max(180, menuColumn.implicitWidth + Theme.spacingS * 2)
|
||||||
implicitHeight: menuColumn.implicitHeight + Theme.spacingS * 2
|
implicitHeight: menuColumn.implicitHeight + Theme.spacingS * 2
|
||||||
color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
color: BlurService.enabled ? Theme.surfaceContainer : Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
border.color: BlurService.enabled ? BlurService.borderColor : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||||
border.width: 1
|
border.width: BlurService.enabled ? BlurService.borderWidth : 1
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -438,7 +438,7 @@ Popup {
|
|||||||
if (root.keyboardNavigation && root.selectedMenuIndex === menuItemDelegate.itemIndex) {
|
if (root.keyboardNavigation && root.selectedMenuIndex === menuItemDelegate.itemIndex) {
|
||||||
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.2);
|
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.2);
|
||||||
}
|
}
|
||||||
return itemMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent";
|
return itemMouseArea.containsMouse ? BlurService.hoverColor(Theme.widgetBaseHoverColor) : "transparent";
|
||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
|
|||||||
@@ -144,10 +144,48 @@ DankModal {
|
|||||||
return "NOTIFICATION_MODAL_TOGGLE_DND_SUCCESS";
|
return "NOTIFICATION_MODAL_TOGGLE_DND_SUCCESS";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function enableDoNotDisturbFor(minutes: int): string {
|
||||||
|
if (minutes <= 0) {
|
||||||
|
return "ERROR: minutes must be > 0";
|
||||||
|
}
|
||||||
|
SessionData.setDoNotDisturb(true, minutes);
|
||||||
|
return "NOTIFICATION_MODAL_DND_SET_FOR_" + minutes + "_SUCCESS";
|
||||||
|
}
|
||||||
|
|
||||||
|
function enableDoNotDisturbUntil(timestampMs: string): string {
|
||||||
|
const ts = Number(timestampMs);
|
||||||
|
if (!ts || ts <= Date.now()) {
|
||||||
|
return "ERROR: timestamp must be a future epoch ms";
|
||||||
|
}
|
||||||
|
SessionData.setDoNotDisturbUntilTimestamp(ts);
|
||||||
|
return "NOTIFICATION_MODAL_DND_SET_UNTIL_SUCCESS";
|
||||||
|
}
|
||||||
|
|
||||||
|
function enableDoNotDisturbIndefinitely(): string {
|
||||||
|
SessionData.setDoNotDisturb(true, 0);
|
||||||
|
return "NOTIFICATION_MODAL_DND_INDEFINITE_SUCCESS";
|
||||||
|
}
|
||||||
|
|
||||||
|
function enableDoNotDisturbUntilTomorrowMorning(): string {
|
||||||
|
const now = new Date();
|
||||||
|
const target = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1, 8, 0, 0, 0);
|
||||||
|
SessionData.setDoNotDisturbUntilTimestamp(target.getTime());
|
||||||
|
return "NOTIFICATION_MODAL_DND_UNTIL_TOMORROW_SUCCESS";
|
||||||
|
}
|
||||||
|
|
||||||
|
function disableDoNotDisturb(): string {
|
||||||
|
SessionData.setDoNotDisturb(false);
|
||||||
|
return "NOTIFICATION_MODAL_DND_DISABLE_SUCCESS";
|
||||||
|
}
|
||||||
|
|
||||||
function getDoNotDisturb(): bool {
|
function getDoNotDisturb(): bool {
|
||||||
return SessionData.doNotDisturb;
|
return SessionData.doNotDisturb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getDoNotDisturbUntil(): string {
|
||||||
|
return String(SessionData.doNotDisturbUntil);
|
||||||
|
}
|
||||||
|
|
||||||
function clearAll(): string {
|
function clearAll(): string {
|
||||||
notificationModal.clearAll();
|
notificationModal.clearAll();
|
||||||
return "NOTIFICATION_MODAL_CLEAR_ALL_SUCCESS";
|
return "NOTIFICATION_MODAL_CLEAR_ALL_SUCCESS";
|
||||||
|
|||||||
@@ -188,6 +188,9 @@ Item {
|
|||||||
case "battery":
|
case "battery":
|
||||||
coreDetailLoader.sourceComponent = batteryDetailComponent;
|
coreDetailLoader.sourceComponent = batteryDetailComponent;
|
||||||
break;
|
break;
|
||||||
|
case "doNotDisturb":
|
||||||
|
coreDetailLoader.sourceComponent = doNotDisturbDetailComponent;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -230,6 +233,11 @@ Item {
|
|||||||
BatteryDetail {}
|
BatteryDetail {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: doNotDisturbDetailComponent
|
||||||
|
DoNotDisturbDetail {}
|
||||||
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: diskUsageDetailComponent
|
id: diskUsageDetailComponent
|
||||||
DiskUsageDetail {
|
DiskUsageDetail {
|
||||||
|
|||||||
@@ -163,6 +163,8 @@ Column {
|
|||||||
return widgetWidth <= 25 ? smallDiskUsageComponent : diskUsagePillComponent;
|
return widgetWidth <= 25 ? smallDiskUsageComponent : diskUsagePillComponent;
|
||||||
} else if (id === "colorPicker") {
|
} else if (id === "colorPicker") {
|
||||||
return colorPickerPillComponent;
|
return colorPickerPillComponent;
|
||||||
|
} else if (id === "doNotDisturb") {
|
||||||
|
return widgetWidth <= 25 ? smallToggleComponent : dndPillComponent;
|
||||||
} else {
|
} else {
|
||||||
return widgetWidth <= 25 ? smallToggleComponent : toggleButtonComponent;
|
return widgetWidth <= 25 ? smallToggleComponent : toggleButtonComponent;
|
||||||
}
|
}
|
||||||
@@ -573,6 +575,22 @@ Column {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: dndPillComponent
|
||||||
|
DndPill {
|
||||||
|
property var widgetData: parent.widgetData || {}
|
||||||
|
property int widgetIndex: parent.widgetIndex || 0
|
||||||
|
width: parent.width
|
||||||
|
height: 60
|
||||||
|
|
||||||
|
onExpandClicked: {
|
||||||
|
if (!root.editMode) {
|
||||||
|
root.expandClicked(widgetData, widgetIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: smallBatteryComponent
|
id: smallBatteryComponent
|
||||||
SmallBatteryButton {
|
SmallBatteryButton {
|
||||||
@@ -603,8 +621,6 @@ Column {
|
|||||||
return DisplayService.nightModeEnabled ? "nightlight" : "dark_mode";
|
return DisplayService.nightModeEnabled ? "nightlight" : "dark_mode";
|
||||||
case "darkMode":
|
case "darkMode":
|
||||||
return "contrast";
|
return "contrast";
|
||||||
case "doNotDisturb":
|
|
||||||
return SessionData.doNotDisturb ? "do_not_disturb_on" : "do_not_disturb_off";
|
|
||||||
case "idleInhibitor":
|
case "idleInhibitor":
|
||||||
return SessionService.idleInhibited ? "motion_sensor_active" : "motion_sensor_idle";
|
return SessionService.idleInhibited ? "motion_sensor_active" : "motion_sensor_idle";
|
||||||
default:
|
default:
|
||||||
@@ -618,8 +634,6 @@ Column {
|
|||||||
return I18n.tr("Night Mode");
|
return I18n.tr("Night Mode");
|
||||||
case "darkMode":
|
case "darkMode":
|
||||||
return I18n.tr("Dark Mode");
|
return I18n.tr("Dark Mode");
|
||||||
case "doNotDisturb":
|
|
||||||
return I18n.tr("Do Not Disturb");
|
|
||||||
case "idleInhibitor":
|
case "idleInhibitor":
|
||||||
return SessionService.idleInhibited ? I18n.tr("Keeping Awake") : I18n.tr("Keep Awake");
|
return SessionService.idleInhibited ? I18n.tr("Keeping Awake") : I18n.tr("Keep Awake");
|
||||||
default:
|
default:
|
||||||
@@ -642,8 +656,6 @@ Column {
|
|||||||
return DisplayService.nightModeEnabled || false;
|
return DisplayService.nightModeEnabled || false;
|
||||||
case "darkMode":
|
case "darkMode":
|
||||||
return !SessionData.isLightMode;
|
return !SessionData.isLightMode;
|
||||||
case "doNotDisturb":
|
|
||||||
return SessionData.doNotDisturb || false;
|
|
||||||
case "idleInhibitor":
|
case "idleInhibitor":
|
||||||
return SessionService.idleInhibited || false;
|
return SessionService.idleInhibited || false;
|
||||||
default:
|
default:
|
||||||
@@ -670,11 +682,6 @@ Column {
|
|||||||
Theme.setLightMode(newMode);
|
Theme.setLightMode(newMode);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "doNotDisturb":
|
|
||||||
{
|
|
||||||
SessionData.setDoNotDisturb(!SessionData.doNotDisturb);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "idleInhibitor":
|
case "idleInhibitor":
|
||||||
{
|
{
|
||||||
SessionService.toggleIdleInhibit();
|
SessionService.toggleIdleInhibit();
|
||||||
|
|||||||
258
quickshell/Modules/ControlCenter/Details/DoNotDisturbDetail.qml
Normal file
258
quickshell/Modules/ControlCenter/Details/DoNotDisturbDetail.qml
Normal file
@@ -0,0 +1,258 @@
|
|||||||
|
import QtQuick
|
||||||
|
import qs.Common
|
||||||
|
import qs.Widgets
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
LayoutMirroring.enabled: I18n.isRtl
|
||||||
|
LayoutMirroring.childrenInherit: true
|
||||||
|
|
||||||
|
implicitHeight: contentColumn.implicitHeight + Theme.spacingL * 2
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
|
||||||
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||||
|
border.width: 0
|
||||||
|
|
||||||
|
property real nowMs: Date.now()
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
interval: 1000
|
||||||
|
repeat: true
|
||||||
|
running: root.visible && SessionData.doNotDisturb && SessionData.doNotDisturbUntil > 0
|
||||||
|
onTriggered: root.nowMs = Date.now()
|
||||||
|
}
|
||||||
|
|
||||||
|
function _pad2(n) {
|
||||||
|
return n < 10 ? "0" + n : "" + n;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatUntil(ts) {
|
||||||
|
if (!ts)
|
||||||
|
return "";
|
||||||
|
const d = new Date(ts);
|
||||||
|
const use24h = (typeof SettingsData !== "undefined") ? SettingsData.use24HourClock : true;
|
||||||
|
if (use24h)
|
||||||
|
return _pad2(d.getHours()) + ":" + _pad2(d.getMinutes());
|
||||||
|
const suffix = d.getHours() >= 12 ? "PM" : "AM";
|
||||||
|
const h12 = ((d.getHours() + 11) % 12) + 1;
|
||||||
|
return h12 + ":" + _pad2(d.getMinutes()) + " " + suffix;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatRemaining(ms) {
|
||||||
|
if (ms <= 0)
|
||||||
|
return "";
|
||||||
|
const totalMinutes = Math.ceil(ms / 60000);
|
||||||
|
if (totalMinutes < 60)
|
||||||
|
return I18n.tr("%1 min left").arg(totalMinutes);
|
||||||
|
const hours = Math.floor(totalMinutes / 60);
|
||||||
|
const mins = totalMinutes - hours * 60;
|
||||||
|
if (mins === 0)
|
||||||
|
return I18n.tr("%1 h left").arg(hours);
|
||||||
|
return I18n.tr("%1 h %2 m left").arg(hours).arg(mins);
|
||||||
|
}
|
||||||
|
|
||||||
|
function minutesUntilTomorrowMorning() {
|
||||||
|
const now = new Date();
|
||||||
|
const target = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1, 8, 0, 0, 0);
|
||||||
|
return Math.max(1, Math.round((target.getTime() - now.getTime()) / 60000));
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property var presets: [
|
||||||
|
{
|
||||||
|
"label": I18n.tr("15 min"),
|
||||||
|
"minutes": 15
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": I18n.tr("30 min"),
|
||||||
|
"minutes": 30
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": I18n.tr("1 hour"),
|
||||||
|
"minutes": 60
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": I18n.tr("3 hours"),
|
||||||
|
"minutes": 180
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": I18n.tr("8 hours"),
|
||||||
|
"minutes": 480
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": I18n.tr("Until 8 AM"),
|
||||||
|
"minutesFn": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: contentColumn
|
||||||
|
width: parent.width - Theme.spacingL * 2
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.margins: Theme.spacingL
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: SessionData.doNotDisturb ? "do_not_disturb_on" : "notifications_paused"
|
||||||
|
size: Theme.iconSizeLarge
|
||||||
|
color: SessionData.doNotDisturb ? Theme.primary : Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
width: parent.width - Theme.iconSizeLarge - Theme.spacingM
|
||||||
|
spacing: 2
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Silence notifications")
|
||||||
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
|
font.weight: Font.Medium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
width: parent.width
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: {
|
||||||
|
if (!SessionData.doNotDisturb)
|
||||||
|
return I18n.tr("Pick how long to pause notifications");
|
||||||
|
if (SessionData.doNotDisturbUntil <= 0)
|
||||||
|
return I18n.tr("On indefinitely");
|
||||||
|
const remaining = Math.max(0, SessionData.doNotDisturbUntil - root.nowMs);
|
||||||
|
return root.formatRemaining(remaining) + " · " + I18n.tr("until %1").arg(root.formatUntil(SessionData.doNotDisturbUntil));
|
||||||
|
}
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
width: parent.width
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Grid {
|
||||||
|
width: parent.width
|
||||||
|
columns: 3
|
||||||
|
columnSpacing: Theme.spacingS
|
||||||
|
rowSpacing: Theme.spacingS
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: root.presets
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
required property var modelData
|
||||||
|
width: (contentColumn.width - Theme.spacingS * 2) / 3
|
||||||
|
height: 36
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: presetArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) : Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
||||||
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||||
|
border.width: 1
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: modelData.label
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
font.weight: Font.Medium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: presetArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
const minutes = modelData.minutesFn ? root.minutesUntilTomorrowMorning() : modelData.minutes;
|
||||||
|
SessionData.setDoNotDisturb(true, minutes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: (contentColumn.width - Theme.spacingS) / 2
|
||||||
|
height: 36
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: foreverArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) : Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
||||||
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||||
|
border.width: 1
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
name: "block"
|
||||||
|
size: Theme.iconSizeSmall
|
||||||
|
color: Theme.surfaceText
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
text: I18n.tr("Until I turn it off")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
font.weight: Font.Medium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: foreverArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: SessionData.setDoNotDisturb(true, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: (contentColumn.width - Theme.spacingS) / 2
|
||||||
|
height: 36
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
visible: SessionData.doNotDisturb
|
||||||
|
color: offArea.containsMouse ? Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.18) : Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
||||||
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||||
|
border.width: 1
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
name: "notifications_active"
|
||||||
|
size: Theme.iconSizeSmall
|
||||||
|
color: offArea.containsMouse ? Theme.error : Theme.surfaceText
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
text: I18n.tr("Turn off")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
font.weight: Font.Medium
|
||||||
|
color: offArea.containsMouse ? Theme.error : Theme.surfaceText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: offArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: SessionData.setDoNotDisturb(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
29
quickshell/Modules/ControlCenter/Widgets/DndPill.qml
Normal file
29
quickshell/Modules/ControlCenter/Widgets/DndPill.qml
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import QtQuick
|
||||||
|
import qs.Common
|
||||||
|
import qs.Modules.ControlCenter.Widgets
|
||||||
|
|
||||||
|
CompoundPill {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
iconName: SessionData.doNotDisturb ? "do_not_disturb_on" : "do_not_disturb_off"
|
||||||
|
iconColor: SessionData.doNotDisturb ? Theme.primary : Theme.surfaceText
|
||||||
|
primaryText: I18n.tr("Do Not Disturb")
|
||||||
|
isActive: SessionData.doNotDisturb
|
||||||
|
|
||||||
|
secondaryText: {
|
||||||
|
if (!SessionData.doNotDisturb)
|
||||||
|
return I18n.tr("Off");
|
||||||
|
if (SessionData.doNotDisturbUntil <= 0)
|
||||||
|
return I18n.tr("On");
|
||||||
|
const d = new Date(SessionData.doNotDisturbUntil);
|
||||||
|
const use24h = (typeof SettingsData !== "undefined") ? SettingsData.use24HourClock : true;
|
||||||
|
const pad = n => n < 10 ? "0" + n : "" + n;
|
||||||
|
if (use24h)
|
||||||
|
return I18n.tr("Until %1").arg(pad(d.getHours()) + ":" + pad(d.getMinutes()));
|
||||||
|
const suffix = d.getHours() >= 12 ? "PM" : "AM";
|
||||||
|
const h12 = ((d.getHours() + 11) % 12) + 1;
|
||||||
|
return I18n.tr("Until %1").arg(h12 + ":" + pad(d.getMinutes()) + " " + suffix);
|
||||||
|
}
|
||||||
|
|
||||||
|
onToggled: SessionData.setDoNotDisturb(!SessionData.doNotDisturb)
|
||||||
|
}
|
||||||
@@ -9,6 +9,15 @@ import qs.Widgets
|
|||||||
PanelWindow {
|
PanelWindow {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
WindowBlur {
|
||||||
|
targetWindow: root
|
||||||
|
blurX: menuContainer.x
|
||||||
|
blurY: menuContainer.y
|
||||||
|
blurWidth: root.visible ? menuContainer.width : 0
|
||||||
|
blurHeight: root.visible ? menuContainer.height : 0
|
||||||
|
blurRadius: Theme.cornerRadius
|
||||||
|
}
|
||||||
|
|
||||||
WlrLayershell.namespace: "dms:dock-context-menu"
|
WlrLayershell.namespace: "dms:dock-context-menu"
|
||||||
|
|
||||||
property var appData: null
|
property var appData: null
|
||||||
@@ -112,8 +121,8 @@ PanelWindow {
|
|||||||
height: Math.max(60, menuColumn.implicitHeight + Theme.spacingS * 2)
|
height: Math.max(60, menuColumn.implicitHeight + Theme.spacingS * 2)
|
||||||
color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
border.color: BlurService.enabled ? BlurService.borderColor : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||||
border.width: 1
|
border.width: BlurService.enabled ? BlurService.borderWidth : 1
|
||||||
|
|
||||||
opacity: root.visible ? 1 : 0
|
opacity: root.visible ? 1 : 0
|
||||||
visible: opacity > 0
|
visible: opacity > 0
|
||||||
@@ -165,7 +174,7 @@ PanelWindow {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
height: 28
|
height: 28
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: windowArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
color: windowArea.containsMouse ? BlurService.hoverColor(Theme.widgetBaseHoverColor) : "transparent"
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
@@ -255,7 +264,7 @@ PanelWindow {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
height: 28
|
height: 28
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: actionArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
color: actionArea.containsMouse ? BlurService.hoverColor(Theme.widgetBaseHoverColor) : "transparent"
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
@@ -330,7 +339,7 @@ PanelWindow {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
height: 28
|
height: 28
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: pinArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
color: pinArea.containsMouse ? BlurService.hoverColor(Theme.widgetBaseHoverColor) : "transparent"
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
@@ -390,7 +399,7 @@ PanelWindow {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
height: 28
|
height: 28
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: nvidiaArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
color: nvidiaArea.containsMouse ? BlurService.hoverColor(Theme.widgetBaseHoverColor) : "transparent"
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
|
|||||||
@@ -148,6 +148,15 @@ BasePill {
|
|||||||
PanelWindow {
|
PanelWindow {
|
||||||
id: contextMenuWindow
|
id: contextMenuWindow
|
||||||
|
|
||||||
|
WindowBlur {
|
||||||
|
targetWindow: contextMenuWindow
|
||||||
|
blurX: menuContainer.x
|
||||||
|
blurY: menuContainer.y
|
||||||
|
blurWidth: contextMenuWindow.visible ? menuContainer.width : 0
|
||||||
|
blurHeight: contextMenuWindow.visible ? menuContainer.height : 0
|
||||||
|
blurRadius: Theme.cornerRadius
|
||||||
|
}
|
||||||
|
|
||||||
WlrLayershell.namespace: "dms:notepad-context-menu"
|
WlrLayershell.namespace: "dms:notepad-context-menu"
|
||||||
|
|
||||||
property bool isVertical: false
|
property bool isVertical: false
|
||||||
@@ -244,8 +253,8 @@ BasePill {
|
|||||||
height: Math.max(60, menuColumn.implicitHeight + Theme.spacingS * 2)
|
height: Math.max(60, menuColumn.implicitHeight + Theme.spacingS * 2)
|
||||||
color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
border.color: BlurService.enabled ? BlurService.borderColor : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||||
border.width: 1
|
border.width: BlurService.enabled ? BlurService.borderWidth : 1
|
||||||
|
|
||||||
opacity: contextMenuWindow.visible ? 1 : 0
|
opacity: contextMenuWindow.visible ? 1 : 0
|
||||||
visible: opacity > 0
|
visible: opacity > 0
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import qs.Common
|
import qs.Common
|
||||||
|
import qs.Modules.Notifications.Center
|
||||||
import qs.Modules.Plugins
|
import qs.Modules.Plugins
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
|
||||||
@@ -34,7 +35,49 @@ BasePill {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onRightClicked: {
|
onRightClicked: (rx, ry) => {
|
||||||
SessionData.setDoNotDisturb(!SessionData.doNotDisturb);
|
const screen = root.parentScreen || Screen;
|
||||||
|
if (!screen)
|
||||||
|
return;
|
||||||
|
const globalPos = root.visualContent.mapToItem(null, 0, 0);
|
||||||
|
const isVertical = root.axis?.isVertical ?? false;
|
||||||
|
const edge = root.axis?.edge ?? "top";
|
||||||
|
const gap = Math.max(Theme.spacingXS, root.barSpacing ?? Theme.spacingXS);
|
||||||
|
const barOffset = root.barThickness + root.barSpacing + gap;
|
||||||
|
|
||||||
|
let anchorX;
|
||||||
|
let anchorY;
|
||||||
|
let anchorEdge;
|
||||||
|
if (isVertical) {
|
||||||
|
anchorY = globalPos.y - (screen.y || 0) + root.visualContent.height / 2;
|
||||||
|
if (edge === "left") {
|
||||||
|
anchorX = barOffset;
|
||||||
|
anchorEdge = "top";
|
||||||
|
} else {
|
||||||
|
anchorX = screen.width - barOffset;
|
||||||
|
anchorEdge = "top";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
anchorX = globalPos.x - (screen.x || 0) + root.visualContent.width / 2;
|
||||||
|
if (edge === "bottom") {
|
||||||
|
anchorY = screen.height - barOffset;
|
||||||
|
anchorEdge = "bottom";
|
||||||
|
} else {
|
||||||
|
anchorY = barOffset;
|
||||||
|
anchorEdge = "top";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dndPopupLoader.active = true;
|
||||||
|
const popup = dndPopupLoader.item;
|
||||||
|
if (!popup)
|
||||||
|
return;
|
||||||
|
popup.showAt(anchorX, anchorY, screen, anchorEdge);
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: dndPopupLoader
|
||||||
|
active: false
|
||||||
|
sourceComponent: DndDurationPopup {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -230,7 +230,7 @@ PanelWindow {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
height: 28
|
height: 28
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: windowArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
color: windowArea.containsMouse ? BlurService.hoverColor(Theme.widgetBaseHoverColor) : "transparent"
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
@@ -320,7 +320,7 @@ PanelWindow {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
height: 28
|
height: 28
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: actionArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
color: actionArea.containsMouse ? BlurService.hoverColor(Theme.widgetBaseHoverColor) : "transparent"
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
@@ -395,7 +395,7 @@ PanelWindow {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
height: 28
|
height: 28
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: pinArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
color: pinArea.containsMouse ? BlurService.hoverColor(Theme.widgetBaseHoverColor) : "transparent"
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
@@ -468,7 +468,7 @@ PanelWindow {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
height: 28
|
height: 28
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: nvidiaArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
color: nvidiaArea.containsMouse ? BlurService.hoverColor(Theme.widgetBaseHoverColor) : "transparent"
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
|
|||||||
250
quickshell/Modules/Notifications/Center/DndDurationMenu.qml
Normal file
250
quickshell/Modules/Notifications/Center/DndDurationMenu.qml
Normal file
@@ -0,0 +1,250 @@
|
|||||||
|
import QtQuick
|
||||||
|
import qs.Common
|
||||||
|
import qs.Services
|
||||||
|
import qs.Widgets
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
LayoutMirroring.enabled: I18n.isRtl
|
||||||
|
LayoutMirroring.childrenInherit: true
|
||||||
|
|
||||||
|
signal dismissed
|
||||||
|
|
||||||
|
readonly property bool currentlyActive: SessionData.doNotDisturb
|
||||||
|
readonly property real currentRemainingMs: SessionData.doNotDisturbUntil > 0 ? Math.max(0, SessionData.doNotDisturbUntil - nowMs) : 0
|
||||||
|
property real nowMs: Date.now()
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
interval: 1000
|
||||||
|
repeat: true
|
||||||
|
running: root.visible && root.currentlyActive && SessionData.doNotDisturbUntil > 0
|
||||||
|
onTriggered: root.nowMs = Date.now()
|
||||||
|
}
|
||||||
|
|
||||||
|
function _pad2(n) {
|
||||||
|
return n < 10 ? "0" + n : "" + n;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatRemaining(ms) {
|
||||||
|
if (ms <= 0)
|
||||||
|
return I18n.tr("Off");
|
||||||
|
const totalMinutes = Math.ceil(ms / 60000);
|
||||||
|
if (totalMinutes < 60)
|
||||||
|
return I18n.tr("%1 min left").arg(totalMinutes);
|
||||||
|
const hours = Math.floor(totalMinutes / 60);
|
||||||
|
const mins = totalMinutes - hours * 60;
|
||||||
|
if (mins === 0)
|
||||||
|
return I18n.tr("%1 h left").arg(hours);
|
||||||
|
return I18n.tr("%1 h %2 m left").arg(hours).arg(mins);
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatUntilTimestamp(ts) {
|
||||||
|
if (!ts)
|
||||||
|
return "";
|
||||||
|
const d = new Date(ts);
|
||||||
|
const hours = d.getHours();
|
||||||
|
const minutes = d.getMinutes();
|
||||||
|
const use24h = (typeof SettingsData !== "undefined") ? SettingsData.use24HourClock : true;
|
||||||
|
if (use24h) {
|
||||||
|
return _pad2(hours) + ":" + _pad2(minutes);
|
||||||
|
}
|
||||||
|
const suffix = hours >= 12 ? "PM" : "AM";
|
||||||
|
const h12 = ((hours + 11) % 12) + 1;
|
||||||
|
return h12 + ":" + _pad2(minutes) + " " + suffix;
|
||||||
|
}
|
||||||
|
|
||||||
|
function minutesUntilTomorrowMorning() {
|
||||||
|
const now = new Date();
|
||||||
|
const target = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1, 8, 0, 0, 0);
|
||||||
|
return Math.max(1, Math.round((target.getTime() - now.getTime()) / 60000));
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property var presetOptions: [
|
||||||
|
{
|
||||||
|
"label": I18n.tr("For 15 minutes"),
|
||||||
|
"minutes": 15
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": I18n.tr("For 30 minutes"),
|
||||||
|
"minutes": 30
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": I18n.tr("For 1 hour"),
|
||||||
|
"minutes": 60
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": I18n.tr("For 3 hours"),
|
||||||
|
"minutes": 180
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": I18n.tr("For 8 hours"),
|
||||||
|
"minutes": 480
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": I18n.tr("Until tomorrow, 8:00 AM"),
|
||||||
|
"minutesFn": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": I18n.tr("Until I turn it off"),
|
||||||
|
"minutes": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
function selectPreset(option) {
|
||||||
|
let minutes = option.minutes;
|
||||||
|
if (option.minutesFn) {
|
||||||
|
minutes = minutesUntilTomorrowMorning();
|
||||||
|
}
|
||||||
|
SessionData.setDoNotDisturb(true, minutes);
|
||||||
|
root.dismissed();
|
||||||
|
}
|
||||||
|
|
||||||
|
function turnOff() {
|
||||||
|
SessionData.setDoNotDisturb(false);
|
||||||
|
root.dismissed();
|
||||||
|
}
|
||||||
|
|
||||||
|
implicitWidth: Math.max(220, menuColumn.implicitWidth + Theme.spacingM * 2)
|
||||||
|
implicitHeight: menuColumn.implicitHeight + Theme.spacingM * 2
|
||||||
|
color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
border.color: BlurService.enabled ? BlurService.borderColor : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||||
|
border.width: BlurService.enabled ? BlurService.borderWidth : 1
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: menuColumn
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.margins: Theme.spacingM
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: SessionData.doNotDisturb ? "notifications_off" : "notifications_paused"
|
||||||
|
size: Theme.iconSize - 2
|
||||||
|
color: SessionData.doNotDisturb ? Theme.primary : Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
width: parent.width - Theme.iconSize - parent.spacing
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Do Not Disturb")
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
font.weight: Font.Medium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
elide: Text.ElideRight
|
||||||
|
width: parent.width
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
visible: root.currentlyActive
|
||||||
|
text: {
|
||||||
|
if (SessionData.doNotDisturbUntil > 0) {
|
||||||
|
return root.formatRemaining(root.currentRemainingMs) + " · " + I18n.tr("until %1").arg(root.formatUntilTimestamp(SessionData.doNotDisturbUntil));
|
||||||
|
}
|
||||||
|
return I18n.tr("On indefinitely");
|
||||||
|
}
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
elide: Text.ElideRight
|
||||||
|
width: parent.width
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width
|
||||||
|
height: 1
|
||||||
|
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.15)
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: root.presetOptions
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: optionRect
|
||||||
|
required property var modelData
|
||||||
|
width: menuColumn.width
|
||||||
|
height: 32
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: optionArea.containsMouse ? BlurService.hoverColor(Theme.widgetBaseHoverColor) : "transparent"
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: Theme.spacingS
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: Theme.spacingS
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
text: optionRect.modelData.label
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceText
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: optionArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: root.selectPreset(optionRect.modelData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
visible: root.currentlyActive
|
||||||
|
width: parent.width
|
||||||
|
height: 1
|
||||||
|
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.15)
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
visible: root.currentlyActive
|
||||||
|
width: menuColumn.width
|
||||||
|
height: 32
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: offArea.containsMouse ? Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.14) : "transparent"
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: Theme.spacingS
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: Theme.spacingS
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
name: "notifications_active"
|
||||||
|
size: Theme.iconSizeSmall
|
||||||
|
color: offArea.containsMouse ? Theme.error : Theme.surfaceText
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
text: I18n.tr("Turn off now")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: offArea.containsMouse ? Theme.error : Theme.surfaceText
|
||||||
|
font.weight: Font.Medium
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: offArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: root.turnOff()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
88
quickshell/Modules/Notifications/Center/DndDurationPopup.qml
Normal file
88
quickshell/Modules/Notifications/Center/DndDurationPopup.qml
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Wayland
|
||||||
|
import qs.Common
|
||||||
|
import qs.Services
|
||||||
|
import qs.Widgets
|
||||||
|
|
||||||
|
PanelWindow {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
WindowBlur {
|
||||||
|
targetWindow: root
|
||||||
|
blurX: menu.x
|
||||||
|
blurY: menu.y
|
||||||
|
blurWidth: root.visible ? menu.width : 0
|
||||||
|
blurHeight: root.visible ? menu.height : 0
|
||||||
|
blurRadius: Theme.cornerRadius
|
||||||
|
}
|
||||||
|
|
||||||
|
WlrLayershell.namespace: "dms:dnd-duration-menu"
|
||||||
|
WlrLayershell.layer: WlrLayershell.Overlay
|
||||||
|
WlrLayershell.exclusiveZone: -1
|
||||||
|
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: true
|
||||||
|
left: true
|
||||||
|
right: true
|
||||||
|
bottom: true
|
||||||
|
}
|
||||||
|
|
||||||
|
property point anchorPos: Qt.point(0, 0)
|
||||||
|
property string anchorEdge: "top"
|
||||||
|
visible: false
|
||||||
|
|
||||||
|
function showAt(x, y, targetScreen, edge) {
|
||||||
|
if (targetScreen)
|
||||||
|
root.screen = targetScreen;
|
||||||
|
anchorPos = Qt.point(x, y);
|
||||||
|
anchorEdge = edge || "top";
|
||||||
|
visible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeMenu() {
|
||||||
|
visible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: PopoutManager
|
||||||
|
function onPopoutOpening() {
|
||||||
|
root.closeMenu();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
z: 0
|
||||||
|
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
|
||||||
|
onClicked: root.closeMenu()
|
||||||
|
}
|
||||||
|
|
||||||
|
DndDurationMenu {
|
||||||
|
id: menu
|
||||||
|
z: 1
|
||||||
|
visible: root.visible
|
||||||
|
|
||||||
|
x: {
|
||||||
|
const left = 10;
|
||||||
|
const right = root.width - width - 10;
|
||||||
|
const want = root.anchorPos.x - width / 2;
|
||||||
|
return Math.max(left, Math.min(right, want));
|
||||||
|
}
|
||||||
|
y: {
|
||||||
|
switch (root.anchorEdge) {
|
||||||
|
case "bottom":
|
||||||
|
return Math.max(10, root.anchorPos.y - height);
|
||||||
|
case "left":
|
||||||
|
case "right":
|
||||||
|
return Math.max(10, Math.min(root.height - height - 10, root.anchorPos.y - height / 2));
|
||||||
|
default:
|
||||||
|
return Math.min(root.height - height - 10, root.anchorPos.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onDismissed: root.closeMenu()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,12 +9,18 @@ Item {
|
|||||||
property var keyboardController: null
|
property var keyboardController: null
|
||||||
property bool showSettings: false
|
property bool showSettings: false
|
||||||
property int currentTab: 0
|
property int currentTab: 0
|
||||||
|
property bool showDndMenu: false
|
||||||
|
|
||||||
onCurrentTabChanged: {
|
onCurrentTabChanged: {
|
||||||
if (currentTab === 1 && !SettingsData.notificationHistoryEnabled)
|
if (currentTab === 1 && !SettingsData.notificationHistoryEnabled)
|
||||||
currentTab = 0;
|
currentTab = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onShowSettingsChanged: {
|
||||||
|
if (showSettings)
|
||||||
|
showDndMenu = false;
|
||||||
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: SettingsData
|
target: SettingsData
|
||||||
function onNotificationHistoryEnabledChanged() {
|
function onNotificationHistoryEnabledChanged() {
|
||||||
@@ -59,8 +65,31 @@ Item {
|
|||||||
iconColor: SessionData.doNotDisturb ? Theme.error : Theme.surfaceText
|
iconColor: SessionData.doNotDisturb ? Theme.error : Theme.surfaceText
|
||||||
buttonSize: Theme.iconSize + Theme.spacingS
|
buttonSize: Theme.iconSize + Theme.spacingS
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
onClicked: SessionData.setDoNotDisturb(!SessionData.doNotDisturb)
|
onClicked: {
|
||||||
onEntered: sharedTooltip.show(I18n.tr("Do Not Disturb"), doNotDisturbButton, 0, 0, "bottom")
|
if (SessionData.doNotDisturb) {
|
||||||
|
SessionData.setDoNotDisturb(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
root.showDndMenu = !root.showDndMenu;
|
||||||
|
if (root.showDndMenu)
|
||||||
|
root.showSettings = false;
|
||||||
|
}
|
||||||
|
onEntered: sharedTooltip.show(SessionData.doNotDisturb ? I18n.tr("Turn off Do Not Disturb") : I18n.tr("Do Not Disturb"), doNotDisturbButton, 0, 0, "bottom")
|
||||||
|
onExited: sharedTooltip.hide()
|
||||||
|
}
|
||||||
|
|
||||||
|
DankActionButton {
|
||||||
|
id: dndScheduleButton
|
||||||
|
iconName: root.showDndMenu ? "expand_less" : "schedule"
|
||||||
|
iconColor: root.showDndMenu ? Theme.primary : Theme.surfaceText
|
||||||
|
buttonSize: Theme.iconSize + Theme.spacingS
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
onClicked: {
|
||||||
|
root.showDndMenu = !root.showDndMenu;
|
||||||
|
if (root.showDndMenu)
|
||||||
|
root.showSettings = false;
|
||||||
|
}
|
||||||
|
onEntered: sharedTooltip.show(I18n.tr("Silence for a while"), dndScheduleButton, 0, 0, "bottom")
|
||||||
onExited: sharedTooltip.hide()
|
onExited: sharedTooltip.hide()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -139,6 +168,13 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DndDurationMenu {
|
||||||
|
id: dndMenu
|
||||||
|
width: parent.width
|
||||||
|
visible: root.showDndMenu
|
||||||
|
onDismissed: root.showDndMenu = false
|
||||||
|
}
|
||||||
|
|
||||||
DankButtonGroup {
|
DankButtonGroup {
|
||||||
id: tabGroup
|
id: tabGroup
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ Popup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
contentItem: Rectangle {
|
contentItem: Rectangle {
|
||||||
color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
color: BlurService.enabled ? Theme.surfaceContainer : Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
border.color: BlurService.enabled ? BlurService.borderColor : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
border.color: BlurService.enabled ? BlurService.borderColor : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||||
border.width: BlurService.enabled ? BlurService.borderWidth : 1
|
border.width: BlurService.enabled ? BlurService.borderWidth : 1
|
||||||
@@ -274,7 +274,7 @@ Popup {
|
|||||||
}
|
}
|
||||||
if (isSelected)
|
if (isSelected)
|
||||||
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.2);
|
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.2);
|
||||||
return menuItemArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent";
|
return menuItemArea.containsMouse ? BlurService.hoverColor(Theme.widgetBaseHoverColor) : "transparent";
|
||||||
}
|
}
|
||||||
opacity: modelData.enabled ? 1 : 0.5
|
opacity: modelData.enabled ? 1 : 0.5
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import QtQuick
|
|||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Wayland
|
import Quickshell.Wayland
|
||||||
import qs.Common
|
import qs.Common
|
||||||
|
import qs.Services
|
||||||
|
|
||||||
PanelWindow {
|
PanelWindow {
|
||||||
id: root
|
id: root
|
||||||
@@ -71,10 +72,10 @@ PanelWindow {
|
|||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
|
color: BlurService.enabled ? Theme.surfaceContainerHigh : Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
border.width: 1
|
border.width: BlurService.enabled ? BlurService.borderWidth : 1
|
||||||
border.color: Theme.outlineMedium
|
border.color: BlurService.enabled ? BlurService.borderColor : Theme.outlineMedium
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: textContent
|
id: textContent
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import qs.Common
|
import qs.Common
|
||||||
|
import qs.Services
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
@@ -111,10 +112,10 @@ Item {
|
|||||||
dim: false
|
dim: false
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
|
color: BlurService.enabled ? Theme.surfaceContainerHigh : Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
border.width: 1
|
border.width: BlurService.enabled ? BlurService.borderWidth : 1
|
||||||
border.color: Theme.outlineMedium
|
border.color: BlurService.enabled ? BlurService.borderColor : Theme.outlineMedium
|
||||||
}
|
}
|
||||||
|
|
||||||
contentItem: Text {
|
contentItem: Text {
|
||||||
|
|||||||
Reference in New Issue
Block a user