forked from Shinonome/dots-hyprland
Merge branch 'end-4:main' into parallax
This commit is contained in:
@@ -138,6 +138,7 @@ Singleton {
|
||||
}
|
||||
property JsonObject palette: JsonObject {
|
||||
property string type: "auto" // Allowed: auto, scheme-content, scheme-expressive, scheme-fidelity, scheme-fruit-salad, scheme-monochrome, scheme-neutral, scheme-rainbow, scheme-tonal-spot
|
||||
property string accentColor: ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,6 +154,7 @@ Singleton {
|
||||
|
||||
property JsonObject apps: JsonObject {
|
||||
property string bluetooth: "kcmshell6 kcm_bluetooth"
|
||||
property string changePassword: "kitty -1 --hold=yes fish -i -c 'passwd'"
|
||||
property string network: "kcmshell6 kcm_networkmanagement"
|
||||
property string manageUser: "kcmshell6 kcm_users"
|
||||
property string networkEthernet: "kcmshell6 kcm_networkmanagement"
|
||||
@@ -346,6 +348,10 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
property JsonObject launcher: JsonObject {
|
||||
property list<string> pinnedApps: [ "org.kde.dolphin", "kitty", "cmake-gui"]
|
||||
}
|
||||
|
||||
property JsonObject light: JsonObject {
|
||||
property JsonObject night: JsonObject {
|
||||
property bool automatic: true
|
||||
@@ -432,6 +438,9 @@ Singleton {
|
||||
property int strokeWidth: 6
|
||||
property int padding: 10
|
||||
}
|
||||
property JsonObject annotation: JsonObject {
|
||||
property bool useSatty: false
|
||||
}
|
||||
}
|
||||
|
||||
property JsonObject resources: JsonObject {
|
||||
|
||||
@@ -12,6 +12,10 @@ Singleton {
|
||||
});
|
||||
}
|
||||
|
||||
function changePassword() {
|
||||
Quickshell.execDetached(["bash", "-c", `${Config.options.apps.changePassword}`]);
|
||||
}
|
||||
|
||||
function lock() {
|
||||
Quickshell.execDetached(["loginctl", "lock-session"]);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
|
||||
Scope {
|
||||
id: root
|
||||
|
||||
required property Component lockSurface
|
||||
property alias context: lockContext
|
||||
property Component sessionLockSurface: WlSessionLockSurface {
|
||||
id: sessionLockSurface
|
||||
color: "transparent"
|
||||
Loader {
|
||||
active: GlobalStates.screenLocked
|
||||
anchors.fill: parent
|
||||
opacity: active ? 1 : 0
|
||||
Behavior on opacity {
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
sourceComponent: root.lockSurface
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: unlockKeyringProc
|
||||
onExited: (exitCode, exitStatus) => {
|
||||
KeyringStorage.fetchKeyringData();
|
||||
}
|
||||
}
|
||||
function unlockKeyring() {
|
||||
unlockKeyringProc.exec({
|
||||
environment: ({
|
||||
"UNLOCK_PASSWORD": lockContext.currentText
|
||||
}),
|
||||
command: ["bash", "-c", Quickshell.shellPath("scripts/keyring/unlock.sh")]
|
||||
})
|
||||
}
|
||||
|
||||
// This stores all the information shared between the lock surfaces on each screen.
|
||||
// https://github.com/quickshell-mirror/quickshell-examples/tree/master/lockscreen
|
||||
LockContext {
|
||||
id: lockContext
|
||||
|
||||
Connections {
|
||||
target: GlobalStates
|
||||
function onScreenLockedChanged() {
|
||||
if (GlobalStates.screenLocked) {
|
||||
lockContext.reset();
|
||||
lockContext.tryFingerUnlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onUnlocked: (targetAction) => {
|
||||
// Perform the target action if it's not just unlocking
|
||||
if (targetAction == LockContext.ActionEnum.Poweroff) {
|
||||
Session.poweroff();
|
||||
return;
|
||||
} else if (targetAction == LockContext.ActionEnum.Reboot) {
|
||||
Session.reboot();
|
||||
return;
|
||||
}
|
||||
|
||||
// Unlock the keyring if configured to do so
|
||||
if (Config.options.lock.security.unlockKeyring) root.unlockKeyring(); // Async
|
||||
|
||||
// Unlock the screen before exiting, or the compositor will display a
|
||||
// fallback lock you can't interact with.
|
||||
GlobalStates.screenLocked = false;
|
||||
|
||||
// Refocus last focused window on unlock (hack)
|
||||
Quickshell.execDetached(["bash", "-c", `sleep 0.2; hyprctl --batch "dispatch togglespecialworkspace; dispatch togglespecialworkspace"`])
|
||||
|
||||
// Reset
|
||||
lockContext.reset();
|
||||
|
||||
// Post-unlock actions
|
||||
if (lockContext.alsoInhibitIdle) {
|
||||
lockContext.alsoInhibitIdle = false;
|
||||
Idle.toggleInhibit(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WlSessionLock {
|
||||
id: lock
|
||||
locked: GlobalStates.screenLocked
|
||||
|
||||
surface: root.sessionLockSurface
|
||||
}
|
||||
|
||||
function lock() {
|
||||
if (Config.options.lock.useHyprlock) {
|
||||
Quickshell.execDetached(["bash", "-c", "pidof hyprlock || hyprlock"]);
|
||||
return;
|
||||
}
|
||||
GlobalStates.screenLocked = true;
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
target: "lock"
|
||||
|
||||
function activate(): void {
|
||||
root.lock();
|
||||
}
|
||||
function focus(): void {
|
||||
lockContext.shouldReFocus();
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "lock"
|
||||
description: "Locks the screen"
|
||||
|
||||
onPressed: {
|
||||
root.lock()
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "lockFocus"
|
||||
description: "Re-focuses the lock screen. This is because Hyprland after waking up for whatever reason"
|
||||
+ "decides to keyboard-unfocus the lock screen"
|
||||
|
||||
onPressed: {
|
||||
lockContext.shouldReFocus();
|
||||
}
|
||||
}
|
||||
|
||||
function initIfReady() {
|
||||
if (!Config.ready || !Persistent.ready) return;
|
||||
if (Config.options.lock.launchOnStartup && Persistent.isNewHyprlandInstance) {
|
||||
root.lock();
|
||||
} else {
|
||||
KeyringStorage.fetchKeyringData();
|
||||
}
|
||||
}
|
||||
Connections {
|
||||
target: Config
|
||||
function onReadyChanged() {
|
||||
root.initIfReady();
|
||||
}
|
||||
}
|
||||
Connections {
|
||||
target: Persistent
|
||||
function onReadyChanged() {
|
||||
root.initIfReady();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.common.functions
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
|
||||
Scope {
|
||||
id: root
|
||||
required property Component contentComponent
|
||||
|
||||
Loader {
|
||||
active: PolkitService.active
|
||||
sourceComponent: Variants {
|
||||
model: Quickshell.screens
|
||||
delegate: PanelWindow {
|
||||
id: panelWindow
|
||||
required property var modelData
|
||||
screen: modelData
|
||||
|
||||
anchors {
|
||||
top: true
|
||||
left: true
|
||||
right: true
|
||||
bottom: true
|
||||
}
|
||||
|
||||
color: "transparent"
|
||||
WlrLayershell.namespace: "quickshell:polkit"
|
||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
|
||||
WlrLayershell.layer: WlrLayer.Overlay
|
||||
exclusionMode: ExclusionMode.Ignore
|
||||
|
||||
Loader {
|
||||
anchors.fill: parent
|
||||
sourceComponent: root.contentComponent
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,11 @@ MouseArea {
|
||||
height: batteryProgress.valueBarHeight
|
||||
|
||||
RowLayout {
|
||||
anchors.centerIn: parent
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
bottom: parent.bottom
|
||||
bottomMargin: (parent.height - height) / 2
|
||||
}
|
||||
spacing: 0
|
||||
|
||||
MaterialSymbol {
|
||||
|
||||
@@ -3,116 +3,39 @@ import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.common.panels.lock
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
|
||||
Scope {
|
||||
LockScreen {
|
||||
id: root
|
||||
|
||||
Process {
|
||||
id: unlockKeyringProc
|
||||
onExited: (exitCode, exitStatus) => {
|
||||
KeyringStorage.fetchKeyringData();
|
||||
}
|
||||
}
|
||||
function unlockKeyring() {
|
||||
unlockKeyringProc.exec({
|
||||
environment: ({
|
||||
"UNLOCK_PASSWORD": lockContext.currentText
|
||||
}),
|
||||
command: ["bash", "-c", Quickshell.shellPath("scripts/keyring/unlock.sh")]
|
||||
})
|
||||
lockSurface: LockSurface {
|
||||
context: root.context
|
||||
}
|
||||
|
||||
// Push everything down
|
||||
property var windowData: []
|
||||
function saveWindowPositionAndTile() {
|
||||
Quickshell.execDetached(["hyprctl", "keyword", "dwindle:pseudotile", "true"])
|
||||
root.windowData = HyprlandData.windowList.filter(w => (w.floating && w.workspace.id === HyprlandData.activeWorkspace.id))
|
||||
Quickshell.execDetached(["hyprctl", "keyword", "dwindle:pseudotile", "true"]);
|
||||
root.windowData = HyprlandData.windowList.filter(w => (w.floating && w.workspace.id === HyprlandData.activeWorkspace.id));
|
||||
root.windowData.forEach(w => {
|
||||
Hyprland.dispatch(`pseudo address:${w.address}`)
|
||||
Hyprland.dispatch(`settiled address:${w.address}`)
|
||||
Hyprland.dispatch(`movetoworkspacesilent ${w.workspace.id},address:${w.address}`)
|
||||
})
|
||||
Hyprland.dispatch(`pseudo address:${w.address}`);
|
||||
Hyprland.dispatch(`settiled address:${w.address}`);
|
||||
Hyprland.dispatch(`movetoworkspacesilent ${w.workspace.id},address:${w.address}`);
|
||||
});
|
||||
}
|
||||
function restoreWindowPositionAndTile() {
|
||||
root.windowData.forEach(w => {
|
||||
Hyprland.dispatch(`setfloating address:${w.address}`)
|
||||
Hyprland.dispatch(`movewindowpixel exact ${w.at[0]} ${w.at[1]}, address:${w.address}`)
|
||||
Hyprland.dispatch(`pseudo address:${w.address}`)
|
||||
})
|
||||
Quickshell.execDetached(["hyprctl", "keyword", "dwindle:pseudotile", "false"])
|
||||
Hyprland.dispatch(`setfloating address:${w.address}`);
|
||||
Hyprland.dispatch(`movewindowpixel exact ${w.at[0]} ${w.at[1]}, address:${w.address}`);
|
||||
Hyprland.dispatch(`pseudo address:${w.address}`);
|
||||
});
|
||||
Quickshell.execDetached(["hyprctl", "keyword", "dwindle:pseudotile", "false"]);
|
||||
}
|
||||
|
||||
// This stores all the information shared between the lock surfaces on each screen.
|
||||
// https://github.com/quickshell-mirror/quickshell-examples/tree/master/lockscreen
|
||||
LockContext {
|
||||
id: lockContext
|
||||
|
||||
Connections {
|
||||
target: GlobalStates
|
||||
function onScreenLockedChanged() {
|
||||
if (GlobalStates.screenLocked) {
|
||||
lockContext.reset();
|
||||
lockContext.tryFingerUnlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onUnlocked: (targetAction) => {
|
||||
// Perform the target action if it's not just unlocking
|
||||
if (targetAction == LockContext.ActionEnum.Poweroff) {
|
||||
Session.poweroff();
|
||||
return;
|
||||
} else if (targetAction == LockContext.ActionEnum.Reboot) {
|
||||
Session.reboot();
|
||||
return;
|
||||
}
|
||||
|
||||
// Unlock the keyring if configured to do so
|
||||
if (Config.options.lock.security.unlockKeyring) root.unlockKeyring(); // Async
|
||||
|
||||
// Unlock the screen before exiting, or the compositor will display a
|
||||
// fallback lock you can't interact with.
|
||||
GlobalStates.screenLocked = false;
|
||||
|
||||
// Refocus last focused window on unlock (hack)
|
||||
Quickshell.execDetached(["bash", "-c", `sleep 0.2; hyprctl --batch "dispatch togglespecialworkspace; dispatch togglespecialworkspace"`])
|
||||
|
||||
// Reset
|
||||
lockContext.reset();
|
||||
|
||||
// Post-unlock actions
|
||||
if (lockContext.alsoInhibitIdle) {
|
||||
lockContext.alsoInhibitIdle = false;
|
||||
Idle.toggleInhibit(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WlSessionLock {
|
||||
id: lock
|
||||
locked: GlobalStates.screenLocked
|
||||
|
||||
WlSessionLockSurface {
|
||||
color: "transparent"
|
||||
Loader {
|
||||
active: GlobalStates.screenLocked
|
||||
anchors.fill: parent
|
||||
opacity: active ? 1 : 0
|
||||
Behavior on opacity {
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
sourceComponent: LockSurface {
|
||||
context: lockContext
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Blur layer hack
|
||||
Variants {
|
||||
model: Quickshell.screens
|
||||
delegate: Scope {
|
||||
@@ -124,71 +47,12 @@ Scope {
|
||||
onShouldPushChanged: {
|
||||
if (shouldPush) {
|
||||
root.saveWindowPositionAndTile();
|
||||
Quickshell.execDetached(["bash", "-c", `hyprctl keyword monitor ${targetMonitorName}, addreserved, ${verticalMovementDistance}, ${-verticalMovementDistance}, ${horizontalSqueeze}, ${horizontalSqueeze}`])
|
||||
Quickshell.execDetached(["bash", "-c", `hyprctl keyword monitor ${targetMonitorName}, addreserved, ${verticalMovementDistance}, ${-verticalMovementDistance}, ${horizontalSqueeze}, ${horizontalSqueeze}`]);
|
||||
} else {
|
||||
Quickshell.execDetached(["bash", "-c", `hyprctl keyword monitor ${targetMonitorName}, addreserved, 0, 0, 0, 0`])
|
||||
Quickshell.execDetached(["bash", "-c", `hyprctl keyword monitor ${targetMonitorName}, addreserved, 0, 0, 0, 0`]);
|
||||
root.restoreWindowPositionAndTile();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function lock() {
|
||||
if (Config.options.lock.useHyprlock) {
|
||||
Quickshell.execDetached(["bash", "-c", "pidof hyprlock || hyprlock"]);
|
||||
return;
|
||||
}
|
||||
GlobalStates.screenLocked = true;
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
target: "lock"
|
||||
|
||||
function activate(): void {
|
||||
root.lock();
|
||||
}
|
||||
function focus(): void {
|
||||
lockContext.shouldReFocus();
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "lock"
|
||||
description: "Locks the screen"
|
||||
|
||||
onPressed: {
|
||||
root.lock()
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "lockFocus"
|
||||
description: "Re-focuses the lock screen. This is because Hyprland after waking up for whatever reason"
|
||||
+ "decides to keyboard-unfocus the lock screen"
|
||||
|
||||
onPressed: {
|
||||
lockContext.shouldReFocus();
|
||||
}
|
||||
}
|
||||
|
||||
function initIfReady() {
|
||||
if (!Config.ready || !Persistent.ready) return;
|
||||
if (Config.options.lock.launchOnStartup && Persistent.isNewHyprlandInstance) {
|
||||
root.lock();
|
||||
} else {
|
||||
KeyringStorage.fetchKeyringData();
|
||||
}
|
||||
}
|
||||
Connections {
|
||||
target: Config
|
||||
function onReadyChanged() {
|
||||
root.initIfReady();
|
||||
}
|
||||
}
|
||||
Connections {
|
||||
target: Persistent
|
||||
function onReadyChanged() {
|
||||
root.initIfReady();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.common.panels.lock
|
||||
import qs.modules.ii.bar as Bar
|
||||
import Quickshell
|
||||
import Quickshell.Services.SystemTray
|
||||
|
||||
@@ -6,37 +6,10 @@ import qs.modules.common.functions
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
|
||||
Scope {
|
||||
FullscreenPolkitWindow {
|
||||
id: root
|
||||
|
||||
Loader {
|
||||
active: PolkitService.active
|
||||
sourceComponent: Variants {
|
||||
model: Quickshell.screens
|
||||
delegate: PanelWindow {
|
||||
id: panelWindow
|
||||
required property var modelData
|
||||
screen: modelData
|
||||
|
||||
anchors {
|
||||
top: true
|
||||
left: true
|
||||
right: true
|
||||
bottom: true
|
||||
}
|
||||
|
||||
color: "transparent"
|
||||
WlrLayershell.namespace: "quickshell:polkit"
|
||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
|
||||
WlrLayershell.layer: WlrLayer.Overlay
|
||||
exclusionMode: ExclusionMode.Ignore
|
||||
|
||||
PolkitContent {
|
||||
anchors.fill: parent
|
||||
}
|
||||
}
|
||||
}
|
||||
contentComponent: Component {
|
||||
PolkitContent {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,12 +66,7 @@ Item {
|
||||
WindowDialogParagraph {
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
text: {
|
||||
if (!PolkitService.flow) return;
|
||||
return PolkitService.flow.message.endsWith(".")
|
||||
? PolkitService.flow.message.slice(0, -1)
|
||||
: PolkitService.flow.message
|
||||
}
|
||||
text: PolkitService.cleanMessage
|
||||
}
|
||||
|
||||
MaterialTextField {
|
||||
@@ -79,11 +74,7 @@ Item {
|
||||
Layout.fillWidth: true
|
||||
focus: true
|
||||
enabled: PolkitService.interactionAvailable
|
||||
placeholderText: {
|
||||
const inputPrompt = PolkitService.flow?.inputPrompt.trim() ?? "";
|
||||
const cleanedInputPrompt = inputPrompt.endsWith(":") ? inputPrompt.slice(0, -1) : inputPrompt;
|
||||
return cleanedInputPrompt || (root.usePasswordChars ? Translation.tr("Password") : Translation.tr("Input"))
|
||||
}
|
||||
placeholderText: PolkitService.cleanPrompt
|
||||
echoMode: root.usePasswordChars ? TextInput.Password : TextInput.Normal
|
||||
onAccepted: root.submit();
|
||||
|
||||
@@ -95,7 +86,7 @@ Item {
|
||||
}
|
||||
|
||||
WindowDialogButtonRow {
|
||||
|
||||
Layout.bottomMargin: 10 // I honestly don't know why this is necessary
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
@@ -261,6 +261,7 @@ PanelWindow {
|
||||
const uploadAndGetUrl = (filePath) => {
|
||||
return `curl -sF files[]=@'${StringUtils.shellSingleQuoteEscape(filePath)}' ${root.fileUploadApiEndpoint} | jq -r '.files[0].url'`
|
||||
}
|
||||
const annotationCommand = `${Config.options.regionSelector.annotation.useSatty ? "satty" : "swappy"} -f -`;
|
||||
switch (root.action) {
|
||||
case RegionSelection.SnipAction.Copy:
|
||||
if (saveScreenshotDir === "") {
|
||||
@@ -282,7 +283,7 @@ PanelWindow {
|
||||
|
||||
break;
|
||||
case RegionSelection.SnipAction.Edit:
|
||||
snipProc.command = ["bash", "-c", `${cropToStdout} | swappy -f - && ${cleanup}`]
|
||||
snipProc.command = ["bash", "-c", `${cropToStdout} | ${annotationCommand} && ${cleanup}`]
|
||||
break;
|
||||
case RegionSelection.SnipAction.Search:
|
||||
snipProc.command = ["bash", "-c", `${cropInPlace} && xdg-open "${root.imageSearchEngineBaseUrl}$(${uploadAndGetUrl(root.screenshotPath)})" && ${cleanup}`]
|
||||
|
||||
@@ -14,61 +14,13 @@ import Quickshell.Hyprland
|
||||
Scope {
|
||||
id: root
|
||||
property var focusedScreen: Quickshell.screens.find(s => s.name === Hyprland.focusedMonitor?.name)
|
||||
property bool packageManagerRunning: false
|
||||
property bool downloadRunning: false
|
||||
|
||||
component DescriptionLabel: Rectangle {
|
||||
id: descriptionLabel
|
||||
property string text
|
||||
property color textColor: Appearance.colors.colOnTooltip
|
||||
color: Appearance.colors.colTooltip
|
||||
clip: true
|
||||
radius: Appearance.rounding.normal
|
||||
implicitHeight: descriptionLabelText.implicitHeight + 10 * 2
|
||||
implicitWidth: descriptionLabelText.implicitWidth + 15 * 2
|
||||
|
||||
Behavior on implicitWidth {
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: descriptionLabelText
|
||||
anchors.centerIn: parent
|
||||
color: descriptionLabel.textColor
|
||||
text: descriptionLabel.text
|
||||
}
|
||||
}
|
||||
|
||||
function detectRunningStuff() {
|
||||
packageManagerRunning = false;
|
||||
downloadRunning = false;
|
||||
detectPackageManagerProc.running = false;
|
||||
detectPackageManagerProc.running = true;
|
||||
detectDownloadProc.running = false;
|
||||
detectDownloadProc.running = true;
|
||||
}
|
||||
|
||||
Process {
|
||||
id: detectPackageManagerProc
|
||||
command: ["bash", "-c", "pidof pacman yay paru dnf zypper apt apx xbps flatpak snap apk yum epsi pikman"]
|
||||
onExited: (exitCode, exitStatus) => {
|
||||
root.packageManagerRunning = (exitCode === 0);
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: detectDownloadProc
|
||||
command: ["bash", "-c", "pidof curl wget aria2c yt-dlp || ls ~/Downloads | grep -E '\.crdownload$|\.part$'"]
|
||||
onExited: (exitCode, exitStatus) => {
|
||||
root.downloadRunning = (exitCode === 0);
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: sessionLoader
|
||||
active: GlobalStates.sessionOpen
|
||||
onActiveChanged: {
|
||||
if (sessionLoader.active) root.detectRunningStuff();
|
||||
if (sessionLoader.active)
|
||||
SessionWarnings.refresh();
|
||||
}
|
||||
|
||||
Connections {
|
||||
@@ -84,7 +36,7 @@ Scope {
|
||||
id: sessionRoot
|
||||
visible: sessionLoader.active
|
||||
property string subtitle
|
||||
|
||||
|
||||
function hide() {
|
||||
GlobalStates.sessionOpen = false;
|
||||
}
|
||||
@@ -110,7 +62,7 @@ Scope {
|
||||
id: sessionMouseArea
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
sessionRoot.hide()
|
||||
sessionRoot.hide();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,7 +71,7 @@ Scope {
|
||||
anchors.centerIn: parent
|
||||
spacing: 15
|
||||
|
||||
Keys.onPressed: (event) => {
|
||||
Keys.onPressed: event => {
|
||||
if (event.key === Qt.Key_Escape) {
|
||||
sessionRoot.hide();
|
||||
}
|
||||
@@ -128,7 +80,8 @@ Scope {
|
||||
ColumnLayout {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
spacing: 0
|
||||
StyledText { // Title
|
||||
StyledText {
|
||||
// Title
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font {
|
||||
@@ -139,7 +92,8 @@ Scope {
|
||||
text: Translation.tr("Session")
|
||||
}
|
||||
|
||||
StyledText { // Small instruction
|
||||
StyledText {
|
||||
// Small instruction
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font.pixelSize: Appearance.font.pixelSize.normal
|
||||
@@ -157,8 +111,14 @@ Scope {
|
||||
focus: sessionRoot.visible
|
||||
buttonIcon: "lock"
|
||||
buttonText: Translation.tr("Lock")
|
||||
onClicked: { Session.lock(); sessionRoot.hide() }
|
||||
onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText }
|
||||
onClicked: {
|
||||
Session.lock();
|
||||
sessionRoot.hide();
|
||||
}
|
||||
onFocusChanged: {
|
||||
if (focus)
|
||||
sessionRoot.subtitle = buttonText;
|
||||
}
|
||||
KeyNavigation.right: sessionSleep
|
||||
KeyNavigation.down: sessionHibernate
|
||||
}
|
||||
@@ -166,8 +126,14 @@ Scope {
|
||||
id: sessionSleep
|
||||
buttonIcon: "dark_mode"
|
||||
buttonText: Translation.tr("Sleep")
|
||||
onClicked: { Session.suspend(); sessionRoot.hide() }
|
||||
onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText }
|
||||
onClicked: {
|
||||
Session.suspend();
|
||||
sessionRoot.hide();
|
||||
}
|
||||
onFocusChanged: {
|
||||
if (focus)
|
||||
sessionRoot.subtitle = buttonText;
|
||||
}
|
||||
KeyNavigation.left: sessionLock
|
||||
KeyNavigation.right: sessionLogout
|
||||
KeyNavigation.down: sessionShutdown
|
||||
@@ -176,8 +142,14 @@ Scope {
|
||||
id: sessionLogout
|
||||
buttonIcon: "logout"
|
||||
buttonText: Translation.tr("Logout")
|
||||
onClicked: { Session.logout(); sessionRoot.hide() }
|
||||
onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText }
|
||||
onClicked: {
|
||||
Session.logout();
|
||||
sessionRoot.hide();
|
||||
}
|
||||
onFocusChanged: {
|
||||
if (focus)
|
||||
sessionRoot.subtitle = buttonText;
|
||||
}
|
||||
KeyNavigation.left: sessionSleep
|
||||
KeyNavigation.right: sessionTaskManager
|
||||
KeyNavigation.down: sessionReboot
|
||||
@@ -186,8 +158,14 @@ Scope {
|
||||
id: sessionTaskManager
|
||||
buttonIcon: "browse_activity"
|
||||
buttonText: Translation.tr("Task Manager")
|
||||
onClicked: { Session.launchTaskManager(); sessionRoot.hide() }
|
||||
onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText }
|
||||
onClicked: {
|
||||
Session.launchTaskManager();
|
||||
sessionRoot.hide();
|
||||
}
|
||||
onFocusChanged: {
|
||||
if (focus)
|
||||
sessionRoot.subtitle = buttonText;
|
||||
}
|
||||
KeyNavigation.left: sessionLogout
|
||||
KeyNavigation.down: sessionFirmwareReboot
|
||||
}
|
||||
@@ -196,8 +174,14 @@ Scope {
|
||||
id: sessionHibernate
|
||||
buttonIcon: "downloading"
|
||||
buttonText: Translation.tr("Hibernate")
|
||||
onClicked: { Session.hibernate(); sessionRoot.hide() }
|
||||
onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText }
|
||||
onClicked: {
|
||||
Session.hibernate();
|
||||
sessionRoot.hide();
|
||||
}
|
||||
onFocusChanged: {
|
||||
if (focus)
|
||||
sessionRoot.subtitle = buttonText;
|
||||
}
|
||||
KeyNavigation.up: sessionLock
|
||||
KeyNavigation.right: sessionShutdown
|
||||
}
|
||||
@@ -205,8 +189,14 @@ Scope {
|
||||
id: sessionShutdown
|
||||
buttonIcon: "power_settings_new"
|
||||
buttonText: Translation.tr("Shutdown")
|
||||
onClicked: { Session.poweroff(); sessionRoot.hide() }
|
||||
onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText }
|
||||
onClicked: {
|
||||
Session.poweroff();
|
||||
sessionRoot.hide();
|
||||
}
|
||||
onFocusChanged: {
|
||||
if (focus)
|
||||
sessionRoot.subtitle = buttonText;
|
||||
}
|
||||
KeyNavigation.left: sessionHibernate
|
||||
KeyNavigation.right: sessionReboot
|
||||
KeyNavigation.up: sessionSleep
|
||||
@@ -215,8 +205,14 @@ Scope {
|
||||
id: sessionReboot
|
||||
buttonIcon: "restart_alt"
|
||||
buttonText: Translation.tr("Reboot")
|
||||
onClicked: { Session.reboot(); sessionRoot.hide() }
|
||||
onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText }
|
||||
onClicked: {
|
||||
Session.reboot();
|
||||
sessionRoot.hide();
|
||||
}
|
||||
onFocusChanged: {
|
||||
if (focus)
|
||||
sessionRoot.subtitle = buttonText;
|
||||
}
|
||||
KeyNavigation.left: sessionShutdown
|
||||
KeyNavigation.right: sessionFirmwareReboot
|
||||
KeyNavigation.up: sessionLogout
|
||||
@@ -225,8 +221,14 @@ Scope {
|
||||
id: sessionFirmwareReboot
|
||||
buttonIcon: "settings_applications"
|
||||
buttonText: Translation.tr("Reboot to firmware settings")
|
||||
onClicked: { Session.rebootToFirmware(); sessionRoot.hide() }
|
||||
onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText }
|
||||
onClicked: {
|
||||
Session.rebootToFirmware();
|
||||
sessionRoot.hide();
|
||||
}
|
||||
onFocusChanged: {
|
||||
if (focus)
|
||||
sessionRoot.subtitle = buttonText;
|
||||
}
|
||||
KeyNavigation.up: sessionTaskManager
|
||||
KeyNavigation.left: sessionReboot
|
||||
}
|
||||
@@ -247,7 +249,7 @@ Scope {
|
||||
spacing: 10
|
||||
|
||||
Loader {
|
||||
active: root.packageManagerRunning
|
||||
active: SessionWarnings.packageManagerRunning
|
||||
visible: active
|
||||
sourceComponent: DescriptionLabel {
|
||||
text: Translation.tr("Your package manager is running")
|
||||
@@ -256,7 +258,7 @@ Scope {
|
||||
}
|
||||
}
|
||||
Loader {
|
||||
active: root.downloadRunning
|
||||
active: SessionWarnings.downloadRunning
|
||||
visible: active
|
||||
sourceComponent: DescriptionLabel {
|
||||
text: Translation.tr("There might be a download in progress")
|
||||
@@ -268,6 +270,28 @@ Scope {
|
||||
}
|
||||
}
|
||||
|
||||
component DescriptionLabel: Rectangle {
|
||||
id: descriptionLabel
|
||||
property string text
|
||||
property color textColor: Appearance.colors.colOnTooltip
|
||||
color: Appearance.colors.colTooltip
|
||||
clip: true
|
||||
radius: Appearance.rounding.normal
|
||||
implicitHeight: descriptionLabelText.implicitHeight + 10 * 2
|
||||
implicitWidth: descriptionLabelText.implicitWidth + 15 * 2
|
||||
|
||||
Behavior on implicitWidth {
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: descriptionLabelText
|
||||
anchors.centerIn: parent
|
||||
color: descriptionLabel.textColor
|
||||
text: descriptionLabel.text
|
||||
}
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
target: "session"
|
||||
|
||||
@@ -276,11 +300,11 @@ Scope {
|
||||
}
|
||||
|
||||
function close(): void {
|
||||
GlobalStates.sessionOpen = false
|
||||
GlobalStates.sessionOpen = false;
|
||||
}
|
||||
|
||||
function open(): void {
|
||||
GlobalStates.sessionOpen = true
|
||||
GlobalStates.sessionOpen = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -298,7 +322,7 @@ Scope {
|
||||
description: "Opens session screen on press"
|
||||
|
||||
onPressed: {
|
||||
GlobalStates.sessionOpen = true
|
||||
GlobalStates.sessionOpen = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -307,8 +331,7 @@ Scope {
|
||||
description: "Closes session screen on press"
|
||||
|
||||
onPressed: {
|
||||
GlobalStates.sessionOpen = false
|
||||
GlobalStates.sessionOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -99,7 +99,7 @@ Item {
|
||||
WPanelSeparator {}
|
||||
|
||||
FooterRectangle {
|
||||
FooterMoreButton {
|
||||
WTextButton {
|
||||
anchors {
|
||||
verticalCenter: parent.verticalCenter
|
||||
left: parent.left
|
||||
|
||||
+9
-49
@@ -87,38 +87,16 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
VerticalPageIndicator {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 6
|
||||
spacing: 6
|
||||
|
||||
NavigationArrow {
|
||||
down: false
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: root.pages
|
||||
delegate: MouseArea {
|
||||
id: pageIndicator
|
||||
required property int index
|
||||
hoverEnabled: true
|
||||
onClicked: root.currentPage = index
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
implicitWidth: 6
|
||||
implicitHeight: 6
|
||||
|
||||
Circle {
|
||||
anchors.centerIn: parent
|
||||
diameter: (index === root.currentPage || pageIndicator.containsMouse) && !pageIndicator.pressed ? 6 : 4
|
||||
color: pageIndicator.containsMouse ? Looks.colors.controlBgHover : Looks.colors.controlBg
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NavigationArrow {
|
||||
down: true
|
||||
}
|
||||
|
||||
currentIndex: root.currentPage
|
||||
count: root.pages
|
||||
onClicked: (index) => root.currentPage = index
|
||||
onIncreasePage: root.increasePage();
|
||||
onDecreasePage: root.decreasePage();
|
||||
}
|
||||
|
||||
FocusedScrollMouseArea {
|
||||
@@ -126,25 +104,7 @@ Item {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.NoButton
|
||||
hoverEnabled: false
|
||||
onScrollUp: decreasePage();
|
||||
onScrollDown: increasePage();
|
||||
}
|
||||
|
||||
component NavigationArrow: FluentIcon {
|
||||
id: navArrow
|
||||
required property bool down
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
implicitHeight: 12
|
||||
implicitWidth: 12 - (2 * upArea.containsPress)
|
||||
icon: down ? "caret-down" : "caret-up"
|
||||
color: upArea.containsMouse ? Looks.colors.controlBgHover : Looks.colors.controlBg
|
||||
filled: true
|
||||
opacity: ((down && root.currentPage < root.pages - 1) || (!down && root.currentPage > 0)) ? 1 : 0
|
||||
MouseArea {
|
||||
id: upArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onClicked: navArrow.down ? root.increasePage() : root.decreasePage();
|
||||
}
|
||||
onScrollUp: root.decreasePage();
|
||||
onScrollDown: root.increasePage();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ Item {
|
||||
WPanelSeparator {}
|
||||
|
||||
FooterRectangle {
|
||||
FooterMoreButton {
|
||||
WTextButton {
|
||||
anchors {
|
||||
verticalCenter: parent.verticalCenter
|
||||
left: parent.left
|
||||
|
||||
@@ -12,9 +12,9 @@ AppButton {
|
||||
iconName: checked ? "system-search-checked" : "system-search"
|
||||
separateLightDark: true
|
||||
|
||||
checked: GlobalStates.overviewOpen
|
||||
checked: GlobalStates.searchOpen && LauncherSearch.query !== ""
|
||||
onClicked: {
|
||||
GlobalStates.overviewOpen = !GlobalStates.overviewOpen; // For now...
|
||||
GlobalStates.searchOpen = !GlobalStates.searchOpen; // For now...
|
||||
}
|
||||
|
||||
BarToolTip {
|
||||
|
||||
@@ -14,7 +14,7 @@ AppButton {
|
||||
leftInset: Config.options.waffles.bar.leftAlignApps ? 12 : 0
|
||||
iconName: down ? "start-here-pressed" : "start-here"
|
||||
|
||||
checked: GlobalStates.searchOpen
|
||||
checked: GlobalStates.searchOpen && LauncherSearch.query === ""
|
||||
onClicked: {
|
||||
GlobalStates.searchOpen = !GlobalStates.searchOpen;
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ Button {
|
||||
}
|
||||
}
|
||||
|
||||
CloseButton {
|
||||
WindowCloseButton {
|
||||
id: closeButton
|
||||
}
|
||||
}
|
||||
@@ -91,46 +91,14 @@ Button {
|
||||
}
|
||||
}
|
||||
|
||||
component CloseButton: Button {
|
||||
id: reusableCloseButton
|
||||
component WindowCloseButton: CloseButton {
|
||||
visible: root.hovered
|
||||
Layout.leftMargin: 4
|
||||
implicitHeight: 30
|
||||
implicitWidth: 30
|
||||
radius: Looks.radius.large - root.padding
|
||||
onClicked: {
|
||||
root.toplevel.close();
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
z: 0
|
||||
color: "transparent"
|
||||
anchors.fill: closeButtonBg
|
||||
anchors.margins: -1
|
||||
opacity: closeButtonBg.opacity
|
||||
border.width: 1
|
||||
radius: closeButtonBg.radius + 1
|
||||
border.color: Looks.colors.bg2Border
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
id: closeButtonBg
|
||||
z: 1
|
||||
opacity: reusableCloseButton.hovered ? 1 : 0
|
||||
radius: Looks.radius.large - root.padding
|
||||
color: reusableCloseButton.pressed ? Looks.colors.dangerActive : Looks.colors.danger
|
||||
Behavior on opacity {
|
||||
animation: Looks.transition.opacity.createObject(this)
|
||||
}
|
||||
Behavior on color {
|
||||
animation: Looks.transition.color.createObject(this)
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: FluentIcon {
|
||||
z: 2
|
||||
anchors.centerIn: parent
|
||||
icon: "dismiss"
|
||||
implicitSize: 10
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,344 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.common.panels.lock
|
||||
import qs.modules.waffle.looks
|
||||
import qs.modules.waffle.sessionScreen as SessionScreen
|
||||
|
||||
LockScreen {
|
||||
id: root
|
||||
|
||||
property bool passwordView: false
|
||||
|
||||
lockSurface: Item {
|
||||
id: lockSurfaceItem
|
||||
|
||||
Component.onCompleted: {
|
||||
root.passwordView = false;
|
||||
lockSurfaceItem.forceActiveFocus();
|
||||
}
|
||||
|
||||
Keys.onPressed: {
|
||||
interactables.switchToFocusedView();
|
||||
}
|
||||
|
||||
Image {
|
||||
id: bg
|
||||
z: 0
|
||||
anchors.fill: parent
|
||||
sourceSize: Qt.size(lockSurfaceItem.width, lockSurfaceItem.height)
|
||||
source: Config.options.background.wallpaperPath
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
}
|
||||
|
||||
GaussianBlur {
|
||||
z: 1
|
||||
anchors.fill: parent
|
||||
source: bg
|
||||
radius: 100
|
||||
samples: radius * 2 + 1
|
||||
scale: root.passwordView ? 1.1 : 1
|
||||
opacity: root.passwordView ? 1 : 0
|
||||
|
||||
Behavior on opacity {
|
||||
animation: Looks.transition.opacity.createObject(this)
|
||||
}
|
||||
|
||||
Behavior on scale {
|
||||
NumberAnimation {
|
||||
duration: 400
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Interactables {
|
||||
id: interactables
|
||||
z: 2
|
||||
anchors.fill: parent
|
||||
}
|
||||
}
|
||||
|
||||
component Interactables: Rectangle {
|
||||
id: interactablesComponent
|
||||
color: ColorUtils.transparentize("#000000", 0.8)
|
||||
// Button {
|
||||
// onClicked: {
|
||||
// root.context.unlocked(LockContext.ActionEnum.Unlock);
|
||||
// GlobalStates.screenLocked = false;
|
||||
// }
|
||||
// text: "woah it doesnt work let me out pls uwu colon three"
|
||||
// }
|
||||
|
||||
function switchToFocusedView() {
|
||||
root.passwordView = true;
|
||||
}
|
||||
|
||||
Item {
|
||||
id: unfocusedContent
|
||||
anchors.fill: parent
|
||||
visible: !root.passwordView
|
||||
ClockTextGroup {
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
top: parent.top
|
||||
topMargin: interactablesComponent.height * 0.1
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
anchors {
|
||||
bottom: parent.bottom
|
||||
right: parent.right
|
||||
bottomMargin: 21
|
||||
rightMargin: 31
|
||||
}
|
||||
IconIndicator {
|
||||
baseIcon: "wifi-1"
|
||||
icon: WIcons.internetIcon
|
||||
}
|
||||
IconIndicator {
|
||||
baseIcon: WIcons.batteryIcon
|
||||
icon: WIcons.batteryLevelIcon
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: focusedContent
|
||||
anchors.fill: parent
|
||||
visible: root.passwordView
|
||||
|
||||
PasswordGroup {
|
||||
visible: root.passwordView
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
visible: root.passwordView
|
||||
anchors {
|
||||
bottom: parent.bottom
|
||||
right: parent.right
|
||||
bottomMargin: 21
|
||||
rightMargin: 31
|
||||
}
|
||||
SessionScreen.PowerButton {
|
||||
id: powerButton
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component IconIndicator: Item {
|
||||
id: iconIndicator
|
||||
required property string baseIcon
|
||||
required property string icon
|
||||
default property alias data: iconWidget.data
|
||||
implicitWidth: 40
|
||||
implicitHeight: 40
|
||||
FluentIcon {
|
||||
id: iconWidget
|
||||
anchors.centerIn: parent
|
||||
icon: iconIndicator.baseIcon
|
||||
color: Looks.darkColors.inactiveIcon
|
||||
implicitSize: 20
|
||||
FluentIcon {
|
||||
anchors.fill: parent
|
||||
icon: iconIndicator.icon
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component ClockTextGroup: Column {
|
||||
id: clockTextGroup
|
||||
spacing: -3
|
||||
|
||||
WText {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
color: Looks.darkColors.fg
|
||||
font.pixelSize: 133
|
||||
font.weight: Looks.font.weight.strong
|
||||
text: {
|
||||
// Don't take am/pm
|
||||
// Match groups of digits separated by non-digit chars (e.g., "12:34", "12.34", "12-34")
|
||||
let match = DateTime.time.match(/(\d{1,2})\D+(\d{2})/);
|
||||
return match ? `${match[1]}${DateTime.time.match(/\D+/)[0]}${match[2]}` : DateTime.time;
|
||||
}
|
||||
}
|
||||
|
||||
WText {
|
||||
id: dateLabel
|
||||
color: Looks.darkColors.fg
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
font.pixelSize: 28
|
||||
font.weight: Looks.font.weight.strong
|
||||
text: DateTime.collapsedCalendarFormat
|
||||
}
|
||||
}
|
||||
|
||||
component PasswordGroup: ColumnLayout {
|
||||
id: passwordGroup
|
||||
spacing: 15
|
||||
|
||||
WUserAvatar {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
sourceSize: Qt.size(192, 192)
|
||||
}
|
||||
|
||||
WText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: SystemInfo.username
|
||||
color: Looks.darkColors.fg
|
||||
font.pixelSize: 26
|
||||
font.weight: Looks.font.weight.strong
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: passwordInputWrapper
|
||||
Layout.topMargin: 10
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.bottomMargin: 132
|
||||
color: "transparent"
|
||||
implicitWidth: 296
|
||||
implicitHeight: 36
|
||||
border.width: 2
|
||||
border.color: Looks.applyContentTransparency(Looks.darkColors.bg1Border)
|
||||
radius: Looks.radius.medium
|
||||
|
||||
Rectangle {
|
||||
id: passwordInputBackground
|
||||
anchors.fill: parent
|
||||
anchors.margins: 2
|
||||
radius: Looks.radius.small + 1
|
||||
color: passwordInput.focus ? Looks.applyBackgroundTransparency(Looks.darkColors.bg1Base) : Looks.applyContentTransparency(Looks.darkColors.bg1)
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 6
|
||||
spacing: 3
|
||||
|
||||
WTextInput {
|
||||
id: passwordInput
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
verticalAlignment: TextInput.AlignVCenter
|
||||
inputMethodHints: Qt.ImhSensitiveData
|
||||
echoMode: passwordVisibilityButton.pressed ? TextInput.Normal : TextInput.Password
|
||||
color: Looks.darkColors.fg
|
||||
|
||||
font.pixelSize: 12
|
||||
WText {
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: passwordInput.text.length === 0
|
||||
text: Translation.tr("Password")
|
||||
font.pixelSize: Looks.font.pixelSize.large
|
||||
color: Looks.darkColors.fg
|
||||
opacity: 0.8
|
||||
}
|
||||
|
||||
onTextChanged: root.context.currentText = this.text
|
||||
onAccepted: {
|
||||
root.context.tryUnlock();
|
||||
}
|
||||
Connections {
|
||||
target: root.context
|
||||
function onCurrentTextChanged() {
|
||||
passwordInput.text = root.context.currentText;
|
||||
}
|
||||
}
|
||||
Connections {
|
||||
target: root
|
||||
function onPasswordViewChanged() {
|
||||
passwordInput.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onPressed: event => {
|
||||
root.context.resetClearTimer();
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.NoButton
|
||||
cursorShape: Qt.IBeamCursor
|
||||
}
|
||||
}
|
||||
|
||||
PasswordBoxButton {
|
||||
id: passwordVisibilityButton
|
||||
property bool passwordVisible: false
|
||||
visible: passwordInput.text.length > 0
|
||||
onPressed: passwordVisible = true
|
||||
onReleased: passwordVisible = false
|
||||
icon.name: passwordVisible ? "eye-off" : "eye"
|
||||
}
|
||||
|
||||
PasswordBoxButton {
|
||||
onClicked: {
|
||||
root.context.tryUnlock();
|
||||
}
|
||||
icon.name: "arrow-right"
|
||||
}
|
||||
}
|
||||
}
|
||||
Rectangle {
|
||||
id: activeIndicatorLine
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
}
|
||||
implicitHeight: 2
|
||||
color: passwordInput.focus ? Looks.colors.accent : Looks.applyContentTransparency(Looks.darkColors.bg2Border)
|
||||
}
|
||||
|
||||
layer.enabled: true
|
||||
layer.effect: OpacityMask {
|
||||
maskSource: Rectangle {
|
||||
width: passwordInputWrapper.width
|
||||
height: passwordInputWrapper.height
|
||||
radius: passwordInputWrapper.radius
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {}
|
||||
}
|
||||
|
||||
component PasswordBoxButton: WButton {
|
||||
id: pwBoxBtn
|
||||
implicitWidth: 28
|
||||
implicitHeight: 22
|
||||
|
||||
property color colBackground: ColorUtils.transparentize(Looks.darkColors.bg1)
|
||||
property color colBackgroundHover: ColorUtils.transparentize(Looks.darkColors.bg2Hover)
|
||||
property color colBackgroundActive: ColorUtils.transparentize(Looks.darkColors.bg2Active)
|
||||
fgColor: checked ? Looks.colors.accentFg : Looks.darkColors.fg
|
||||
|
||||
checked: hovered
|
||||
|
||||
contentItem: Item {
|
||||
FluentIcon {
|
||||
color: pwBoxBtn.fgColor
|
||||
anchors.centerIn: parent
|
||||
icon: pwBoxBtn.icon.name
|
||||
implicitSize: 16
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
import qs.modules.waffle.bar
|
||||
import Quickshell
|
||||
|
||||
Button {
|
||||
id: reusableCloseButton
|
||||
implicitHeight: 30
|
||||
implicitWidth: 30
|
||||
property alias radius: closeButtonBg.radius
|
||||
|
||||
Rectangle {
|
||||
z: 0
|
||||
color: "transparent"
|
||||
anchors.fill: closeButtonBg
|
||||
anchors.margins: -1
|
||||
opacity: closeButtonBg.opacity
|
||||
border.width: 1
|
||||
radius: closeButtonBg.radius + 1
|
||||
border.color: Looks.colors.bg2Border
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
id: closeButtonBg
|
||||
z: 1
|
||||
opacity: reusableCloseButton.hovered ? 1 : 0
|
||||
color: reusableCloseButton.pressed ? Looks.colors.dangerActive : Looks.colors.danger
|
||||
Behavior on opacity {
|
||||
animation: Looks.transition.opacity.createObject(this)
|
||||
}
|
||||
Behavior on color {
|
||||
animation: Looks.transition.color.createObject(this)
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: FluentIcon {
|
||||
z: 2
|
||||
anchors.centerIn: parent
|
||||
icon: "dismiss"
|
||||
implicitSize: 10
|
||||
}
|
||||
}
|
||||
@@ -96,12 +96,12 @@ Singleton {
|
||||
property color bg0Opaque: root.dark ? root.darkColors.bg0 : root.lightColors.bg0
|
||||
property color bg0: ColorUtils.transparentize(bg0Opaque, root.backgroundTransparency)
|
||||
property color bg0Border: ColorUtils.transparentize(root.dark ? root.darkColors.bg0Border : root.lightColors.bg0Border, root.backgroundTransparency)
|
||||
property color bg1Base: ColorUtils.transparentize(root.dark ? root.darkColors.bg1Base : root.lightColors.bg1Base, root.backgroundTransparency)
|
||||
property color bg1Base: root.dark ? root.darkColors.bg1Base : root.lightColors.bg1Base
|
||||
property color bg1: ColorUtils.transparentize(root.dark ? root.darkColors.bg1 : root.lightColors.bg1, root.contentTransparency)
|
||||
property color bg1Hover: ColorUtils.transparentize(root.dark ? root.darkColors.bg1Hover : root.lightColors.bg1Hover, root.contentTransparency)
|
||||
property color bg1Active: ColorUtils.transparentize(root.dark ? root.darkColors.bg1Active : root.lightColors.bg1Active, root.contentTransparency)
|
||||
property color bg1Border: ColorUtils.transparentize(root.dark ? root.darkColors.bg1Border : root.lightColors.bg1Border, root.contentTransparency)
|
||||
property color bg2Base: ColorUtils.transparentize(root.dark ? root.darkColors.bg2Base : root.lightColors.bg2Base, root.backgroundTransparency)
|
||||
property color bg2Base: root.dark ? root.darkColors.bg2Base : root.lightColors.bg2Base
|
||||
property color bg2: ColorUtils.transparentize(root.dark ? root.darkColors.bg2 : root.lightColors.bg2, root.contentTransparency)
|
||||
property color bg2Hover: ColorUtils.transparentize(root.dark ? root.darkColors.bg2Hover : root.lightColors.bg2Hover, root.contentTransparency)
|
||||
property color bg2Active: ColorUtils.transparentize(root.dark ? root.darkColors.bg2Active : root.lightColors.bg2Active, root.contentTransparency)
|
||||
@@ -146,7 +146,8 @@ Singleton {
|
||||
property int thin: Font.Normal
|
||||
property int regular: Font.Medium
|
||||
property int strong: Font.DemiBold
|
||||
property int stronger: Font.Bold
|
||||
property int stronger: (Font.DemiBold + 2*Font.Bold) / 3
|
||||
property int strongest: Font.Bold
|
||||
}
|
||||
property QtObject pixelSize: QtObject {
|
||||
property real normal: 11
|
||||
@@ -154,6 +155,11 @@ Singleton {
|
||||
property real larger: 15
|
||||
property real xlarger: 17
|
||||
}
|
||||
property QtObject variableAxes: QtObject {
|
||||
property var ui: ({
|
||||
"wdth": 25
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
transition: QtObject {
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
import Qt.labs.synchronizer
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
Column {
|
||||
id: root
|
||||
|
||||
property bool showArrows: true
|
||||
property int currentIndex: 0
|
||||
property int count: 1
|
||||
signal clicked(int index)
|
||||
signal increasePage()
|
||||
signal decreasePage()
|
||||
|
||||
visible: count > 1
|
||||
spacing: 6
|
||||
|
||||
NavigationArrow {
|
||||
visible: root.showArrows
|
||||
down: false
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: root.count
|
||||
delegate: MouseArea {
|
||||
id: pageIndicator
|
||||
required property int index
|
||||
hoverEnabled: true
|
||||
onClicked: root.clicked(index);
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
implicitWidth: 6
|
||||
implicitHeight: 6
|
||||
|
||||
Circle {
|
||||
anchors.centerIn: parent
|
||||
diameter: (index === root.currentIndex || pageIndicator.containsMouse) && !pageIndicator.pressed ? 6 : 4
|
||||
color: pageIndicator.containsMouse ? Looks.colors.controlBgHover : Looks.colors.controlBg
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NavigationArrow {
|
||||
visible: root.showArrows
|
||||
down: true
|
||||
}
|
||||
|
||||
component NavigationArrow: FluentIcon {
|
||||
id: navArrow
|
||||
required property bool down
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
implicitHeight: 12
|
||||
implicitWidth: 12 - (2 * upArea.containsPress)
|
||||
icon: down ? "caret-down" : "caret-up"
|
||||
color: upArea.containsMouse ? Looks.colors.controlBgHover : Looks.colors.controlBg
|
||||
filled: true
|
||||
opacity: ((down && root.currentIndex < root.count - 1) || (!down && root.currentIndex > 0)) ? 1 : 0
|
||||
MouseArea {
|
||||
id: upArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onClicked: navArrow.down ? root.increasePage() : root.decreasePage();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,4 +17,6 @@ Kirigami.Icon {
|
||||
roundToIconSize: false
|
||||
fallback: root.iconName
|
||||
source: tryCustomIcon ? `${Looks.iconsPath}/${root.iconName}${!root.separateLightDark ? "" : Looks.dark ? "-dark" : "-light"}.svg` : fallback
|
||||
|
||||
color: Looks.colors.fg
|
||||
}
|
||||
|
||||
@@ -14,6 +14,9 @@ Menu {
|
||||
property bool downDirection: false
|
||||
property bool hasIcons: false // TODO: implement
|
||||
|
||||
property color color: Looks.colors.bg1Base
|
||||
property alias backgroundPane: bgPane
|
||||
|
||||
implicitWidth: background.implicitWidth + margins * 2
|
||||
implicitHeight: background.implicitHeight + margins * 2
|
||||
margins: 10
|
||||
@@ -58,7 +61,7 @@ Menu {
|
||||
bottomMargin: root.downDirection ? root.margins : root.sourceEdgeMargin
|
||||
}
|
||||
contentItem: Rectangle {
|
||||
color: Looks.colors.bg1Base
|
||||
color: root.color
|
||||
implicitWidth: menuListView.implicitWidth + root.padding * 2
|
||||
implicitHeight: root.contentItem.implicitHeight + root.padding * 2
|
||||
}
|
||||
@@ -66,6 +69,10 @@ Menu {
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
menuListView.itemAtIndex(0)?.forceActiveFocus();
|
||||
}
|
||||
|
||||
contentItem: Item {
|
||||
implicitWidth: menuListView.implicitWidth
|
||||
implicitHeight: menuListView.implicitHeight
|
||||
@@ -91,6 +98,5 @@ Menu {
|
||||
|
||||
delegate: WMenuItem {
|
||||
id: menuItemDelegate
|
||||
width: ListView.view?.width
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,6 +55,9 @@ MenuItem {
|
||||
rightInset: inset
|
||||
horizontalPadding: 11
|
||||
|
||||
width: ListView.view?.width
|
||||
height: visible ? implicitHeight : 0
|
||||
|
||||
background: Rectangle {
|
||||
id: backgroundRect
|
||||
radius: Looks.radius.medium
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import QtQuick
|
||||
import QtQuick.Effects
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
|
||||
Item {
|
||||
default property Item contentItem
|
||||
property Item shadow: WRectangularShadow {
|
||||
target: contentItem
|
||||
}
|
||||
implicitWidth: contentItem.implicitWidth
|
||||
implicitHeight: contentItem.implicitHeight
|
||||
|
||||
children: [shadow, contentItem]
|
||||
}
|
||||
@@ -8,10 +8,11 @@ Text {
|
||||
color: Looks.colors.fg
|
||||
|
||||
font {
|
||||
hintingPreference: Font.PreferFullHinting
|
||||
hintingPreference: Font.PreferDefaultHinting
|
||||
family: Looks.font.family.ui
|
||||
pixelSize: Looks.font.pixelSize.normal
|
||||
weight: Looks.font.weight.regular
|
||||
variableAxes: Looks.font.variableAxes.ui
|
||||
}
|
||||
|
||||
linkColor: Looks.colors.link
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import qs.modules.common
|
||||
import QtQuick
|
||||
import QtQuick.Controls.FluentWinUI3
|
||||
import QtQuick.Controls
|
||||
|
||||
TextField {
|
||||
id: root
|
||||
|
||||
clip: true
|
||||
renderType: Text.NativeRendering
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
color: Looks.colors.fg
|
||||
|
||||
palette {
|
||||
active: Looks.colors.accent
|
||||
}
|
||||
|
||||
font {
|
||||
hintingPreference: Font.PreferDefaultHinting
|
||||
family: Looks.font.family.ui
|
||||
pixelSize: Looks.font.pixelSize.normal
|
||||
weight: Looks.font.weight.regular
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.NoButton
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.IBeamCursor
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
color: "#000000"
|
||||
readonly property bool usePasswordChars: !PolkitService.flow?.responseVisible ?? true
|
||||
|
||||
Keys.onPressed: event => { // Esc to close
|
||||
if (event.key === Qt.Key_Escape) {
|
||||
PolkitService.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
StyledImage {
|
||||
anchors.fill: parent
|
||||
source: Config.options.background.wallpaperPath
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: ColorUtils.transparentize("#000000", 0.31)
|
||||
|
||||
PolkitDialog {
|
||||
id: dialog
|
||||
DragHandler {
|
||||
target: null
|
||||
property real startX: dialog.x
|
||||
property real startY: dialog.y
|
||||
onActiveChanged: {
|
||||
if (!active) return;
|
||||
startX = dialog.x;
|
||||
startY = dialog.y;
|
||||
}
|
||||
xAxis.onActiveValueChanged: {
|
||||
dialog.x = Math.round(startX + xAxis.activeValue);
|
||||
}
|
||||
yAxis.onActiveValueChanged: {
|
||||
dialog.y = Math.round(startY + yAxis.activeValue);
|
||||
}
|
||||
}
|
||||
x: Math.round((parent.width - width) / 2)
|
||||
y: Math.round((parent.height - height) / 2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component PolkitDialog: WPane {
|
||||
borderColor: Looks.colors.ambientShadow
|
||||
|
||||
contentItem: WPanelPageColumn {
|
||||
PolkitDialogHeader {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
BodyRectangle {
|
||||
id: dialogBody
|
||||
implicitHeight: bodyContent.implicitHeight + 48
|
||||
implicitWidth: 434
|
||||
color: Looks.colors.bg1Base
|
||||
|
||||
ColumnLayout {
|
||||
id: bodyContent
|
||||
anchors.fill: parent
|
||||
anchors.margins: 24
|
||||
spacing: 20
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 15
|
||||
|
||||
WAppIcon {
|
||||
iconName: PolkitService.flow?.iconName ?? "window-shield"
|
||||
fallback: PolkitService.flow?.iconName == "" ? `${Looks.iconsPath}/window-shield` : PolkitService.flow.iconName
|
||||
isMask: PolkitService.flow?.iconName === ""
|
||||
tryCustomIcon: false
|
||||
}
|
||||
WText {
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
font.pixelSize: Looks.font.pixelSize.larger
|
||||
font.weight: Looks.font.weight.strongest
|
||||
text: {
|
||||
const iconName = PolkitService.flow?.iconName ?? "";
|
||||
if (iconName === "")
|
||||
return Translation.tr("Command-line-invoked Action");
|
||||
const desktopEntry = DesktopEntries.applications.values.find(entry => {
|
||||
return entry.icon == iconName;
|
||||
});
|
||||
return desktopEntry ? desktopEntry.name : Translation.tr("Unknown Application");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WText {
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.Wrap
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
text: PolkitService.cleanMessage
|
||||
}
|
||||
|
||||
WTextField {
|
||||
id: inputField
|
||||
Layout.fillWidth: true
|
||||
focus: true
|
||||
enabled: PolkitService.interactionAvailable
|
||||
placeholderText: PolkitService.cleanPrompt
|
||||
echoMode: root.usePasswordChars ? TextInput.Password : TextInput.Normal
|
||||
onAccepted: PolkitService.submit(inputField.text)
|
||||
|
||||
Keys.onPressed: event => { // Esc to close
|
||||
if (event.key === Qt.Key_Escape) {
|
||||
PolkitService.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: forceActiveFocus()
|
||||
Connections {
|
||||
target: PolkitService
|
||||
function onInteractionAvailableChanged() {
|
||||
if (!PolkitService.interactionAvailable)
|
||||
return;
|
||||
inputField.text = "";
|
||||
inputField.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BodyRectangle {
|
||||
implicitHeight: 80
|
||||
color: Looks.colors.bgPanelFooterBase
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 24
|
||||
spacing: 8
|
||||
uniformCellSizes: true
|
||||
|
||||
WButton {
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: 32
|
||||
colBackground: Looks.colors.bg1
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: Translation.tr("Yes")
|
||||
onClicked: PolkitService.submit(inputField.text)
|
||||
}
|
||||
WButton {
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: 32
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
checked: true
|
||||
text: Translation.tr("No")
|
||||
onClicked: PolkitService.cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component PolkitDialogHeader: BodyRectangle {
|
||||
implicitHeight: headerContent.implicitHeight
|
||||
color: Looks.colors.bg2Base
|
||||
|
||||
CloseButton {
|
||||
anchors {
|
||||
top: parent.top
|
||||
right: parent.right
|
||||
}
|
||||
radius: 0
|
||||
implicitWidth: 32
|
||||
implicitHeight: 32
|
||||
|
||||
onClicked: {
|
||||
PolkitService.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: headerContent
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 24
|
||||
anchors.rightMargin: 24
|
||||
spacing: 18
|
||||
|
||||
WText {
|
||||
Layout.topMargin: 20
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
text: Translation.tr("Polkit")
|
||||
}
|
||||
WText {
|
||||
Layout.fillWidth: true
|
||||
Layout.bottomMargin: 12
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
wrapMode: Text.Wrap
|
||||
text: Translation.tr("Do you want to allow this app to make changes to your device?")
|
||||
font.pixelSize: Looks.font.pixelSize.xlarger
|
||||
font.weight: Looks.font.weight.strongest
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.common.functions
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
|
||||
FullscreenPolkitWindow {
|
||||
id: root
|
||||
contentComponent: Component {
|
||||
WPolkitContent {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
|
||||
WSessionScreenTextButton {
|
||||
id: root
|
||||
implicitWidth: 40
|
||||
implicitHeight: 40
|
||||
focusRingRadius: Looks.radius.large
|
||||
colBackground: ColorUtils.transparentize(Looks.darkColors.bg2)
|
||||
colBackgroundHover: Looks.applyContentTransparency(Looks.darkColors.bg2Hover)
|
||||
colBackgroundActive: Looks.applyContentTransparency(Looks.darkColors.bg2Active)
|
||||
property color color: {
|
||||
if (root.down) {
|
||||
return root.colBackgroundActive;
|
||||
} else if (root.hovered) {
|
||||
return root.colBackgroundHover;
|
||||
} else {
|
||||
return root.colBackground;
|
||||
}
|
||||
}
|
||||
background: Rectangle {
|
||||
id: background
|
||||
radius: Looks.radius.medium
|
||||
color: root.color
|
||||
}
|
||||
contentItem: Item {
|
||||
FluentIcon {
|
||||
anchors.centerIn: parent
|
||||
implicitSize: 20
|
||||
icon: "power"
|
||||
color: root.fgColor
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
powerMenu.visible = !powerMenu.visible;
|
||||
}
|
||||
|
||||
WMenu {
|
||||
id: powerMenu
|
||||
x: -powerMenu.implicitWidth / 2 + root.implicitWidth / 2
|
||||
y: -powerMenu.implicitHeight
|
||||
|
||||
color: Looks.darkColors.bg1Base
|
||||
Component.onCompleted: {
|
||||
powerMenu.backgroundPane.borderColor = Looks.applyContentTransparency(Looks.darkColors.bg2Border);
|
||||
}
|
||||
delegate: WMenuItem {
|
||||
id: menuItemDelegate
|
||||
colBackground: ColorUtils.transparentize(Looks.darkColors.bg1Base)
|
||||
colBackgroundHover: Looks.applyContentTransparency(Looks.darkColors.bg2Hover)
|
||||
colBackgroundActive: Looks.applyContentTransparency(Looks.darkColors.bg2Active)
|
||||
colForeground: Looks.darkColors.fg
|
||||
}
|
||||
|
||||
Action {
|
||||
icon.name: "power"
|
||||
text: Translation.tr("Shut down")
|
||||
onTriggered: Session.poweroff()
|
||||
}
|
||||
Action {
|
||||
icon.name: "arrow-counterclockwise"
|
||||
text: Translation.tr("Restart")
|
||||
onTriggered: Session.reboot()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
Component.onCompleted: {
|
||||
lockButton.forceActiveFocus();
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.centerIn: parent
|
||||
spacing: 4
|
||||
|
||||
WSessionScreenTextButton {
|
||||
id: lockButton
|
||||
focus: true
|
||||
text: Translation.tr("Lock")
|
||||
onClicked: {
|
||||
GlobalStates.sessionOpen = false;
|
||||
Session.lock();
|
||||
}
|
||||
KeyNavigation.up: powerButton
|
||||
KeyNavigation.down: signOutButton
|
||||
}
|
||||
WSessionScreenTextButton {
|
||||
id: signOutButton
|
||||
focus: true
|
||||
text: Translation.tr("Sign out")
|
||||
onClicked: {
|
||||
GlobalStates.sessionOpen = false;
|
||||
Session.logout();
|
||||
}
|
||||
KeyNavigation.up: lockButton
|
||||
KeyNavigation.down: changePasswordButton
|
||||
}
|
||||
|
||||
WSessionScreenTextButton {
|
||||
id: changePasswordButton
|
||||
focus: true
|
||||
text: Translation.tr("Change password")
|
||||
onClicked: {
|
||||
GlobalStates.sessionOpen = false;
|
||||
Session.changePassword();
|
||||
}
|
||||
KeyNavigation.up: signOutButton
|
||||
KeyNavigation.down: taskManagerButton
|
||||
}
|
||||
|
||||
WSessionScreenTextButton {
|
||||
id: taskManagerButton
|
||||
focus: true
|
||||
text: Translation.tr("Task Manager")
|
||||
onClicked: {
|
||||
GlobalStates.sessionOpen = false;
|
||||
Session.launchTaskManager();
|
||||
}
|
||||
KeyNavigation.up: signOutButton
|
||||
KeyNavigation.down: cancelButton
|
||||
}
|
||||
|
||||
CancelButton {
|
||||
id: cancelButton
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 5
|
||||
Layout.rightMargin: 5
|
||||
Layout.topMargin: 38
|
||||
onClicked: GlobalStates.sessionOpen = false
|
||||
KeyNavigation.up: taskManagerButton
|
||||
KeyNavigation.down: powerButton
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
anchors {
|
||||
bottom: parent.bottom
|
||||
right: parent.right
|
||||
bottomMargin: 21
|
||||
rightMargin: 31
|
||||
}
|
||||
PowerButton {
|
||||
id: powerButton
|
||||
KeyNavigation.up: cancelButton
|
||||
KeyNavigation.down: lockButton
|
||||
}
|
||||
}
|
||||
|
||||
component CancelButton: WBorderlessButton {
|
||||
id: root
|
||||
implicitHeight: 32
|
||||
colBackground: Looks.darkColors.bg1Base
|
||||
colBackgroundHover: Qt.lighter(Looks.darkColors.bg1Base, 1.2)
|
||||
colBackgroundActive: Qt.lighter(Looks.darkColors.bg1Base, 1.1)
|
||||
colForeground: Looks.darkColors.fg
|
||||
|
||||
property bool keyboardDown: false
|
||||
|
||||
Keys.onPressed: event => {
|
||||
if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
|
||||
keyboardDown = true;
|
||||
event.accepted = true;
|
||||
}
|
||||
}
|
||||
Keys.onReleased: event => {
|
||||
if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
|
||||
keyboardDown = false;
|
||||
root.clicked();
|
||||
event.accepted = true;
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: WText {
|
||||
text: Translation.tr("Cancel")
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font.pixelSize: Looks.font.pixelSize.large
|
||||
color: root.colForeground
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
visible: cancelButton.focus
|
||||
anchors {
|
||||
fill: parent
|
||||
margins: -3
|
||||
}
|
||||
radius: cancelButton.background.radius + 4
|
||||
color: "transparent"
|
||||
border.width: 2
|
||||
border.color: "#ffffff"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import qs
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
WTextButton {
|
||||
id: root
|
||||
|
||||
implicitWidth: 135
|
||||
implicitHeight: 40
|
||||
horizontalPadding: 5
|
||||
|
||||
property bool keyboardDown: false
|
||||
property alias focusRingRadius: focusRing.radius
|
||||
fgColor: (root.pressed || root.keyboardDown) ? Looks.darkColors.fg1 : Looks.darkColors.fg
|
||||
|
||||
Keys.onPressed: event => {
|
||||
if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
|
||||
keyboardDown = true;
|
||||
event.accepted = true;
|
||||
}
|
||||
}
|
||||
Keys.onReleased: event => {
|
||||
if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
|
||||
keyboardDown = false;
|
||||
root.clicked();
|
||||
event.accepted = true;
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: Item {
|
||||
id: contentItem
|
||||
implicitWidth: buttonText.implicitWidth
|
||||
|
||||
WText {
|
||||
id: buttonText
|
||||
anchors.fill: parent
|
||||
color: root.fgColor
|
||||
text: root.text
|
||||
font.pixelSize: Looks.font.pixelSize.large
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: focusRing
|
||||
visible: root.focus
|
||||
anchors {
|
||||
fill: parent
|
||||
margins: -4
|
||||
}
|
||||
color: "transparent"
|
||||
border.width: 2
|
||||
border.color: "#ffffff"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.common.functions
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
|
||||
Scope {
|
||||
id: root
|
||||
property var focusedScreen: Quickshell.screens.find(s => s.name === Hyprland.focusedMonitor?.name)
|
||||
|
||||
Loader {
|
||||
id: sessionLoader
|
||||
active: GlobalStates.sessionOpen
|
||||
onActiveChanged: {
|
||||
if (sessionLoader.active) SessionWarnings.refresh();
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: GlobalStates
|
||||
function onScreenLockedChanged() {
|
||||
if (GlobalStates.screenLocked) {
|
||||
GlobalStates.sessionOpen = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sourceComponent: PanelWindow { // Session menu
|
||||
id: sessionRoot
|
||||
visible: sessionLoader.active
|
||||
property string subtitle
|
||||
|
||||
function hide() {
|
||||
GlobalStates.sessionOpen = false;
|
||||
}
|
||||
|
||||
exclusionMode: ExclusionMode.Ignore
|
||||
WlrLayershell.namespace: "quickshell:session"
|
||||
WlrLayershell.layer: WlrLayer.Overlay
|
||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
|
||||
// This is a big surface so we needa carefully choose the transparency,
|
||||
// or we'll get a large scary rgb blob
|
||||
color: "#000000"
|
||||
|
||||
anchors {
|
||||
top: true
|
||||
left: true
|
||||
right: true
|
||||
bottom: true
|
||||
}
|
||||
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
Keys.onPressed: (event) => {
|
||||
if (event.key === Qt.Key_Escape) {
|
||||
sessionRoot.hide();
|
||||
}
|
||||
}
|
||||
|
||||
SessionScreenContent {
|
||||
anchors.fill: parent
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
target: "session"
|
||||
|
||||
function toggle(): void {
|
||||
GlobalStates.sessionOpen = !GlobalStates.sessionOpen;
|
||||
}
|
||||
|
||||
function close(): void {
|
||||
GlobalStates.sessionOpen = false
|
||||
}
|
||||
|
||||
function open(): void {
|
||||
GlobalStates.sessionOpen = true
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "sessionToggle"
|
||||
description: "Toggles session screen on press"
|
||||
|
||||
onPressed: {
|
||||
GlobalStates.sessionOpen = !GlobalStates.sessionOpen;
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "sessionOpen"
|
||||
description: "Opens session screen on press"
|
||||
|
||||
onPressed: {
|
||||
GlobalStates.sessionOpen = true
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "sessionClose"
|
||||
description: "Closes session screen on press"
|
||||
|
||||
onPressed: {
|
||||
GlobalStates.sessionOpen = false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,6 +9,8 @@ import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
import qs.modules.waffle.startMenu.startPage
|
||||
import qs.modules.waffle.startMenu.searchPage
|
||||
|
||||
WBarAttachedPanelContent {
|
||||
id: root
|
||||
@@ -99,7 +101,7 @@ WBarAttachedPanelContent {
|
||||
}
|
||||
}
|
||||
Item {
|
||||
implicitHeight: root.searching ? 736 : 736 // TODO: Make sizes naturally inferred
|
||||
implicitHeight: root.searching ? 800 : 800 // TODO: Make sizes naturally inferred
|
||||
Layout.fillWidth: true
|
||||
Loader {
|
||||
id: pageContentLoader
|
||||
|
||||
@@ -1,227 +0,0 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import Quickshell
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
WPanelPageColumn {
|
||||
id: root
|
||||
|
||||
WPanelSeparator {}
|
||||
|
||||
BodyRectangle {
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
|
||||
WPanelSeparator {}
|
||||
|
||||
StartFooter {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
component StartFooter: FooterRectangle {
|
||||
implicitHeight: 63
|
||||
|
||||
UserButton {
|
||||
anchors {
|
||||
left: parent.left
|
||||
leftMargin: 52
|
||||
bottom: parent.bottom
|
||||
bottomMargin: 12
|
||||
}
|
||||
}
|
||||
|
||||
PowerButton {
|
||||
anchors {
|
||||
right: parent.right
|
||||
rightMargin: 52
|
||||
bottom: parent.bottom
|
||||
bottomMargin: 12
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component UserButton: WBorderlessButton {
|
||||
id: userButton
|
||||
implicitWidth: userButtonRow.implicitWidth + 12 * 2
|
||||
implicitHeight: 40
|
||||
|
||||
contentItem: Item {
|
||||
RowLayout {
|
||||
id: userButtonRow
|
||||
anchors.centerIn: parent
|
||||
spacing: 12
|
||||
|
||||
WUserAvatar {
|
||||
sourceSize: Qt.size(32, 32)
|
||||
}
|
||||
WText {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
text: SystemInfo.username
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
userMenu.open();
|
||||
}
|
||||
|
||||
WToolTip {
|
||||
text: SystemInfo.username
|
||||
}
|
||||
|
||||
Popup {
|
||||
id: userMenu
|
||||
x: -51
|
||||
y: -userMenu.implicitHeight + userButton.implicitHeight / 2 - 10
|
||||
|
||||
background: null
|
||||
|
||||
WToolTipContent {
|
||||
id: popupContent
|
||||
horizontalPadding: 10
|
||||
verticalPadding: 7
|
||||
radius: Looks.radius.large
|
||||
realContentItem: Item {
|
||||
implicitWidth: userMenuContentLayout.implicitWidth
|
||||
implicitHeight: userMenuContentLayout.implicitHeight
|
||||
|
||||
ColumnLayout {
|
||||
id: userMenuContentLayout
|
||||
anchors {
|
||||
fill: parent
|
||||
leftMargin: popupContent.horizontalPadding
|
||||
rightMargin: popupContent.horizontalPadding
|
||||
topMargin: popupContent.verticalPadding
|
||||
bottomMargin: popupContent.verticalPadding
|
||||
}
|
||||
spacing: 5
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 6
|
||||
FluentIcon {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
implicitSize: 22
|
||||
icon: "corporation"
|
||||
monochrome: false
|
||||
}
|
||||
WText {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
text: "Megahard"
|
||||
font.pixelSize: Looks.font.pixelSize.large
|
||||
font.weight: Looks.font.weight.strong
|
||||
}
|
||||
Item { Layout.fillWidth: true }
|
||||
WBorderlessButton {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
implicitHeight: 36
|
||||
implicitWidth: textItem.implicitWidth + 10 * 2
|
||||
contentItem: WText {
|
||||
id: textItem
|
||||
text: Translation.tr("Sign out")
|
||||
font.pixelSize: Looks.font.pixelSize.large
|
||||
}
|
||||
onClicked: Session.logout()
|
||||
}
|
||||
}
|
||||
Item { // Force min width 360 (using min on the item somehow doesn't work)
|
||||
implicitWidth: 334
|
||||
}
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.bottomMargin: 7
|
||||
Layout.leftMargin: 6
|
||||
spacing: 12
|
||||
WUserAvatar {
|
||||
sourceSize: Qt.size(58, 58)
|
||||
}
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
spacing: 2
|
||||
WText {
|
||||
text: SystemInfo.username
|
||||
font.pixelSize: Looks.font.pixelSize.larger
|
||||
font.weight: Looks.font.weight.strong
|
||||
}
|
||||
WText {
|
||||
color: Looks.colors.fg1
|
||||
text: Translation.tr("Local account")
|
||||
}
|
||||
WText {
|
||||
color: Looks.colors.accent
|
||||
text: Translation.tr("Manage my account")
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
Quickshell.execDetached(["bash", "-c", Config.options.apps.manageUser])
|
||||
GlobalStates.searchOpen = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component PowerButton: WBorderlessButton {
|
||||
id: powerButton
|
||||
implicitWidth: 40
|
||||
implicitHeight: 40
|
||||
|
||||
contentItem: Item {
|
||||
FluentIcon {
|
||||
anchors.centerIn: parent
|
||||
icon: "power"
|
||||
implicitSize: 20
|
||||
}
|
||||
}
|
||||
|
||||
WToolTip {
|
||||
extraVisibleCondition: !powerMenu.visible
|
||||
text: qsTr("Power")
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
powerMenu.open()
|
||||
}
|
||||
|
||||
WMenu {
|
||||
id: powerMenu
|
||||
x: -powerMenu.implicitWidth / 2 + powerButton.implicitWidth / 2
|
||||
y: -powerMenu.implicitHeight - 4
|
||||
Action {
|
||||
icon.name: "lock-closed"
|
||||
text: Translation.tr("Lock")
|
||||
onTriggered: Session.lock()
|
||||
}
|
||||
Action {
|
||||
icon.name: "weather-moon"
|
||||
text: Translation.tr("Sleep")
|
||||
onTriggered: Session.suspend()
|
||||
}
|
||||
Action {
|
||||
icon.name: "power"
|
||||
text: Translation.tr("Shut down")
|
||||
onTriggered: Session.poweroff()
|
||||
}
|
||||
Action {
|
||||
icon.name: "arrow-counterclockwise"
|
||||
text: Translation.tr("Restart")
|
||||
onTriggered: Session.reboot()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -70,6 +70,19 @@ Scope {
|
||||
}
|
||||
}
|
||||
|
||||
function toggleClipboard() {
|
||||
if (LauncherSearch.query.startsWith(Config.options.search.prefix.clipboard) || !GlobalStates.searchOpen) {
|
||||
GlobalStates.searchOpen = !GlobalStates.searchOpen;
|
||||
}
|
||||
LauncherSearch.ensurePrefix(Config.options.search.prefix.clipboard);
|
||||
}
|
||||
function toggleEmojis() {
|
||||
if (LauncherSearch.query.startsWith(Config.options.search.prefix.emojis) || !GlobalStates.searchOpen) {
|
||||
GlobalStates.searchOpen = !GlobalStates.searchOpen;
|
||||
}
|
||||
LauncherSearch.ensurePrefix(Config.options.search.prefix.emojis);
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
target: "search"
|
||||
|
||||
@@ -119,4 +132,22 @@ Scope {
|
||||
GlobalStates.superReleaseMightTrigger = false;
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "overviewClipboardToggle"
|
||||
description: "Toggle clipboard query on overview widget"
|
||||
|
||||
onPressed: {
|
||||
root.toggleClipboard();
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "overviewEmojiToggle"
|
||||
description: "Toggle emoji query on overview widget"
|
||||
|
||||
onPressed: {
|
||||
root.toggleEmojis();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+14
-2
@@ -5,6 +5,7 @@ import qs.modules.common
|
||||
import qs.modules.waffle.looks
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.common.models
|
||||
import qs.modules.waffle.startMenu
|
||||
import Quickshell
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
@@ -119,7 +120,7 @@ RowLayout {
|
||||
onModelChanged: {
|
||||
root.focusFirstItem();
|
||||
}
|
||||
delegate: WSearchResultButton {
|
||||
delegate: SearchResultButton {
|
||||
required property int index
|
||||
required property var modelData
|
||||
entry: modelData
|
||||
@@ -189,6 +190,7 @@ RowLayout {
|
||||
const isAppEntry = resultPreview.entry.type === Translation.tr("App");
|
||||
const appId = isAppEntry ? resultPreview.entry.id : "";
|
||||
const pinned = isAppEntry ? (Config.options.dock.pinnedApps.includes(appId)) : false;
|
||||
const startPinned = isAppEntry ? (Config.options.launcher.pinnedApps.includes(appId)) : false;
|
||||
var result = [
|
||||
searchResultComp.createObject(null, {
|
||||
name: resultPreview.entry.verb,
|
||||
@@ -198,6 +200,16 @@ RowLayout {
|
||||
resultPreview.entry.execute();
|
||||
}
|
||||
}),
|
||||
...(isAppEntry ? [
|
||||
searchResultComp.createObject(null, {
|
||||
name: startPinned ? Translation.tr("Unpin from Start") : Translation.tr("Pin to Start"),
|
||||
iconName: startPinned ? "keep_off" : "keep",
|
||||
iconType: LauncherSearchResult.IconType.Material,
|
||||
execute: () => {
|
||||
LauncherApps.togglePin(appId);
|
||||
}
|
||||
})
|
||||
] : []),
|
||||
...(isAppEntry ? [
|
||||
searchResultComp.createObject(null, {
|
||||
name: pinned ? Translation.tr("Unpin from taskbar") : Translation.tr("Pin to taskbar"),
|
||||
@@ -207,7 +219,7 @@ RowLayout {
|
||||
TaskbarApps.togglePin(appId);
|
||||
}
|
||||
})
|
||||
] : [])
|
||||
] : []),
|
||||
];
|
||||
result = result.concat(resultPreview.entry.actions);
|
||||
return result;
|
||||
+3
-2
@@ -8,6 +8,7 @@ import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
import qs.modules.waffle.startMenu
|
||||
|
||||
RowLayout {
|
||||
id: root
|
||||
@@ -65,8 +66,8 @@ RowLayout {
|
||||
|
||||
WMenu {
|
||||
id: accountsMenu
|
||||
x: -accountsMenu.implicitWidth + optionsButton.implicitWidth
|
||||
y: optionsButton.height + 10
|
||||
x: -accountsMenu.implicitWidth + optionsButton.implicitWidth + 10
|
||||
y: optionsButton.height
|
||||
downDirection: true
|
||||
Action {
|
||||
icon.name: "people-settings"
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
import QtQuick
|
||||
import qs.services
|
||||
|
||||
QtObject {
|
||||
property string name
|
||||
property list<string> categories
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
GridLayout {
|
||||
id: root
|
||||
|
||||
columns: 4
|
||||
|
||||
Component {
|
||||
id: aggAppCatComp
|
||||
AggregatedAppCategoryModel {}
|
||||
}
|
||||
property list<AggregatedAppCategoryModel> aggregatedCategories: [
|
||||
aggAppCatComp.createObject(null, {
|
||||
name: Translation.tr("Productivity"),
|
||||
categories: ["Development", "Education", "Network", "Office"]
|
||||
}), aggAppCatComp.createObject(null, {
|
||||
name: Translation.tr("Utilities & Tools"),
|
||||
categories: ["Utility", "Science"]
|
||||
}), aggAppCatComp.createObject(null, {
|
||||
name: Translation.tr("Creativity"),
|
||||
categories: ["AudioVideo", "Graphics"]
|
||||
}), aggAppCatComp.createObject(null, {
|
||||
name: Translation.tr("Other"),
|
||||
categories: ["Game"]
|
||||
}), aggAppCatComp.createObject(null, {
|
||||
name: Translation.tr("System"),
|
||||
categories: ["Settings", "System"]
|
||||
})
|
||||
]
|
||||
|
||||
Repeater {
|
||||
model: root.aggregatedCategories
|
||||
delegate: AppCategory {
|
||||
required property var modelData
|
||||
aggregatedCategory: modelData
|
||||
}
|
||||
}
|
||||
|
||||
columnSpacing: 27
|
||||
rowSpacing: 12
|
||||
component AppCategory: Item {
|
||||
id: categoryItem
|
||||
property AggregatedAppCategoryModel aggregatedCategory
|
||||
implicitWidth: categoryLayout.implicitWidth
|
||||
implicitHeight: categoryLayout.implicitHeight
|
||||
ColumnLayout {
|
||||
id: categoryLayout
|
||||
anchors.fill: parent
|
||||
spacing: 4
|
||||
|
||||
AppCategoryGrid {
|
||||
id: categoryGrid
|
||||
Layout.fillWidth: true
|
||||
aggregatedCategory: categoryItem.aggregatedCategory
|
||||
}
|
||||
|
||||
WButton {
|
||||
id: categoryButton
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: 32
|
||||
|
||||
contentItem: WText {
|
||||
id: categoryButtonText
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
elide: Text.ElideRight
|
||||
text: categoryItem.aggregatedCategory.name
|
||||
}
|
||||
onClicked: {
|
||||
categoryGrid.openCategoryFolder();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,341 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
property AggregatedAppCategoryModel aggregatedCategory
|
||||
property list<DesktopEntry> desktopEntries: [...DesktopEntries.applications.values.filter(app => {
|
||||
const appCategories = app.categories;
|
||||
const gridCategories = root.aggregatedCategory.categories;
|
||||
return appCategories.some(cat => gridCategories.indexOf(cat) !== -1);
|
||||
})].sort((a, b) => a.name.localeCompare(b.name));
|
||||
|
||||
property Item windowRootItem: {
|
||||
var item = root;
|
||||
// print("FINDING ROOT")
|
||||
while (item.parent != null) {
|
||||
if (item.parent.toString().includes("ProxyWindow"))
|
||||
break;
|
||||
item = item.parent;
|
||||
}
|
||||
// print(item.width, item.height)
|
||||
return item;
|
||||
}
|
||||
function openCategoryFolder() {
|
||||
categoryFolderPopup.open();
|
||||
}
|
||||
|
||||
radius: Looks.radius.large
|
||||
color: Looks.colors.bg1
|
||||
border.width: 1
|
||||
border.color: ColorUtils.transparentize(Looks.colors.ambientShadow, 0.7)
|
||||
implicitWidth: 156
|
||||
implicitHeight: 156
|
||||
|
||||
GridLayout {
|
||||
id: categoryAppsGrid
|
||||
anchors.fill: parent
|
||||
anchors.margins: 10
|
||||
columns: 2
|
||||
rows: 2
|
||||
columnSpacing: 0
|
||||
rowSpacing: 0
|
||||
uniformCellHeights: true
|
||||
uniformCellWidths: true
|
||||
|
||||
Repeater {
|
||||
model: ScriptModel {
|
||||
values: root.desktopEntries.slice(0, 3)
|
||||
}
|
||||
delegate: SmallGridAppButton {
|
||||
required property DesktopEntry modelData
|
||||
desktopEntry: modelData
|
||||
}
|
||||
}
|
||||
Loader {
|
||||
id: categoryOpenButtonLoader
|
||||
// It's like this on the real thing - you get an invisible button if there's not enough items
|
||||
opacity: root.desktopEntries.length > 3 ? 1 : 0
|
||||
active: true
|
||||
sourceComponent: CategoryOpenButton {
|
||||
aggregatedCategory: root.aggregatedCategory
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Popup {
|
||||
id: categoryFolderPopup
|
||||
// I don't even know what the fuck is going on at this point
|
||||
// I hate point mapping
|
||||
property point originPoint: categoryOpenButtonLoader.mapToItem(root, categoryOpenButtonLoader.width / 2, categoryOpenButtonLoader.height / 2)
|
||||
property point windowCenterPoint: {
|
||||
const rootContentItem = root.windowRootItem;
|
||||
const canvasPosInRoot = root.mapFromItem(rootContentItem, rootContentItem.width / 2, rootContentItem.height / 2);
|
||||
const sectionItem = root.parent.parent.parent;
|
||||
const positionInSection = sectionItem.mapFromItem(categoryOpenButtonLoader, categoryOpenButtonLoader.x, categoryOpenButtonLoader.y);
|
||||
const targetY = Math.max(-positionInSection.y + 212, canvasPosInRoot.y);
|
||||
return Qt.point(canvasPosInRoot.x, targetY);
|
||||
}
|
||||
|
||||
enter: Transition {
|
||||
NumberAnimation {
|
||||
target: categoryFolderPopup
|
||||
property: "x"
|
||||
from: categoryFolderPopup.originPoint.x - categoryOpenButtonLoader.width * 5 / 2
|
||||
to: categoryFolderPopup.windowCenterPoint.x - categoryFolderPopup.width / 2
|
||||
duration: 300
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn
|
||||
}
|
||||
NumberAnimation {
|
||||
target: categoryFolderPopup
|
||||
property: "y"
|
||||
from: categoryFolderPopup.originPoint.y - categoryOpenButtonLoader.height * 3 / 2
|
||||
to: categoryFolderPopup.windowCenterPoint.y - categoryFolderPopup.height / 2
|
||||
duration: 300
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn
|
||||
}
|
||||
NumberAnimation {
|
||||
target: categoryFolderPopup
|
||||
property: "scale"
|
||||
from: 0
|
||||
to: 1
|
||||
duration: 300
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn
|
||||
}
|
||||
}
|
||||
|
||||
exit: Transition {
|
||||
NumberAnimation {
|
||||
target: categoryFolderPopup
|
||||
property: "x"
|
||||
to: categoryFolderPopup.originPoint.x - categoryOpenButtonLoader.width * 5 / 2
|
||||
duration: 200
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Looks.transition.easing.bezierCurve.easeOut
|
||||
}
|
||||
NumberAnimation {
|
||||
target: categoryFolderPopup
|
||||
property: "y"
|
||||
to: categoryFolderPopup.originPoint.y - categoryOpenButtonLoader.height * 3 / 2
|
||||
duration: 200
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Looks.transition.easing.bezierCurve.easeOut
|
||||
}
|
||||
NumberAnimation {
|
||||
target: categoryFolderPopup
|
||||
property: "scale"
|
||||
from: 1
|
||||
to: 0
|
||||
duration: 200
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Looks.transition.easing.bezierCurve.easeOut
|
||||
}
|
||||
}
|
||||
|
||||
background: null
|
||||
|
||||
Loader {
|
||||
id: folderContentLoader
|
||||
active: categoryFolderPopup.visible
|
||||
sourceComponent: WRectangularShadowThis {
|
||||
CategoryFolderContent {
|
||||
title: root.aggregatedCategory.name
|
||||
desktopEntries: root.desktopEntries
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component CategoryFolderContent: WToolTipContent {
|
||||
id: categoryFolderContent
|
||||
property string title
|
||||
property list<DesktopEntry> desktopEntries: root.desktopEntries
|
||||
horizontalPadding: 0
|
||||
verticalPadding: 0
|
||||
radius: Looks.radius.large
|
||||
realContentItem: Item {
|
||||
implicitWidth: 448
|
||||
implicitHeight: 376
|
||||
ColumnLayout {
|
||||
anchors {
|
||||
fill: parent
|
||||
leftMargin: 32
|
||||
rightMargin: 32
|
||||
topMargin: 40
|
||||
bottomMargin: 32
|
||||
}
|
||||
spacing: 28
|
||||
WText {
|
||||
Layout.fillWidth: true
|
||||
text: categoryFolderContent.title
|
||||
font.pixelSize: Looks.font.pixelSize.xlarger
|
||||
font.weight: Looks.font.weight.stronger
|
||||
elide: Text.ElideRight
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
SwipeView {
|
||||
id: categoryFolderSwipeView
|
||||
anchors.fill: parent
|
||||
orientation: Qt.Vertical
|
||||
clip: true
|
||||
|
||||
Repeater {
|
||||
model: Math.ceil(root.desktopEntries.length / 12)
|
||||
delegate: Item {
|
||||
id: folderPage
|
||||
required property int index
|
||||
width: SwipeView.view.width
|
||||
height: SwipeView.view.height
|
||||
BigAppGrid {
|
||||
anchors {
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
}
|
||||
columns: 4
|
||||
rows: 3
|
||||
desktopEntries: root.desktopEntries.slice(folderPage.index * 12, (folderPage.index + 1) * 12)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
VerticalPageIndicator {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: categoryFolderSwipeView.right
|
||||
anchors.rightMargin: -19
|
||||
|
||||
showArrows: false
|
||||
currentIndex: categoryFolderSwipeView.currentIndex
|
||||
count: Math.ceil(root.desktopEntries.length / 12)
|
||||
onClicked: index => categoryFolderSwipeView.currentIndex = index
|
||||
}
|
||||
}
|
||||
}
|
||||
FocusedScrollMouseArea {
|
||||
z: 999
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.NoButton
|
||||
hoverEnabled: false
|
||||
onScrollUp: categoryFolderSwipeView.decrementCurrentIndex()
|
||||
onScrollDown: categoryFolderSwipeView.incrementCurrentIndex()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component CategoryOpenButton: SmallGridButton {
|
||||
id: categoryOpenButton
|
||||
property AggregatedAppCategoryModel aggregatedCategory
|
||||
|
||||
onClicked: root.openCategoryFolder()
|
||||
contentItem: Item {
|
||||
Behavior on scale {
|
||||
NumberAnimation {
|
||||
id: scaleAnim
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn
|
||||
}
|
||||
}
|
||||
GridLayout {
|
||||
anchors.centerIn: parent
|
||||
rows: 2
|
||||
columns: 2
|
||||
rowSpacing: 2
|
||||
columnSpacing: 2
|
||||
|
||||
Repeater {
|
||||
model: root.desktopEntries.slice(3, 7)
|
||||
delegate: WAppIcon {
|
||||
required property DesktopEntry modelData
|
||||
tryCustomIcon: false
|
||||
iconName: modelData.icon
|
||||
implicitSize: 16
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component SmallGridAppButton: SmallGridButton {
|
||||
id: smallGridAppButton
|
||||
property DesktopEntry desktopEntry
|
||||
|
||||
property bool pinnedStart: LauncherApps.isPinned(smallGridAppButton.desktopEntry.id);
|
||||
property bool pinnedTaskbar: TaskbarApps.isPinned(smallGridAppButton.desktopEntry.id);
|
||||
|
||||
onClicked: {
|
||||
GlobalStates.searchOpen = false;
|
||||
desktopEntry.execute();
|
||||
}
|
||||
|
||||
contentItem: Item {
|
||||
Behavior on scale {
|
||||
NumberAnimation {
|
||||
id: scaleAnim
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn
|
||||
}
|
||||
}
|
||||
WAppIcon {
|
||||
anchors.centerIn: parent
|
||||
tryCustomIcon: false
|
||||
iconName: smallGridAppButton.desktopEntry.icon
|
||||
implicitSize: 34
|
||||
}
|
||||
}
|
||||
|
||||
WToolTip {
|
||||
text: smallGridAppButton.desktopEntry.name
|
||||
}
|
||||
|
||||
altAction: () => {
|
||||
appMenu.popup();
|
||||
}
|
||||
|
||||
WMenu {
|
||||
id: appMenu
|
||||
downDirection: true
|
||||
|
||||
WMenuItem {
|
||||
icon.name: smallGridAppButton.pinnedStart ? "pin-off" : "pin"
|
||||
text: smallGridAppButton.pinnedStart ? Translation.tr("Unpin from Start") : Translation.tr("Pin to Start")
|
||||
onTriggered: {
|
||||
LauncherApps.togglePin(smallGridAppButton.desktopEntry.id);
|
||||
}
|
||||
}
|
||||
WMenuItem {
|
||||
icon.name: smallGridAppButton.pinnedTaskbar ? "pin-off" : "pin"
|
||||
text: smallGridAppButton.pinnedTaskbar ? Translation.tr("Unpin from taskbar") : Translation.tr("Pin to taskbar")
|
||||
onTriggered: {
|
||||
TaskbarApps.togglePin(smallGridAppButton.desktopEntry.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component SmallGridButton: WButton {
|
||||
id: root
|
||||
implicitWidth: 68
|
||||
implicitHeight: 68
|
||||
|
||||
property real pressedScale: 5 / 6
|
||||
|
||||
onDownChanged: {
|
||||
contentItem.scale = root.down ? root.pressedScale : 1; // If/When we do dragging, the scale is 1.25
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import Quickshell
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
GridLayout {
|
||||
id: root
|
||||
|
||||
property list<var> desktopEntries: []
|
||||
|
||||
columnSpacing: 0
|
||||
rowSpacing: 0
|
||||
|
||||
uniformCellHeights: true
|
||||
uniformCellWidths: true
|
||||
|
||||
Repeater {
|
||||
model: root.desktopEntries
|
||||
delegate: StartAppButton {
|
||||
id: pinnedAppButton
|
||||
required property var modelData
|
||||
desktopEntry: modelData
|
||||
onClicked: {
|
||||
GlobalStates.searchOpen = false;
|
||||
desktopEntry.execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import Quickshell
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
WButton {
|
||||
id: root
|
||||
required property DesktopEntry desktopEntry
|
||||
|
||||
property bool pinnedStart: LauncherApps.isPinned(root.desktopEntry.id);
|
||||
property bool pinnedTaskbar: TaskbarApps.isPinned(root.desktopEntry.id);
|
||||
|
||||
implicitWidth: 96
|
||||
implicitHeight: 84
|
||||
horizontalPadding: 0
|
||||
verticalPadding: 0
|
||||
contentItem: ColumnLayout {
|
||||
spacing: 3
|
||||
WAppIcon {
|
||||
Layout.topMargin: 12
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
iconName: root.desktopEntry.icon
|
||||
implicitSize: 34
|
||||
tryCustomIcon: false
|
||||
}
|
||||
WText {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 8
|
||||
Layout.rightMargin: 8
|
||||
text: root.desktopEntry.name
|
||||
wrapMode: Text.Wrap
|
||||
elide: Text.ElideRight
|
||||
maximumLineCount: 2
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignTop
|
||||
}
|
||||
}
|
||||
WToolTip {
|
||||
text: root.desktopEntry.name
|
||||
}
|
||||
|
||||
altAction: () => {
|
||||
appMenu.popup()
|
||||
}
|
||||
|
||||
WMenu {
|
||||
id: appMenu
|
||||
downDirection: true
|
||||
|
||||
WMenuItem {
|
||||
visible: root.pinnedStart
|
||||
icon.name: "arrow-up-left"
|
||||
text: Translation.tr("Move to front")
|
||||
onTriggered: {
|
||||
LauncherApps.moveToFront(root.desktopEntry.id);
|
||||
}
|
||||
}
|
||||
WMenuItem {
|
||||
visible: root.pinnedStart
|
||||
icon.name: "arrow-left"
|
||||
text: Translation.tr("Move left")
|
||||
onTriggered: {
|
||||
LauncherApps.moveLeft(root.desktopEntry.id);
|
||||
}
|
||||
}
|
||||
WMenuItem {
|
||||
visible: root.pinnedStart
|
||||
icon.name: "arrow-right"
|
||||
text: Translation.tr("Move right")
|
||||
onTriggered: {
|
||||
LauncherApps.moveRight(root.desktopEntry.id);
|
||||
}
|
||||
}
|
||||
WMenuItem {
|
||||
icon.name: root.pinnedStart ? "pin-off" : "pin"
|
||||
text: root.pinnedStart ? Translation.tr("Unpin from Start") : Translation.tr("Pin to Start")
|
||||
onTriggered: {
|
||||
LauncherApps.togglePin(root.desktopEntry.id);
|
||||
}
|
||||
}
|
||||
WMenuItem {
|
||||
icon.name: root.pinnedTaskbar ? "pin-off" : "pin"
|
||||
text: root.pinnedTaskbar ? Translation.tr("Unpin from taskbar") : Translation.tr("Pin to taskbar")
|
||||
onTriggered: {
|
||||
TaskbarApps.togglePin(root.desktopEntry.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
BodyRectangle {
|
||||
id: root
|
||||
|
||||
ColumnLayout {
|
||||
anchors {
|
||||
fill: parent
|
||||
leftMargin: 32
|
||||
rightMargin: 32
|
||||
topMargin: 25
|
||||
bottomMargin: 30
|
||||
}
|
||||
spacing: 26
|
||||
|
||||
PinnedApps {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
AllApps {
|
||||
implicitHeight: 300 // for now
|
||||
}
|
||||
}
|
||||
|
||||
component PinnedApps: PageSection {
|
||||
title: Translation.tr("Pinned")
|
||||
|
||||
BigAppGrid {
|
||||
Layout.fillWidth: true
|
||||
columns: 8
|
||||
desktopEntries: Config.options.launcher.pinnedApps.map(appId => DesktopEntries.byId(appId))
|
||||
}
|
||||
}
|
||||
|
||||
component AllApps: PageSection {
|
||||
title: Translation.tr("All")
|
||||
// TODO: Do we wanna also implement list view and grid view?
|
||||
// (instead of only category view)
|
||||
AllAppsGrid {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Layout.leftMargin: 32
|
||||
Layout.rightMargin: 32
|
||||
}
|
||||
}
|
||||
|
||||
component PageSection: ColumnLayout {
|
||||
id: pageSection
|
||||
required property string title
|
||||
default property alias data: pageSectionContentArea.data
|
||||
|
||||
spacing: 16
|
||||
|
||||
WText {
|
||||
Layout.leftMargin: 32
|
||||
text: pageSection.title
|
||||
font.pixelSize: Looks.font.pixelSize.large
|
||||
font.weight: Looks.font.weight.stronger
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: pageSectionContentArea
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import Quickshell
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
WPanelPageColumn {
|
||||
id: root
|
||||
|
||||
WPanelSeparator {}
|
||||
|
||||
StartPageApps {
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
|
||||
WPanelSeparator {}
|
||||
|
||||
StartFooter {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
component StartFooter: FooterRectangle {
|
||||
implicitHeight: 63
|
||||
|
||||
StartUserButton {
|
||||
anchors {
|
||||
left: parent.left
|
||||
leftMargin: 52
|
||||
bottom: parent.bottom
|
||||
bottomMargin: 12
|
||||
}
|
||||
}
|
||||
|
||||
PowerButton {
|
||||
anchors {
|
||||
right: parent.right
|
||||
rightMargin: 52
|
||||
bottom: parent.bottom
|
||||
bottomMargin: 12
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component PowerButton: WBorderlessButton {
|
||||
id: powerButton
|
||||
implicitWidth: 40
|
||||
implicitHeight: 40
|
||||
|
||||
contentItem: Item {
|
||||
FluentIcon {
|
||||
anchors.centerIn: parent
|
||||
icon: "power"
|
||||
implicitSize: 20
|
||||
}
|
||||
}
|
||||
|
||||
WToolTip {
|
||||
extraVisibleCondition: !powerMenu.visible
|
||||
text: qsTr("Power")
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
powerMenu.open()
|
||||
}
|
||||
|
||||
WMenu {
|
||||
id: powerMenu
|
||||
x: -powerMenu.implicitWidth / 2 + powerButton.implicitWidth / 2
|
||||
y: -powerMenu.implicitHeight - 4
|
||||
Action {
|
||||
icon.name: "lock-closed"
|
||||
text: Translation.tr("Lock")
|
||||
onTriggered: Session.lock()
|
||||
}
|
||||
Action {
|
||||
icon.name: "weather-moon"
|
||||
text: Translation.tr("Sleep")
|
||||
onTriggered: Session.suspend()
|
||||
}
|
||||
Action {
|
||||
icon.name: "power"
|
||||
text: Translation.tr("Shut down")
|
||||
onTriggered: Session.poweroff()
|
||||
}
|
||||
Action {
|
||||
icon.name: "arrow-counterclockwise"
|
||||
text: Translation.tr("Restart")
|
||||
onTriggered: Session.reboot()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import Quickshell
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
WBorderlessButton {
|
||||
id: userButton
|
||||
implicitWidth: userButtonRow.implicitWidth + 12 * 2
|
||||
implicitHeight: 40
|
||||
|
||||
contentItem: Item {
|
||||
RowLayout {
|
||||
id: userButtonRow
|
||||
anchors.centerIn: parent
|
||||
spacing: 12
|
||||
|
||||
WUserAvatar {
|
||||
sourceSize: Qt.size(32, 32)
|
||||
}
|
||||
WText {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
text: SystemInfo.username
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
userMenu.open();
|
||||
}
|
||||
|
||||
WToolTip {
|
||||
text: SystemInfo.username
|
||||
}
|
||||
|
||||
Popup {
|
||||
id: userMenu
|
||||
x: -51
|
||||
y: -userMenu.implicitHeight + userButton.implicitHeight / 2 - 10
|
||||
|
||||
background: null
|
||||
|
||||
WToolTipContent {
|
||||
id: popupContent
|
||||
horizontalPadding: 10
|
||||
verticalPadding: 7
|
||||
radius: Looks.radius.large
|
||||
realContentItem: Item {
|
||||
implicitWidth: userMenuContentLayout.implicitWidth
|
||||
implicitHeight: userMenuContentLayout.implicitHeight
|
||||
|
||||
ColumnLayout {
|
||||
id: userMenuContentLayout
|
||||
anchors {
|
||||
fill: parent
|
||||
leftMargin: popupContent.horizontalPadding
|
||||
rightMargin: popupContent.horizontalPadding
|
||||
topMargin: popupContent.verticalPadding
|
||||
bottomMargin: popupContent.verticalPadding
|
||||
}
|
||||
spacing: 5
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 6
|
||||
FluentIcon {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
implicitSize: 22
|
||||
icon: "corporation"
|
||||
monochrome: false
|
||||
}
|
||||
WText {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
text: "Megahard"
|
||||
font.pixelSize: Looks.font.pixelSize.large
|
||||
font.weight: Looks.font.weight.strong
|
||||
}
|
||||
Item { Layout.fillWidth: true }
|
||||
WBorderlessButton {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
implicitHeight: 36
|
||||
implicitWidth: textItem.implicitWidth + 10 * 2
|
||||
contentItem: WText {
|
||||
id: textItem
|
||||
text: Translation.tr("Sign out")
|
||||
font.pixelSize: Looks.font.pixelSize.large
|
||||
}
|
||||
onClicked: Session.logout()
|
||||
}
|
||||
}
|
||||
Item { // Force min width 360 (using min on the item somehow doesn't work)
|
||||
implicitWidth: 334
|
||||
}
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.bottomMargin: 7
|
||||
Layout.leftMargin: 6
|
||||
spacing: 12
|
||||
WUserAvatar {
|
||||
sourceSize: Qt.size(58, 58)
|
||||
}
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
spacing: 2
|
||||
WText {
|
||||
text: SystemInfo.username
|
||||
font.pixelSize: Looks.font.pixelSize.larger
|
||||
font.weight: Looks.font.weight.strong
|
||||
}
|
||||
WText {
|
||||
color: Looks.colors.fg1
|
||||
text: Translation.tr("Local account")
|
||||
}
|
||||
WText {
|
||||
color: Looks.colors.accent
|
||||
text: Translation.tr("Manage my account")
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
Quickshell.execDetached(["bash", "-c", Config.options.apps.manageUser])
|
||||
GlobalStates.searchOpen = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user