mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-04-04 04:42:05 -04:00
feat: Implement M3 design elevation & shadow effects
- Added global toggles in the Themes tab - Light color & directional user ovverides - Independent shadow overrides per/bar - Refactored various components to sync the updated designs
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import QtQuick
|
||||
import QtQuick.Effects
|
||||
import Quickshell
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
@@ -30,7 +31,21 @@ Rectangle {
|
||||
width: parent ? parent.width : 400
|
||||
height: baseCardHeight + contentItem.extraHeight
|
||||
radius: Theme.cornerRadius
|
||||
clip: true
|
||||
clip: false
|
||||
readonly property bool shadowsAllowed: Theme.elevationEnabled && Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1"
|
||||
|
||||
ElevationShadow {
|
||||
id: shadowLayer
|
||||
anchors.fill: parent
|
||||
z: -1
|
||||
level: Theme.elevationLevel1
|
||||
fallbackOffset: 1
|
||||
targetRadius: root.radius
|
||||
targetColor: root.color
|
||||
borderColor: root.border.color
|
||||
borderWidth: root.border.width
|
||||
shadowEnabled: root.shadowsAllowed
|
||||
}
|
||||
|
||||
color: {
|
||||
if (isSelected && keyboardNavigationActive)
|
||||
@@ -49,7 +64,7 @@ Rectangle {
|
||||
return 1.5;
|
||||
if (historyItem.urgency === 2)
|
||||
return 2;
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
Behavior on border.color {
|
||||
|
||||
@@ -232,6 +232,11 @@ Item {
|
||||
height: parent.height - filterChips.height - Theme.spacingS
|
||||
clip: true
|
||||
spacing: Theme.spacingS
|
||||
readonly property real horizontalShadowGutter: Theme.snap(Math.max(Theme.spacingXS, 4), 1)
|
||||
readonly property real verticalShadowGutter: Theme.snap(Math.max(Theme.spacingS, 8), 1)
|
||||
readonly property real delegateShadowGutter: Theme.snap(Math.max(Theme.spacingXS, 4), 1)
|
||||
topMargin: verticalShadowGutter
|
||||
bottomMargin: verticalShadowGutter
|
||||
|
||||
model: ScriptModel {
|
||||
id: historyModel
|
||||
@@ -263,13 +268,14 @@ Item {
|
||||
}
|
||||
|
||||
width: ListView.view.width
|
||||
height: historyCard.height
|
||||
clip: true
|
||||
height: historyCard.height + historyListView.delegateShadowGutter
|
||||
clip: false
|
||||
|
||||
HistoryNotificationCard {
|
||||
id: historyCard
|
||||
width: parent.width
|
||||
x: delegateRoot.swipeOffset
|
||||
width: Math.max(0, parent.width - (historyListView.horizontalShadowGutter * 2))
|
||||
y: historyListView.delegateShadowGutter / 2
|
||||
x: historyListView.horizontalShadowGutter + delegateRoot.swipeOffset
|
||||
historyItem: modelData
|
||||
isSelected: root.keyboardActive && root.selectedIndex === index
|
||||
keyboardNavigationActive: root.keyboardActive
|
||||
|
||||
@@ -18,6 +18,10 @@ DankListView {
|
||||
property real swipingCardOffset: 0
|
||||
property real __pendingStableHeight: 0
|
||||
property real __heightUpdateThreshold: 20
|
||||
readonly property real shadowBlurPx: Theme.elevationEnabled ? ((Theme.elevationLevel1 && Theme.elevationLevel1.blurPx !== undefined) ? Theme.elevationLevel1.blurPx : 4) : 0
|
||||
readonly property real shadowHorizontalGutter: Theme.snap(Math.max(Theme.spacingS, Math.min(32, shadowBlurPx * 1.5 + 6)), 1)
|
||||
readonly property real shadowVerticalGutter: Theme.snap(Math.max(Theme.spacingXS, 6), 1)
|
||||
readonly property real delegateShadowGutter: Theme.snap(Math.max(Theme.spacingXS, 4), 1)
|
||||
|
||||
Component.onCompleted: {
|
||||
Qt.callLater(() => {
|
||||
@@ -56,21 +60,26 @@ DankListView {
|
||||
let delta = 0;
|
||||
for (let i = 0; i < count; i++) {
|
||||
const item = itemAtIndex(i);
|
||||
if (item && item.children[0] && item.children[0].isAnimating)
|
||||
delta += item.children[0].targetHeight - item.height;
|
||||
if (item && item.children[0] && item.children[0].isAnimating) {
|
||||
const targetDelegateHeight = item.children[0].targetHeight + listView.delegateShadowGutter;
|
||||
delta += targetDelegateHeight - item.height;
|
||||
}
|
||||
}
|
||||
const targetHeight = contentHeight + delta;
|
||||
// During expansion, always update immediately without threshold check
|
||||
stableContentHeight = targetHeight;
|
||||
} else {
|
||||
__pendingStableHeight = contentHeight;
|
||||
heightUpdateDebounce.restart();
|
||||
heightUpdateDebounce.stop();
|
||||
stableContentHeight = __pendingStableHeight;
|
||||
}
|
||||
}
|
||||
|
||||
clip: true
|
||||
model: NotificationService.groupedNotifications
|
||||
spacing: Theme.spacingL
|
||||
topMargin: shadowVerticalGutter
|
||||
bottomMargin: shadowVerticalGutter
|
||||
|
||||
onIsUserScrollingChanged: {
|
||||
if (isUserScrolling && keyboardController && keyboardController.keyboardNavigationActive) {
|
||||
@@ -134,8 +143,7 @@ DankListView {
|
||||
readonly property real dismissThreshold: width * 0.35
|
||||
property bool __delegateInitialized: false
|
||||
|
||||
readonly property bool isAdjacentToSwipe: listView.count >= 2 && listView.swipingCardIndex !== -1 &&
|
||||
(index === listView.swipingCardIndex - 1 || index === listView.swipingCardIndex + 1)
|
||||
readonly property bool isAdjacentToSwipe: listView.count >= 2 && listView.swipingCardIndex !== -1 && (index === listView.swipingCardIndex - 1 || index === listView.swipingCardIndex + 1)
|
||||
readonly property real adjacentSwipeInfluence: isAdjacentToSwipe ? listView.swipingCardOffset * 0.10 : 0
|
||||
readonly property real adjacentScaleInfluence: isAdjacentToSwipe ? 1.0 - Math.abs(listView.swipingCardOffset) / width * 0.02 : 1.0
|
||||
readonly property real swipeFadeStartOffset: width * 0.75
|
||||
@@ -149,13 +157,14 @@ DankListView {
|
||||
}
|
||||
|
||||
width: ListView.view.width
|
||||
height: notificationCard.height
|
||||
clip: notificationCard.isAnimating
|
||||
height: notificationCard.height + listView.delegateShadowGutter
|
||||
clip: false
|
||||
|
||||
NotificationCard {
|
||||
id: notificationCard
|
||||
width: parent.width
|
||||
x: delegateRoot.swipeOffset + delegateRoot.adjacentSwipeInfluence
|
||||
width: Math.max(0, parent.width - (listView.shadowHorizontalGutter * 2))
|
||||
y: listView.delegateShadowGutter / 2
|
||||
x: listView.shadowHorizontalGutter + delegateRoot.swipeOffset + delegateRoot.adjacentSwipeInfluence
|
||||
listLevelAdjacentScaleInfluence: delegateRoot.adjacentScaleInfluence
|
||||
listLevelScaleAnimationsEnabled: listView.swipingCardIndex === -1 || !delegateRoot.isAdjacentToSwipe
|
||||
notificationGroup: modelData
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Effects
|
||||
import Quickshell
|
||||
import Quickshell.Services.Notifications
|
||||
import qs.Common
|
||||
@@ -38,7 +39,14 @@ Rectangle {
|
||||
height: expanded ? (expandedContent.height + cardPadding * 2) : (baseCardHeight + collapsedContent.extraHeight)
|
||||
readonly property real targetHeight: expanded ? (expandedContent.height + cardPadding * 2) : (baseCardHeight + collapsedContent.extraHeight)
|
||||
radius: Theme.cornerRadius
|
||||
scale: (cardHoverHandler.hovered ? 1.01 : 1.0) * listLevelAdjacentScaleInfluence
|
||||
scale: (cardHoverHandler.hovered ? 1.004 : 1.0) * listLevelAdjacentScaleInfluence
|
||||
readonly property bool shadowsAllowed: Theme.elevationEnabled && Quickshell.env("DMS_DISABLE_LAYER") !== "true" && Quickshell.env("DMS_DISABLE_LAYER") !== "1"
|
||||
readonly property var shadowElevation: Theme.elevationLevel1
|
||||
readonly property real baseShadowBlurPx: (shadowElevation && shadowElevation.blurPx !== undefined) ? shadowElevation.blurPx : 4
|
||||
readonly property real hoverShadowBlurBoost: cardHoverHandler.hovered ? Math.min(2, baseShadowBlurPx * 0.25) : 0
|
||||
property real shadowBlurPx: shadowsAllowed ? (baseShadowBlurPx + hoverShadowBlurBoost) : 0
|
||||
property real shadowOffsetXPx: shadowsAllowed ? Theme.elevationOffsetX(shadowElevation) : 0
|
||||
property real shadowOffsetYPx: shadowsAllowed ? (Theme.elevationOffsetY(shadowElevation, 1) + (cardHoverHandler.hovered ? 0.35 : 0)) : 0
|
||||
property bool __initialized: false
|
||||
|
||||
Component.onCompleted: {
|
||||
@@ -56,6 +64,27 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on shadowBlurPx {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on shadowOffsetXPx {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on shadowOffsetYPx {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on border.color {
|
||||
enabled: root.__initialized
|
||||
ColorAnimation {
|
||||
@@ -95,14 +124,31 @@ Rectangle {
|
||||
if (notificationGroup?.latestNotification?.urgency === NotificationUrgency.Critical) {
|
||||
return 2;
|
||||
}
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
clip: true
|
||||
clip: false
|
||||
|
||||
HoverHandler {
|
||||
id: cardHoverHandler
|
||||
}
|
||||
|
||||
ElevationShadow {
|
||||
id: shadowLayer
|
||||
anchors.fill: parent
|
||||
z: -1
|
||||
level: root.shadowElevation
|
||||
targetRadius: root.radius
|
||||
targetColor: root.color
|
||||
borderColor: root.border.color
|
||||
borderWidth: root.border.width
|
||||
shadowBlurPx: root.shadowBlurPx
|
||||
shadowSpreadPx: 0
|
||||
shadowOffsetX: root.shadowOffsetXPx
|
||||
shadowOffsetY: root.shadowOffsetYPx
|
||||
shadowColor: root.shadowElevation ? Theme.elevationShadowColor(root.shadowElevation) : "transparent"
|
||||
shadowEnabled: root.shadowsAllowed
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: parent.radius
|
||||
@@ -304,8 +350,13 @@ Rectangle {
|
||||
|
||||
onClicked: mouse => {
|
||||
if (!parent.hoveredLink && (parent.hasMoreText || descriptionExpanded)) {
|
||||
root.userInitiatedExpansion = true;
|
||||
const messageId = (notificationGroup && notificationGroup.latestNotification && notificationGroup.latestNotification.notification && notificationGroup.latestNotification.notification.id) ? (notificationGroup.latestNotification.notification.id + "_desc") : "";
|
||||
NotificationService.toggleMessageExpansion(messageId);
|
||||
Qt.callLater(() => {
|
||||
if (root && !root.isAnimating)
|
||||
root.userInitiatedExpansion = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -419,9 +470,7 @@ Rectangle {
|
||||
id: delegateRect
|
||||
width: parent.width
|
||||
|
||||
readonly property bool isAdjacentToSwipe: root.swipingNotificationIndex !== -1 &&
|
||||
(expandedDelegateWrapper.index === root.swipingNotificationIndex - 1 ||
|
||||
expandedDelegateWrapper.index === root.swipingNotificationIndex + 1)
|
||||
readonly property bool isAdjacentToSwipe: root.swipingNotificationIndex !== -1 && (expandedDelegateWrapper.index === root.swipingNotificationIndex - 1 || expandedDelegateWrapper.index === root.swipingNotificationIndex + 1)
|
||||
readonly property real adjacentSwipeInfluence: isAdjacentToSwipe ? root.swipingNotificationOffset * 0.10 : 0
|
||||
readonly property real adjacentScaleInfluence: isAdjacentToSwipe ? 1.0 - Math.abs(root.swipingNotificationOffset) / width * 0.02 : 1.0
|
||||
|
||||
@@ -605,7 +654,12 @@ Rectangle {
|
||||
|
||||
onClicked: mouse => {
|
||||
if (!parent.hoveredLink && (bodyText.hasMoreText || messageExpanded)) {
|
||||
root.userInitiatedExpansion = true;
|
||||
NotificationService.toggleMessageExpansion(modelData?.notification?.id || "");
|
||||
Qt.callLater(() => {
|
||||
if (root && !root.isAnimating)
|
||||
root.userInitiatedExpansion = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,15 +7,22 @@ DankPopout {
|
||||
id: root
|
||||
|
||||
layerNamespace: "dms:notification-center-popout"
|
||||
fullHeightSurface: true
|
||||
fullHeightSurface: false
|
||||
|
||||
property bool notificationHistoryVisible: false
|
||||
property var triggerScreen: null
|
||||
property real stablePopupHeight: 400
|
||||
property real _lastAlignedContentHeight: -1
|
||||
property bool _pendingSizedOpen: false
|
||||
|
||||
function updateStablePopupHeight() {
|
||||
const item = contentLoader.item;
|
||||
if (item && !root.shouldBeVisible) {
|
||||
const notificationList = findChild(item, "notificationList");
|
||||
if (notificationList && typeof notificationList.forceLayout === "function") {
|
||||
notificationList.forceLayout();
|
||||
}
|
||||
}
|
||||
const target = item ? Theme.px(item.implicitHeight, dpr) : 400;
|
||||
if (Math.abs(target - _lastAlignedContentHeight) < 0.5)
|
||||
return;
|
||||
@@ -26,7 +33,7 @@ DankPopout {
|
||||
NotificationKeyboardController {
|
||||
id: keyboardController
|
||||
listView: null
|
||||
isOpen: notificationHistoryVisible
|
||||
isOpen: root.shouldBeVisible
|
||||
onClose: () => {
|
||||
notificationHistoryVisible = false;
|
||||
}
|
||||
@@ -40,20 +47,42 @@ DankPopout {
|
||||
suspendShadowWhileResizing: false
|
||||
|
||||
screen: triggerScreen
|
||||
shouldBeVisible: notificationHistoryVisible
|
||||
|
||||
function toggle() {
|
||||
notificationHistoryVisible = !notificationHistoryVisible;
|
||||
}
|
||||
|
||||
function openSized() {
|
||||
if (!notificationHistoryVisible)
|
||||
return;
|
||||
|
||||
primeContent();
|
||||
if (contentLoader.item) {
|
||||
updateStablePopupHeight();
|
||||
_pendingSizedOpen = false;
|
||||
Qt.callLater(() => {
|
||||
if (!notificationHistoryVisible)
|
||||
return;
|
||||
updateStablePopupHeight();
|
||||
open();
|
||||
clearPrimedContent();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
_pendingSizedOpen = true;
|
||||
}
|
||||
|
||||
onBackgroundClicked: {
|
||||
notificationHistoryVisible = false;
|
||||
}
|
||||
|
||||
onNotificationHistoryVisibleChanged: {
|
||||
if (notificationHistoryVisible) {
|
||||
open();
|
||||
openSized();
|
||||
} else {
|
||||
_pendingSizedOpen = false;
|
||||
clearPrimedContent();
|
||||
close();
|
||||
}
|
||||
}
|
||||
@@ -82,6 +111,17 @@ DankPopout {
|
||||
target: contentLoader
|
||||
function onLoaded() {
|
||||
root.updateStablePopupHeight();
|
||||
if (root._pendingSizedOpen && root.notificationHistoryVisible) {
|
||||
Qt.callLater(() => {
|
||||
if (!root._pendingSizedOpen || !root.notificationHistoryVisible)
|
||||
return;
|
||||
root.updateStablePopupHeight();
|
||||
root._pendingSizedOpen = false;
|
||||
root.open();
|
||||
root.clearPrimedContent();
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (root.shouldBeVisible)
|
||||
Qt.callLater(root.setupKeyboardNavigation);
|
||||
}
|
||||
@@ -139,7 +179,8 @@ DankPopout {
|
||||
baseHeight += Theme.spacingM * 2;
|
||||
|
||||
const settingsHeight = notificationSettings.expanded ? notificationSettings.contentHeight : 0;
|
||||
let listHeight = notificationHeader.currentTab === 0 ? notificationList.stableContentHeight : Math.max(200, NotificationService.historyList.length * 80);
|
||||
const currentListHeight = root.shouldBeVisible ? notificationList.stableContentHeight : notificationList.listContentHeight;
|
||||
let listHeight = notificationHeader.currentTab === 0 ? currentListHeight : Math.max(200, NotificationService.historyList.length * 80);
|
||||
if (notificationHeader.currentTab === 0 && NotificationService.groupedNotifications.length === 0) {
|
||||
listHeight = 200;
|
||||
}
|
||||
@@ -233,13 +274,21 @@ DankPopout {
|
||||
expanded: notificationHeader.showSettings
|
||||
}
|
||||
|
||||
KeyboardNavigatedNotificationList {
|
||||
id: notificationList
|
||||
objectName: "notificationList"
|
||||
Item {
|
||||
visible: notificationHeader.currentTab === 0
|
||||
width: parent.width
|
||||
height: parent.height - notificationContent.cachedHeaderHeight - notificationSettings.height - contentColumnInner.spacing * 2
|
||||
cardAnimateExpansion: true
|
||||
|
||||
KeyboardNavigatedNotificationList {
|
||||
id: notificationList
|
||||
objectName: "notificationList"
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: -shadowHorizontalGutter
|
||||
anchors.rightMargin: -shadowHorizontalGutter
|
||||
anchors.topMargin: -(shadowVerticalGutter + delegateShadowGutter / 2)
|
||||
anchors.bottomMargin: -(shadowVerticalGutter + delegateShadowGutter / 2)
|
||||
cardAnimateExpansion: true
|
||||
}
|
||||
}
|
||||
|
||||
HistoryNotificationList {
|
||||
|
||||
@@ -118,8 +118,8 @@ PanelWindow {
|
||||
WlrLayershell.exclusiveZone: -1
|
||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
|
||||
color: "transparent"
|
||||
implicitWidth: screen ? Math.min(400, Math.max(320, screen.width * 0.23)) : 380
|
||||
implicitHeight: {
|
||||
readonly property real contentImplicitWidth: screen ? Math.min(400, Math.max(320, screen.width * 0.23)) : 380
|
||||
readonly property real contentImplicitHeight: {
|
||||
if (SettingsData.notificationPopupPrivacyMode && !descriptionExpanded)
|
||||
return basePopupHeightPrivacy;
|
||||
if (!descriptionExpanded)
|
||||
@@ -130,6 +130,8 @@ PanelWindow {
|
||||
return basePopupHeight + bodyTextHeight - collapsedBodyHeight;
|
||||
return basePopupHeight;
|
||||
}
|
||||
implicitWidth: contentImplicitWidth + (windowShadowPad * 2)
|
||||
implicitHeight: contentImplicitHeight + (windowShadowPad * 2)
|
||||
|
||||
Behavior on implicitHeight {
|
||||
enabled: !exiting && !_isDestroying
|
||||
@@ -182,11 +184,15 @@ PanelWindow {
|
||||
property bool isTopCenter: SettingsData.notificationPopupPosition === -1
|
||||
property bool isBottomCenter: SettingsData.notificationPopupPosition === SettingsData.Position.BottomCenter
|
||||
property bool isCenterPosition: isTopCenter || isBottomCenter
|
||||
readonly property real maxPopupShadowBlurPx: Math.max((Theme.elevationLevel3 && Theme.elevationLevel3.blurPx !== undefined) ? Theme.elevationLevel3.blurPx : 12, (Theme.elevationLevel4 && Theme.elevationLevel4.blurPx !== undefined) ? Theme.elevationLevel4.blurPx : 16)
|
||||
readonly property real maxPopupShadowOffsetXPx: Math.max(Math.abs(Theme.elevationOffsetX(Theme.elevationLevel3)), Math.abs(Theme.elevationOffsetX(Theme.elevationLevel4)))
|
||||
readonly property real maxPopupShadowOffsetYPx: Math.max(Math.abs(Theme.elevationOffsetY(Theme.elevationLevel3, 6)), Math.abs(Theme.elevationOffsetY(Theme.elevationLevel4, 8)))
|
||||
readonly property real windowShadowPad: Theme.elevationEnabled && SettingsData.notificationPopupShadowEnabled ? Theme.snap(Math.max(16, maxPopupShadowBlurPx + Math.max(maxPopupShadowOffsetXPx, maxPopupShadowOffsetYPx) + 8), dpr) : 0
|
||||
|
||||
anchors.top: true
|
||||
anchors.bottom: true
|
||||
anchors.left: SettingsData.notificationPopupPosition === SettingsData.Position.Left || SettingsData.notificationPopupPosition === SettingsData.Position.Bottom
|
||||
anchors.right: SettingsData.notificationPopupPosition === SettingsData.Position.Top || SettingsData.notificationPopupPosition === SettingsData.Position.Right
|
||||
anchors.left: true
|
||||
anchors.bottom: false
|
||||
anchors.right: false
|
||||
|
||||
mask: contentInputMask
|
||||
|
||||
@@ -205,10 +211,10 @@ PanelWindow {
|
||||
}
|
||||
|
||||
margins {
|
||||
top: _storedTopMargin
|
||||
bottom: _storedBottomMargin
|
||||
left: getLeftMargin()
|
||||
right: getRightMargin()
|
||||
top: getWindowTopMargin()
|
||||
bottom: 0
|
||||
left: getWindowLeftMargin()
|
||||
right: 0
|
||||
}
|
||||
|
||||
function getBarInfo() {
|
||||
@@ -250,7 +256,7 @@ PanelWindow {
|
||||
|
||||
function getLeftMargin() {
|
||||
if (isCenterPosition)
|
||||
return screen ? (screen.width - implicitWidth) / 2 : 0;
|
||||
return screen ? (screen.width - alignedWidth) / 2 : 0;
|
||||
|
||||
const popupPos = SettingsData.notificationPopupPosition;
|
||||
const isLeft = popupPos === SettingsData.Position.Left || popupPos === SettingsData.Position.Bottom;
|
||||
@@ -274,23 +280,56 @@ PanelWindow {
|
||||
return barInfo.rightBar > 0 ? barInfo.rightBar : Theme.popupDistance;
|
||||
}
|
||||
|
||||
function getContentX() {
|
||||
if (!screen)
|
||||
return 0;
|
||||
|
||||
const popupPos = SettingsData.notificationPopupPosition;
|
||||
const barLeft = getLeftMargin();
|
||||
const barRight = getRightMargin();
|
||||
|
||||
if (isCenterPosition)
|
||||
return Theme.snap((screen.width - alignedWidth) / 2, dpr);
|
||||
if (popupPos === SettingsData.Position.Left || popupPos === SettingsData.Position.Bottom)
|
||||
return Theme.snap(barLeft, dpr);
|
||||
return Theme.snap(screen.width - alignedWidth - barRight, dpr);
|
||||
}
|
||||
|
||||
function getContentY() {
|
||||
if (!screen)
|
||||
return 0;
|
||||
|
||||
const popupPos = SettingsData.notificationPopupPosition;
|
||||
const barTop = getTopMargin();
|
||||
const barBottom = getBottomMargin();
|
||||
const isTop = isTopCenter || popupPos === SettingsData.Position.Top || popupPos === SettingsData.Position.Left;
|
||||
if (isTop)
|
||||
return Theme.snap(barTop, dpr);
|
||||
return Theme.snap(screen.height - alignedHeight - barBottom, dpr);
|
||||
}
|
||||
|
||||
function getWindowLeftMargin() {
|
||||
if (!screen)
|
||||
return 0;
|
||||
return Theme.snap(getContentX() - windowShadowPad, dpr);
|
||||
}
|
||||
|
||||
function getWindowTopMargin() {
|
||||
if (!screen)
|
||||
return 0;
|
||||
return Theme.snap(getContentY() - windowShadowPad, dpr);
|
||||
}
|
||||
|
||||
readonly property bool screenValid: win.screen && !_isDestroying
|
||||
readonly property real dpr: screenValid ? CompositorService.getScreenScale(win.screen) : 1
|
||||
readonly property real alignedWidth: Theme.px(implicitWidth, dpr)
|
||||
readonly property real alignedHeight: Theme.px(implicitHeight, dpr)
|
||||
readonly property real alignedWidth: Theme.px(Math.max(0, implicitWidth - (windowShadowPad * 2)), dpr)
|
||||
readonly property real alignedHeight: Theme.px(Math.max(0, implicitHeight - (windowShadowPad * 2)), dpr)
|
||||
|
||||
Item {
|
||||
id: content
|
||||
|
||||
x: Theme.snap((win.width - alignedWidth) / 2, dpr)
|
||||
y: {
|
||||
const isTop = isTopCenter || SettingsData.notificationPopupPosition === SettingsData.Position.Top || SettingsData.notificationPopupPosition === SettingsData.Position.Left;
|
||||
if (isTop) {
|
||||
return Theme.snap(screenY, dpr);
|
||||
} else {
|
||||
return Theme.snap(win.height - alignedHeight - screenY, dpr);
|
||||
}
|
||||
}
|
||||
x: Theme.snap(windowShadowPad, dpr)
|
||||
y: Theme.snap(windowShadowPad, dpr)
|
||||
width: alignedWidth
|
||||
height: alignedHeight
|
||||
visible: !win._finalized
|
||||
@@ -313,12 +352,13 @@ PanelWindow {
|
||||
readonly property bool swipeActive: swipeDragHandler.active
|
||||
property bool swipeDismissing: false
|
||||
|
||||
readonly property real radiusForShadow: Theme.cornerRadius
|
||||
property real shadowBlurPx: SettingsData.notificationPopupShadowEnabled ? ((2 + radiusForShadow * 0.2) * (cardHoverHandler.hovered ? 1.2 : 1)) : 0
|
||||
property real shadowSpreadPx: SettingsData.notificationPopupShadowEnabled ? (radiusForShadow * (cardHoverHandler.hovered ? 0.06 : 0)) : 0
|
||||
property real shadowBaseAlpha: 0.35
|
||||
readonly property real popupSurfaceAlpha: SettingsData.popupTransparency
|
||||
readonly property real effectiveShadowAlpha: Math.max(0, Math.min(1, shadowBaseAlpha * popupSurfaceAlpha))
|
||||
readonly property bool shadowsAllowed: Theme.elevationEnabled && SettingsData.notificationPopupShadowEnabled
|
||||
readonly property var elevLevel: cardHoverHandler.hovered ? Theme.elevationLevel4 : Theme.elevationLevel3
|
||||
readonly property real cardInset: Theme.snap(4, win.dpr)
|
||||
readonly property real shadowRenderPadding: shadowsAllowed ? Theme.snap(Math.max(16, shadowBlurPx + Math.max(Math.abs(shadowOffsetX), Math.abs(shadowOffsetY)) + 8), win.dpr) : 0
|
||||
property real shadowBlurPx: shadowsAllowed ? (elevLevel && elevLevel.blurPx !== undefined ? elevLevel.blurPx : 12) : 0
|
||||
property real shadowOffsetX: shadowsAllowed ? Theme.elevationOffsetX(elevLevel) : 0
|
||||
property real shadowOffsetY: shadowsAllowed ? Theme.elevationOffsetY(elevLevel, 6) : 0
|
||||
|
||||
Behavior on shadowBlurPx {
|
||||
NumberAnimation {
|
||||
@@ -327,50 +367,50 @@ PanelWindow {
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on shadowSpreadPx {
|
||||
Behavior on shadowOffsetX {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Behavior on shadowOffsetY {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
|
||||
ElevationShadow {
|
||||
id: bgShadowLayer
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.snap(4, win.dpr)
|
||||
layer.enabled: !win._isDestroying && win.screenValid
|
||||
layer.smooth: false
|
||||
anchors.margins: -content.shadowRenderPadding
|
||||
level: content.elevLevel
|
||||
fallbackOffset: 6
|
||||
shadowBlurPx: content.shadowBlurPx
|
||||
shadowOffsetX: content.shadowOffsetX
|
||||
shadowOffsetY: content.shadowOffsetY
|
||||
shadowColor: content.shadowsAllowed && content.elevLevel ? Theme.elevationShadowColor(content.elevLevel) : "transparent"
|
||||
shadowEnabled: !win._isDestroying && win.screenValid && content.shadowsAllowed
|
||||
layer.textureSize: Qt.size(Math.round(width * win.dpr), Math.round(height * win.dpr))
|
||||
layer.textureMirroring: ShaderEffectSource.MirrorVertically
|
||||
|
||||
readonly property int blurMax: 64
|
||||
|
||||
layer.effect: MultiEffect {
|
||||
id: shadowFx
|
||||
autoPaddingEnabled: true
|
||||
shadowEnabled: SettingsData.notificationPopupShadowEnabled
|
||||
blurEnabled: false
|
||||
maskEnabled: false
|
||||
shadowBlur: Math.max(0, Math.min(1, content.shadowBlurPx / bgShadowLayer.blurMax))
|
||||
shadowScale: 1 + (2 * content.shadowSpreadPx) / Math.max(1, Math.min(bgShadowLayer.width, bgShadowLayer.height))
|
||||
shadowColor: {
|
||||
const baseColor = Theme.isLightMode ? Qt.rgba(0, 0, 0, 1) : Theme.surfaceContainerHighest;
|
||||
return Theme.withAlpha(baseColor, content.effectiveShadowAlpha);
|
||||
}
|
||||
}
|
||||
sourceRect.anchors.fill: undefined
|
||||
sourceRect.x: content.shadowRenderPadding + content.cardInset
|
||||
sourceRect.y: content.shadowRenderPadding + content.cardInset
|
||||
sourceRect.width: Math.max(0, content.width - (content.cardInset * 2))
|
||||
sourceRect.height: Math.max(0, content.height - (content.cardInset * 2))
|
||||
sourceRect.radius: Theme.cornerRadius
|
||||
sourceRect.color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
||||
sourceRect.border.color: notificationData && notificationData.urgency === NotificationUrgency.Critical ? Theme.withAlpha(Theme.primary, 0.3) : Theme.withAlpha(Theme.outline, 0.08)
|
||||
sourceRect.border.width: notificationData && notificationData.urgency === NotificationUrgency.Critical ? 2 : 0
|
||||
|
||||
Rectangle {
|
||||
id: shadowShapeSource
|
||||
anchors.fill: parent
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
||||
border.color: notificationData && notificationData.urgency === NotificationUrgency.Critical ? Theme.withAlpha(Theme.primary, 0.3) : Theme.withAlpha(Theme.outline, 0.08)
|
||||
border.width: notificationData && notificationData.urgency === NotificationUrgency.Critical ? 2 : 0
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: shadowShapeSource.radius
|
||||
x: bgShadowLayer.sourceRect.x
|
||||
y: bgShadowLayer.sourceRect.y
|
||||
width: bgShadowLayer.sourceRect.width
|
||||
height: bgShadowLayer.sourceRect.height
|
||||
radius: bgShadowLayer.sourceRect.radius
|
||||
visible: notificationData && notificationData.urgency === NotificationUrgency.Critical
|
||||
opacity: 1
|
||||
clip: true
|
||||
@@ -399,7 +439,7 @@ PanelWindow {
|
||||
Item {
|
||||
id: backgroundContainer
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.snap(4, win.dpr)
|
||||
anchors.margins: content.cardInset
|
||||
clip: true
|
||||
|
||||
HoverHandler {
|
||||
|
||||
Reference in New Issue
Block a user