1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-05-03 02:52:07 -04:00

numerous animation improvements, convert a bunch of stuff to use

Animator, etc.
This commit is contained in:
bbedward
2026-04-30 16:54:33 -04:00
committed by purian23
parent 7138c546e1
commit 9018002959
67 changed files with 1525 additions and 989 deletions

View File

@@ -214,13 +214,13 @@ Item {
color: root.accentColor
Behavior on x {
NumberAnimation {
XAnimator {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
Behavior on y {
NumberAnimation {
YAnimator {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
@@ -278,13 +278,13 @@ Item {
color: root.accentColor
Behavior on x {
NumberAnimation {
XAnimator {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
}
Behavior on y {
NumberAnimation {
YAnimator {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}

View File

@@ -47,7 +47,7 @@ Rectangle {
opacity: mouseArea.containsMouse ? 0.08 : 0.0
Behavior on opacity {
NumberAnimation {
OpacityAnimator {
duration: Theme.shortDuration
}
}

View File

@@ -23,11 +23,15 @@ Item {
signal toggleWidgetSize(int index)
width: {
const widgetWidth = widgetData?.width || 50
if (widgetWidth <= 25) return gridCellWidth
else if (widgetWidth <= 50) return gridCellWidth * 2
else if (widgetWidth <= 75) return gridCellWidth * 3
else return gridCellWidth * 4
const widgetWidth = widgetData?.width || 50;
if (widgetWidth <= 25)
return gridCellWidth;
else if (widgetWidth <= 50)
return gridCellWidth * 2;
else if (widgetWidth <= 75)
return gridCellWidth * 3;
else
return gridCellWidth * 4;
}
height: isSlider ? 16 : gridCellHeight
@@ -42,10 +46,14 @@ Item {
z: dragArea.drag.active ? 10000 : 1
Behavior on border.width {
NumberAnimation { duration: 150 }
NumberAnimation {
duration: 150
}
}
Behavior on opacity {
NumberAnimation { duration: 150 }
OpacityAnimator {
duration: 150
}
}
}
@@ -58,14 +66,17 @@ Item {
property int globalWidgetIndex: root.widgetIndex
property int widgetWidth: root.widgetData?.width || 50
MouseArea {
id: editModeBlocker
anchors.fill: parent
enabled: root.editMode
acceptedButtons: Qt.AllButtons
onPressed: function(mouse) { mouse.accepted = true }
onWheel: function(wheel) { wheel.accepted = true }
onPressed: function (mouse) {
mouse.accepted = true;
}
onWheel: function (wheel) {
wheel.accepted = true;
}
z: 100
}
}
@@ -79,19 +90,19 @@ Item {
drag.axis: Drag.XAndYAxis
drag.smoothed: true
onPressed: function(mouse) {
onPressed: function (mouse) {
if (editMode) {
cursorShape = Qt.ClosedHandCursor
cursorShape = Qt.ClosedHandCursor;
if (root.gridLayout && root.gridLayout.moveToTop) {
root.gridLayout.moveToTop(root)
root.gridLayout.moveToTop(root);
}
}
}
onReleased: function(mouse) {
onReleased: function (mouse) {
if (editMode) {
cursorShape = Qt.OpenHandCursor
root.snapToGrid()
cursorShape = Qt.OpenHandCursor;
root.snapToGrid();
}
}
}
@@ -101,9 +112,11 @@ Item {
Drag.hotSpot.y: height / 2
function swapIndices(i, j) {
if (i === j) return;
if (i === j)
return;
const arr = SettingsData.controlCenterWidgets;
if (!arr || i < 0 || j < 0 || i >= arr.length || j >= arr.length) return;
if (!arr || i < 0 || j < 0 || i >= arr.length || j >= arr.length)
return;
const copy = arr.slice();
const tmp = copy[i];
@@ -114,37 +127,41 @@ Item {
}
function snapToGrid() {
if (!editMode || !gridLayout) return
if (!editMode || !gridLayout)
return;
const globalPos = root.mapToItem(gridLayout, 0, 0);
const cellWidth = gridLayout.width / gridColumns;
const cellHeight = gridCellHeight + Theme.spacingS;
const globalPos = root.mapToItem(gridLayout, 0, 0)
const cellWidth = gridLayout.width / gridColumns
const cellHeight = gridCellHeight + Theme.spacingS
const centerX = globalPos.x + (root.width / 2);
const centerY = globalPos.y + (root.height / 2);
const centerX = globalPos.x + (root.width / 2)
const centerY = globalPos.y + (root.height / 2)
let targetCol = Math.max(0, Math.floor(centerX / cellWidth));
let targetRow = Math.max(0, Math.floor(centerY / cellHeight));
let targetCol = Math.max(0, Math.floor(centerX / cellWidth))
let targetRow = Math.max(0, Math.floor(centerY / cellHeight))
targetCol = Math.min(targetCol, gridColumns - 1);
targetCol = Math.min(targetCol, gridColumns - 1)
const newIndex = findBestInsertionIndex(targetRow, targetCol)
const newIndex = findBestInsertionIndex(targetRow, targetCol);
if (newIndex !== widgetIndex && newIndex >= 0 && newIndex < (SettingsData.controlCenterWidgets?.length || 0)) {
swapIndices(widgetIndex, newIndex)
swapIndices(widgetIndex, newIndex);
}
}
function findBestInsertionIndex(targetRow, targetCol) {
const widgets = SettingsData.controlCenterWidgets || [];
const n = widgets.length;
if (!n || widgetIndex < 0 || widgetIndex >= n) return -1;
if (!n || widgetIndex < 0 || widgetIndex >= n)
return -1;
function spanFor(width) {
const w = width ?? 50;
if (w <= 25) return 1;
if (w <= 50) return 2;
if (w <= 75) return 3;
if (w <= 25)
return 1;
if (w <= 50)
return 2;
if (w <= 75)
return 3;
return 4;
}
@@ -169,7 +186,13 @@ Item {
if (i === widgetIndex) {
draggedOrigKey = centerKey;
} else {
pos.push({ index: i, row, startCol, span, centerKey });
pos.push({
index: i,
row,
startCol,
span,
centerKey
});
}
col += span;
@@ -179,7 +202,8 @@ Item {
}
}
if (pos.length === 0) return -1;
if (pos.length === 0)
return -1;
const centerColCoord = targetCol + 0.5;
const targetKey = targetRow * cols + centerColCoord;
@@ -192,15 +216,20 @@ Item {
}
let lo = 0, hi = pos.length - 1;
if (targetKey <= pos[0].centerKey) return pos[0].index;
if (targetKey >= pos[hi].centerKey) return pos[hi].index;
if (targetKey <= pos[0].centerKey)
return pos[0].index;
if (targetKey >= pos[hi].centerKey)
return pos[hi].index;
while (lo <= hi) {
const mid = (lo + hi) >> 1;
const mk = pos[mid].centerKey;
if (targetKey < mk) hi = mid - 1;
else if (targetKey > mk) lo = mid + 1;
else return pos[mid].index;
if (targetKey < mk)
hi = mid - 1;
else if (targetKey > mk)
lo = mid + 1;
else
return pos[mid].index;
}
const movingUp = (draggedOrigKey != null) ? (targetKey < draggedOrigKey) : false;
return (movingUp ? pos[lo].index : pos[hi].index);
@@ -240,11 +269,11 @@ Item {
currentSize: root.widgetData?.width || 50
isSlider: root.isSlider
widgetIndex: root.widgetIndex
onSizeChanged: (newSize) => {
var widgets = SettingsData.controlCenterWidgets.slice()
onSizeChanged: newSize => {
var widgets = SettingsData.controlCenterWidgets.slice();
if (widgetIndex >= 0 && widgetIndex < widgets.length) {
widgets[widgetIndex].width = newSize
SettingsData.set("controlCenterWidgets", widgets)
widgets[widgetIndex].width = newSize;
SettingsData.set("controlCenterWidgets", widgets);
}
}
}
@@ -270,7 +299,9 @@ Item {
}
Behavior on opacity {
NumberAnimation { duration: 150 }
OpacityAnimator {
duration: 150
}
}
}
@@ -283,7 +314,9 @@ Item {
z: -1
Behavior on color {
ColorAnimation { duration: Theme.shortDuration }
ColorAnimation {
duration: Theme.shortDuration
}
}
}
}

View File

@@ -195,7 +195,7 @@ DankPopout {
Behavior on opacity {
enabled: !Theme.isDirectionalEffect
NumberAnimation {
OpacityAnimator {
duration: Theme.shortDuration
easing.type: Easing.BezierSpline
easing.bezierCurve: root.shouldBeVisible ? Theme.variantPopoutEnterCurve : Theme.variantPopoutExitCurve

View File

@@ -127,7 +127,7 @@ Item {
opacity: modalVisible ? 1 : 0
Behavior on opacity {
NumberAnimation {
OpacityAnimator {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
@@ -316,14 +316,14 @@ Item {
}
Behavior on opacity {
NumberAnimation {
OpacityAnimator {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
}
Behavior on scale {
NumberAnimation {
ScaleAnimator {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}

View File

@@ -64,7 +64,7 @@ Rectangle {
opacity: 0.08
antialiasing: true
Behavior on opacity {
NumberAnimation {
OpacityAnimator {
duration: Theme.shortDuration
}
}

View File

@@ -53,7 +53,7 @@ Rectangle {
opacity: mouseArea.containsMouse ? 0.08 : 0.0
Behavior on opacity {
NumberAnimation {
OpacityAnimator {
duration: Theme.shortDuration
}
}

View File

@@ -474,14 +474,14 @@ BasePill {
height: (isInOverflow && !root.overflowExpanded) ? 0 : visualHeight
Behavior on opacity {
NumberAnimation {
OpacityAnimator {
duration: Theme.shortDuration
easing.type: Easing.OutCubic
}
}
Behavior on scale {
NumberAnimation {
ScaleAnimator {
duration: Theme.shortDuration
easing.type: Easing.OutCubic
}
@@ -666,7 +666,7 @@ BasePill {
transformOrigin: Item.Center
scale: appItem.enlargeScale
Behavior on scale {
NumberAnimation {
ScaleAnimator {
duration: 120
easing.type: Easing.OutCubic
}
@@ -711,7 +711,7 @@ BasePill {
transformOrigin: Item.Center
scale: appItem.enlargeScale
Behavior on scale {
NumberAnimation {
ScaleAnimator {
duration: 120
easing.type: Easing.OutCubic
}
@@ -734,7 +734,7 @@ BasePill {
transformOrigin: Item.Center
scale: appItem.enlargeScale
Behavior on scale {
NumberAnimation {
ScaleAnimator {
duration: 120
easing.type: Easing.OutCubic
}
@@ -758,7 +758,7 @@ BasePill {
transformOrigin: Item.Center
scale: appItem.enlargeScale
Behavior on scale {
NumberAnimation {
ScaleAnimator {
duration: 120
easing.type: Easing.OutCubic
}

View File

@@ -36,7 +36,7 @@ Item {
rotation: isVertical ? (overflowExpanded ? 180 : 0) : (overflowExpanded ? 90 : -90)
Behavior on rotation {
NumberAnimation {
RotationAnimator {
duration: Theme.shortDuration
easing.type: Easing.OutCubic
}

View File

@@ -39,7 +39,7 @@ BasePill {
]
Behavior on opacity {
NumberAnimation {
OpacityAnimator {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}

View File

@@ -173,7 +173,7 @@ BasePill {
opacity: root.playerAvailable ? 1 : 0
Behavior on opacity {
NumberAnimation {
OpacityAnimator {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}

View File

@@ -50,7 +50,7 @@ BasePill {
]
Behavior on opacity {
NumberAnimation {
OpacityAnimator {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
@@ -238,7 +238,7 @@ BasePill {
Behavior on opacity {
enabled: hasActivePrivacy && root.visible
NumberAnimation {
OpacityAnimator {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}

View File

@@ -271,7 +271,7 @@ BasePill {
]
Behavior on opacity {
NumberAnimation {
OpacityAnimator {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
@@ -625,7 +625,7 @@ BasePill {
opacity: root.inlineExpanded ? 1 : 0
Behavior on opacity {
NumberAnimation {
OpacityAnimator {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
@@ -1214,14 +1214,14 @@ BasePill {
scale: root.menuOpen ? 1 : 0.85
Behavior on opacity {
NumberAnimation {
OpacityAnimator {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
}
Behavior on scale {
NumberAnimation {
ScaleAnimator {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
@@ -1666,14 +1666,14 @@ BasePill {
scale: menuRoot.showMenu ? 1 : 0.85
Behavior on opacity {
NumberAnimation {
OpacityAnimator {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
}
Behavior on scale {
NumberAnimation {
ScaleAnimator {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}

View File

@@ -36,7 +36,7 @@ BasePill {
}
Behavior on opacity {
NumberAnimation {
OpacityAnimator {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}

View File

@@ -47,7 +47,7 @@ BasePill {
anchors.centerIn: parent
Behavior on opacity {
NumberAnimation {
OpacityAnimator {
duration: Theme.shortDuration
easing.type: Easing.InOutQuad
}

View File

@@ -1471,7 +1471,7 @@ Item {
}
Behavior on opacity {
NumberAnimation {
OpacityAnimator {
duration: Theme.shortDuration
easing.type: Theme.emphasizedEasing
}

View File

@@ -97,7 +97,8 @@ Item {
Behavior on opacity {
enabled: !Theme.isDirectionalEffect
DankAnim {
NumberAnimation {
easing.type: Easing.BezierSpline
duration: Math.round(Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, dropdownType === 1) * Theme.variantOpacityDurationScale)
easing.bezierCurve: dropdownType === 1 ? Theme.variantPopoutEnterCurve : Theme.variantPopoutExitCurve
}
@@ -238,7 +239,8 @@ Item {
Behavior on opacity {
enabled: !Theme.isDirectionalEffect
DankAnim {
NumberAnimation {
easing.type: Easing.BezierSpline
duration: Math.round(Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, dropdownType === 2) * Theme.variantOpacityDurationScale)
easing.bezierCurve: dropdownType === 2 ? Theme.variantPopoutEnterCurve : Theme.variantPopoutExitCurve
}
@@ -393,7 +395,8 @@ Item {
Behavior on opacity {
enabled: !Theme.isDirectionalEffect
DankAnim {
NumberAnimation {
easing.type: Easing.BezierSpline
duration: Math.round(Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, dropdownType === 3) * Theme.variantOpacityDurationScale)
easing.bezierCurve: dropdownType === 3 ? Theme.variantPopoutEnterCurve : Theme.variantPopoutExitCurve
}

View File

@@ -402,7 +402,7 @@ Rectangle {
opacity: isToday ? 0.9 : 0.7
Behavior on opacity {
NumberAnimation {
OpacityAnimator {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}

View File

@@ -514,14 +514,14 @@ Item {
height: (isInOverflow && !root.overflowExpanded) ? 0 : (itemData.type === "separator" ? (root.isVertical ? 8 : root.iconSize) : (root.isVertical ? root.iconSize * 1.2 : root.iconSize))
Behavior on opacity {
NumberAnimation {
OpacityAnimator {
duration: Theme.shortDuration
easing.type: Easing.OutCubic
}
}
Behavior on scale {
NumberAnimation {
ScaleAnimator {
duration: Theme.shortDuration
easing.type: Easing.OutCubic
}

View File

@@ -35,7 +35,7 @@ Item {
rotation: isVertical ? (overflowExpanded ? 180 : 0) : (overflowExpanded ? 90 : -90)
Behavior on rotation {
NumberAnimation {
RotationAnimator {
duration: Theme.shortDuration
easing.type: Easing.OutCubic
}

View File

@@ -1,4 +1,3 @@
import QtCore
import QtQuick
import QtQuick.Effects
import QtQuick.Layouts
@@ -119,15 +118,7 @@ Item {
function greeterPamStackHasModule(moduleName) {
if (pamModuleEnabled(greetdPamText, moduleName))
return true;
const includedPamStacks = [
["system-auth", systemAuthPamText],
["common-auth", commonAuthPamText],
["password-auth", passwordAuthPamText],
["system-login", systemLoginPamText],
["system-local-login", systemLocalLoginPamText],
["common-auth-pc", commonAuthPcPamText],
["login", loginPamText]
];
const includedPamStacks = [["system-auth", systemAuthPamText], ["common-auth", commonAuthPamText], ["password-auth", passwordAuthPamText], ["system-login", systemLoginPamText], ["system-local-login", systemLocalLoginPamText], ["common-auth-pc", commonAuthPcPamText], ["login", loginPamText]];
for (let i = 0; i < includedPamStacks.length; i++) {
const stack = includedPamStacks[i];
if (pamTextIncludesFile(greetdPamText, stack[0]) && pamModuleEnabled(stack[1], moduleName))
@@ -609,13 +600,7 @@ Item {
running: false
// sh wrapper: emits PROBE_UNAVAILABLE if gdbus is absent or fprintd unreachable,
// keeping the PAM-only fallback active in those cases.
command: ["sh", "-c",
"command -v gdbus >/dev/null 2>&1 || { echo PROBE_UNAVAILABLE; exit 0; }; " +
"gdbus call --system " +
"--dest net.reactivated.Fprint " +
"--object-path /net/reactivated/Fprint/Manager " +
"--method net.reactivated.Fprint.Manager.GetDevices 2>/dev/null " +
"|| echo PROBE_UNAVAILABLE"]
command: ["sh", "-c", "command -v gdbus >/dev/null 2>&1 || { echo PROBE_UNAVAILABLE; exit 0; }; " + "gdbus call --system " + "--dest net.reactivated.Fprint " + "--object-path /net/reactivated/Fprint/Manager " + "--method net.reactivated.Fprint.Manager.GetDevices 2>/dev/null " + "|| echo PROBE_UNAVAILABLE"]
stdout: StdioCollector {
onStreamFinished: {
if (text.includes("PROBE_UNAVAILABLE"))
@@ -625,7 +610,7 @@ Item {
root.maybeAutoStartExternalAuth();
}
}
onExited: function(exitCode, exitStatus) {
onExited: function (exitCode, exitStatus) {
if (!root.fprintdProbeComplete)
root.maybeAutoStartExternalAuth(); // PAM-only fallback stays active
}
@@ -729,7 +714,7 @@ Item {
}
Behavior on opacity {
NumberAnimation {
OpacityAnimator {
duration: Theme.mediumDuration
easing.type: Theme.standardEasing
}
@@ -1027,7 +1012,7 @@ Item {
opacity: (GreeterState.showPasswordInput ? GreeterState.passwordBuffer.length === 0 : GreeterState.usernameInput.length === 0) ? 1 : 0
Behavior on opacity {
NumberAnimation {
OpacityAnimator {
duration: Theme.mediumDuration
easing.type: Theme.standardEasing
}
@@ -1064,7 +1049,7 @@ Item {
horizontalAlignment: implicitWidth > width ? Text.AlignRight : Text.AlignLeft
Behavior on opacity {
NumberAnimation {
OpacityAnimator {
duration: Theme.mediumDuration
easing.type: Theme.standardEasing
}
@@ -1136,7 +1121,7 @@ Item {
}
Behavior on opacity {
NumberAnimation {
OpacityAnimator {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
@@ -1166,7 +1151,7 @@ Item {
opacity: root.authFeedbackMessage !== "" ? 1 : 0
Behavior on opacity {
NumberAnimation {
OpacityAnimator {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
@@ -1184,7 +1169,7 @@ Item {
enabled: GreeterState.showPasswordInput
Behavior on opacity {
NumberAnimation {
OpacityAnimator {
duration: Theme.mediumDuration
easing.type: Theme.standardEasing
}
@@ -1754,7 +1739,7 @@ Item {
authTimeout.interval = defaultAuthTimeoutMs;
authTimeout.stop();
if (resumePasswordSubmit) {
Qt.callLater(function() {
Qt.callLater(function () {
root.startAuthSession(true);
});
return;

View File

@@ -779,7 +779,7 @@ Rectangle {
opacity: root.showHoldHint ? 1 : 0.5
Behavior on opacity {
NumberAnimation {
OpacityAnimator {
duration: 150
}
}

View File

@@ -214,7 +214,7 @@ Item {
}
Behavior on opacity {
NumberAnimation {
OpacityAnimator {
duration: Theme.mediumDuration
easing.type: Theme.standardEasing
}
@@ -740,7 +740,7 @@ Item {
opacity: pam.passwd.active ? 0 : 1
Behavior on opacity {
NumberAnimation {
OpacityAnimator {
duration: Theme.mediumDuration
easing.type: Theme.standardEasing
}
@@ -879,7 +879,7 @@ Item {
opacity: (demoMode || root.passwordBuffer.length === 0) ? 1 : 0
Behavior on opacity {
NumberAnimation {
OpacityAnimator {
duration: Theme.mediumDuration
easing.type: Theme.standardEasing
}
@@ -916,7 +916,7 @@ Item {
horizontalAlignment: implicitWidth > width ? Text.AlignRight : Text.AlignLeft
Behavior on opacity {
NumberAnimation {
OpacityAnimator {
duration: Theme.mediumDuration
easing.type: Theme.standardEasing
}
@@ -1053,7 +1053,7 @@ Item {
}
Behavior on opacity {
NumberAnimation {
OpacityAnimator {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
@@ -1084,7 +1084,7 @@ Item {
opacity: text.length > 0 ? 1 : 0
Behavior on opacity {
NumberAnimation {
OpacityAnimator {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
@@ -1114,7 +1114,7 @@ Item {
}
Behavior on opacity {
NumberAnimation {
OpacityAnimator {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}

View File

@@ -47,7 +47,7 @@ FocusScope {
}
Behavior on opacity {
NumberAnimation {
OpacityAnimator {
duration: 200
}
}

View File

@@ -1,19 +1,18 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell.Io
import qs.Common
import qs.Services
import qs.Widgets
pragma ComponentBehavior: Bound
Column {
id: root
Component.onCompleted: {
if (PluginService.isPluginLoaded("dankNotepadModule")) {
pluginHighlightedHtml = SettingsData.getBuiltInPluginSetting("dankNotepadModule", "highlightedHtml", "")
pluginHighlightedHtml = SettingsData.getBuiltInPluginSetting("dankNotepadModule", "highlightedHtml", "");
}
}
@@ -33,65 +32,57 @@ Column {
property string lastPluginContent: ""
property int loadRequestId: 0
signal saveRequested()
signal openRequested()
signal newRequested()
signal previewRequested()
signal escapePressed()
signal contentChanged()
signal settingsRequested()
signal saveRequested
signal openRequested
signal newRequested
signal previewRequested
signal escapePressed
signal contentChanged
signal settingsRequested
function hasUnsavedChanges() {
if (!currentTab || !contentLoaded) {
return false
return false;
}
if (currentTab.isTemporary) {
return textArea.text.length > 0
return textArea.text.length > 0;
}
return textArea.text !== lastSavedContent
return textArea.text !== lastSavedContent;
}
function loadCurrentTabContent() {
if (!currentTab) return
const requestedTabId = currentTab.id
const requestId = ++loadRequestId
contentLoaded = false
NotepadStorageService.loadTabContent(
NotepadStorageService.currentTabIndex,
(content) => {
const activeTab = NotepadStorageService.tabs.length > NotepadStorageService.currentTabIndex
? NotepadStorageService.tabs[NotepadStorageService.currentTabIndex]
: null
if (requestId !== loadRequestId || !activeTab || activeTab.id !== requestedTabId)
return
lastSavedContent = content
textArea.text = content
contentLoaded = true
syncContentToPlugin()
}
)
if (!currentTab)
return;
const requestedTabId = currentTab.id;
const requestId = ++loadRequestId;
contentLoaded = false;
NotepadStorageService.loadTabContent(NotepadStorageService.currentTabIndex, content => {
const activeTab = NotepadStorageService.tabs.length > NotepadStorageService.currentTabIndex ? NotepadStorageService.tabs[NotepadStorageService.currentTabIndex] : null;
if (requestId !== loadRequestId || !activeTab || activeTab.id !== requestedTabId)
return;
lastSavedContent = content;
textArea.text = content;
contentLoaded = true;
syncContentToPlugin();
});
}
function saveCurrentTabContent() {
if (!currentTab || !contentLoaded) return
NotepadStorageService.saveTabContent(
NotepadStorageService.currentTabIndex,
textArea.text
)
lastSavedContent = textArea.text
if (!currentTab || !contentLoaded)
return;
NotepadStorageService.saveTabContent(NotepadStorageService.currentTabIndex, textArea.text);
lastSavedContent = textArea.text;
}
function autoSaveToSession() {
if (!currentTab || !contentLoaded) return
saveCurrentTabContent()
if (!currentTab || !contentLoaded)
return;
saveCurrentTabContent();
}
function setTextDocumentLineHeight() {
return
return;
}
property string lastTextForLineModel: ""
@@ -99,147 +90,146 @@ Column {
function updateLineModel() {
if (!SettingsData.notepadShowLineNumbers) {
lineModel = []
lastTextForLineModel = ""
return
lineModel = [];
lastTextForLineModel = "";
return;
}
if (textArea.text !== lastTextForLineModel || lineModel.length === 0) {
lastTextForLineModel = textArea.text
lineModel = textArea.text.split('\n')
lastTextForLineModel = textArea.text;
lineModel = textArea.text.split('\n');
}
}
function performSearch() {
let matches = []
currentMatchIndex = -1
let matches = [];
currentMatchIndex = -1;
if (!searchQuery || searchQuery.length === 0) {
searchMatches = []
matchCount = 0
textArea.select(0, 0)
return
searchMatches = [];
matchCount = 0;
textArea.select(0, 0);
return;
}
const text = textArea.text
const query = searchQuery.toLowerCase()
let index = 0
const text = textArea.text;
const query = searchQuery.toLowerCase();
let index = 0;
while (index < text.length) {
const foundIndex = text.toLowerCase().indexOf(query, index)
if (foundIndex === -1) break
const foundIndex = text.toLowerCase().indexOf(query, index);
if (foundIndex === -1)
break;
matches.push({
start: foundIndex,
end: foundIndex + searchQuery.length
})
index = foundIndex + 1
});
index = foundIndex + 1;
}
searchMatches = matches
matchCount = matches.length
searchMatches = matches;
matchCount = matches.length;
if (matchCount > 0) {
currentMatchIndex = 0
highlightCurrentMatch()
currentMatchIndex = 0;
highlightCurrentMatch();
} else {
textArea.select(0, 0)
textArea.select(0, 0);
}
}
function highlightCurrentMatch() {
if (currentMatchIndex >= 0 && currentMatchIndex < searchMatches.length) {
const match = searchMatches[currentMatchIndex]
const match = searchMatches[currentMatchIndex];
textArea.cursorPosition = match.start
textArea.moveCursorSelection(match.end, TextEdit.SelectCharacters)
textArea.cursorPosition = match.start;
textArea.moveCursorSelection(match.end, TextEdit.SelectCharacters);
const flickable = textArea.parent
const flickable = textArea.parent;
if (flickable && flickable.contentY !== undefined) {
const lineHeight = textArea.font.pixelSize * 1.5
const approxLine = textArea.text.substring(0, match.start).split('\n').length
const targetY = approxLine * lineHeight - flickable.height / 2
flickable.contentY = Math.max(0, Math.min(targetY, flickable.contentHeight - flickable.height))
const lineHeight = textArea.font.pixelSize * 1.5;
const approxLine = textArea.text.substring(0, match.start).split('\n').length;
const targetY = approxLine * lineHeight - flickable.height / 2;
flickable.contentY = Math.max(0, Math.min(targetY, flickable.contentHeight - flickable.height));
}
}
}
function findNext() {
if (matchCount === 0 || searchMatches.length === 0) return
currentMatchIndex = (currentMatchIndex + 1) % matchCount
highlightCurrentMatch()
if (matchCount === 0 || searchMatches.length === 0)
return;
currentMatchIndex = (currentMatchIndex + 1) % matchCount;
highlightCurrentMatch();
}
function findPrevious() {
if (matchCount === 0 || searchMatches.length === 0) return
currentMatchIndex = currentMatchIndex <= 0 ? matchCount - 1 : currentMatchIndex - 1
highlightCurrentMatch()
if (matchCount === 0 || searchMatches.length === 0)
return;
currentMatchIndex = currentMatchIndex <= 0 ? matchCount - 1 : currentMatchIndex - 1;
highlightCurrentMatch();
}
function showSearch() {
searchVisible = true
searchVisible = true;
Qt.callLater(() => {
searchField.forceActiveFocus()
})
searchField.forceActiveFocus();
});
}
function togglePreview() {
if (!inlinePreviewVisible) {
inlinePreviewVisible = true
previewMode = "split"
inlinePreviewVisible = true;
previewMode = "split";
} else if (previewMode === "split") {
previewMode = "full"
previewMode = "full";
} else {
inlinePreviewVisible = false
previewMode = "split"
inlinePreviewVisible = false;
previewMode = "split";
}
syncContentToPlugin()
syncContentToPlugin();
}
function renderPreviewHtml() {
if (!inlinePreviewVisible) return ""
return pluginHighlightedHtml.length > 0 ? pluginHighlightedHtml : "<p><i>Rendering preview…</i></p>"
if (!inlinePreviewVisible)
return "";
return pluginHighlightedHtml.length > 0 ? pluginHighlightedHtml : "<p><i>Rendering preview…</i></p>";
}
function syncContentToPlugin() {
if (!PluginService.isPluginLoaded("dankNotepadModule"))
return
return;
if (!currentTab)
return
const filePath = currentTab?.filePath || ""
const ext = filePath.split('.').pop().toLowerCase()
const content = textArea.text
return;
const filePath = currentTab?.filePath || "";
const ext = filePath.split('.').pop().toLowerCase();
const content = textArea.text;
if (content === lastPluginContent && SettingsData.getBuiltInPluginSetting("dankNotepadModule", "previewActive", false) === inlinePreviewVisible) {
return
return;
}
lastPluginContent = content
SettingsData.setBuiltInPluginSetting("dankNotepadModule", "previewActive", inlinePreviewVisible)
SettingsData.setBuiltInPluginSetting("dankNotepadModule", "currentFilePath", filePath)
SettingsData.setBuiltInPluginSetting("dankNotepadModule", "currentFileExtension", ext)
SettingsData.setBuiltInPluginSetting("dankNotepadModule", "sourceContent", content)
SettingsData.setBuiltInPluginSetting("dankNotepadModule", "updatedAt", Date.now())
lastPluginContent = content;
SettingsData.setBuiltInPluginSetting("dankNotepadModule", "previewActive", inlinePreviewVisible);
SettingsData.setBuiltInPluginSetting("dankNotepadModule", "currentFilePath", filePath);
SettingsData.setBuiltInPluginSetting("dankNotepadModule", "currentFileExtension", ext);
SettingsData.setBuiltInPluginSetting("dankNotepadModule", "sourceContent", content);
SettingsData.setBuiltInPluginSetting("dankNotepadModule", "updatedAt", Date.now());
}
function hideSearch() {
searchVisible = false
searchQuery = ""
searchMatches = []
matchCount = 0
currentMatchIndex = -1
textArea.select(0, 0)
textArea.forceActiveFocus()
searchVisible = false;
searchQuery = "";
searchMatches = [];
matchCount = 0;
currentMatchIndex = -1;
textArea.select(0, 0);
textArea.forceActiveFocus();
}
function copyPlainTextToClipboard() {
if (!inlinePreviewVisible || !textArea.text) return
const content = textArea.text
if (!inlinePreviewVisible || !textArea.text)
return;
const content = textArea.text;
if (content.length > 0) {
const proc = Qt.createQmlObject(`
import QtQuick
@@ -249,22 +239,19 @@ Column {
command: ["sh", "-c", "printf '%s' \\"$CONTENT\\" | dms clipboard copy"]
environment: { "CONTENT": content }
running: false
}`,
root,
"copyProc"
)
proc.content = content
proc.running = true
}`, root, "copyProc");
proc.content = content;
proc.running = true;
proc.exited.connect(() => {
ToastService.showInfo(I18n.tr("Copied to clipboard"))
proc.destroy()
})
ToastService.showInfo(I18n.tr("Copied to clipboard"));
proc.destroy();
});
}
}
function copyHtmlToClipboard() {
if (!inlinePreviewVisible || !pluginHighlightedHtml) return
if (!inlinePreviewVisible || !pluginHighlightedHtml)
return;
if (pluginHighlightedHtml.length > 0) {
const proc = Qt.createQmlObject(`
import QtQuick
@@ -274,16 +261,13 @@ Column {
command: ["sh", "-c", "printf '%s' \\"$CONTENT\\" | dms clipboard copy"]
environment: { "CONTENT": content }
running: false
}`,
root,
"copyProcHtml"
)
proc.content = pluginHighlightedHtml
proc.running = true
}`, root, "copyProcHtml");
proc.content = pluginHighlightedHtml;
proc.running = true;
proc.exited.connect(() => {
ToastService.showInfo(I18n.tr("HTML copied to clipboard"))
proc.destroy()
})
ToastService.showInfo(I18n.tr("HTML copied to clipboard"));
proc.destroy();
});
}
}
@@ -301,7 +285,7 @@ Column {
radius: Theme.cornerRadius
Behavior on opacity {
NumberAnimation {
OpacityAnimator {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
@@ -334,43 +318,43 @@ Column {
clip: true
Component.onCompleted: {
text = root.searchQuery
text = root.searchQuery;
}
Connections {
target: root
function onSearchQueryChanged() {
if (searchField.text !== root.searchQuery) {
searchField.text = root.searchQuery
searchField.text = root.searchQuery;
}
}
}
onTextChanged: {
if (root.searchQuery !== text) {
root.searchQuery = text
root.performSearch()
root.searchQuery = text;
root.performSearch();
}
}
Keys.onEscapePressed: event => {
root.hideSearch()
event.accepted = true
root.hideSearch();
event.accepted = true;
}
Keys.onReturnPressed: event => {
if (event.modifiers & Qt.ShiftModifier) {
root.findPrevious()
root.findPrevious();
} else {
root.findNext()
root.findNext();
}
event.accepted = true
event.accepted = true;
}
Keys.onEnterPressed: event => {
if (event.modifiers & Qt.ShiftModifier) {
root.findPrevious()
root.findPrevious();
} else {
root.findNext()
root.findNext();
}
event.accepted = true
event.accepted = true;
}
}
@@ -541,31 +525,41 @@ Column {
SequentialAnimation on opacity {
running: textArea.activeFocus
loops: Animation.Infinite
PropertyAnimation { from: 1.0; to: 0.0; duration: 650; easing.type: Easing.InOutQuad }
PropertyAnimation { from: 0.0; to: 1.0; duration: 650; easing.type: Easing.InOutQuad }
PropertyAnimation {
from: 1.0
to: 0.0
duration: 650
easing.type: Easing.InOutQuad
}
PropertyAnimation {
from: 0.0
to: 1.0
duration: 650
easing.type: Easing.InOutQuad
}
}
}
Component.onCompleted: {
loadCurrentTabContent()
setTextDocumentLineHeight()
root.updateLineModel()
loadCurrentTabContent();
setTextDocumentLineHeight();
root.updateLineModel();
Qt.callLater(() => {
textArea.forceActiveFocus()
})
textArea.forceActiveFocus();
});
}
Connections {
target: NotepadStorageService
function onCurrentTabIndexChanged() {
loadCurrentTabContent()
loadCurrentTabContent();
Qt.callLater(() => {
textArea.forceActiveFocus()
})
textArea.forceActiveFocus();
});
}
function onTabsChanged() {
if (NotepadStorageService.tabs.length > 0 && !contentLoaded) {
loadCurrentTabContent()
loadCurrentTabContent();
}
}
}
@@ -573,53 +567,53 @@ Column {
Connections {
target: SettingsData
function onNotepadShowLineNumbersChanged() {
root.updateLineModel()
root.updateLineModel();
}
}
onTextChanged: {
if (contentLoaded && text !== lastSavedContent) {
autoSaveTimer.restart()
autoSaveTimer.restart();
}
root.contentChanged()
root.updateLineModel()
pluginSyncTimer.restart()
root.contentChanged();
root.updateLineModel();
pluginSyncTimer.restart();
}
Keys.onEscapePressed: (event) => {
root.escapePressed()
event.accepted = true
Keys.onEscapePressed: event => {
root.escapePressed();
event.accepted = true;
}
Keys.onPressed: (event) => {
Keys.onPressed: event => {
if (event.modifiers & Qt.ControlModifier) {
switch (event.key) {
case Qt.Key_S:
event.accepted = true
root.saveRequested()
break
event.accepted = true;
root.saveRequested();
break;
case Qt.Key_O:
event.accepted = true
root.openRequested()
break
event.accepted = true;
root.openRequested();
break;
case Qt.Key_N:
event.accepted = true
root.newRequested()
break
event.accepted = true;
root.newRequested();
break;
case Qt.Key_A:
event.accepted = true
textArea.selectAll()
break
event.accepted = true;
textArea.selectAll();
break;
case Qt.Key_F:
event.accepted = true
root.showSearch()
break
event.accepted = true;
root.showSearch();
break;
case Qt.Key_P:
if (PluginService.isPluginLoaded("dankNotepadModule")) {
event.accepted = true
root.previewRequested()
event.accepted = true;
root.previewRequested();
}
break
break;
}
}
}
@@ -845,19 +839,16 @@ Column {
StyledText {
text: {
const len = textArea.text.length;
if (len === 0) return I18n.tr("Empty");
return len === 1
? I18n.tr("%1 character").arg(len)
: I18n.tr("%1 characters").arg(len);
if (len === 0)
return I18n.tr("Empty");
return len === 1 ? I18n.tr("%1 character").arg(len) : I18n.tr("%1 characters").arg(len);
}
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceTextMedium
}
StyledText {
text: textArea.lineCount === 1
? I18n.tr("Line: %1").arg(textArea.lineCount)
: I18n.tr("Lines: %1").arg(textArea.lineCount)
text: textArea.lineCount === 1 ? I18n.tr("Line: %1").arg(textArea.lineCount) : I18n.tr("Lines: %1").arg(textArea.lineCount)
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceTextMedium
visible: textArea.text.length > 0
@@ -867,29 +858,29 @@ Column {
StyledText {
text: {
if (autoSaveTimer.running) {
return I18n.tr("Auto-saving...")
return I18n.tr("Auto-saving...");
}
if (hasUnsavedChanges()) {
if (currentTab && currentTab.isTemporary) {
return I18n.tr("Unsaved note...")
return I18n.tr("Unsaved note...");
} else {
return I18n.tr("Unsaved changes")
return I18n.tr("Unsaved changes");
}
} else {
return I18n.tr("Saved")
return I18n.tr("Saved");
}
}
font.pixelSize: Theme.fontSizeSmall
color: {
if (autoSaveTimer.running) {
return Theme.primary
return Theme.primary;
}
if (hasUnsavedChanges()) {
return Theme.warning
return Theme.warning;
} else {
return Theme.success
return Theme.success;
}
}
opacity: textArea.text.length > 0 ? 1.0 : 0.0
@@ -902,7 +893,7 @@ Column {
interval: 2000
repeat: false
onTriggered: {
autoSaveToSession()
autoSaveToSession();
}
}
@@ -917,7 +908,7 @@ Column {
target: SettingsData
function onBuiltInPluginSettingsChanged() {
if (PluginService.isPluginLoaded("dankNotepadModule")) {
pluginHighlightedHtml = SettingsData.getBuiltInPluginSetting("dankNotepadModule", "highlightedHtml", "")
pluginHighlightedHtml = SettingsData.getBuiltInPluginSetting("dankNotepadModule", "highlightedHtml", "");
}
}
}

View File

@@ -283,7 +283,7 @@ Item {
Behavior on x {
enabled: !swipeDragHandler.active && delegateRoot.__delegateInitialized
NumberAnimation {
XAnimator {
duration: Theme.notificationExitDuration
easing.type: Theme.standardEasing
}
@@ -291,7 +291,7 @@ Item {
Behavior on opacity {
enabled: delegateRoot.__delegateInitialized
NumberAnimation {
OpacityAnimator {
duration: delegateRoot.__delegateInitialized ? Theme.notificationExitDuration : 0
}
}

View File

@@ -226,7 +226,7 @@ DankListView {
Behavior on x {
enabled: !swipeDragHandler.active && !delegateRoot.isDismissing && (listView.swipingCardIndex === -1 || !delegateRoot.isAdjacentToSwipe) && listView.listInitialized
NumberAnimation {
XAnimator {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
@@ -234,7 +234,7 @@ DankListView {
Behavior on opacity {
enabled: listView.listInitialized
NumberAnimation {
OpacityAnimator {
duration: listView.listInitialized ? Theme.shortDuration : 0
}
}

View File

@@ -75,7 +75,7 @@ Rectangle {
Behavior on scale {
enabled: listLevelScaleAnimationsEnabled
NumberAnimation {
ScaleAnimator {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
@@ -545,7 +545,7 @@ Rectangle {
Behavior on x {
enabled: !expandedSwipeHandler.active && !expandedDelegateWrapper.isDismissing
NumberAnimation {
XAnimator {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
@@ -553,7 +553,7 @@ Rectangle {
Behavior on scale {
enabled: !expandedSwipeHandler.active
NumberAnimation {
ScaleAnimator {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
@@ -762,7 +762,7 @@ Rectangle {
spacing: contentSpacing
Behavior on opacity {
NumberAnimation {
OpacityAnimator {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
@@ -822,7 +822,7 @@ Rectangle {
color: isHovered ? Theme.withAlpha(Theme.primary, Theme.stateLayerHover) : "transparent"
Behavior on opacity {
NumberAnimation {
OpacityAnimator {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}

View File

@@ -42,7 +42,7 @@ Rectangle {
}
Behavior on opacity {
NumberAnimation {
OpacityAnimator {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}

View File

@@ -27,7 +27,7 @@ Rectangle {
opacity: expanded ? 1 : 0
Behavior on opacity {
NumberAnimation {
OpacityAnimator {
duration: Theme.shortDuration
easing.type: Theme.emphasizedEasing
}
@@ -123,327 +123,327 @@ Rectangle {
anchors.margins: Theme.spacingL
spacing: Theme.spacingM
StyledText {
text: I18n.tr("Notification Settings")
font.pixelSize: Theme.fontSizeMedium
font.weight: Font.Bold
color: Theme.surfaceText
}
Item {
width: parent.width
height: Math.max(dndRow.implicitHeight, dndToggle.implicitHeight) + Theme.spacingS
Row {
id: dndRow
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingM
DankIcon {
name: SessionData.doNotDisturb ? "notifications_off" : "notifications"
size: Theme.iconSizeSmall
color: SessionData.doNotDisturb ? Theme.error : Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: I18n.tr("Do Not Disturb")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: I18n.tr("Notification Settings")
font.pixelSize: Theme.fontSizeMedium
font.weight: Font.Bold
color: Theme.surfaceText
}
DankToggle {
id: dndToggle
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
checked: SessionData.doNotDisturb
onToggled: SessionData.setDoNotDisturb(!SessionData.doNotDisturb)
}
}
Item {
width: parent.width
height: Math.max(dndRow.implicitHeight, dndToggle.implicitHeight) + Theme.spacingS
Rectangle {
width: parent.width
height: 1
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.1)
}
Row {
id: dndRow
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingM
StyledText {
text: I18n.tr("Notification Timeouts")
font.pixelSize: Theme.fontSizeSmall
font.weight: Font.Medium
color: Theme.surfaceVariantText
}
DankDropdown {
text: I18n.tr("Low Priority")
description: I18n.tr("Timeout for low priority notifications")
currentValue: getTimeoutText(SettingsData.notificationTimeoutLow)
options: timeoutOptions.map(opt => opt.text)
onValueChanged: value => {
for (let i = 0; i < timeoutOptions.length; i++) {
if (timeoutOptions[i].text === value) {
SettingsData.set("notificationTimeoutLow", timeoutOptions[i].value);
break;
DankIcon {
name: SessionData.doNotDisturb ? "notifications_off" : "notifications"
size: Theme.iconSizeSmall
color: SessionData.doNotDisturb ? Theme.error : Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
}
}
}
DankDropdown {
text: I18n.tr("Normal Priority")
description: I18n.tr("Timeout for normal priority notifications")
currentValue: getTimeoutText(SettingsData.notificationTimeoutNormal)
options: timeoutOptions.map(opt => opt.text)
onValueChanged: value => {
for (let i = 0; i < timeoutOptions.length; i++) {
if (timeoutOptions[i].text === value) {
SettingsData.set("notificationTimeoutNormal", timeoutOptions[i].value);
break;
}
}
}
}
DankDropdown {
text: I18n.tr("Critical Priority")
description: I18n.tr("Timeout for critical priority notifications")
currentValue: getTimeoutText(SettingsData.notificationTimeoutCritical)
options: timeoutOptions.map(opt => opt.text)
onValueChanged: value => {
for (let i = 0; i < timeoutOptions.length; i++) {
if (timeoutOptions[i].text === value) {
SettingsData.set("notificationTimeoutCritical", timeoutOptions[i].value);
break;
}
}
}
}
Rectangle {
width: parent.width
height: 1
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.1)
}
Item {
width: parent.width
height: Math.max(overlayRow.implicitHeight, overlayToggle.implicitHeight) + Theme.spacingS
Row {
id: overlayRow
anchors.left: parent.left
anchors.right: overlayToggle.left
anchors.rightMargin: Theme.spacingM
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingM
DankIcon {
name: "notifications_active"
size: Theme.iconSizeSmall
color: SettingsData.notificationOverlayEnabled ? Theme.primary : Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
Column {
spacing: 2
anchors.verticalCenter: parent.verticalCenter
width: overlayRow.width - Theme.iconSizeSmall - Theme.spacingM
StyledText {
width: parent.width
text: I18n.tr("Notification Overlay")
text: I18n.tr("Do Not Disturb")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
wrapMode: Text.Wrap
anchors.verticalCenter: parent.verticalCenter
}
}
StyledText {
width: parent.width
text: I18n.tr("Display all priorities over fullscreen apps")
font.pixelSize: Theme.fontSizeSmall - 1
color: Theme.surfaceVariantText
wrapMode: Text.Wrap
DankToggle {
id: dndToggle
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
checked: SessionData.doNotDisturb
onToggled: SessionData.setDoNotDisturb(!SessionData.doNotDisturb)
}
}
Rectangle {
width: parent.width
height: 1
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.1)
}
StyledText {
text: I18n.tr("Notification Timeouts")
font.pixelSize: Theme.fontSizeSmall
font.weight: Font.Medium
color: Theme.surfaceVariantText
}
DankDropdown {
text: I18n.tr("Low Priority")
description: I18n.tr("Timeout for low priority notifications")
currentValue: getTimeoutText(SettingsData.notificationTimeoutLow)
options: timeoutOptions.map(opt => opt.text)
onValueChanged: value => {
for (let i = 0; i < timeoutOptions.length; i++) {
if (timeoutOptions[i].text === value) {
SettingsData.set("notificationTimeoutLow", timeoutOptions[i].value);
break;
}
}
}
}
DankToggle {
id: overlayToggle
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
checked: SettingsData.notificationOverlayEnabled
onToggled: toggled => SettingsData.set("notificationOverlayEnabled", toggled)
DankDropdown {
text: I18n.tr("Normal Priority")
description: I18n.tr("Timeout for normal priority notifications")
currentValue: getTimeoutText(SettingsData.notificationTimeoutNormal)
options: timeoutOptions.map(opt => opt.text)
onValueChanged: value => {
for (let i = 0; i < timeoutOptions.length; i++) {
if (timeoutOptions[i].text === value) {
SettingsData.set("notificationTimeoutNormal", timeoutOptions[i].value);
break;
}
}
}
}
}
Item {
width: parent.width
height: Math.max(privacyRow.implicitHeight, privacyToggle.implicitHeight) + Theme.spacingS
DankDropdown {
text: I18n.tr("Critical Priority")
description: I18n.tr("Timeout for critical priority notifications")
currentValue: getTimeoutText(SettingsData.notificationTimeoutCritical)
options: timeoutOptions.map(opt => opt.text)
onValueChanged: value => {
for (let i = 0; i < timeoutOptions.length; i++) {
if (timeoutOptions[i].text === value) {
SettingsData.set("notificationTimeoutCritical", timeoutOptions[i].value);
break;
}
}
}
}
Row {
id: privacyRow
anchors.left: parent.left
anchors.right: privacyToggle.left
anchors.rightMargin: Theme.spacingM
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingM
Rectangle {
width: parent.width
height: 1
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.1)
}
DankIcon {
name: "privacy_tip"
size: Theme.iconSizeSmall
color: SettingsData.notificationPopupPrivacyMode ? Theme.primary : Theme.surfaceText
Item {
width: parent.width
height: Math.max(overlayRow.implicitHeight, overlayToggle.implicitHeight) + Theme.spacingS
Row {
id: overlayRow
anchors.left: parent.left
anchors.right: overlayToggle.left
anchors.rightMargin: Theme.spacingM
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingM
DankIcon {
name: "notifications_active"
size: Theme.iconSizeSmall
color: SettingsData.notificationOverlayEnabled ? Theme.primary : Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
Column {
spacing: 2
anchors.verticalCenter: parent.verticalCenter
width: overlayRow.width - Theme.iconSizeSmall - Theme.spacingM
StyledText {
width: parent.width
text: I18n.tr("Notification Overlay")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
wrapMode: Text.Wrap
}
StyledText {
width: parent.width
text: I18n.tr("Display all priorities over fullscreen apps")
font.pixelSize: Theme.fontSizeSmall - 1
color: Theme.surfaceVariantText
wrapMode: Text.Wrap
}
}
}
Column {
spacing: 2
DankToggle {
id: overlayToggle
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
width: privacyRow.width - Theme.iconSizeSmall - Theme.spacingM
checked: SettingsData.notificationOverlayEnabled
onToggled: toggled => SettingsData.set("notificationOverlayEnabled", toggled)
}
}
Item {
width: parent.width
height: Math.max(privacyRow.implicitHeight, privacyToggle.implicitHeight) + Theme.spacingS
Row {
id: privacyRow
anchors.left: parent.left
anchors.right: privacyToggle.left
anchors.rightMargin: Theme.spacingM
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingM
DankIcon {
name: "privacy_tip"
size: Theme.iconSizeSmall
color: SettingsData.notificationPopupPrivacyMode ? Theme.primary : Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
Column {
spacing: 2
anchors.verticalCenter: parent.verticalCenter
width: privacyRow.width - Theme.iconSizeSmall - Theme.spacingM
StyledText {
width: parent.width
text: I18n.tr("Privacy Mode")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
wrapMode: Text.Wrap
}
StyledText {
width: parent.width
text: I18n.tr("Hide notification content until expanded")
font.pixelSize: Theme.fontSizeSmall - 1
color: Theme.surfaceVariantText
wrapMode: Text.Wrap
}
}
}
DankToggle {
id: privacyToggle
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
checked: SettingsData.notificationPopupPrivacyMode
onToggled: toggled => SettingsData.set("notificationPopupPrivacyMode", toggled)
}
}
Rectangle {
width: parent.width
height: 1
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.1)
}
StyledText {
text: I18n.tr("History Settings")
font.pixelSize: Theme.fontSizeSmall
font.weight: Font.Medium
color: Theme.surfaceVariantText
}
Item {
width: parent.width
height: Math.max(lowRow.implicitHeight, lowToggle.implicitHeight) + Theme.spacingS
Row {
id: lowRow
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingM
DankIcon {
name: "low_priority"
size: Theme.iconSizeSmall
color: SettingsData.notificationHistorySaveLow ? Theme.primary : Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
width: parent.width
text: I18n.tr("Privacy Mode")
text: I18n.tr("Low Priority")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
wrapMode: Text.Wrap
anchors.verticalCenter: parent.verticalCenter
}
}
DankToggle {
id: lowToggle
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
checked: SettingsData.notificationHistorySaveLow
onToggled: toggled => SettingsData.set("notificationHistorySaveLow", toggled)
}
}
Item {
width: parent.width
height: Math.max(normalRow.implicitHeight, normalToggle.implicitHeight) + Theme.spacingS
Row {
id: normalRow
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingM
DankIcon {
name: "notifications"
size: Theme.iconSizeSmall
color: SettingsData.notificationHistorySaveNormal ? Theme.primary : Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
width: parent.width
text: I18n.tr("Hide notification content until expanded")
font.pixelSize: Theme.fontSizeSmall - 1
color: Theme.surfaceVariantText
wrapMode: Text.Wrap
text: I18n.tr("Normal Priority")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
}
}
DankToggle {
id: privacyToggle
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
checked: SettingsData.notificationPopupPrivacyMode
onToggled: toggled => SettingsData.set("notificationPopupPrivacyMode", toggled)
}
}
Rectangle {
width: parent.width
height: 1
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.1)
}
StyledText {
text: I18n.tr("History Settings")
font.pixelSize: Theme.fontSizeSmall
font.weight: Font.Medium
color: Theme.surfaceVariantText
}
Item {
width: parent.width
height: Math.max(lowRow.implicitHeight, lowToggle.implicitHeight) + Theme.spacingS
Row {
id: lowRow
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingM
DankIcon {
name: "low_priority"
size: Theme.iconSizeSmall
color: SettingsData.notificationHistorySaveLow ? Theme.primary : Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: I18n.tr("Low Priority")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
DankToggle {
id: normalToggle
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
checked: SettingsData.notificationHistorySaveNormal
onToggled: toggled => SettingsData.set("notificationHistorySaveNormal", toggled)
}
}
DankToggle {
id: lowToggle
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
checked: SettingsData.notificationHistorySaveLow
onToggled: toggled => SettingsData.set("notificationHistorySaveLow", toggled)
Item {
width: parent.width
height: Math.max(criticalRow.implicitHeight, criticalToggle.implicitHeight) + Theme.spacingS
Row {
id: criticalRow
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingM
DankIcon {
name: "priority_high"
size: Theme.iconSizeSmall
color: SettingsData.notificationHistorySaveCritical ? Theme.primary : Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: I18n.tr("Critical Priority")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
}
DankToggle {
id: criticalToggle
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
checked: SettingsData.notificationHistorySaveCritical
onToggled: toggled => SettingsData.set("notificationHistorySaveCritical", toggled)
}
}
}
Item {
width: parent.width
height: Math.max(normalRow.implicitHeight, normalToggle.implicitHeight) + Theme.spacingS
Row {
id: normalRow
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingM
DankIcon {
name: "notifications"
size: Theme.iconSizeSmall
color: SettingsData.notificationHistorySaveNormal ? Theme.primary : Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: I18n.tr("Normal Priority")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
}
DankToggle {
id: normalToggle
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
checked: SettingsData.notificationHistorySaveNormal
onToggled: toggled => SettingsData.set("notificationHistorySaveNormal", toggled)
}
}
Item {
width: parent.width
height: Math.max(criticalRow.implicitHeight, criticalToggle.implicitHeight) + Theme.spacingS
Row {
id: criticalRow
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingM
DankIcon {
name: "priority_high"
size: Theme.iconSizeSmall
color: SettingsData.notificationHistorySaveCritical ? Theme.primary : Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: I18n.tr("Critical Priority")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
}
DankToggle {
id: criticalToggle
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
checked: SettingsData.notificationHistorySaveCritical
onToggled: toggled => SettingsData.set("notificationHistorySaveCritical", toggled)
}
}
}
}
}

View File

@@ -36,10 +36,13 @@ PanelWindow {
WindowBlur {
targetWindow: win
blurX: content.x + content.cardInset + swipeTx.x + tx.x
blurY: content.y + content.cardInset + swipeTx.y + tx.y
blurWidth: !win._finalized && !win.connectedFrameMode ? Math.max(0, content.width - content.cardInset * 2) : 0
blurHeight: !win._finalized && !win.connectedFrameMode ? Math.max(0, content.height - content.cardInset * 2) : 0
readonly property real s: Math.min(1, content.scale) * Math.max(0, content.opacity)
readonly property real innerW: Math.max(0, content.width - content.cardInset * 2)
readonly property real innerH: Math.max(0, content.height - content.cardInset * 2)
blurX: content.x + content.cardInset + swipeTx.x + tx.x + innerW * (1 - s) * 0.5
blurY: content.y + content.cardInset + swipeTx.y + tx.y + innerH * (1 - s) * 0.5
blurWidth: !win._finalized && !win.connectedFrameMode ? innerW * s : 0
blurHeight: !win._finalized && !win.connectedFrameMode ? innerH * s : 0
blurRadius: win.connectedFrameMode ? Theme.connectedSurfaceRadius : Theme.cornerRadius
}
@@ -993,7 +996,7 @@ PanelWindow {
z: 20
Behavior on opacity {
NumberAnimation {
OpacityAnimator {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
@@ -1048,7 +1051,7 @@ PanelWindow {
visible: actionCount < 3 && cardHoverHandler.hovered
opacity: visible ? 1 : 0
Behavior on opacity {
NumberAnimation {
OpacityAnimator {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
@@ -1136,7 +1139,7 @@ PanelWindow {
}
onTranslationChanged: {
if (win.exiting)
if (win.exiting || content.swipeDismissing)
return;
content.swipeOffset = translation.x;
@@ -1152,7 +1155,7 @@ PanelWindow {
}
Behavior on opacity {
enabled: !content.swipeActive
enabled: !content.swipeActive && !content.swipeDismissing
NumberAnimation {
duration: Theme.shortDuration
}
@@ -1269,7 +1272,6 @@ PanelWindow {
NumberAnimation {
target: content
property: "opacity"
from: 1
to: Theme.isDirectionalEffect ? 1 : 0
duration: Theme.notificationExitDuration
easing.type: Easing.BezierSpline
@@ -1279,7 +1281,6 @@ PanelWindow {
NumberAnimation {
target: content
property: "scale"
from: 1
to: Theme.isDirectionalEffect ? 1 : Theme.effectScaleCollapsed
duration: Theme.notificationExitDuration
easing.type: Easing.BezierSpline

View File

@@ -519,7 +519,7 @@ Item {
}
Behavior on opacity {
NumberAnimation {
OpacityAnimator {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}

View File

@@ -182,7 +182,7 @@ SettingsCard {
opacity: visible ? 1 : 0
Behavior on opacity {
NumberAnimation {
OpacityAnimator {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}

View File

@@ -107,7 +107,7 @@ Item {
}
Behavior on opacity {
NumberAnimation {
OpacityAnimator {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}

View File

@@ -587,7 +587,7 @@ FloatingWindow {
border.color: buttonState === "incompatible" ? Theme.warning : Theme.outline
Behavior on opacity {
NumberAnimation {
OpacityAnimator {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}

View File

@@ -619,7 +619,7 @@ FloatingWindow {
border.color: isInstalled ? (uninstallMouseArea.containsMouse ? Theme.error : Theme.outline) : "transparent"
Behavior on opacity {
NumberAnimation {
OpacityAnimator {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}

View File

@@ -380,7 +380,7 @@ Item {
}
Behavior on scale {
NumberAnimation {
ScaleAnimator {
duration: Theme.shortDuration
easing.type: Theme.emphasizedEasing
}
@@ -633,7 +633,7 @@ Item {
scale: isActive ? 1.03 : 1
Behavior on scale {
NumberAnimation {
ScaleAnimator {
duration: Theme.shortDuration
easing.type: Theme.emphasizedEasing
}
@@ -989,7 +989,7 @@ Item {
}
Behavior on scale {
NumberAnimation {
ScaleAnimator {
duration: Theme.shortDuration
easing.type: Theme.emphasizedEasing
}

View File

@@ -308,7 +308,7 @@ Column {
}
Behavior on opacity {
NumberAnimation {
OpacityAnimator {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
@@ -620,7 +620,7 @@ Column {
}
Behavior on opacity {
NumberAnimation {
OpacityAnimator {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}

View File

@@ -97,7 +97,7 @@ Item {
}
Behavior on opacity {
NumberAnimation {
OpacityAnimator {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}

View File

@@ -416,7 +416,7 @@ PanelWindow {
}
Behavior on opacity {
NumberAnimation {
OpacityAnimator {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}

View File

@@ -48,268 +48,271 @@ Scope {
bottom: true
}
HyprlandFocusGrab {
id: grab
windows: [root]
active: false
property bool hasBeenActivated: false
onActiveChanged: {
if (active) {
hasBeenActivated = true
}
}
onCleared: () => {
if (hasBeenActivated && overviewScope.overviewOpen) {
overviewScope.overviewOpen = false
}
}
}
Connections {
target: overviewScope
function onOverviewOpenChanged() {
if (overviewScope.overviewOpen) {
grab.hasBeenActivated = false
if (CompositorService.useHyprlandFocusGrab)
delayedGrabTimer.start()
} else {
delayedGrabTimer.stop()
grab.active = false
grab.hasBeenActivated = false
}
}
}
Connections {
target: root
function onMonitorIsFocusedChanged() {
if (!CompositorService.useHyprlandFocusGrab)
return;
if (overviewScope.overviewOpen && root.monitorIsFocused && !grab.active) {
grab.hasBeenActivated = false
grab.active = true
} else if (overviewScope.overviewOpen && !root.monitorIsFocused && grab.active) {
grab.active = false
}
}
}
Timer {
id: delayedGrabTimer
interval: 150
repeat: false
onTriggered: {
if (CompositorService.useHyprlandFocusGrab && overviewScope.overviewOpen && root.monitorIsFocused) {
grab.active = true
}
}
}
Timer {
id: closeTimer
interval: Theme.expressiveDurations.expressiveDefaultSpatial + 120
onTriggered: {
root.visible = false
}
}
Rectangle {
id: background
anchors.fill: parent
color: "black"
opacity: overviewScope.overviewOpen ? 0.5 : 0
Behavior on opacity {
NumberAnimation {
duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, overviewScope.overviewOpen)
easing.type: Easing.BezierSpline
easing.bezierCurve: overviewScope.overviewOpen ? Theme.variantModalEnterCurve : Theme.variantModalExitCurve
}
}
MouseArea {
anchors.fill: parent
onClicked: mouse => {
const localPos = mapToItem(contentContainer, mouse.x, mouse.y)
if (localPos.x < 0 || localPos.x > contentContainer.width || localPos.y < 0 || localPos.y > contentContainer.height) {
overviewScope.overviewOpen = false
closeTimer.restart()
HyprlandFocusGrab {
id: grab
windows: [root]
active: false
property bool hasBeenActivated: false
onActiveChanged: {
if (active) {
hasBeenActivated = true;
}
}
}
}
Item {
id: contentContainer
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: 100
width: childrenRect.width
height: childrenRect.height
opacity: overviewScope.overviewOpen ? 1 : 0
transform: [scaleTransform, motionTransform]
Scale {
id: scaleTransform
origin.x: contentContainer.width / 2
origin.y: contentContainer.height / 2
xScale: overviewScope.overviewOpen ? 1 : Theme.effectScaleCollapsed
yScale: overviewScope.overviewOpen ? 1 : Theme.effectScaleCollapsed
Behavior on xScale {
NumberAnimation {
duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, overviewScope.overviewOpen)
easing.type: Easing.BezierSpline
easing.bezierCurve: overviewScope.overviewOpen ? Theme.variantModalEnterCurve : Theme.variantModalExitCurve
}
}
Behavior on yScale {
NumberAnimation {
duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, overviewScope.overviewOpen)
easing.type: Easing.BezierSpline
easing.bezierCurve: overviewScope.overviewOpen ? Theme.variantModalEnterCurve : Theme.variantModalExitCurve
onCleared: () => {
if (hasBeenActivated && overviewScope.overviewOpen) {
overviewScope.overviewOpen = false;
}
}
}
Translate {
id: motionTransform
x: {
if (overviewScope.overviewOpen)
return 0;
if (Theme.isDirectionalEffect)
return 0;
if (Theme.isDepthEffect)
return Theme.effectAnimOffset * 0.25;
return 0;
}
y: {
if (overviewScope.overviewOpen)
return 0;
if (Theme.isDirectionalEffect)
return -Math.max(contentContainer.height * 0.8, Theme.effectAnimOffset * 1.1);
if (Theme.isDepthEffect)
return Math.max(Theme.effectAnimOffset * 0.85, 28);
return Theme.effectAnimOffset;
}
Behavior on x {
NumberAnimation {
duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, overviewScope.overviewOpen)
easing.type: Easing.BezierSpline
easing.bezierCurve: overviewScope.overviewOpen ? Theme.variantModalEnterCurve : Theme.variantModalExitCurve
}
}
Behavior on y {
NumberAnimation {
duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, overviewScope.overviewOpen)
easing.type: Easing.BezierSpline
easing.bezierCurve: overviewScope.overviewOpen ? Theme.variantModalEnterCurve : Theme.variantModalExitCurve
}
}
}
Behavior on opacity {
NumberAnimation {
duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, overviewScope.overviewOpen)
easing.type: Easing.BezierSpline
easing.bezierCurve: overviewScope.overviewOpen ? Theme.variantModalEnterCurve : Theme.variantModalExitCurve
}
}
Loader {
id: overviewLoader
active: overviewScope.overviewOpen
asynchronous: false
sourceComponent: OverviewWidget {
panelWindow: root
overviewOpen: overviewScope.overviewOpen
}
}
}
FocusScope {
id: focusScope
anchors.fill: parent
visible: overviewScope.overviewOpen
focus: overviewScope.overviewOpen && root.monitorIsFocused
Keys.onEscapePressed: event => {
if (!root.monitorIsFocused) return
overviewScope.overviewOpen = false
closeTimer.restart()
event.accepted = true
}
Keys.onPressed: event => {
if (!root.monitorIsFocused) return
if (event.key === Qt.Key_Left || event.key === Qt.Key_Right) {
if (!overviewLoader.item) return
const thisMonitorWorkspaceIds = overviewLoader.item.thisMonitorWorkspaceIds
if (thisMonitorWorkspaceIds.length === 0) return
const currentId = root.monitor.activeWorkspace?.id ?? thisMonitorWorkspaceIds[0]
const currentIndex = thisMonitorWorkspaceIds.indexOf(currentId)
let targetIndex
if (event.key === Qt.Key_Left) {
targetIndex = currentIndex - 1
if (targetIndex < 0) targetIndex = thisMonitorWorkspaceIds.length - 1
Connections {
target: overviewScope
function onOverviewOpenChanged() {
if (overviewScope.overviewOpen) {
grab.hasBeenActivated = false;
if (CompositorService.useHyprlandFocusGrab)
delayedGrabTimer.start();
} else {
targetIndex = currentIndex + 1
if (targetIndex >= thisMonitorWorkspaceIds.length) targetIndex = 0
delayedGrabTimer.stop();
grab.active = false;
grab.hasBeenActivated = false;
}
const targetId = thisMonitorWorkspaceIds[targetIndex]
Hyprland.dispatch("workspace " + targetId)
event.accepted = true
}
}
onVisibleChanged: {
if (visible && overviewScope.overviewOpen && root.monitorIsFocused) {
Qt.callLater(() => focusScope.forceActiveFocus())
}
}
Connections {
target: root
function onMonitorIsFocusedChanged() {
if (root.monitorIsFocused && overviewScope.overviewOpen) {
Qt.callLater(() => focusScope.forceActiveFocus())
if (!CompositorService.useHyprlandFocusGrab)
return;
if (overviewScope.overviewOpen && root.monitorIsFocused && !grab.active) {
grab.hasBeenActivated = false;
grab.active = true;
} else if (overviewScope.overviewOpen && !root.monitorIsFocused && grab.active) {
grab.active = false;
}
}
}
Timer {
id: delayedGrabTimer
interval: 150
repeat: false
onTriggered: {
if (CompositorService.useHyprlandFocusGrab && overviewScope.overviewOpen && root.monitorIsFocused) {
grab.active = true;
}
}
}
Timer {
id: closeTimer
interval: Theme.expressiveDurations.expressiveDefaultSpatial + 120
onTriggered: {
root.visible = false;
}
}
Rectangle {
id: background
anchors.fill: parent
color: "black"
opacity: overviewScope.overviewOpen ? 0.5 : 0
Behavior on opacity {
OpacityAnimator {
duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, overviewScope.overviewOpen)
easing.type: Easing.BezierSpline
easing.bezierCurve: overviewScope.overviewOpen ? Theme.variantModalEnterCurve : Theme.variantModalExitCurve
}
}
MouseArea {
anchors.fill: parent
onClicked: mouse => {
const localPos = mapToItem(contentContainer, mouse.x, mouse.y);
if (localPos.x < 0 || localPos.x > contentContainer.width || localPos.y < 0 || localPos.y > contentContainer.height) {
overviewScope.overviewOpen = false;
closeTimer.restart();
}
}
}
}
Item {
id: contentContainer
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: 100
width: childrenRect.width
height: childrenRect.height
opacity: overviewScope.overviewOpen ? 1 : 0
transform: [scaleTransform, motionTransform]
Scale {
id: scaleTransform
origin.x: contentContainer.width / 2
origin.y: contentContainer.height / 2
xScale: overviewScope.overviewOpen ? 1 : Theme.effectScaleCollapsed
yScale: overviewScope.overviewOpen ? 1 : Theme.effectScaleCollapsed
Behavior on xScale {
NumberAnimation {
duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, overviewScope.overviewOpen)
easing.type: Easing.BezierSpline
easing.bezierCurve: overviewScope.overviewOpen ? Theme.variantModalEnterCurve : Theme.variantModalExitCurve
}
}
Behavior on yScale {
NumberAnimation {
duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, overviewScope.overviewOpen)
easing.type: Easing.BezierSpline
easing.bezierCurve: overviewScope.overviewOpen ? Theme.variantModalEnterCurve : Theme.variantModalExitCurve
}
}
}
Translate {
id: motionTransform
x: {
if (overviewScope.overviewOpen)
return 0;
if (Theme.isDirectionalEffect)
return 0;
if (Theme.isDepthEffect)
return Theme.effectAnimOffset * 0.25;
return 0;
}
y: {
if (overviewScope.overviewOpen)
return 0;
if (Theme.isDirectionalEffect)
return -Math.max(contentContainer.height * 0.8, Theme.effectAnimOffset * 1.1);
if (Theme.isDepthEffect)
return Math.max(Theme.effectAnimOffset * 0.85, 28);
return Theme.effectAnimOffset;
}
Behavior on x {
NumberAnimation {
duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, overviewScope.overviewOpen)
easing.type: Easing.BezierSpline
easing.bezierCurve: overviewScope.overviewOpen ? Theme.variantModalEnterCurve : Theme.variantModalExitCurve
}
}
Behavior on y {
NumberAnimation {
duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, overviewScope.overviewOpen)
easing.type: Easing.BezierSpline
easing.bezierCurve: overviewScope.overviewOpen ? Theme.variantModalEnterCurve : Theme.variantModalExitCurve
}
}
}
Behavior on opacity {
OpacityAnimator {
duration: Theme.variantDuration(Theme.expressiveDurations.expressiveDefaultSpatial, overviewScope.overviewOpen)
easing.type: Easing.BezierSpline
easing.bezierCurve: overviewScope.overviewOpen ? Theme.variantModalEnterCurve : Theme.variantModalExitCurve
}
}
Loader {
id: overviewLoader
active: overviewScope.overviewOpen
asynchronous: false
sourceComponent: OverviewWidget {
panelWindow: root
overviewOpen: overviewScope.overviewOpen
}
}
}
FocusScope {
id: focusScope
anchors.fill: parent
visible: overviewScope.overviewOpen
focus: overviewScope.overviewOpen && root.monitorIsFocused
Keys.onEscapePressed: event => {
if (!root.monitorIsFocused)
return;
overviewScope.overviewOpen = false;
closeTimer.restart();
event.accepted = true;
}
Keys.onPressed: event => {
if (!root.monitorIsFocused)
return;
if (event.key === Qt.Key_Left || event.key === Qt.Key_Right) {
if (!overviewLoader.item)
return;
const thisMonitorWorkspaceIds = overviewLoader.item.thisMonitorWorkspaceIds;
if (thisMonitorWorkspaceIds.length === 0)
return;
const currentId = root.monitor.activeWorkspace?.id ?? thisMonitorWorkspaceIds[0];
const currentIndex = thisMonitorWorkspaceIds.indexOf(currentId);
let targetIndex;
if (event.key === Qt.Key_Left) {
targetIndex = currentIndex - 1;
if (targetIndex < 0)
targetIndex = thisMonitorWorkspaceIds.length - 1;
} else {
targetIndex = currentIndex + 1;
if (targetIndex >= thisMonitorWorkspaceIds.length)
targetIndex = 0;
}
const targetId = thisMonitorWorkspaceIds[targetIndex];
Hyprland.dispatch("workspace " + targetId);
event.accepted = true;
}
}
onVisibleChanged: {
if (visible && overviewScope.overviewOpen && root.monitorIsFocused) {
Qt.callLater(() => focusScope.forceActiveFocus());
}
}
Connections {
target: root
function onMonitorIsFocusedChanged() {
if (root.monitorIsFocused && overviewScope.overviewOpen) {
Qt.callLater(() => focusScope.forceActiveFocus());
}
}
}
}
onVisibleChanged: {
if (visible && overviewScope.overviewOpen) {
Qt.callLater(() => focusScope.forceActiveFocus());
} else if (!visible) {
grab.active = false;
}
}
Connections {
target: overviewScope
function onOverviewOpenChanged() {
if (overviewScope.overviewOpen) {
closeTimer.stop();
root.visible = true;
Qt.callLater(() => focusScope.forceActiveFocus());
} else {
closeTimer.restart();
grab.active = false;
}
}
}
}
onVisibleChanged: {
if (visible && overviewScope.overviewOpen) {
Qt.callLater(() => focusScope.forceActiveFocus())
} else if (!visible) {
grab.active = false
}
}
Connections {
target: overviewScope
function onOverviewOpenChanged() {
if (overviewScope.overviewOpen) {
closeTimer.stop()
root.visible = true
Qt.callLater(() => focusScope.forceActiveFocus())
} else {
closeTimer.restart()
grab.active = false
}
}
}
}
}
}
}