Compare commits
16 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a16c957a8b | ||
|
|
b80b9475ac | ||
|
|
deb40a6536 | ||
|
|
92a4273cf5 | ||
|
|
3c7df45505 | ||
|
|
362c1ec670 | ||
|
|
c46593d438 | ||
|
|
3457164e19 | ||
|
|
a1124c8228 | ||
|
|
63b522648e | ||
|
|
4684e8a24f | ||
|
|
ac13eec68c | ||
|
|
0d37255873 | ||
|
|
81d2a27f50 | ||
|
|
66a9f2f220 | ||
|
|
8a3bb46db6 |
139 changed files with 964 additions and 4393 deletions
3
.gitattributes
vendored
3
.gitattributes
vendored
|
|
@ -1,3 +0,0 @@
|
|||
*.qml text
|
||||
*.conf text
|
||||
*.json text
|
||||
|
|
@ -75,10 +75,12 @@ endif()
|
|||
if("m3shapes" IN_LIST ENABLE_MODULES)
|
||||
message(STATUS "Fetching M3Shapes module")
|
||||
include(FetchContent)
|
||||
set(M3SHAPES_REV bdc327b29f95394a732baf3c9b19658ba23755b6)
|
||||
FetchContent_Declare(
|
||||
m3shapes_external
|
||||
GIT_REPOSITORY https://github.com/soramanew/m3shapes.git
|
||||
GIT_TAG 356825d31f16052a782735cce264331cba0cb71b
|
||||
GIT_TAG ${M3SHAPES_REV}
|
||||
SOURCE_DIR "${CMAKE_BINARY_DIR}/_deps/m3shapes-${M3SHAPES_REV}"
|
||||
)
|
||||
FetchContent_MakeAvailable(m3shapes_external)
|
||||
message(STATUS "Done fetching M3Shapes module")
|
||||
|
|
|
|||
|
|
@ -279,6 +279,7 @@ For example, to disable the bar on DP-1:
|
|||
"font": {
|
||||
"scale": 1,
|
||||
"clock": "Rubik",
|
||||
"workspaces": "Rubik",
|
||||
"headline": {
|
||||
"family": "GoogleSansFlex",
|
||||
"large": { "size": 32, "weight": 500, "italic": false, "vaxes": { "ROND": 25 } },
|
||||
|
|
@ -433,7 +434,7 @@ For example, to disable the bar on DP-1:
|
|||
"maxWindowIcons": 5,
|
||||
"activeTrail": false,
|
||||
"perMonitorWorkspaces": true,
|
||||
"label": " ",
|
||||
"label": " ",
|
||||
"occupiedLabel": "",
|
||||
"activeLabel": "",
|
||||
"capitalisation": "preserve",
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 44 KiB |
|
|
@ -1,20 +0,0 @@
|
|||
#%PAM-1.0
|
||||
# Caelestia lock-screen authentication (NoSignal).
|
||||
#
|
||||
# Deliberately does NOT use pam_faillock: a desktop screen-lock must never lock
|
||||
# the user out of their OWN running session. With faillock in the path (the old
|
||||
# "passwd"->system-auth route), a few failed unlocks tripped a temporary account
|
||||
# lock that then refused the *correct* password until /run/faillock cleared on
|
||||
# reboot. See README.md / finding F1.
|
||||
#
|
||||
# pam_unix verifies the password supplied via the PAM conversation (the lock UI)
|
||||
# using the setuid unix_chkpwd helper, so it works for the uid-1000 Quickshell
|
||||
# process. Add pam_systemd_home below if/when systemd-homed users are supported.
|
||||
#
|
||||
# F8a: deliberately NO `nullok` — a screen lock must never accept an empty
|
||||
# password. (Only changes behaviour for empty-password accounts, which are
|
||||
# correctly rejected; normal password auth is unaffected.)
|
||||
auth required pam_unix.so
|
||||
account required pam_unix.so
|
||||
password required pam_unix.so
|
||||
session required pam_unix.so
|
||||
|
|
@ -12,10 +12,10 @@ ColumnLayout {
|
|||
spacing: Tokens.spacing.medium
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
NsIcon {
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
animate: true
|
||||
icon: root.icon
|
||||
text: root.icon
|
||||
fontStyle: Tokens.font.icon.builders.extraLarge.scale(3).weight(Font.Bold).build()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,32 +0,0 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
|
||||
// NoSignal icon: renders a Nerd Font (JetBrainsMono NF) glyph from a Material
|
||||
// Symbol name via the Glyphs map. Drop-in for MaterialIcon but the input is
|
||||
// `icon` (the name) instead of `text`, so dynamic bindings stay reactive.
|
||||
|
||||
import QtQuick
|
||||
import qs.services
|
||||
|
||||
Text {
|
||||
id: root
|
||||
|
||||
property string icon: ""
|
||||
property int size: 16
|
||||
// MaterialIcon API parity so NsIcon is a drop-in (these are no-ops here —
|
||||
// Nerd Font has no fill/grade variable axes; sizing comes from fontStyle).
|
||||
property real fill: 0
|
||||
property int grade: 0
|
||||
property bool animate: false
|
||||
property var fontStyle: null
|
||||
|
||||
renderType: Text.NativeRendering
|
||||
textFormat: Text.PlainText
|
||||
text: Glyphs.get(root.icon)
|
||||
// "Mono" variant centers each icon glyph in a fixed cell (the plain NF
|
||||
// variant uses a wide advance, which left/right-shifts centered icons).
|
||||
font.family: "JetBrainsMono Nerd Font Mono"
|
||||
font.pixelSize: root.fontStyle ? (root.fontStyle.pixelSize > 0 ? root.fontStyle.pixelSize : Math.round((root.fontStyle.pointSize || 12) * 1.33)) : root.size
|
||||
color: Theme.text
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
|
||||
// NoSignal floating popout container: dark-glass card, 1px border, 4px radius.
|
||||
// Content goes in a ColumnLayout (default children). Sizes to its content height;
|
||||
// set `width` on the instance. A background MouseArea swallows clicks so clicking
|
||||
// inside the panel doesn't fall through to the overlay's close-catcher.
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.services
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property int pad: Theme.size.panelPad
|
||||
property alias spacing: holder.spacing
|
||||
default property alias content: holder.data
|
||||
|
||||
// anchoring hint read by NsOverlay: "center" | "right" | "trigger"
|
||||
property string anchorMode: "right"
|
||||
|
||||
implicitHeight: holder.implicitHeight + root.pad * 2
|
||||
color: Theme.panelBg
|
||||
radius: Theme.radius.panel
|
||||
border.width: 1
|
||||
border.color: Theme.panelBorder
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
// swallow clicks/hover so they don't reach the overlay close-catcher
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: holder
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: root.pad
|
||||
spacing: Theme.size.gap
|
||||
}
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
|
||||
// NoSignal toggle switch (animated 16px knob) used in panel headers.
|
||||
|
||||
import QtQuick
|
||||
import qs.services
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property bool on: false
|
||||
signal toggled
|
||||
|
||||
implicitWidth: 38
|
||||
implicitHeight: 22
|
||||
radius: 0
|
||||
color: on ? Theme.accent : Theme.fillSubtle
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Theme.hoverMs
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
x: root.on ? parent.width - width - 3 : 3
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
implicitWidth: 16
|
||||
implicitHeight: 16
|
||||
radius: 0
|
||||
color: root.on ? Theme.onAccent : Theme.text
|
||||
|
||||
Behavior on x {
|
||||
NumberAnimation {
|
||||
duration: 120
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: root.toggled()
|
||||
}
|
||||
}
|
||||
|
|
@ -21,7 +21,7 @@ StyledRect {
|
|||
property alias shapeMorph: stateLayer.shapeMorph
|
||||
property bool fillWidth // For ButtonRow
|
||||
|
||||
property font font
|
||||
property font font: Tokens.font.body.small
|
||||
property int type: ButtonBase.Filled
|
||||
|
||||
property real padding
|
||||
|
|
|
|||
|
|
@ -45,8 +45,8 @@ ColumnLayout {
|
|||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
NsIcon {
|
||||
icon: "expand_more"
|
||||
MaterialIcon {
|
||||
text: "expand_more"
|
||||
rotation: root.expanded ? 180 : 0
|
||||
color: Colours.palette.m3onSurfaceVariant
|
||||
fontStyle: Tokens.font.icon.medium
|
||||
|
|
|
|||
|
|
@ -110,11 +110,11 @@ RowLayout {
|
|||
}
|
||||
}
|
||||
|
||||
NsIcon {
|
||||
MaterialIcon {
|
||||
id: upIcon
|
||||
|
||||
anchors.centerIn: parent
|
||||
icon: "keyboard_arrow_up"
|
||||
text: "keyboard_arrow_up"
|
||||
color: Colours.palette.m3onPrimary
|
||||
}
|
||||
}
|
||||
|
|
@ -145,11 +145,11 @@ RowLayout {
|
|||
onReleased: timer.stop()
|
||||
}
|
||||
|
||||
NsIcon {
|
||||
MaterialIcon {
|
||||
id: downIcon
|
||||
|
||||
anchors.centerIn: parent
|
||||
icon: "keyboard_arrow_down"
|
||||
text: "keyboard_arrow_down"
|
||||
color: Colours.palette.m3onPrimary
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ Slider {
|
|||
|
||||
background: StyledRect {
|
||||
color: Colours.layer(Colours.palette.m3surfaceContainer, 2)
|
||||
radius: 0
|
||||
radius: Tokens.rounding.full
|
||||
|
||||
StyledRect {
|
||||
anchors.left: parent.left
|
||||
|
|
@ -51,7 +51,7 @@ Slider {
|
|||
anchors.fill: parent
|
||||
|
||||
color: Colours.palette.m3inverseSurface
|
||||
radius: 0
|
||||
radius: Tokens.rounding.full
|
||||
|
||||
MouseArea {
|
||||
id: handleInteraction
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import qs.services
|
|||
ButtonBase {
|
||||
id: root
|
||||
|
||||
property alias icon: label.icon
|
||||
property alias icon: label.text
|
||||
readonly property alias label: label
|
||||
|
||||
font: Tokens.font.icon.medium
|
||||
|
|
@ -34,10 +34,11 @@ ButtonBase {
|
|||
return h;
|
||||
}
|
||||
|
||||
NsIcon {
|
||||
MaterialIcon {
|
||||
id: label
|
||||
|
||||
anchors.centerIn: parent
|
||||
anchors.verticalCenterOffset: 1 // AHHHHHHH material symbols whyyyy
|
||||
color: root.onColour
|
||||
fontStyle: root.font
|
||||
fill: !root.isToggle || root.internalChecked ? 1 : 0
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import qs.services
|
|||
ButtonBase {
|
||||
id: root
|
||||
|
||||
property string icon
|
||||
property alias icon: iconLabel.text
|
||||
property alias text: label.text
|
||||
|
||||
readonly property alias iconLabel: iconLabel
|
||||
|
|
@ -44,17 +44,15 @@ ButtonBase {
|
|||
anchors.centerIn: parent
|
||||
spacing: Tokens.spacing.small
|
||||
|
||||
NsIcon {
|
||||
MaterialIcon {
|
||||
id: iconLabel
|
||||
|
||||
icon: root.icon
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.topMargin: Math.round(fontInfo.pointSize * 0.0575)
|
||||
color: root.onColour
|
||||
fill: root.internalChecked ? 1 : 0
|
||||
fontStyle: {
|
||||
const f = Qt.font(root.font);
|
||||
f.pointSize = Math.round(root.font.pointSize * 1.1);
|
||||
f.pointSize = Math.round(root.font.pointSize * 1.2);
|
||||
return f;
|
||||
}
|
||||
|
||||
|
|
@ -69,7 +67,7 @@ ButtonBase {
|
|||
id: label
|
||||
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.topMargin: -Math.round(iconLabel.fontInfo.pointSize * 0.0575)
|
||||
Layout.topMargin: 1
|
||||
color: root.onColour
|
||||
font: root.font
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,6 +94,12 @@ MouseArea {
|
|||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onWheel: e => e.accepted = true
|
||||
}
|
||||
|
||||
StyledRect {
|
||||
anchors.fill: parent
|
||||
radius: parent.radius
|
||||
|
|
@ -157,9 +163,9 @@ MouseArea {
|
|||
anchors.margins: Tokens.padding.medium
|
||||
spacing: Tokens.spacing.small
|
||||
|
||||
NsIcon {
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
icon: item.modelData?.icon ?? ""
|
||||
text: item.modelData?.icon ?? ""
|
||||
color: item.active ? Colours.palette.m3onTertiaryContainer : Colours.palette.m3onSurfaceVariant
|
||||
}
|
||||
|
||||
|
|
@ -176,8 +182,8 @@ MouseArea {
|
|||
active: item.modelData?.trailingIcon.length > 0
|
||||
visible: active
|
||||
|
||||
sourceComponent: NsIcon {
|
||||
icon: item.modelData.trailingIcon
|
||||
sourceComponent: MaterialIcon {
|
||||
text: item.modelData.trailingIcon
|
||||
color: item.active ? Colours.palette.m3onTertiaryContainer : Colours.palette.m3onSurfaceVariant
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,12 +64,12 @@ Row {
|
|||
anchors.horizontalCenterOffset: Math.floor(root.verticalPadding / 4)
|
||||
spacing: Tokens.spacing.small
|
||||
|
||||
NsIcon {
|
||||
MaterialIcon {
|
||||
id: iconLabel
|
||||
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
animate: true
|
||||
icon: root.active?.activeIcon ?? root.fallbackIcon
|
||||
text: root.active?.activeIcon ?? root.fallbackIcon
|
||||
color: root.disabled ? root.disabledTextColour : root.textColour
|
||||
fill: 1
|
||||
}
|
||||
|
|
@ -116,13 +116,13 @@ Row {
|
|||
onClicked: root.expanded = !root.expanded
|
||||
}
|
||||
|
||||
NsIcon {
|
||||
MaterialIcon {
|
||||
id: expandIcon
|
||||
|
||||
anchors.centerIn: parent
|
||||
anchors.horizontalCenterOffset: root.expanded ? 0 : -Math.floor(root.verticalPadding / 4)
|
||||
|
||||
icon: "expand_more"
|
||||
text: "expand_more"
|
||||
color: root.disabled ? root.disabledTextColour : root.textColour
|
||||
rotation: root.expanded ? 180 : 0
|
||||
|
||||
|
|
|
|||
|
|
@ -39,5 +39,6 @@ ButtonBase {
|
|||
|
||||
anchors.centerIn: parent
|
||||
color: root.onColour
|
||||
font: root.font
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,12 +54,12 @@ StyledRect {
|
|||
anchors.centerIn: parent
|
||||
spacing: Tokens.spacing.medium
|
||||
|
||||
NsIcon {
|
||||
MaterialIcon {
|
||||
id: toggleBtnIcon
|
||||
|
||||
visible: !!text
|
||||
fill: root.toggled ? 1 : 0
|
||||
icon: root.icon
|
||||
text: root.icon
|
||||
color: root.toggled ? Colours.palette[`m3on${root.accent}`] : Colours.palette[`m3on${root.accent}Container`]
|
||||
fontStyle: Tokens.font.icon.size(root.iconSize).build()
|
||||
|
||||
|
|
|
|||
|
|
@ -57,11 +57,11 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
NsIcon {
|
||||
MaterialIcon {
|
||||
anchors.centerIn: parent
|
||||
|
||||
grade: 200
|
||||
icon: image.status === Image.Error ? "broken_image" : "art_track"
|
||||
text: image.status === Image.Error ? "broken_image" : "art_track"
|
||||
color: Colours.palette.m3onSurfaceVariant
|
||||
fontStyle: Tokens.font.icon.size((parent.width * 0.35) || 1).build()
|
||||
opacity: image.status === Image.Null || image.status === Image.Error ? 1 : 0
|
||||
|
|
|
|||
7
flake.lock
generated
7
flake.lock
generated
|
|
@ -24,16 +24,17 @@
|
|||
"m3shapes": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1777812556,
|
||||
"narHash": "sha256-E5nqOucRQBWKuT31AMekmfgMywRdHgSbU86R7t4BKKA=",
|
||||
"lastModified": 1781017666,
|
||||
"narHash": "sha256-kfHyzZaPHgqZML48OA+5JwBOsLdQJ2ci/aGPShvUB4Y=",
|
||||
"owner": "soramanew",
|
||||
"repo": "m3shapes",
|
||||
"rev": "356825d31f16052a782735cce264331cba0cb71b",
|
||||
"rev": "bdc327b29f95394a732baf3c9b19658ba23755b6",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "soramanew",
|
||||
"repo": "m3shapes",
|
||||
"rev": "bdc327b29f95394a732baf3c9b19658ba23755b6",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
};
|
||||
|
||||
m3shapes = {
|
||||
url = "github:soramanew/m3shapes/356825d31f16052a782735cce264331cba0cb71b";
|
||||
url = "github:soramanew/m3shapes/bdc327b29f95394a732baf3c9b19658ba23755b6";
|
||||
flake = false;
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -54,9 +54,8 @@ Scope {
|
|||
onPressed: {
|
||||
if (root.hasFullscreen)
|
||||
return;
|
||||
// NoSignal: drive the redesigned full-screen Power Menu (NsOverlay),
|
||||
// not the old caelestia session panel (now dead code).
|
||||
NsShell.toggle("power", 0);
|
||||
const visibilities = Visibilities.getForActive();
|
||||
visibilities.session = !visibilities.session;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,18 +10,14 @@ import Caelestia.Config
|
|||
import qs.components
|
||||
import qs.services
|
||||
|
||||
// NoSignal: horizontal top bar. The widgets flow left->right in a RowLayout and
|
||||
// the bar's "thickness" is its HEIGHT; hit-testing / scroll / popout anchoring all
|
||||
// key off the x-axis (the position along the bar). Upstream caelestia is a
|
||||
// vertical left-edge bar — this is the NoSignal layout fork (Phase 3).
|
||||
RowLayout {
|
||||
ColumnLayout {
|
||||
id: root
|
||||
|
||||
required property ShellScreen screen
|
||||
required property DrawerVisibilities visibilities
|
||||
required property BarPopouts.Wrapper popouts
|
||||
required property bool fullscreen
|
||||
readonly property int hPadding: Tokens.padding.large
|
||||
readonly property int vPadding: Tokens.padding.large
|
||||
|
||||
function closeTray(): void {
|
||||
if (!Config.bar.tray.compact)
|
||||
|
|
@ -35,8 +31,8 @@ RowLayout {
|
|||
}
|
||||
}
|
||||
|
||||
function checkPopout(x: real): void {
|
||||
const ch = childAt(x, height / 2) as WrappedLoader;
|
||||
function checkPopout(y: real): void {
|
||||
const ch = childAt(width / 2, y) as WrappedLoader;
|
||||
|
||||
if (ch?.id !== "tray")
|
||||
closeTray();
|
||||
|
|
@ -47,24 +43,24 @@ RowLayout {
|
|||
}
|
||||
|
||||
const id = ch.id;
|
||||
const left = ch.x;
|
||||
const top = ch.y;
|
||||
|
||||
if (id === "statusIcons" && Config.bar.popouts.statusIcons) {
|
||||
const items = (ch.item as StatusIcons).items;
|
||||
const icon = items.childAt(mapToItem(items, x, 0).x, items.height / 2);
|
||||
const icon = items.childAt(items.width / 2, mapToItem(items, 0, y).y);
|
||||
if (icon) {
|
||||
popouts.currentName = icon.name;
|
||||
popouts.currentCenter = Qt.binding(() => icon.mapToItem(root, icon.implicitWidth / 2, 0).x);
|
||||
popouts.currentCenter = Qt.binding(() => icon.mapToItem(root, 0, icon.implicitHeight / 2).y);
|
||||
popouts.hasCurrent = true;
|
||||
}
|
||||
} else if (id === "tray" && Config.bar.popouts.tray) {
|
||||
const tray = ch.item as Tray;
|
||||
if (!Config.bar.tray.compact || (tray.expanded && !tray.expandIcon.contains(mapToItem(tray.expandIcon, x, tray.implicitHeight / 2)))) {
|
||||
const index = Math.floor(((x - left - tray.padding * 2 + tray.spacing) / tray.layout.implicitWidth) * tray.items.count);
|
||||
if (!Config.bar.tray.compact || (tray.expanded && !tray.expandIcon.contains(mapToItem(tray.expandIcon, tray.implicitWidth / 2, y)))) {
|
||||
const index = Math.floor(((y - top - tray.padding * 2 + tray.spacing) / tray.layout.implicitHeight) * tray.items.count);
|
||||
const trayItem = tray.items.itemAt(index);
|
||||
if (trayItem) {
|
||||
popouts.currentName = `traymenu${index}`;
|
||||
popouts.currentCenter = Qt.binding(() => trayItem.mapToItem(root, trayItem.implicitWidth / 2, 0).x);
|
||||
popouts.currentCenter = Qt.binding(() => trayItem.mapToItem(root, 0, trayItem.implicitHeight / 2).y);
|
||||
popouts.hasCurrent = true;
|
||||
} else {
|
||||
popouts.hasCurrent = false;
|
||||
|
|
@ -75,13 +71,13 @@ RowLayout {
|
|||
}
|
||||
} else if (id === "activeWindow" && Config.bar.popouts.activeWindow && Config.bar.activeWindow.showOnHover) {
|
||||
popouts.currentName = id.toLowerCase();
|
||||
popouts.currentCenter = (ch.item as Item).mapToItem(root, (ch.item as Item).implicitWidth / 2, 0).x ?? 0;
|
||||
popouts.currentCenter = (ch.item as Item).mapToItem(root, 0, (ch.item as Item).implicitHeight / 2).y ?? 0;
|
||||
popouts.hasCurrent = true;
|
||||
}
|
||||
}
|
||||
|
||||
function handleWheel(x: real, angleDelta: point): void {
|
||||
const ch = childAt(x, height / 2) as WrappedLoader;
|
||||
function handleWheel(y: real, angleDelta: point): void {
|
||||
const ch = childAt(width / 2, y) as WrappedLoader;
|
||||
if (ch?.id === "workspaces" && Config.bar.scrollActions.workspaces) {
|
||||
// Workspace scroll
|
||||
const mon = (GlobalConfig.bar.workspaces.perMonitorWorkspaces ? Hypr.monitorFor(screen) : Hypr.focusedMonitor);
|
||||
|
|
@ -90,14 +86,14 @@ RowLayout {
|
|||
Hypr.dispatch(`togglespecialworkspace ${specialWs.slice(8)}`);
|
||||
else if (angleDelta.y < 0 || (GlobalConfig.bar.workspaces.perMonitorWorkspaces ? mon.activeWorkspace?.id : Hypr.activeWsId) > 1)
|
||||
Hypr.dispatch(`workspace r${angleDelta.y > 0 ? "-" : "+"}1`);
|
||||
} else if (x < screen.width / 2 && Config.bar.scrollActions.volume) {
|
||||
// Volume scroll on left half
|
||||
} else if (y < screen.height / 2 && Config.bar.scrollActions.volume) {
|
||||
// Volume scroll on top half
|
||||
if (angleDelta.y > 0)
|
||||
Audio.incrementVolume();
|
||||
else if (angleDelta.y < 0)
|
||||
Audio.decrementVolume();
|
||||
} else if (Config.bar.scrollActions.brightness) {
|
||||
// Brightness scroll on right half
|
||||
// Brightness scroll on bottom half
|
||||
const monitor = Brightness.getMonitorForScreen(screen);
|
||||
if (angleDelta.y > 0)
|
||||
monitor.setBrightness(monitor.brightness + GlobalConfig.services.brightnessIncrement);
|
||||
|
|
@ -119,7 +115,7 @@ RowLayout {
|
|||
DelegateChoice {
|
||||
roleValue: "spacer"
|
||||
delegate: WrappedLoader {
|
||||
Layout.fillWidth: enabled
|
||||
Layout.fillHeight: enabled
|
||||
}
|
||||
}
|
||||
DelegateChoice {
|
||||
|
|
@ -205,11 +201,11 @@ RowLayout {
|
|||
}
|
||||
|
||||
asynchronous: true
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
// Cursed ahh thing to add padding to first and last enabled components
|
||||
Layout.leftMargin: findFirstEnabled() === this ? root.hPadding : 0
|
||||
Layout.rightMargin: findLastEnabled() === this ? root.hPadding : 0
|
||||
Layout.topMargin: findFirstEnabled() === this ? root.vPadding : 0
|
||||
Layout.bottomMargin: findLastEnabled() === this ? root.vPadding : 0
|
||||
|
||||
visible: enabled
|
||||
active: enabled
|
||||
|
|
|
|||
|
|
@ -17,40 +17,35 @@ Item {
|
|||
|
||||
readonly property bool disabled: Strings.testRegexList(Config.bar.excludedScreens, screen.name)
|
||||
|
||||
// NoSignal horizontal top bar: the bar's "thickness" is its HEIGHT (was width
|
||||
// for the upstream vertical left-edge bar). clampedHeight/contentHeight and
|
||||
// implicitHeight carry the thickness; the wrapper fills the screen width.
|
||||
readonly property int clampedHeight: Math.max(Config.border.minThickness, implicitHeight)
|
||||
readonly property int clampedWidth: Math.max(Config.border.minThickness, implicitWidth)
|
||||
readonly property int padding: Math.max(Tokens.padding.small, Config.border.thickness)
|
||||
readonly property int contentHeight: Tokens.sizes.bar.innerWidth + padding * 2
|
||||
readonly property int exclusiveZone: !disabled && (Config.bar.persistent || visibilities.bar) ? contentHeight : Config.border.thickness
|
||||
// NoSignal redesign: the old caelestia bar is replaced by the slim NsBar
|
||||
// PanelWindow. Collapse it (the drawer engine is kept for launcher/OSD/lock).
|
||||
readonly property bool shouldBeVisible: false
|
||||
readonly property int contentWidth: Tokens.sizes.bar.innerWidth + padding * 2
|
||||
readonly property int exclusiveZone: !disabled && (Config.bar.persistent || visibilities.bar) ? contentWidth : Config.border.thickness
|
||||
readonly property bool shouldBeVisible: !fullscreen && !disabled && (Config.bar.persistent || visibilities.bar || isHovered)
|
||||
property bool isHovered
|
||||
|
||||
function closeTray(): void {
|
||||
(content.item as Bar)?.closeTray();
|
||||
}
|
||||
|
||||
function checkPopout(x: real): void {
|
||||
(content.item as Bar)?.checkPopout(x);
|
||||
function checkPopout(y: real): void {
|
||||
(content.item as Bar)?.checkPopout(y);
|
||||
}
|
||||
|
||||
function handleWheel(x: real, angleDelta: point): void {
|
||||
(content.item as Bar)?.handleWheel(x, angleDelta);
|
||||
function handleWheel(y: real, angleDelta: point): void {
|
||||
(content.item as Bar)?.handleWheel(y, angleDelta);
|
||||
}
|
||||
|
||||
clip: true
|
||||
visible: height > Config.border.thickness
|
||||
implicitHeight: fullscreen ? 0 : Config.border.thickness
|
||||
visible: width > Config.border.thickness
|
||||
implicitWidth: fullscreen ? 0 : Config.border.thickness
|
||||
|
||||
states: State {
|
||||
name: "visible"
|
||||
when: root.shouldBeVisible
|
||||
|
||||
PropertyChanges {
|
||||
root.implicitHeight: root.contentHeight
|
||||
root.implicitWidth: root.contentWidth
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -61,7 +56,7 @@ Item {
|
|||
|
||||
Anim {
|
||||
target: root
|
||||
property: "implicitHeight"
|
||||
property: "implicitWidth"
|
||||
}
|
||||
},
|
||||
Transition {
|
||||
|
|
@ -70,7 +65,7 @@ Item {
|
|||
|
||||
Anim {
|
||||
target: root
|
||||
property: "implicitHeight"
|
||||
property: "implicitWidth"
|
||||
type: Anim.Emphasized
|
||||
}
|
||||
}
|
||||
|
|
@ -79,14 +74,14 @@ Item {
|
|||
Loader {
|
||||
id: content
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
|
||||
active: root.shouldBeVisible
|
||||
|
||||
sourceComponent: Bar {
|
||||
height: root.contentHeight
|
||||
width: root.contentWidth
|
||||
screen: root.screen
|
||||
visibilities: root.visibilities
|
||||
popouts: root.popouts // qmllint disable incompatible-type
|
||||
|
|
|
|||
|
|
@ -1,34 +1,106 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
|
||||
// NoSignal redesign: center clock module — date (muted) + time (DemiBold),
|
||||
// JetBrains Mono. The hover/active pill is applied by the bar module wrapper;
|
||||
// this is just the content. Opens the Calendar popout.
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Caelestia.Config
|
||||
import qs.components
|
||||
import qs.services
|
||||
|
||||
RowLayout {
|
||||
StyledRect {
|
||||
id: root
|
||||
|
||||
spacing: 8
|
||||
readonly property color colour: Colours.palette.m3tertiary
|
||||
readonly property int padding: Config.bar.clock.background ? Tokens.padding.medium : Tokens.padding.extraSmall
|
||||
readonly property var font: Tokens.font.body.builders.small.scale(1.1)
|
||||
|
||||
StyledText {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
text: Time.format("ddd d MMM")
|
||||
color: Theme.textMuted
|
||||
font.family: Theme.font.family
|
||||
font.pixelSize: Theme.font.bar
|
||||
font.weight: Font.Normal
|
||||
}
|
||||
implicitWidth: Tokens.sizes.bar.innerWidth
|
||||
implicitHeight: layout.implicitHeight + root.padding * 2
|
||||
|
||||
StyledText {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
text: Time.format("hh:mm")
|
||||
color: Theme.text
|
||||
font.family: Theme.font.family
|
||||
font.pixelSize: Theme.font.bar
|
||||
font.weight: Font.DemiBold
|
||||
color: Qt.alpha(Colours.tPalette.m3surfaceContainer, Config.bar.clock.background ? Colours.tPalette.m3surfaceContainer.a : 0)
|
||||
radius: Tokens.rounding.full
|
||||
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
|
||||
anchors.centerIn: parent
|
||||
spacing: Tokens.spacing.extraSmall
|
||||
|
||||
Loader {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
asynchronous: true
|
||||
active: Config.bar.clock.showIcon
|
||||
visible: active
|
||||
|
||||
sourceComponent: MaterialIcon {
|
||||
text: "calendar_month"
|
||||
color: root.colour
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
visible: Config.bar.clock.showDate
|
||||
|
||||
horizontalAlignment: StyledText.AlignHCenter
|
||||
text: Time.format("ddd\nd")
|
||||
font: Tokens.font.body.small
|
||||
color: root.colour
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
visible: Config.bar.clock.showDate
|
||||
implicitHeight: 1
|
||||
color: Colours.palette.m3outlineVariant
|
||||
}
|
||||
|
||||
StyledText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: Time.hourStr
|
||||
font: {
|
||||
const scale = text === "11" ? 1.15 : Math.min(1.05, Math.max(hourMetrics.width, minMetrics.width) / hourMetrics.width);
|
||||
return root.font.width(scale * 100).letterSpacing(scale).build();
|
||||
}
|
||||
color: root.colour
|
||||
|
||||
TextMetrics {
|
||||
id: hourMetrics
|
||||
|
||||
font: root.font.build()
|
||||
text: Time.hourStr
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
Layout.topMargin: -parent.spacing - 4
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: Time.minuteStr
|
||||
font: {
|
||||
const scale = text === "11" ? 1.15 : Math.min(1.05, Math.max(hourMetrics.width, minMetrics.width) / minMetrics.width);
|
||||
return root.font.width(scale * 100).letterSpacing(scale).build();
|
||||
}
|
||||
color: root.colour
|
||||
|
||||
TextMetrics {
|
||||
id: minMetrics
|
||||
|
||||
font: root.font.build()
|
||||
text: Time.minuteStr
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
Layout.topMargin: -parent.spacing - 4
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
asynchronous: true
|
||||
active: GlobalConfig.services.useTwelveHourClock
|
||||
visible: active
|
||||
|
||||
sourceComponent: StyledText {
|
||||
text: Time.amPmStr.toLowerCase()
|
||||
font: Tokens.font.body.builders.small.scale(0.9).build()
|
||||
color: root.colour
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,13 +20,16 @@ StyledRect {
|
|||
radius: Tokens.rounding.full
|
||||
|
||||
clip: true
|
||||
implicitHeight: Tokens.sizes.bar.innerWidth
|
||||
implicitWidth: iconColumn.implicitWidth + Tokens.padding.medium * 2 - (Config.bar.status.showLockStatus && !Hypr.capsLock && !Hypr.numLock ? iconColumn.spacing : 0)
|
||||
implicitWidth: Tokens.sizes.bar.innerWidth
|
||||
implicitHeight: iconColumn.implicitHeight + Tokens.padding.medium * 2 - (Config.bar.status.showLockStatus && !Hypr.capsLock && !Hypr.numLock ? iconColumn.spacing : 0)
|
||||
|
||||
RowLayout {
|
||||
ColumnLayout {
|
||||
id: iconColumn
|
||||
|
||||
anchors.centerIn: parent
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: Tokens.padding.medium
|
||||
|
||||
spacing: Tokens.spacing.medium / 2
|
||||
|
||||
|
|
@ -258,7 +261,7 @@ StyledRect {
|
|||
required property string name
|
||||
|
||||
asynchronous: true
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
visible: active
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,27 +19,27 @@ StyledRect {
|
|||
|
||||
property bool expanded
|
||||
|
||||
readonly property real nonAnimWidth: {
|
||||
readonly property real nonAnimHeight: {
|
||||
if (!Config.bar.tray.compact)
|
||||
return layout.implicitWidth + padding * 2;
|
||||
return (expanded ? expandIcon.implicitWidth + layout.implicitWidth + spacing : expandIcon.implicitWidth) + padding * 2;
|
||||
return layout.implicitHeight + padding * 2;
|
||||
return (expanded ? expandIcon.implicitHeight + layout.implicitHeight + spacing : expandIcon.implicitHeight) + padding * 2;
|
||||
}
|
||||
|
||||
clip: true
|
||||
visible: width > 0
|
||||
visible: height > 0
|
||||
|
||||
implicitHeight: Tokens.sizes.bar.innerWidth
|
||||
implicitWidth: nonAnimWidth
|
||||
implicitWidth: Tokens.sizes.bar.innerWidth
|
||||
implicitHeight: nonAnimHeight
|
||||
|
||||
color: Qt.alpha(Colours.tPalette.m3surfaceContainer, (Config.bar.tray.background && items.count > 0) ? Colours.tPalette.m3surfaceContainer.a : 0)
|
||||
radius: Tokens.rounding.full
|
||||
|
||||
Row {
|
||||
Column {
|
||||
id: layout
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: root.padding
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: root.padding
|
||||
spacing: Tokens.spacing.small
|
||||
|
||||
opacity: root.expanded || !Config.bar.tray.compact ? 1 : 0
|
||||
|
|
@ -86,37 +86,37 @@ StyledRect {
|
|||
|
||||
asynchronous: true
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: parent.right
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
active: Config.bar.tray.compact && items.count > 0
|
||||
|
||||
sourceComponent: Item {
|
||||
implicitHeight: expandIconInner.implicitHeight
|
||||
implicitWidth: expandIconInner.implicitWidth - Tokens.padding.small
|
||||
implicitWidth: expandIconInner.implicitWidth
|
||||
implicitHeight: expandIconInner.implicitHeight - Tokens.padding.small
|
||||
|
||||
MaterialIcon {
|
||||
id: expandIconInner
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Config.bar.tray.background ? Tokens.padding.extraSmall : -Tokens.padding.extraSmall
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: Config.bar.tray.background ? Tokens.padding.extraSmall : -Tokens.padding.extraSmall
|
||||
text: "expand_less"
|
||||
fontStyle: Tokens.font.icon.large
|
||||
rotation: root.expanded ? 270 : 90
|
||||
rotation: root.expanded ? 180 : 0
|
||||
|
||||
Behavior on rotation {
|
||||
Anim {}
|
||||
}
|
||||
|
||||
Behavior on anchors.rightMargin {
|
||||
Behavior on anchors.bottomMargin {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on implicitWidth {
|
||||
Behavior on implicitHeight {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ ColumnLayout {
|
|||
readonly property bool isOccupied: occupied[ws] ?? false
|
||||
readonly property bool hasWindows: isOccupied && Config.bar.workspaces.showWindows
|
||||
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.preferredHeight: size
|
||||
|
||||
spacing: 0
|
||||
|
|
@ -52,7 +52,7 @@ ColumnLayout {
|
|||
}
|
||||
color: Config.bar.workspaces.occupiedBg || root.isOccupied || root.activeWsId === root.ws ? Colours.palette.m3onSurface : Colours.layer(Colours.palette.m3outlineVariant, 2)
|
||||
verticalAlignment: Qt.AlignVCenter
|
||||
font.family: "Rubik" // Hard code rubik for now since google sans doesn't play well with certain unicode symbols apparently
|
||||
font.family: Tokens.font.workspaces
|
||||
}
|
||||
|
||||
Loader {
|
||||
|
|
|
|||
|
|
@ -27,8 +27,8 @@ StyledClippingRect {
|
|||
|
||||
property real blur: onSpecial ? 1 : 0
|
||||
|
||||
implicitHeight: Tokens.sizes.bar.innerWidth
|
||||
implicitWidth: layout.implicitWidth + Tokens.padding.small
|
||||
implicitWidth: Tokens.sizes.bar.innerWidth
|
||||
implicitHeight: layout.implicitHeight + Tokens.padding.small
|
||||
|
||||
color: Colours.tPalette.m3surfaceContainer
|
||||
radius: Tokens.rounding.full
|
||||
|
|
@ -60,7 +60,7 @@ StyledClippingRect {
|
|||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
|
||||
anchors.centerIn: parent
|
||||
|
|
@ -81,7 +81,7 @@ StyledClippingRect {
|
|||
|
||||
Loader {
|
||||
asynchronous: true
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
active: Config.bar.workspaces.activeIndicator
|
||||
|
||||
sourceComponent: ActiveIndicator {
|
||||
|
|
|
|||
|
|
@ -12,24 +12,21 @@ Item {
|
|||
required property real borderThickness
|
||||
|
||||
readonly property alias content: content
|
||||
property real offsetScale: y > 0 || content.hasCurrent ? 0 : 1
|
||||
property real offsetScale: x > 0 || content.hasCurrent ? 0 : 1
|
||||
|
||||
visible: width > 0 && height > 0
|
||||
clip: true
|
||||
|
||||
// NoSignal top bar: popouts drop DOWN from the bar. The collapsing axis is
|
||||
// now height (was width for the vertical left bar); position along the bar
|
||||
// is x (from the hovered widget's x-centre), the docked edge is the top.
|
||||
implicitWidth: content.implicitWidth
|
||||
implicitHeight: content.implicitHeight * (1 - offsetScale)
|
||||
implicitWidth: content.implicitWidth * (1 - offsetScale)
|
||||
implicitHeight: content.implicitHeight
|
||||
|
||||
y: content.isDetached ? (parent.height - content.nonAnimHeight) / 2 : 0
|
||||
x: {
|
||||
x: content.isDetached ? (parent.width - content.nonAnimWidth) / 2 : 0
|
||||
y: {
|
||||
if (content.isDetached)
|
||||
return (parent.width - content.nonAnimWidth) / 2;
|
||||
return (parent.height - content.nonAnimHeight) / 2;
|
||||
|
||||
const off = content.currentCenter - borderThickness - content.nonAnimWidth / 2;
|
||||
const diff = parent.width - Math.floor(off + content.nonAnimWidth);
|
||||
const off = content.currentCenter - borderThickness - content.nonAnimHeight / 2;
|
||||
const diff = parent.height - Math.floor(off + content.nonAnimHeight);
|
||||
if (diff < 0)
|
||||
return off + diff;
|
||||
return Math.max(off, 0);
|
||||
|
|
@ -39,14 +36,14 @@ Item {
|
|||
Anim {}
|
||||
}
|
||||
|
||||
Behavior on y {
|
||||
Behavior on x {
|
||||
Anim {
|
||||
duration: content.animLength
|
||||
easing: content.animCurve
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on x {
|
||||
Behavior on y {
|
||||
enabled: root.offsetScale < 1
|
||||
|
||||
Anim {
|
||||
|
|
@ -61,8 +58,8 @@ Item {
|
|||
screen: root.screen
|
||||
offsetScale: root.offsetScale
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: (-implicitHeight - 5) * root.offsetScale
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: (-implicitWidth - 5) * root.offsetScale
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -149,14 +149,14 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
Behavior on implicitHeight {
|
||||
Behavior on implicitWidth {
|
||||
Anim {
|
||||
duration: root.animLength
|
||||
easing: root.animCurve
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on implicitWidth {
|
||||
Behavior on implicitHeight {
|
||||
enabled: root.offsetScale < 1
|
||||
|
||||
Anim {
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ Item {
|
|||
anchors.bottom: parent.bottom
|
||||
anchors.margins: Tokens.padding.large
|
||||
|
||||
radius: 0 // nosignal: square dashboard cards to match the redesign (F-D1)
|
||||
radius: Tokens.rounding.large
|
||||
color: "transparent"
|
||||
|
||||
Flickable {
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ GridLayout {
|
|||
Layout.preferredWidth: Tokens.sizes.dashboard.userWidth
|
||||
Layout.fillHeight: true
|
||||
|
||||
radius: 0 // nosignal: square dashboard cards to match the redesign (F-D1)
|
||||
radius: Tokens.rounding.extraLarge
|
||||
|
||||
User {
|
||||
id: user
|
||||
|
|
@ -37,7 +37,7 @@ GridLayout {
|
|||
Layout.preferredWidth: Tokens.sizes.dashboard.weatherWidth
|
||||
Layout.preferredHeight: weather.implicitHeight
|
||||
|
||||
radius: 0 // nosignal: square dashboard cards to match the redesign (F-D1)
|
||||
radius: Tokens.rounding.extraLarge * 1.5
|
||||
|
||||
SmallWeather {
|
||||
id: weather
|
||||
|
|
@ -49,7 +49,7 @@ GridLayout {
|
|||
Layout.preferredWidth: dateTime.implicitWidth
|
||||
Layout.fillHeight: true
|
||||
|
||||
radius: 0 // nosignal: square dashboard cards to match the redesign (F-D1)
|
||||
radius: Tokens.rounding.large
|
||||
|
||||
DateTime {
|
||||
id: dateTime
|
||||
|
|
@ -63,7 +63,7 @@ GridLayout {
|
|||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: calendar.implicitHeight
|
||||
|
||||
radius: 0 // nosignal: square dashboard cards to match the redesign (F-D1)
|
||||
radius: Tokens.rounding.extraLarge
|
||||
|
||||
Calendar {
|
||||
id: calendar
|
||||
|
|
@ -78,7 +78,7 @@ GridLayout {
|
|||
Layout.preferredWidth: resources.implicitWidth
|
||||
Layout.fillHeight: true
|
||||
|
||||
radius: 0 // nosignal: square dashboard cards to match the redesign (F-D1)
|
||||
radius: Tokens.rounding.large
|
||||
|
||||
Resources {
|
||||
id: resources
|
||||
|
|
@ -92,7 +92,7 @@ GridLayout {
|
|||
Layout.preferredWidth: media.implicitWidth
|
||||
Layout.fillHeight: true
|
||||
|
||||
radius: 0 // nosignal: square dashboard cards to match the redesign (F-D1)
|
||||
radius: Tokens.rounding.extraLarge * 2
|
||||
|
||||
Media {
|
||||
id: media
|
||||
|
|
|
|||
|
|
@ -102,11 +102,11 @@ Item {
|
|||
CAnim {}
|
||||
}
|
||||
|
||||
NsIcon {
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
|
||||
anchors.centerIn: parent
|
||||
icon: "queue_music"
|
||||
text: "queue_music"
|
||||
fontStyle: Tokens.font.icon.builders.large.scale(2).build()
|
||||
color: Colours.palette.m3onPrimaryContainer
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,9 +23,9 @@ Item {
|
|||
sourceComponent: ColumnLayout {
|
||||
spacing: Tokens.spacing.medium
|
||||
|
||||
NsIcon {
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
icon: "tune"
|
||||
text: "tune"
|
||||
fontStyle: Tokens.font.icon.builders.extraLarge.scale(2).build()
|
||||
color: Colours.palette.m3onSurfaceVariant
|
||||
}
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ Item {
|
|||
implicitHeight: parent.implicitHeight * 2
|
||||
|
||||
color: Colours.palette.m3primary
|
||||
radius: 0 // nosignal: square dashboard cards to match the redesign (F-D1)
|
||||
radius: Tokens.rounding.full
|
||||
}
|
||||
|
||||
Behavior on x {
|
||||
|
|
@ -148,18 +148,18 @@ Item {
|
|||
anchors.verticalCenter: parent.verticalCenter
|
||||
implicitHeight: parent.height + Tokens.sizes.dashboard.tabIndicatorSpacing * 2
|
||||
|
||||
radius: 0 // nosignal: square dashboard cards to match the redesign (F-D1)
|
||||
radius: Tokens.rounding.medium
|
||||
color: tab.current ? Colours.palette.m3primary : Colours.palette.m3onSurface
|
||||
onClicked: root.dashState.currentTab = tab.TabBar.index
|
||||
}
|
||||
|
||||
NsIcon {
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: label.top
|
||||
|
||||
icon: tab.iconName
|
||||
text: tab.iconName
|
||||
color: tab.current ? Colours.palette.m3primary : Colours.palette.m3onSurfaceVariant
|
||||
fill: tab.current ? 1 : 0
|
||||
fontStyle: Tokens.font.icon.medium
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ Item {
|
|||
Layout.fillWidth: true
|
||||
implicitHeight: bigInfoRow.implicitHeight + Tokens.padding.small
|
||||
|
||||
radius: 0 // nosignal: square dashboard cards to match the redesign (F-D1)
|
||||
radius: Tokens.rounding.extraLarge * 2
|
||||
color: Colours.tPalette.m3surfaceContainer
|
||||
|
||||
RowLayout {
|
||||
|
|
@ -76,9 +76,9 @@ Item {
|
|||
anchors.centerIn: parent
|
||||
spacing: Tokens.spacing.largeIncreased
|
||||
|
||||
NsIcon {
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
icon: Weather.icon
|
||||
text: Weather.icon
|
||||
fontStyle: Tokens.font.icon.builders.extraLarge.scale(3).build()
|
||||
color: Colours.palette.m3secondary
|
||||
animate: true
|
||||
|
|
@ -155,7 +155,7 @@ Item {
|
|||
Layout.fillWidth: true
|
||||
implicitHeight: forecastItemColumn.implicitHeight + Tokens.padding.medium * 2
|
||||
|
||||
radius: 0 // nosignal: square dashboard cards to match the redesign (F-D1)
|
||||
radius: Tokens.rounding.large
|
||||
color: Colours.tPalette.m3surfaceContainer
|
||||
|
||||
ColumnLayout {
|
||||
|
|
@ -180,16 +180,20 @@ Item {
|
|||
color: Colours.palette.m3onSurfaceVariant
|
||||
}
|
||||
|
||||
NsIcon {
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
icon: forecastItem.modelData.icon
|
||||
text: forecastItem.modelData.icon
|
||||
fontStyle: Tokens.font.icon.extraLarge
|
||||
color: Colours.palette.m3secondary
|
||||
}
|
||||
|
||||
StyledText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: GlobalConfig.services.useFahrenheit ? forecastItem.modelData.maxTempF + "°" + " / " + forecastItem.modelData.minTempF + "°" : forecastItem.modelData.maxTempC + "°" + " / " + forecastItem.modelData.minTempC + "°"
|
||||
text: {
|
||||
const min = Weather.formatTemp(forecastItem.modelData.minTempC).slice(0, -1);
|
||||
const max = Weather.formatTemp(forecastItem.modelData.maxTempC).slice(0, -1);
|
||||
return `${min} / ${max}`;
|
||||
}
|
||||
font: Tokens.font.body.builders.small.weight(Font.DemiBold).build()
|
||||
color: Colours.palette.m3tertiary
|
||||
}
|
||||
|
|
@ -209,15 +213,15 @@ Item {
|
|||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 60
|
||||
radius: 0 // nosignal: square dashboard cards to match the redesign (F-D1)
|
||||
radius: Tokens.rounding.medium
|
||||
color: Colours.tPalette.m3surfaceContainer
|
||||
|
||||
Row {
|
||||
anchors.centerIn: parent
|
||||
spacing: Tokens.spacing.medium
|
||||
|
||||
NsIcon {
|
||||
icon: detailRoot.icon
|
||||
MaterialIcon {
|
||||
text: detailRoot.icon
|
||||
color: detailRoot.colour
|
||||
fontStyle: Tokens.font.icon.large
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
|
@ -252,8 +256,8 @@ Item {
|
|||
|
||||
spacing: Tokens.spacing.small
|
||||
|
||||
NsIcon {
|
||||
icon: weatherStat.icon
|
||||
MaterialIcon {
|
||||
text: weatherStat.icon
|
||||
fontStyle: Tokens.font.icon.extraLarge
|
||||
color: weatherStat.colour
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ CustomMouseArea {
|
|||
|
||||
StateLayer {
|
||||
color: Colours.palette.m3primary
|
||||
radius: 0 // nosignal: square dashboard cards to match the redesign (F-D1)
|
||||
radius: pressed ? Tokens.rounding.small : Tokens.rounding.large
|
||||
disabled: {
|
||||
const now = new Date();
|
||||
return root.currMonth === now.getMonth() && root.currYear === now.getFullYear();
|
||||
|
|
|
|||
|
|
@ -65,10 +65,10 @@ Item {
|
|||
Anim {}
|
||||
}
|
||||
|
||||
NsIcon {
|
||||
MaterialIcon {
|
||||
anchors.centerIn: parent
|
||||
icon: res.icon
|
||||
fontStyle: Tokens.font.icon.large
|
||||
text: res.icon
|
||||
font: Tokens.font.icon.large
|
||||
color: res.fgColour
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,14 +13,14 @@ Item {
|
|||
|
||||
Component.onCompleted: Weather.reload()
|
||||
|
||||
NsIcon {
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
|
||||
animate: true
|
||||
icon: Weather.icon
|
||||
text: Weather.icon
|
||||
color: Colours.palette.m3secondary
|
||||
fontStyle: Tokens.font.icon.builders.extraLarge.scale(1.6).build()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,8 +75,8 @@ Item {
|
|||
asynchronous: true
|
||||
active: pfp.status !== Image.Ready
|
||||
|
||||
sourceComponent: NsIcon {
|
||||
icon: "person_add"
|
||||
sourceComponent: MaterialIcon {
|
||||
text: "person_add"
|
||||
color: Colours.palette.m3onSurfaceVariant
|
||||
fontStyle: Tokens.font.icon.extraLarge
|
||||
fill: 1
|
||||
|
|
@ -120,9 +120,9 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
NsIcon {
|
||||
MaterialIcon {
|
||||
anchors.centerIn: parent
|
||||
icon: "person_edit"
|
||||
text: "person_edit"
|
||||
color: Colours.palette.m3onPrimary
|
||||
fontStyle: Tokens.font.icon.large
|
||||
}
|
||||
|
|
@ -187,9 +187,9 @@ Item {
|
|||
CAnim {}
|
||||
}
|
||||
|
||||
NsIcon {
|
||||
MaterialIcon {
|
||||
anchors.centerIn: parent
|
||||
icon: "clock_arrow_up"
|
||||
text: "clock_arrow_up"
|
||||
color: Colours.palette.m3onTertiaryContainer
|
||||
fontStyle: Tokens.font.icon.medium
|
||||
}
|
||||
|
|
@ -216,7 +216,7 @@ Item {
|
|||
|
||||
implicitWidth: 10
|
||||
implicitHeight: 10
|
||||
radius: 0 // nosignal: square dashboard cards to match the redesign (F-D1)
|
||||
radius: Tokens.rounding.full
|
||||
color: Colours.palette.m3secondaryContainer
|
||||
}
|
||||
|
||||
|
|
@ -229,7 +229,7 @@ Item {
|
|||
|
||||
implicitWidth: 15
|
||||
implicitHeight: 15
|
||||
radius: 0 // nosignal: square dashboard cards to match the redesign (F-D1)
|
||||
radius: Tokens.rounding.full
|
||||
color: Colours.palette.m3secondaryContainer
|
||||
}
|
||||
|
||||
|
|
@ -240,7 +240,7 @@ Item {
|
|||
anchors.leftMargin: -Tokens.padding.medium
|
||||
y: Tokens.padding.extraSmall
|
||||
|
||||
radius: 0 // nosignal: square dashboard cards to match the redesign (F-D1)
|
||||
radius: Tokens.rounding.largeIncreased
|
||||
color: Colours.palette.m3secondaryContainer
|
||||
implicitWidth: wmLabel.implicitWidth + Tokens.padding.medium * 2
|
||||
implicitHeight: wmLabel.implicitHeight + Tokens.padding.small * 2
|
||||
|
|
@ -251,11 +251,11 @@ Item {
|
|||
anchors.centerIn: parent
|
||||
spacing: Tokens.spacing.extraSmall
|
||||
|
||||
NsIcon {
|
||||
MaterialIcon {
|
||||
id: wmIcon
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
icon: "select_window"
|
||||
text: "select_window"
|
||||
color: Colours.palette.m3onSecondaryContainer
|
||||
fontStyle: wmText.font
|
||||
}
|
||||
|
|
|
|||
|
|
@ -176,7 +176,7 @@ Item {
|
|||
implicitWidth: shape.implicitSize + Tokens.padding.medium * 2
|
||||
implicitHeight: shape.implicitSize + Tokens.padding.medium * 2
|
||||
color: Colours.palette.m3primaryContainer
|
||||
radius: 0 // nosignal: square dashboard cards to match the redesign (F-D1)
|
||||
radius: Tokens.rounding.full
|
||||
|
||||
LoadingIndicator {
|
||||
id: shape
|
||||
|
|
@ -212,9 +212,9 @@ Item {
|
|||
sourceComponent: ColumnLayout {
|
||||
spacing: Tokens.spacing.small
|
||||
|
||||
NsIcon {
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
icon: "sentiment_sad"
|
||||
text: "sentiment_sad"
|
||||
fontStyle: Tokens.font.icon.builders.large.scale(2).build()
|
||||
color: Colours.palette.m3outline
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,9 +20,9 @@ Item {
|
|||
spacing: Tokens.spacing.medium
|
||||
z: 1
|
||||
|
||||
NsIcon {
|
||||
MaterialIcon {
|
||||
Layout.topMargin: Math.round(fontInfo.pointSize * 0.12)
|
||||
icon: "lyrics"
|
||||
text: "lyrics"
|
||||
fontStyle: Tokens.font.icon.medium
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ Item {
|
|||
anchors.fill: parent
|
||||
anchors.margins: !btn.pressed && btn.containsMouse ? -Tokens.padding.extraSmall : 0
|
||||
group: blobGroup
|
||||
radius: 0 // nosignal: square dashboard cards to match the redesign (F-D1)
|
||||
radius: Tokens.rounding.medium
|
||||
|
||||
Behavior on anchors.margins {
|
||||
Anim {}
|
||||
|
|
@ -50,7 +50,7 @@ Item {
|
|||
implicitHeight: parent.height
|
||||
|
||||
group: blobGroup
|
||||
radius: 0 // nosignal: square dashboard cards to match the redesign (F-D1)
|
||||
radius: Tokens.rounding.medium
|
||||
deformScale: 0.00001
|
||||
|
||||
states: State {
|
||||
|
|
@ -197,11 +197,11 @@ Item {
|
|||
hoverEnabled: true
|
||||
onClicked: root.open = !root.open
|
||||
|
||||
NsIcon {
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
|
||||
anchors.centerIn: parent
|
||||
icon: "more_vert"
|
||||
text: "more_vert"
|
||||
fontStyle: Tokens.font.icon.medium
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ StyledClippingRect {
|
|||
property real animPerc: UPower.displayDevice.percentage
|
||||
|
||||
color: Colours.palette.m3secondaryContainer
|
||||
radius: 0 // nosignal: square dashboard cards to match the redesign (F-D1)
|
||||
radius: Tokens.rounding.large
|
||||
|
||||
implicitWidth: Config.dashboard.performance.showCpu || (Config.dashboard.performance.showGpu && Gpu.type !== Gpu.None) || Config.dashboard.performance.showStorage || Config.dashboard.performance.showMemory ? Tokens.sizes.dashboard.perfBattWidth : Tokens.sizes.dashboard.perfBattWidthSingle
|
||||
implicitHeight: Tokens.sizes.dashboard.perfBattHeight
|
||||
|
|
@ -39,7 +39,7 @@ StyledClippingRect {
|
|||
implicitHeight: parent.height * root.animPerc
|
||||
|
||||
color: Colours.palette.m3secondary
|
||||
radius: 0 // nosignal: square dashboard cards to match the redesign (F-D1)
|
||||
radius: Tokens.rounding.extraSmall
|
||||
clip: true
|
||||
|
||||
Contents {
|
||||
|
|
@ -65,9 +65,9 @@ StyledClippingRect {
|
|||
|
||||
spacing: 0
|
||||
|
||||
NsIcon {
|
||||
MaterialIcon {
|
||||
Layout.leftMargin: -Tokens.padding.extraSmall
|
||||
icon: "battery_full"
|
||||
text: "battery_full"
|
||||
color: contents.accentColour
|
||||
fontStyle: Tokens.font.icon.large
|
||||
}
|
||||
|
|
@ -115,8 +115,8 @@ StyledClippingRect {
|
|||
Layout.alignment: Qt.AlignRight
|
||||
spacing: Tokens.spacing.extraSmall
|
||||
|
||||
NsIcon {
|
||||
icon: "bolt"
|
||||
MaterialIcon {
|
||||
text: "bolt"
|
||||
color: contents.accentColour
|
||||
fontStyle: Tokens.font.icon.large
|
||||
fill: 1
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ StyledRect {
|
|||
required property real temperature
|
||||
|
||||
color: Colours.tPalette.m3surfaceContainer
|
||||
radius: 0 // nosignal: square dashboard cards to match the redesign (F-D1)
|
||||
radius: Tokens.rounding.extraLarge
|
||||
|
||||
implicitWidth: Tokens.sizes.dashboard.perfHeroCardWidth
|
||||
implicitHeight: Math.max(tempProg.implicitHeight + detailsRow.implicitHeight + Tokens.spacing.large, usageShape.implicitHeight + usageLabel.implicitHeight) + Tokens.padding.large * 2
|
||||
|
|
@ -40,11 +40,11 @@ StyledRect {
|
|||
Anim {}
|
||||
}
|
||||
|
||||
NsIcon {
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
|
||||
anchors.centerIn: parent
|
||||
icon: root.icon
|
||||
text: root.icon
|
||||
color: root.accent
|
||||
fontStyle: Tokens.font.icon.medium
|
||||
}
|
||||
|
|
@ -84,9 +84,9 @@ StyledRect {
|
|||
Layout.leftMargin: -Tokens.padding.extraSmall
|
||||
spacing: Tokens.spacing.extraSmall
|
||||
|
||||
NsIcon {
|
||||
MaterialIcon {
|
||||
Layout.topMargin: Math.round(fontInfo.pointSize * 0.08)
|
||||
icon: root.temperature > 90 ? "thermometer_alert" : "thermometer"
|
||||
text: root.temperature > 90 ? "thermometer_alert" : "thermometer"
|
||||
color: root.temperature > 90 ? Colours.palette.m3error : root.accent
|
||||
fontStyle: Tokens.font.icon.medium
|
||||
fill: 1
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ StyledRect {
|
|||
readonly property color accent: Colours.palette.m3tertiary
|
||||
|
||||
color: Colours.tPalette.m3surfaceContainer
|
||||
radius: 0 // nosignal: square dashboard cards to match the redesign (F-D1)
|
||||
radius: Tokens.rounding.medium
|
||||
|
||||
implicitWidth: layout.implicitWidth + Tokens.padding.extraLargeIncreased * 2
|
||||
implicitHeight: layout.implicitHeight + Tokens.padding.large * 2
|
||||
|
|
@ -31,8 +31,8 @@ StyledRect {
|
|||
Layout.leftMargin: -Tokens.padding.extraSmall
|
||||
spacing: Tokens.spacing.small
|
||||
|
||||
NsIcon {
|
||||
icon: "memory_alt"
|
||||
MaterialIcon {
|
||||
text: "memory_alt"
|
||||
fill: 1
|
||||
color: root.accent
|
||||
fontStyle: Tokens.font.icon.builders.medium.weight(Font.DemiBold).build() // DemiBold to fix fill issues
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ StyledRect {
|
|||
id: root
|
||||
|
||||
color: Colours.tPalette.m3surfaceContainer
|
||||
radius: 0 // nosignal: square dashboard cards to match the redesign (F-D1)
|
||||
radius: Tokens.rounding.extraLarge
|
||||
|
||||
implicitWidth: Tokens.sizes.dashboard.perfNetworkCardWidth
|
||||
implicitHeight: Tokens.sizes.dashboard.perfNetworkCardHeight
|
||||
|
|
@ -30,8 +30,8 @@ StyledRect {
|
|||
RowLayout {
|
||||
spacing: Tokens.spacing.small
|
||||
|
||||
NsIcon {
|
||||
icon: "swap_vert"
|
||||
MaterialIcon {
|
||||
text: "swap_vert"
|
||||
color: Colours.palette.m3primary
|
||||
fontStyle: Tokens.font.icon.medium
|
||||
}
|
||||
|
|
@ -105,8 +105,8 @@ StyledRect {
|
|||
Layout.fillWidth: true
|
||||
spacing: Tokens.spacing.small
|
||||
|
||||
NsIcon {
|
||||
icon: "download"
|
||||
MaterialIcon {
|
||||
text: "download"
|
||||
color: Colours.palette.m3tertiary
|
||||
fontStyle: Tokens.font.icon.medium
|
||||
}
|
||||
|
|
@ -136,8 +136,8 @@ StyledRect {
|
|||
Layout.fillWidth: true
|
||||
spacing: Tokens.spacing.small
|
||||
|
||||
NsIcon {
|
||||
icon: "upload"
|
||||
MaterialIcon {
|
||||
text: "upload"
|
||||
color: Colours.palette.m3secondary
|
||||
fontStyle: Tokens.font.icon.medium
|
||||
}
|
||||
|
|
@ -167,8 +167,8 @@ StyledRect {
|
|||
Layout.fillWidth: true
|
||||
spacing: Tokens.spacing.small
|
||||
|
||||
NsIcon {
|
||||
icon: "history"
|
||||
MaterialIcon {
|
||||
text: "history"
|
||||
color: Colours.palette.m3onSurfaceVariant
|
||||
fontStyle: Tokens.font.icon.medium
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ StyledRect {
|
|||
readonly property real percentage: Storage.primaryDisk?.perc ?? 0
|
||||
|
||||
color: Colours.tPalette.m3surfaceContainer
|
||||
radius: 0 // nosignal: square dashboard cards to match the redesign (F-D1)
|
||||
radius: Tokens.rounding.extraExtraLarge
|
||||
|
||||
implicitWidth: layout.implicitWidth + layout.anchors.margins * 2
|
||||
implicitHeight: layout.implicitHeight + Tokens.padding.large * 2
|
||||
|
|
@ -55,9 +55,9 @@ StyledRect {
|
|||
anchors.centerIn: parent
|
||||
spacing: 0
|
||||
|
||||
NsIcon {
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
icon: "hard_drive"
|
||||
text: "hard_drive"
|
||||
color: root.accent
|
||||
fontStyle: Tokens.font.icon.medium
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ StyledWindow {
|
|||
|
||||
readonly property HyprlandMonitor monitor: Hypr.monitorFor(screen)
|
||||
readonly property bool hasSpecialWorkspace: (monitor?.lastIpcObject.specialWorkspace?.name.length ?? 0) > 0
|
||||
readonly property bool hasFullscreenOnNormalWs: monitor?.activeWorkspace?.toplevels.values.some(t => t.lastIpcObject.fullscreen > 1) ?? false
|
||||
readonly property bool hasFullscreen: {
|
||||
if (hasSpecialWorkspace) {
|
||||
const specialName = monitor?.lastIpcObject.specialWorkspace?.name;
|
||||
|
|
@ -29,13 +30,13 @@ StyledWindow {
|
|||
const specialWs = Hypr.workspaces.values.find(ws => ws.name === specialName);
|
||||
return specialWs?.toplevels.values.some(t => t.lastIpcObject.fullscreen > 1) ?? false;
|
||||
}
|
||||
return monitor?.activeWorkspace?.toplevels.values.some(t => t.lastIpcObject.fullscreen > 1) ?? false;
|
||||
return hasFullscreenOnNormalWs;
|
||||
}
|
||||
|
||||
property real fsTransitionProg: hasFullscreen ? 1 : 0
|
||||
readonly property real sdfBorderOffset: 2 * fsTransitionProg // SDFs joins are not exact, so offset by 2px to ensure nothing shows
|
||||
readonly property real borderThickness: 0 // NoSignal: no screen-edge frame (NsBar owns the top)
|
||||
readonly property real borderRounding: 0 // NoSignal: no rounded screen frame
|
||||
readonly property real borderThickness: contentItem.Config.border.thickness * (1 - fsTransitionProg)
|
||||
readonly property real borderRounding: contentItem.Config.border.rounding * (1 - fsTransitionProg)
|
||||
readonly property real shadowOpacity: 0.7 * (1 - fsTransitionProg)
|
||||
readonly property real borderLayoutThickness: hasFullscreen ? 0 : contentItem.Config.border.thickness
|
||||
|
||||
|
|
@ -64,7 +65,7 @@ StyledWindow {
|
|||
|
||||
name: "drawers"
|
||||
WlrLayershell.exclusionMode: ExclusionMode.Ignore
|
||||
WlrLayershell.layer: fsTransitionProg > 0 && contentItem.Config.general.showOverFullscreen ? WlrLayer.Overlay : WlrLayer.Top
|
||||
WlrLayershell.layer: (fsTransitionProg > 0 && contentItem.Config.general.showOverFullscreen) || (hasSpecialWorkspace && hasFullscreenOnNormalWs) ? WlrLayer.Overlay : WlrLayer.Top
|
||||
WlrLayershell.keyboardFocus: visibilities.launcher || visibilities.session ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
|
||||
|
||||
mask: hasFullscreen ? emptyRegion : regions
|
||||
|
|
@ -85,14 +86,14 @@ StyledWindow {
|
|||
Region {
|
||||
id: emptyRegion
|
||||
|
||||
x: panels.notifications.x + root.borderThickness
|
||||
y: panels.notifications.y + bar.implicitHeight
|
||||
x: panels.notifications.x + bar.implicitWidth
|
||||
y: panels.notifications.y + root.borderThickness
|
||||
width: panels.notifications.width
|
||||
height: panels.notifications.height
|
||||
|
||||
Region {
|
||||
x: root.width - width
|
||||
y: panels.osdWrapper.y + bar.implicitHeight
|
||||
y: panels.osdWrapper.y + root.borderThickness
|
||||
width: panels.osdWrapper.width * (1 - panels.osd.offsetScale) + root.borderThickness
|
||||
height: panels.osd.height
|
||||
}
|
||||
|
|
@ -155,9 +156,9 @@ StyledWindow {
|
|||
anchors.margins: -50 // Make border thicker to smooth out bulge from closed drawers
|
||||
group: blobGroup
|
||||
radius: root.borderRounding
|
||||
borderLeft: root.borderThickness - anchors.margins - root.sdfBorderOffset
|
||||
borderLeft: bar.implicitWidth - anchors.margins - root.sdfBorderOffset
|
||||
borderRight: root.borderThickness - anchors.margins - root.sdfBorderOffset
|
||||
borderTop: -anchors.margins - root.sdfBorderOffset // NoSignal: no top frame
|
||||
borderTop: root.borderThickness - anchors.margins - root.sdfBorderOffset
|
||||
borderBottom: root.borderThickness - anchors.margins - root.sdfBorderOffset
|
||||
}
|
||||
|
||||
|
|
@ -180,7 +181,7 @@ StyledWindow {
|
|||
|
||||
panel: panels.sessionWrapper
|
||||
deformAmount: 0.2
|
||||
x: panels.sessionWrapper.x + panels.session.x + root.borderThickness
|
||||
x: panels.sessionWrapper.x + panels.session.x + bar.implicitWidth
|
||||
implicitWidth: panels.session.width
|
||||
}
|
||||
|
||||
|
|
@ -198,10 +199,8 @@ StyledWindow {
|
|||
id: osdBg
|
||||
|
||||
panel: panels.osdWrapper
|
||||
deformAmount: 0 // NoSignal: square OSD (no organic blob deformation)
|
||||
radius: 0
|
||||
opacity: 0 // NoSignal: OSD draws its own flat square glass bg (osd/Content)
|
||||
x: panels.osdWrapper.x + panels.osd.x + root.borderThickness
|
||||
deformAmount: 0.25
|
||||
x: panels.osdWrapper.x + panels.osd.x + bar.implicitWidth
|
||||
implicitWidth: panels.osd.width
|
||||
}
|
||||
|
||||
|
|
@ -209,12 +208,6 @@ StyledWindow {
|
|||
id: notifsBg
|
||||
|
||||
panel: panels.notifications
|
||||
deformAmount: 0 // NoSignal: square notification toasts
|
||||
radius: 0
|
||||
opacity: 0
|
||||
visible: false
|
||||
implicitWidth: 0 // collapse the blob entirely (toasts draw their own square cards)
|
||||
implicitHeight: 0
|
||||
}
|
||||
|
||||
PanelBg {
|
||||
|
|
@ -234,7 +227,7 @@ StyledWindow {
|
|||
|
||||
panel: panels.popoutsWrapper
|
||||
deformAmount: panels.popouts.isDetached ? 0.05 : panels.popouts.hasCurrent ? 0.15 : 0.1
|
||||
x: panels.popoutsWrapper.x + panels.popouts.x + root.borderThickness - panels.popouts.width * extraWidth
|
||||
x: panels.popoutsWrapper.x + panels.popouts.x + bar.implicitWidth - panels.popouts.width * extraWidth
|
||||
implicitWidth: panels.popouts.width * (1 + extraWidth)
|
||||
|
||||
Behavior on extraWidth {
|
||||
|
|
@ -300,9 +293,8 @@ StyledWindow {
|
|||
BarWrapper {
|
||||
id: bar
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
screen: root.screen
|
||||
visibilities: visibilities
|
||||
|
|
@ -319,8 +311,8 @@ StyledWindow {
|
|||
property real deformAmount: 0.15
|
||||
|
||||
group: blobGroup
|
||||
x: panel.x + root.borderThickness
|
||||
y: panel.y + bar.implicitHeight
|
||||
x: panel.x + bar.implicitWidth
|
||||
y: panel.y + root.borderThickness
|
||||
implicitWidth: panel.width
|
||||
implicitHeight: panel.height
|
||||
radius: Tokens.rounding.extraLarge
|
||||
|
|
|
|||
|
|
@ -14,11 +14,11 @@ Scope {
|
|||
|
||||
ExclusionZone {
|
||||
anchors.left: true
|
||||
exclusiveZone: root.bar.exclusiveZone
|
||||
}
|
||||
|
||||
ExclusionZone {
|
||||
anchors.top: true
|
||||
exclusiveZone: 0 // NoSignal: NsBar owns the top exclusive zone
|
||||
}
|
||||
|
||||
ExclusionZone {
|
||||
|
|
@ -32,7 +32,7 @@ Scope {
|
|||
component ExclusionZone: StyledWindow {
|
||||
screen: root.screen
|
||||
name: "border-exclusion"
|
||||
exclusiveZone: 0 // NoSignal: no screen-edge frame
|
||||
exclusiveZone: contentItem.Config.border.thickness
|
||||
mask: Region {}
|
||||
implicitWidth: 1
|
||||
implicitHeight: 1
|
||||
|
|
|
|||
|
|
@ -23,29 +23,27 @@ CustomMouseArea {
|
|||
property bool osdShortcutActive
|
||||
property bool utilitiesShortcutActive
|
||||
|
||||
// NoSignal top bar: the bar's thickness now insets the TOP, so panel Y-origins
|
||||
// are offset by bar.implicitHeight; the left inset is just the border thickness.
|
||||
function withinPanelHeight(panel: Item, x: real, y: real): bool {
|
||||
const panelY = root.bar.implicitHeight + panel.y;
|
||||
const panelY = root.borderThickness + panel.y;
|
||||
return y >= panelY - Config.border.rounding && y <= panelY + panel.height + Config.border.rounding;
|
||||
}
|
||||
|
||||
function withinPanelWidth(panel: Item, x: real, y: real): bool {
|
||||
const panelX = root.borderThickness + panel.x;
|
||||
const panelX = bar.implicitWidth + panel.x;
|
||||
return x >= panelX - Config.border.rounding && x <= panelX + panel.width + Config.border.rounding;
|
||||
}
|
||||
|
||||
function inLeftPanel(panel: Item, x: real, y: real): bool {
|
||||
return x < root.borderThickness + panel.x + panel.width && withinPanelHeight(panel, x, y);
|
||||
return x < bar.implicitWidth + panel.x + panel.width && withinPanelHeight(panel, x, y);
|
||||
}
|
||||
|
||||
function inRightPanel(panel: Item, x: real, y: real): bool {
|
||||
return x > Math.min(width - Config.border.minThickness, root.borderThickness + panel.x) && withinPanelHeight(panel, x, y);
|
||||
return x > Math.min(width - Config.border.minThickness, bar.implicitWidth + panel.x) && withinPanelHeight(panel, x, y);
|
||||
}
|
||||
|
||||
function inTopPanel(panel: Item, x: real, y: real): bool {
|
||||
const panelHeight = panel.height * (1 - (panel.offsetScale ?? 0)); // qmllint disable missing-property
|
||||
return y < Math.max(Config.border.minThickness, root.bar.implicitHeight + panelHeight) && withinPanelWidth(panel, x, y);
|
||||
return y < Math.max(Config.border.minThickness, Config.border.thickness + panelHeight) && withinPanelWidth(panel, x, y);
|
||||
}
|
||||
|
||||
function inBottomPanel(panel: Item, x: real, y: real, isCorner = false): bool {
|
||||
|
|
@ -56,8 +54,8 @@ CustomMouseArea {
|
|||
function onWheel(event: WheelEvent): void {
|
||||
if (fullscreen)
|
||||
return;
|
||||
if (event.y < bar.implicitHeight) {
|
||||
bar.handleWheel(event.x, event.angleDelta);
|
||||
if (event.x < bar.implicitWidth) {
|
||||
bar.handleWheel(event.y, event.angleDelta);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -104,15 +102,15 @@ CustomMouseArea {
|
|||
return;
|
||||
}
|
||||
|
||||
// Show bar in non-exclusive mode on hover (top edge)
|
||||
if (!visibilities.bar && Config.bar.showOnHover && y < bar.clampedHeight)
|
||||
// Show bar in non-exclusive mode on hover
|
||||
if (!visibilities.bar && Config.bar.showOnHover && x < bar.clampedWidth)
|
||||
bar.isHovered = true;
|
||||
|
||||
// Show/hide bar on drag (vertical, from the top edge)
|
||||
if (pressed && dragStart.y < bar.clampedHeight) {
|
||||
if (dragY > Config.bar.dragThreshold)
|
||||
// Show/hide bar on drag
|
||||
if (pressed && dragStart.x < bar.clampedWidth) {
|
||||
if (dragX > Config.bar.dragThreshold)
|
||||
visibilities.bar = true;
|
||||
else if (dragY < -Config.bar.dragThreshold)
|
||||
else if (dragX < -Config.bar.dragThreshold)
|
||||
visibilities.bar = false;
|
||||
}
|
||||
|
||||
|
|
@ -130,7 +128,7 @@ CustomMouseArea {
|
|||
root.panels.osd.hovered = true;
|
||||
}
|
||||
|
||||
const showSidebar = pressed && dragStart.x > Math.min(width - Config.border.minThickness, root.borderThickness + panels.sidebar.x);
|
||||
const showSidebar = pressed && dragStart.x > Math.min(width - Config.border.minThickness, bar.implicitWidth + panels.sidebar.x);
|
||||
|
||||
// Show/hide session on drag
|
||||
if (pressed && inRightPanel(panels.sessionWrapper, dragStart.x, dragStart.y) && withinPanelHeight(panels.sessionWrapper, x, y)) {
|
||||
|
|
@ -215,9 +213,9 @@ CustomMouseArea {
|
|||
utilitiesShortcutActive = false;
|
||||
}
|
||||
|
||||
// Show popouts on hover (top bar)
|
||||
if (y < bar.implicitHeight) {
|
||||
bar.checkPopout(x);
|
||||
// Show popouts on hover
|
||||
if (x < bar.implicitWidth) {
|
||||
bar.checkPopout(y);
|
||||
} else if ((!popouts.currentName.startsWith("traymenu") || ((popouts.current as StackView)?.depth ?? 0) <= 1) && !inLeftPanel(panels.popoutsWrapper, x, y)) {
|
||||
popouts.hasCurrent = false;
|
||||
bar.closeTray();
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ Item {
|
|||
|
||||
anchors.fill: parent
|
||||
anchors.margins: borderThickness
|
||||
anchors.topMargin: bar.implicitHeight
|
||||
anchors.leftMargin: bar.implicitWidth
|
||||
|
||||
Item {
|
||||
id: osdWrapper
|
||||
|
|
@ -70,7 +70,7 @@ Item {
|
|||
sessionPanel: sessionWrapper
|
||||
utilitiesPanel: utilities
|
||||
|
||||
y: 46 // NoSignal: drop the toasts below the 38px NsBar (+ gap)
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,10 +15,10 @@ Region {
|
|||
readonly property real borderThickness: win.contentItem.Config.border.thickness
|
||||
readonly property real clampedThickness: win.contentItem.Config.border.clampedThickness
|
||||
|
||||
x: clampedThickness + win.dragMaskPadding
|
||||
y: bar.clampedHeight + win.dragMaskPadding
|
||||
width: win.width - clampedThickness * 2 - win.dragMaskPadding * 2
|
||||
height: win.height - bar.clampedHeight - clampedThickness - win.dragMaskPadding * 2
|
||||
x: bar.clampedWidth + win.dragMaskPadding
|
||||
y: clampedThickness + win.dragMaskPadding
|
||||
width: win.width - bar.clampedWidth - clampedThickness - win.dragMaskPadding * 2
|
||||
height: win.height - clampedThickness * 2 - win.dragMaskPadding * 2
|
||||
intersection: Intersection.Xor
|
||||
|
||||
R {
|
||||
|
|
@ -75,8 +75,8 @@ Region {
|
|||
component R: Region {
|
||||
required property Item panel
|
||||
|
||||
x: panel.x + root.borderThickness
|
||||
y: panel.y + root.bar.implicitHeight
|
||||
x: panel.x + root.bar.implicitWidth
|
||||
y: panel.y + root.borderThickness
|
||||
width: panel.width
|
||||
height: panel.height
|
||||
intersection: Intersection.Subtract
|
||||
|
|
|
|||
|
|
@ -58,14 +58,14 @@ Item {
|
|||
|
||||
implicitHeight: Math.max(searchIcon.implicitHeight, search.implicitHeight, clearIcon.implicitHeight)
|
||||
|
||||
NsIcon {
|
||||
MaterialIcon {
|
||||
id: searchIcon
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: root.padding
|
||||
|
||||
icon: "search"
|
||||
text: "search"
|
||||
color: Colours.palette.m3onSurfaceVariant
|
||||
}
|
||||
|
||||
|
|
@ -145,7 +145,7 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
NsIcon {
|
||||
MaterialIcon {
|
||||
id: clearIcon
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
|
@ -163,7 +163,7 @@ Item {
|
|||
return 1;
|
||||
}
|
||||
|
||||
icon: "close"
|
||||
text: "close"
|
||||
color: Colours.palette.m3onSurfaceVariant
|
||||
|
||||
MouseArea {
|
||||
|
|
|
|||
|
|
@ -116,8 +116,8 @@ Item {
|
|||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
NsIcon {
|
||||
icon: root.state === "wallpapers" ? "wallpaper_slideshow" : "manage_search"
|
||||
MaterialIcon {
|
||||
text: root.state === "wallpapers" ? "wallpaper_slideshow" : "manage_search"
|
||||
color: Colours.palette.m3onSurfaceVariant
|
||||
fontStyle: Tokens.font.icon.extraLarge
|
||||
|
||||
|
|
|
|||
|
|
@ -25,11 +25,11 @@ Item {
|
|||
anchors.rightMargin: Tokens.padding.medium
|
||||
anchors.margins: Tokens.padding.small
|
||||
|
||||
NsIcon {
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
icon: root.modelData?.icon ?? ""
|
||||
text: root.modelData?.icon ?? ""
|
||||
color: Colours.palette.m3onSurfaceVariant
|
||||
fontStyle: Tokens.font.icon.builders.large.scale(1.3).build()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,8 +79,8 @@ Item {
|
|||
anchors.right: parent.right
|
||||
active: root.modelData && Strings.testRegexList(GlobalConfig.launcher.favouriteApps, root.modelData.id)
|
||||
|
||||
sourceComponent: NsIcon {
|
||||
icon: "favorite"
|
||||
sourceComponent: MaterialIcon {
|
||||
text: "favorite"
|
||||
fill: 1
|
||||
color: Colours.palette.m3primary
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,8 +40,8 @@ Item {
|
|||
|
||||
spacing: Tokens.spacing.medium
|
||||
|
||||
NsIcon {
|
||||
icon: "function"
|
||||
MaterialIcon {
|
||||
text: "function"
|
||||
fontStyle: Tokens.font.icon.extraLarge
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
|
|
@ -105,14 +105,14 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
NsIcon {
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Tokens.padding.medium
|
||||
|
||||
icon: "open_in_new"
|
||||
text: "open_in_new"
|
||||
color: Colours.palette.m3onTertiary
|
||||
fontStyle: Tokens.font.icon.large
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,8 +92,8 @@ Item {
|
|||
|
||||
active: `${root.modelData?.name} ${root.modelData?.flavour}` === Schemes.currentScheme
|
||||
|
||||
sourceComponent: NsIcon {
|
||||
icon: "check"
|
||||
sourceComponent: MaterialIcon {
|
||||
text: "check"
|
||||
color: Colours.palette.m3onSurfaceVariant
|
||||
fontStyle: Tokens.font.icon.large
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,10 +26,10 @@ Item {
|
|||
anchors.rightMargin: Tokens.padding.medium
|
||||
anchors.margins: Tokens.padding.small
|
||||
|
||||
NsIcon {
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
|
||||
icon: root.modelData?.icon ?? ""
|
||||
text: root.modelData?.icon ?? ""
|
||||
fontStyle: Tokens.font.icon.extraLarge
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
|
@ -68,8 +68,8 @@ Item {
|
|||
|
||||
active: root.modelData?.variant === Schemes.currentVariant
|
||||
|
||||
sourceComponent: NsIcon {
|
||||
icon: "check"
|
||||
sourceComponent: MaterialIcon {
|
||||
text: "check"
|
||||
color: Colours.palette.m3onSurfaceVariant
|
||||
fontStyle: Tokens.font.icon.large
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,9 +57,9 @@ Item {
|
|||
implicitWidth: Tokens.sizes.launcher.wallpaperWidth
|
||||
implicitHeight: implicitWidth / 16 * 9
|
||||
|
||||
NsIcon {
|
||||
MaterialIcon {
|
||||
anchors.centerIn: parent
|
||||
icon: "image"
|
||||
text: "image"
|
||||
color: Colours.tPalette.m3outline
|
||||
fontStyle: Tokens.font.icon.builders.extraLarge.scale(2).weight(Font.DemiBold).build()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ ColumnLayout {
|
|||
|
||||
PasswordInput {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
centerScale: Math.max(0.8, root.centerScale)
|
||||
centerWidth: root.centerWidth
|
||||
lock: root.lock
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,11 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
|
||||
// NoSignal lock screen surface (interface redesign 2026-06-14).
|
||||
// Secure ext-session-lock content: blurred wallpaper + scrim, top status row,
|
||||
// centred clock/date/avatar/username/password, bottom now-playing strip.
|
||||
// Reuses caelestia's Pam (buffer + handleKey + state) for PAM auth and the
|
||||
// WlSessionLock unlock signal — input is fed to pam.handleKey, never compared
|
||||
// in QML. JetBrains Mono / Nerd Font / NoSignal Theme tokens.
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Effects
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Bluetooth
|
||||
import Quickshell.Services.UPower
|
||||
import Caelestia.Config
|
||||
import qs.components
|
||||
import qs.services
|
||||
import qs.utils
|
||||
|
||||
WlSessionLockSurface {
|
||||
id: root
|
||||
|
|
@ -24,26 +13,153 @@ WlSessionLockSurface {
|
|||
required property WlSessionLock lock
|
||||
required property Pam pam
|
||||
|
||||
readonly property bool errored: root.pam.state === "error" || root.pam.state === "fail"
|
||||
readonly property alias unlocking: unlockAnim.running
|
||||
|
||||
contentItem.Config.screen: screen?.name ?? ""
|
||||
contentItem.Tokens.screen: screen?.name ?? ""
|
||||
contentItem.Config.screen: screen.name
|
||||
contentItem.Tokens.screen: screen.name
|
||||
|
||||
color: "transparent"
|
||||
|
||||
Connections {
|
||||
function onUnlock(): void {
|
||||
root.lock.locked = false;
|
||||
unlockAnim.start();
|
||||
}
|
||||
|
||||
target: root.lock
|
||||
}
|
||||
|
||||
// ── Blurred wallpaper + scrim ──────────────────────────────────────────
|
||||
SequentialAnimation {
|
||||
id: unlockAnim
|
||||
|
||||
ParallelAnimation {
|
||||
Anim {
|
||||
target: lockContent
|
||||
properties: "implicitWidth,implicitHeight"
|
||||
to: lockContent.size
|
||||
}
|
||||
Anim {
|
||||
target: lockBg
|
||||
property: "radius"
|
||||
to: lockContent.radius
|
||||
}
|
||||
Anim {
|
||||
target: content
|
||||
property: "scale"
|
||||
to: 0
|
||||
}
|
||||
Anim {
|
||||
target: content
|
||||
property: "opacity"
|
||||
to: 0
|
||||
type: Anim.StandardSmall
|
||||
}
|
||||
Anim {
|
||||
target: lockIcon
|
||||
property: "opacity"
|
||||
to: 1
|
||||
type: Anim.StandardLarge
|
||||
}
|
||||
Anim {
|
||||
target: background
|
||||
property: "opacity"
|
||||
to: 0
|
||||
type: Anim.StandardLarge
|
||||
}
|
||||
SequentialAnimation {
|
||||
PauseAnimation {
|
||||
duration: Tokens.anim.durations.small
|
||||
}
|
||||
Anim {
|
||||
type: Anim.Standard
|
||||
target: lockContent
|
||||
property: "opacity"
|
||||
to: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
PropertyAction {
|
||||
target: root.lock
|
||||
property: "locked"
|
||||
value: false
|
||||
}
|
||||
}
|
||||
|
||||
ParallelAnimation {
|
||||
id: initAnim
|
||||
|
||||
running: true
|
||||
|
||||
Anim {
|
||||
target: background
|
||||
property: "opacity"
|
||||
to: 1
|
||||
type: Anim.StandardLarge
|
||||
}
|
||||
SequentialAnimation {
|
||||
ParallelAnimation {
|
||||
Anim {
|
||||
target: lockContent
|
||||
property: "scale"
|
||||
to: 1
|
||||
type: Anim.FastSpatial
|
||||
}
|
||||
Anim {
|
||||
target: lockContent
|
||||
property: "rotation"
|
||||
to: 360
|
||||
duration: Tokens.anim.durations.expressiveFastSpatial
|
||||
easing: Tokens.anim.standardAccel
|
||||
}
|
||||
}
|
||||
ParallelAnimation {
|
||||
Anim {
|
||||
target: lockIcon
|
||||
property: "rotation"
|
||||
to: 360
|
||||
easing: Tokens.anim.standardDecel
|
||||
}
|
||||
Anim {
|
||||
type: Anim.DefaultEffects
|
||||
target: lockIcon
|
||||
property: "opacity"
|
||||
to: 0
|
||||
}
|
||||
Anim {
|
||||
type: Anim.DefaultEffects
|
||||
target: content
|
||||
property: "opacity"
|
||||
to: 1
|
||||
}
|
||||
Anim {
|
||||
target: content
|
||||
property: "scale"
|
||||
to: 1
|
||||
}
|
||||
Anim {
|
||||
target: lockBg
|
||||
property: "radius"
|
||||
to: lockContent.Tokens.rounding.extraLarge * 1.5
|
||||
}
|
||||
Anim {
|
||||
target: lockContent
|
||||
property: "implicitWidth"
|
||||
to: (root.screen?.height ?? 0) * lockContent.Tokens.sizes.lock.heightMult * lockContent.Tokens.sizes.lock.ratio
|
||||
}
|
||||
Anim {
|
||||
target: lockContent
|
||||
property: "implicitHeight"
|
||||
to: (root.screen?.height ?? 0) * lockContent.Tokens.sizes.lock.heightMult
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ScreencopyView {
|
||||
id: background
|
||||
|
||||
anchors.fill: parent
|
||||
captureSource: root.screen
|
||||
opacity: 0
|
||||
|
||||
layer.enabled: true
|
||||
layer.effect: MultiEffect {
|
||||
|
|
@ -52,265 +168,57 @@ WlSessionLockSurface {
|
|||
blur: 1
|
||||
blurMax: 64
|
||||
blurMultiplier: 1
|
||||
brightness: -0.38
|
||||
saturation: 0.1
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: Qt.rgba(8 / 255, 10 / 255, 14 / 255, 0.5)
|
||||
}
|
||||
|
||||
// ── Keyboard capture (feed PAM; Escape does nothing) ───────────────────
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
focus: true
|
||||
Component.onCompleted: forceActiveFocus()
|
||||
Keys.onPressed: event => root.pam.handleKey(event)
|
||||
}
|
||||
id: lockContent
|
||||
|
||||
// ── Top status row ─────────────────────────────────────────────────────
|
||||
RowLayout {
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 22
|
||||
anchors.leftMargin: 28
|
||||
anchors.rightMargin: 28
|
||||
readonly property int size: lockIcon.implicitHeight + Tokens.padding.large * 4
|
||||
readonly property int radius: size / 4 * Tokens.rounding.scale
|
||||
|
||||
RowLayout {
|
||||
spacing: 7
|
||||
NsIcon {
|
||||
icon: "lock"
|
||||
color: Theme.textMuted
|
||||
size: 15
|
||||
}
|
||||
StyledText {
|
||||
text: "Locked"
|
||||
color: Theme.textMuted
|
||||
font.family: Theme.font.family
|
||||
font.pixelSize: Theme.font.bar
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: 14
|
||||
|
||||
NsIcon {
|
||||
icon: Nmcli.active ? Icons.getNetworkIcon(Nmcli.active.strength ?? 0) : "wifi_off"
|
||||
color: Theme.textMuted
|
||||
size: 15
|
||||
}
|
||||
NsIcon {
|
||||
icon: Bluetooth.defaultAdapter?.enabled ? "bluetooth_connected" : "bluetooth_disabled"
|
||||
color: Theme.textMuted
|
||||
size: 15
|
||||
}
|
||||
RowLayout {
|
||||
spacing: 6
|
||||
visible: UPower.displayDevice.isLaptopBattery
|
||||
NsIcon {
|
||||
icon: Icons.getBatteryIcon(UPower.displayDevice.percentage, [UPowerDeviceState.Charging, UPowerDeviceState.FullyCharged].includes(UPower.displayDevice.state))
|
||||
color: Theme.textMuted
|
||||
size: 15
|
||||
}
|
||||
StyledText {
|
||||
text: Math.round(UPower.displayDevice.percentage * 100) + "%"
|
||||
color: Theme.textMuted
|
||||
font.family: Theme.font.family
|
||||
font.pixelSize: Theme.font.bar
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Centre block ───────────────────────────────────────────────────────
|
||||
ColumnLayout {
|
||||
anchors.centerIn: parent
|
||||
spacing: 0
|
||||
implicitWidth: size
|
||||
implicitHeight: size
|
||||
|
||||
StyledText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: Time.format("hh:mm")
|
||||
color: Theme.text
|
||||
font.family: Theme.font.family
|
||||
font.pixelSize: 108
|
||||
font.weight: Font.ExtraLight
|
||||
font.letterSpacing: 3
|
||||
}
|
||||
rotation: 180
|
||||
scale: 0
|
||||
|
||||
StyledText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: Time.format("ddd, d MMMM yyyy")
|
||||
color: Theme.textMuted
|
||||
font.family: Theme.font.family
|
||||
font.pixelSize: 15
|
||||
}
|
||||
StyledRect {
|
||||
id: lockBg
|
||||
|
||||
Rectangle {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.topMargin: 64
|
||||
implicitWidth: 74
|
||||
implicitHeight: 74
|
||||
radius: 0
|
||||
gradient: Gradient {
|
||||
orientation: Gradient.Vertical
|
||||
GradientStop {
|
||||
position: 0
|
||||
color: Theme.accent
|
||||
}
|
||||
GradientStop {
|
||||
position: 1
|
||||
color: Theme.blue
|
||||
}
|
||||
}
|
||||
NsIcon {
|
||||
anchors.centerIn: parent
|
||||
icon: "person"
|
||||
color: Theme.onAccent
|
||||
size: 34
|
||||
anchors.fill: parent
|
||||
color: Colours.palette.m3surface
|
||||
radius: parent.radius
|
||||
opacity: Colours.transparency.enabled ? Colours.transparency.base : 1
|
||||
|
||||
layer.enabled: true
|
||||
layer.effect: MultiEffect {
|
||||
shadowEnabled: true
|
||||
blurMax: 15
|
||||
shadowColor: Qt.alpha(Colours.palette.m3shadow, 0.7)
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.topMargin: 14
|
||||
text: SysInfo.user || "user"
|
||||
color: Theme.text
|
||||
font.family: Theme.font.family
|
||||
font.pixelSize: 14
|
||||
MaterialIcon {
|
||||
id: lockIcon
|
||||
|
||||
anchors.centerIn: parent
|
||||
text: "lock"
|
||||
fontStyle: Tokens.font.icon.builders.extraLarge.scale(4).weight(Font.Bold).build()
|
||||
rotation: 180
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.topMargin: 16
|
||||
implicitWidth: 312
|
||||
implicitHeight: 44
|
||||
radius: Theme.radius.panel
|
||||
color: Qt.rgba(17 / 255, 20 / 255, 26 / 255, 0.55)
|
||||
border.width: 1
|
||||
border.color: root.errored ? Qt.rgba(224 / 255, 116 / 255, 106 / 255, 0.6) : Qt.rgba(1, 1, 1, 0.1)
|
||||
Content {
|
||||
id: content
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 14
|
||||
anchors.rightMargin: 12
|
||||
spacing: 10
|
||||
anchors.centerIn: parent
|
||||
width: (root.screen?.height ?? 0) * Tokens.sizes.lock.heightMult * Tokens.sizes.lock.ratio - Tokens.padding.extraLargeIncreased
|
||||
height: (root.screen?.height ?? 0) * Tokens.sizes.lock.heightMult - Tokens.padding.extraLargeIncreased
|
||||
|
||||
NsIcon {
|
||||
icon: "key"
|
||||
color: Theme.textMuted
|
||||
size: 15
|
||||
}
|
||||
|
||||
StyledText {
|
||||
Layout.fillWidth: true
|
||||
text: root.pam.buffer.length > 0 ? "•".repeat(root.pam.buffer.length) : "Enter password"
|
||||
color: root.pam.buffer.length > 0 ? Theme.text : Theme.textFaint
|
||||
elide: Text.ElideRight
|
||||
font.family: Theme.font.family
|
||||
font.pixelSize: Theme.font.body
|
||||
font.letterSpacing: root.pam.buffer.length > 0 ? 2 : 0
|
||||
}
|
||||
|
||||
NsIcon {
|
||||
icon: "login"
|
||||
color: root.pam.buffer.length > 0 ? Theme.accent : Theme.textFaint
|
||||
size: 16
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
anchors.margins: -6
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: if (root.pam.buffer.length > 0)
|
||||
root.pam.passwd.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.topMargin: 8
|
||||
Layout.preferredHeight: 14
|
||||
text: root.errored ? "Incorrect — try again" : (root.pam.lockMessage || "")
|
||||
color: Theme.danger
|
||||
font.family: Theme.font.family
|
||||
font.pixelSize: 12
|
||||
}
|
||||
}
|
||||
|
||||
// ── Bottom strip ───────────────────────────────────────────────────────
|
||||
RowLayout {
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottomMargin: 26
|
||||
anchors.leftMargin: 28
|
||||
anchors.rightMargin: 28
|
||||
|
||||
RowLayout {
|
||||
spacing: 10
|
||||
visible: Players.active !== null
|
||||
|
||||
Rectangle {
|
||||
implicitWidth: 42
|
||||
implicitHeight: 42
|
||||
radius: Theme.radius.panel
|
||||
gradient: Gradient {
|
||||
orientation: Gradient.Vertical
|
||||
GradientStop {
|
||||
position: 0
|
||||
color: Theme.accent
|
||||
}
|
||||
GradientStop {
|
||||
position: 1
|
||||
color: Theme.blue
|
||||
}
|
||||
}
|
||||
NsIcon {
|
||||
anchors.centerIn: parent
|
||||
icon: "music_note"
|
||||
color: Theme.onAccent
|
||||
size: 18
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 0
|
||||
StyledText {
|
||||
text: Players.active?.trackTitle || ""
|
||||
color: Theme.text
|
||||
elide: Text.ElideRight
|
||||
Layout.maximumWidth: 280
|
||||
font.family: Theme.font.family
|
||||
font.pixelSize: Theme.font.bodySmall
|
||||
}
|
||||
StyledText {
|
||||
text: Players.active?.trackArtist || ""
|
||||
color: Theme.textMuted
|
||||
elide: Text.ElideRight
|
||||
Layout.maximumWidth: 280
|
||||
font.family: Theme.font.family
|
||||
font.pixelSize: Theme.font.meta
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: "Press Enter to unlock"
|
||||
color: Theme.textFaint
|
||||
font.family: Theme.font.family
|
||||
font.pixelSize: 11
|
||||
lock: root
|
||||
opacity: 0
|
||||
scale: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,12 +40,7 @@ Scope {
|
|||
PamContext {
|
||||
id: passwd
|
||||
|
||||
// NoSignal: faillock-free lock auth (finding F1). The stock "passwd"
|
||||
// service routes through pam_faillock, which can lock the user out of
|
||||
// their OWN session after a few failed unlocks — refusing even the
|
||||
// correct password until /run/faillock clears on reboot. The bundled
|
||||
// "caelestia" service (assets/pam.d/caelestia) uses plain pam_unix.
|
||||
config: "caelestia"
|
||||
config: "passwd"
|
||||
configDirectory: Quickshell.shellDir + "/assets/pam.d"
|
||||
|
||||
onMessageChanged: {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ pragma ComponentBehavior: Bound
|
|||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import M3Shapes
|
||||
import Caelestia.Config
|
||||
import qs.components
|
||||
import qs.services
|
||||
|
|
@ -10,9 +11,19 @@ import qs.modules.lock
|
|||
Item {
|
||||
id: root
|
||||
|
||||
required property real centerScale
|
||||
required property Pam pam
|
||||
readonly property alias placeholder: placeholder
|
||||
readonly property alias placeholderWidth: nonAnimPlaceholder.width
|
||||
property string buffer
|
||||
readonly property list<int> shapeQueue: {
|
||||
const shapes = [MaterialShape.Slanted, MaterialShape.Arch, MaterialShape.Fan, MaterialShape.Arrow, MaterialShape.SemiCircle, MaterialShape.Triangle, MaterialShape.Diamond, MaterialShape.ClamShell, MaterialShape.Pentagon, MaterialShape.Gem, MaterialShape.Sunny, MaterialShape.VerySunny, MaterialShape.Cookie4Sided, MaterialShape.Ghostish, MaterialShape.SoftBurst];
|
||||
for (let i = shapes.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * (i + 1));
|
||||
[shapes[i], shapes[j]] = [shapes[j], shapes[i]];
|
||||
}
|
||||
return shapes;
|
||||
}
|
||||
|
||||
clip: true
|
||||
|
||||
|
|
@ -31,22 +42,30 @@ Item {
|
|||
target: root.pam
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: placeholder
|
||||
|
||||
anchors.centerIn: parent
|
||||
TextMetrics {
|
||||
id: nonAnimPlaceholder
|
||||
|
||||
text: {
|
||||
if (root.pam.passwd.active)
|
||||
return qsTr("Loading...");
|
||||
if (root.pam.state === "max")
|
||||
return qsTr("You have reached the maximum number of tries");
|
||||
return qsTr("Max tries reached");
|
||||
return qsTr("Enter your password");
|
||||
}
|
||||
font: placeholder.font
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: placeholder
|
||||
|
||||
anchors.centerIn: parent
|
||||
anchors.verticalCenterOffset: 1
|
||||
|
||||
text: nonAnimPlaceholder.text
|
||||
|
||||
animate: true
|
||||
color: root.pam.passwd.active ? Colours.palette.m3secondary : Colours.palette.m3outline
|
||||
font: Tokens.font.mono.medium
|
||||
font: Tokens.font.body.builders.medium.scale(root.centerScale).width(110).build()
|
||||
|
||||
opacity: root.buffer ? 0 : 1
|
||||
|
||||
|
|
@ -60,7 +79,12 @@ Item {
|
|||
ListView {
|
||||
id: charList
|
||||
|
||||
readonly property int fullWidth: count * (implicitHeight + spacing) - spacing
|
||||
readonly property int fullWidth: {
|
||||
let w = (count - 1) * spacing;
|
||||
for (let i = 0; i < count; i++)
|
||||
w += ((itemAtIndex(i) as CharItem)?.nonAnimWidthScale ?? 1) * implicitHeight;
|
||||
return w + implicitHeight; // Extra padding at ends
|
||||
}
|
||||
|
||||
function bindImWidth(): void {
|
||||
imWidthBehavior.enabled = false;
|
||||
|
|
@ -82,63 +106,7 @@ Item {
|
|||
values: root.buffer.split("")
|
||||
}
|
||||
|
||||
delegate: StyledRect {
|
||||
id: ch
|
||||
|
||||
implicitWidth: implicitHeight
|
||||
implicitHeight: charList.implicitHeight
|
||||
|
||||
color: Colours.palette.m3onSurface
|
||||
radius: Tokens.rounding.medium / 2
|
||||
|
||||
opacity: 0
|
||||
scale: 0
|
||||
Component.onCompleted: {
|
||||
opacity = 1;
|
||||
scale = 1;
|
||||
}
|
||||
ListView.onRemove: removeAnim.start()
|
||||
|
||||
SequentialAnimation {
|
||||
id: removeAnim
|
||||
|
||||
PropertyAction {
|
||||
target: ch
|
||||
property: "ListView.delayRemove"
|
||||
value: true
|
||||
}
|
||||
ParallelAnimation {
|
||||
Anim {
|
||||
type: Anim.DefaultEffects
|
||||
target: ch
|
||||
property: "opacity"
|
||||
to: 0
|
||||
}
|
||||
Anim {
|
||||
target: ch
|
||||
property: "scale"
|
||||
to: 0.5
|
||||
}
|
||||
}
|
||||
PropertyAction {
|
||||
target: ch
|
||||
property: "ListView.delayRemove"
|
||||
value: false
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
type: Anim.DefaultEffects
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on scale {
|
||||
Anim {
|
||||
type: Anim.FastSpatial
|
||||
}
|
||||
}
|
||||
}
|
||||
delegate: CharItem {}
|
||||
|
||||
Behavior on implicitWidth {
|
||||
id: imWidthBehavior
|
||||
|
|
@ -146,4 +114,121 @@ Item {
|
|||
Anim {}
|
||||
}
|
||||
}
|
||||
|
||||
component CharItem: Item {
|
||||
id: char
|
||||
|
||||
required property int index
|
||||
property real nonAnimWidthScale: 1
|
||||
|
||||
implicitHeight: charList.implicitHeight
|
||||
|
||||
ListView.onRemove: {
|
||||
initAnim.stop();
|
||||
removeAnim.start();
|
||||
}
|
||||
|
||||
MaterialShape {
|
||||
id: charShape
|
||||
|
||||
anchors.centerIn: parent
|
||||
implicitSize: charList.implicitHeight * 1.5
|
||||
shape: root.shapeQueue[char.index % root.shapeQueue.length] ?? MaterialShape.Circle
|
||||
color: Colours.palette.m3onSurface
|
||||
|
||||
Behavior on color {
|
||||
CAnim {}
|
||||
}
|
||||
|
||||
SequentialAnimation {
|
||||
id: initAnim
|
||||
|
||||
running: true
|
||||
|
||||
ParallelAnimation {
|
||||
Anim {
|
||||
target: charShape
|
||||
property: "opacity"
|
||||
from: 0
|
||||
to: 1
|
||||
type: Anim.DefaultEffects
|
||||
}
|
||||
Anim {
|
||||
target: charShape
|
||||
property: "scale"
|
||||
from: 0
|
||||
to: 1
|
||||
type: Anim.FastSpatial
|
||||
}
|
||||
Anim {
|
||||
target: char
|
||||
property: "implicitWidth"
|
||||
from: charList.implicitHeight
|
||||
to: charList.implicitHeight * 1.3
|
||||
type: Anim.DefaultEffects
|
||||
}
|
||||
PropertyAction {
|
||||
target: char
|
||||
property: "nonAnimWidthScale"
|
||||
value: 1.5
|
||||
}
|
||||
}
|
||||
PauseAnimation {
|
||||
duration: 180 * Tokens.anim.durations.scale
|
||||
}
|
||||
PropertyAction {
|
||||
target: charShape
|
||||
property: "shape"
|
||||
value: MaterialShape.Circle
|
||||
}
|
||||
ParallelAnimation {
|
||||
Anim {
|
||||
target: charShape
|
||||
property: "scale"
|
||||
to: 2 / 3
|
||||
type: Anim.FastSpatial
|
||||
}
|
||||
Anim {
|
||||
target: char
|
||||
property: "implicitWidth"
|
||||
to: charList.implicitHeight
|
||||
type: Anim.DefaultEffects
|
||||
}
|
||||
PropertyAction {
|
||||
target: char
|
||||
property: "nonAnimWidthScale"
|
||||
value: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SequentialAnimation {
|
||||
id: removeAnim
|
||||
|
||||
PropertyAction {
|
||||
target: char
|
||||
property: "ListView.delayRemove"
|
||||
value: true
|
||||
}
|
||||
ParallelAnimation {
|
||||
Anim {
|
||||
type: Anim.DefaultEffects
|
||||
target: charShape
|
||||
property: "opacity"
|
||||
to: 0
|
||||
}
|
||||
Anim {
|
||||
target: charShape
|
||||
property: "scale"
|
||||
to: 0.5
|
||||
}
|
||||
}
|
||||
PropertyAction {
|
||||
target: char
|
||||
property: "ListView.delayRemove"
|
||||
value: false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import M3Shapes
|
||||
import Caelestia.Config
|
||||
import qs.components
|
||||
import qs.components.controls
|
||||
|
|
@ -8,10 +11,14 @@ import qs.services
|
|||
StyledRect {
|
||||
id: root
|
||||
|
||||
required property real centerScale
|
||||
required property int centerWidth
|
||||
required property var lock
|
||||
|
||||
implicitWidth: centerWidth * 0.8
|
||||
implicitWidth: {
|
||||
const w = centerWidth * 0.8;
|
||||
return lock.pam.buffer ? w : Math.min(w, inputField.placeholderWidth + iconWrapper.implicitWidth + enterButton.implicitWidth + input.spacing * 2 + Tokens.padding.medium * 2);
|
||||
}
|
||||
implicitHeight: input.implicitHeight + Tokens.padding.small
|
||||
|
||||
color: Colours.tPalette.m3surfaceContainer
|
||||
|
|
@ -33,6 +40,10 @@ StyledRect {
|
|||
root.lock.pam.handleKey(event);
|
||||
}
|
||||
|
||||
Behavior on implicitWidth {
|
||||
Anim {}
|
||||
}
|
||||
|
||||
StateLayer {
|
||||
hoverEnabled: false
|
||||
cursorShape: Qt.IBeamCursor
|
||||
|
|
@ -47,44 +58,39 @@ StyledRect {
|
|||
spacing: Tokens.spacing.medium
|
||||
|
||||
Item {
|
||||
id: iconWrapper
|
||||
|
||||
Layout.fillHeight: true
|
||||
implicitWidth: height
|
||||
|
||||
MaterialIcon {
|
||||
id: fprintIcon
|
||||
|
||||
AnimLoader {
|
||||
anchors.centerIn: parent
|
||||
animate: true
|
||||
text: {
|
||||
if (root.lock.pam.fprint.tries >= GlobalConfig.lock.maxFprintTries)
|
||||
return "fingerprint_off";
|
||||
if (root.lock.pam.fprint.active)
|
||||
return "fingerprint";
|
||||
return "lock";
|
||||
}
|
||||
color: root.lock.pam.fprint.tries >= GlobalConfig.lock.maxFprintTries ? Colours.palette.m3error : Colours.palette.m3onSurface
|
||||
opacity: root.lock.pam.passwd.active ? 0 : 1
|
||||
anchors.verticalCenterOffset: sourceComponent === iconComp ? 1 : 0
|
||||
sourceComp: root.lock.pam.passwd.active ? loadingComp : iconComp
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
type: Anim.DefaultEffects
|
||||
Component {
|
||||
id: iconComp
|
||||
|
||||
MaterialIcon {
|
||||
animate: true
|
||||
text: {
|
||||
if (root.lock.pam.fprint.tries >= GlobalConfig.lock.maxFprintTries)
|
||||
return "fingerprint_off";
|
||||
if (root.lock.pam.fprint.active)
|
||||
return "fingerprint";
|
||||
return "lock";
|
||||
}
|
||||
color: root.lock.pam.fprint.tries >= GlobalConfig.lock.maxFprintTries ? Colours.palette.m3error : Colours.palette.m3onSurfaceVariant
|
||||
fontStyle: Tokens.font.icon.builders.medium.scale(root.centerScale).build()
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Tokens.padding.small
|
||||
Component {
|
||||
id: loadingComp
|
||||
|
||||
active: opacity > 0
|
||||
opacity: root.lock.pam.passwd.active ? 1 : 0
|
||||
|
||||
sourceComponent: LoadingIndicator {}
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
type: Anim.DefaultEffects
|
||||
}
|
||||
LoadingIndicator {
|
||||
implicitSize: iconWrapper.height - Tokens.padding.small * 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -95,19 +101,45 @@ StyledRect {
|
|||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
centerScale: root.centerScale
|
||||
pam: root.lock.pam
|
||||
}
|
||||
|
||||
StyledRect {
|
||||
Item {
|
||||
id: enterButton
|
||||
|
||||
implicitWidth: implicitHeight
|
||||
implicitHeight: enterIcon.implicitHeight + Tokens.padding.small
|
||||
implicitHeight: {
|
||||
const h = enterIcon.implicitHeight + Tokens.padding.extraSmall * 2;
|
||||
return h % 2 === 0 ? h : h + 1;
|
||||
}
|
||||
|
||||
color: root.lock.pam.buffer ? Colours.palette.m3primary : Colours.layer(Colours.palette.m3surfaceContainerHigh, 2)
|
||||
radius: Tokens.rounding.full
|
||||
MaterialShape {
|
||||
anchors.fill: parent
|
||||
|
||||
StateLayer {
|
||||
color: root.lock.pam.buffer ? Colours.palette.m3onPrimary : Colours.palette.m3onSurface
|
||||
onClicked: root.lock.pam.passwd.start()
|
||||
color: root.lock.pam.buffer ? Colours.palette.m3primary : Colours.layer(Colours.palette.m3surfaceContainerHigh, 2)
|
||||
shape: root.lock.pam.buffer ? MaterialShape.Arrow : MaterialShape.Circle
|
||||
scale: !root.lock.pam.buffer ? 1 : mouse.pressed ? 0.6 : mouse.containsMouse ? 0.8 : 0.7
|
||||
rotation: 90
|
||||
|
||||
Behavior on scale {
|
||||
Anim {
|
||||
type: Anim.FastSpatial
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
CAnim {}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouse
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: root.lock.pam.buffer ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
onClicked: root.lock.pam.buffer && root.lock.pam.passwd.start()
|
||||
}
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
|
|
@ -115,8 +147,15 @@ StyledRect {
|
|||
|
||||
anchors.centerIn: parent
|
||||
text: "arrow_forward"
|
||||
color: root.lock.pam.buffer ? Colours.palette.m3onPrimary : Colours.palette.m3onSurface
|
||||
fontStyle: Tokens.font.icon.size(Tokens.font.icon.large.pointSize).weight(Font.Medium).build()
|
||||
color: Colours.palette.m3onSurfaceVariant
|
||||
fontStyle: Tokens.font.icon.builders.medium.scale(root.centerScale * 1.2).build()
|
||||
opacity: root.lock.pam.buffer ? 0 : 1
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
type: Anim.DefaultEffects
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ Item {
|
|||
opacity: root.stateMsgShouldBeVisible && !root.msg ? 1 : 0
|
||||
color: Colours.palette.m3onSurfaceVariant
|
||||
|
||||
font: Tokens.font.mono.small
|
||||
font: Tokens.font.body.small
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
|
||||
lineHeight: 1.2
|
||||
|
|
@ -145,7 +145,7 @@ Item {
|
|||
opacity: 0
|
||||
color: Colours.palette.m3error
|
||||
|
||||
font: Tokens.font.mono.small
|
||||
font: Tokens.font.body.small
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ Item {
|
|||
|
||||
onClose: root.close()
|
||||
}
|
||||
property color blobColour: Theme.panelBg // NoSignal: fixed dark-glass window surface
|
||||
property color blobColour: Colours.tPalette.m3surfaceContainerLow
|
||||
|
||||
signal close
|
||||
|
||||
|
|
@ -30,7 +30,7 @@ Item {
|
|||
BlobGroup {
|
||||
id: blobGroup
|
||||
|
||||
smoothing: 0 // NoSignal: crisp corners (no organic blob smoothing)
|
||||
smoothing: root.Tokens.rounding.medium
|
||||
color: root.blobColour
|
||||
}
|
||||
|
||||
|
|
@ -38,7 +38,7 @@ Item {
|
|||
anchors.fill: parent
|
||||
group: blobGroup
|
||||
opacity: root.blobColour.a
|
||||
radius: Theme.radius.panel // NoSignal: sharp 4px corners
|
||||
radius: Tokens.rounding.large
|
||||
|
||||
borderLeft: navPane.width + navPane.anchors.margins * 2
|
||||
borderRight: Tokens.padding.medium
|
||||
|
|
|
|||
|
|
@ -73,20 +73,10 @@ QtObject {
|
|||
|
||||
// System
|
||||
Component {
|
||||
// Updates (NoSignal)
|
||||
StackPage {
|
||||
Component {
|
||||
UpdatesPage {}
|
||||
}
|
||||
}
|
||||
PlaceholderComp {}
|
||||
},
|
||||
Component {
|
||||
// Additions (NoSignal)
|
||||
StackPage {
|
||||
Component {
|
||||
AdditionsPage {}
|
||||
}
|
||||
}
|
||||
PlaceholderComp {}
|
||||
},
|
||||
|
||||
// Shell
|
||||
|
|
@ -165,11 +155,11 @@ QtObject {
|
|||
anchors.centerIn: parent
|
||||
spacing: Tokens.padding.extraSmall
|
||||
|
||||
NsIcon {
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
icon: "handyman"
|
||||
text: "handyman"
|
||||
color: Colours.palette.m3outlineVariant
|
||||
fontStyle: Tokens.font.icon.extraLarge
|
||||
font: Tokens.font.icon.extraLarge
|
||||
}
|
||||
|
||||
StyledText {
|
||||
|
|
|
|||
|
|
@ -50,9 +50,9 @@ QtObject {
|
|||
category: "system"
|
||||
},
|
||||
{
|
||||
label: qsTr("Additions"),
|
||||
label: qsTr("Plugins"),
|
||||
icon: "extension",
|
||||
description: qsTr("Install optional software"),
|
||||
description: qsTr("Manage plugins"),
|
||||
category: "system"
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -58,13 +58,13 @@ ItemList {
|
|||
radius: Tokens.rounding.full
|
||||
color: device.active ? Colours.palette.m3primary : Colours.palette.m3secondaryContainer
|
||||
|
||||
NsIcon {
|
||||
MaterialIcon {
|
||||
id: devIcon
|
||||
|
||||
anchors.centerIn: parent
|
||||
icon: root.iconName
|
||||
text: root.iconName
|
||||
color: device.active ? Colours.palette.m3onPrimary : Colours.palette.m3onSecondaryContainer
|
||||
fontStyle: Tokens.font.icon.medium
|
||||
font: Tokens.font.icon.medium
|
||||
fill: device.active ? 1 : 0
|
||||
|
||||
Behavior on fill {
|
||||
|
|
@ -80,10 +80,10 @@ ItemList {
|
|||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
NsIcon {
|
||||
icon: "check"
|
||||
MaterialIcon {
|
||||
text: "check"
|
||||
color: Colours.palette.m3primary
|
||||
fontStyle: Tokens.font.icon.medium
|
||||
font: Tokens.font.icon.medium
|
||||
opacity: device.active ? 1 : 0
|
||||
|
||||
Behavior on opacity {
|
||||
|
|
|
|||
|
|
@ -1,73 +0,0 @@
|
|||
// CachyRepoToggleRow.qml (NoSignal) — Settings -> Services toggle to enable /
|
||||
// disable the CachyOS pacman repositories. Auto-detects the best x86-64 microarch
|
||||
// tier the CPU supports (v4 > v3). Live state polls `nosignal-cachy-repo status`
|
||||
// (no root). enable/disable run in a floating terminal so the pacman/download
|
||||
// output stays visible; the grant is passwordless via sudo -n
|
||||
// (/etc/sudoers.d/02-nosignal-cachy). enable adds repos, converts userspace to
|
||||
// the optimized builds (pacman -Suu, pacman pinned stock), and installs the
|
||||
// linux-cachyos kernel; disable reverts to stock and removes the cachy kernel
|
||||
// (keeping stock `linux` bootable). A reboot switches the running kernel.
|
||||
//
|
||||
// Untracked file under modules/nexus/common — auto-discovered as the type
|
||||
// `CachyRepoToggleRow` via `import qs.modules.nexus.common`. Survives caelestia
|
||||
// upgrades; pairs with the one-line insert in ServicesPage.qml.
|
||||
import QtQuick
|
||||
import Quickshell.Io
|
||||
import qs.modules.nexus.common
|
||||
|
||||
ToggleRow {
|
||||
id: root
|
||||
|
||||
property bool repoOn: false // NB: not `enabled` (reserved Item property)
|
||||
|
||||
text: qsTr("CachyOS repositories")
|
||||
subtext: repoOn
|
||||
? qsTr("On — CachyOS optimized builds + linux-cachyos kernel (reboot to run it)")
|
||||
: qsTr("Switch to CachyOS optimized builds + linux-cachyos kernel (reboot after)")
|
||||
|
||||
onToggled: {
|
||||
if (checked)
|
||||
enableProc.running = true;
|
||||
else
|
||||
disableProc.running = true;
|
||||
reconcile.restart();
|
||||
}
|
||||
|
||||
// --- live state -----------------------------------------------------------
|
||||
Process {
|
||||
id: statusProc
|
||||
command: ["/usr/local/bin/nosignal-cachy-repo", "status"]
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
root.repoOn = text.trim() === "enabled";
|
||||
root.checked = root.repoOn; // drive switch from real state
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- actions (passwordless; shown in a terminal for pacman output) ---------
|
||||
Process {
|
||||
id: enableProc
|
||||
command: ["kitty", "--class", "TUI.float", "-e", "sh", "-c", "sudo -n /usr/local/bin/nosignal-cachy-repo enable; printf '\\nPress Enter to close...'; read _"]
|
||||
onExited: reconcile.restart()
|
||||
}
|
||||
Process {
|
||||
id: disableProc
|
||||
command: ["kitty", "--class", "TUI.float", "-e", "sh", "-c", "sudo -n /usr/local/bin/nosignal-cachy-repo disable; printf '\\nPress Enter to close...'; read _"]
|
||||
onExited: reconcile.restart()
|
||||
}
|
||||
|
||||
// --- polling --------------------------------------------------------------
|
||||
Timer {
|
||||
interval: 5000
|
||||
repeat: true
|
||||
running: true
|
||||
triggeredOnStart: true
|
||||
onTriggered: statusProc.running = true
|
||||
}
|
||||
Timer {
|
||||
id: reconcile
|
||||
interval: 2000
|
||||
onTriggered: statusProc.running = true
|
||||
}
|
||||
}
|
||||
|
|
@ -7,10 +7,9 @@ StyledRect {
|
|||
property bool first
|
||||
property bool last
|
||||
|
||||
// NoSignal: dark-glass cards, 4px outer corners (connected-list look)
|
||||
color: Theme.fillSubtle
|
||||
topLeftRadius: first ? Theme.radius.panel : 0
|
||||
topRightRadius: first ? Theme.radius.panel : 0
|
||||
bottomLeftRadius: last ? Theme.radius.panel : 0
|
||||
bottomRightRadius: last ? Theme.radius.panel : 0
|
||||
color: Colours.tPalette.m3surfaceContainer
|
||||
topLeftRadius: first ? Tokens.rounding.extraLarge : Tokens.rounding.extraSmall
|
||||
topRightRadius: first ? Tokens.rounding.extraLarge : Tokens.rounding.extraSmall
|
||||
bottomLeftRadius: last ? Tokens.rounding.extraLarge : Tokens.rounding.extraSmall
|
||||
bottomRightRadius: last ? Tokens.rounding.extraLarge : Tokens.rounding.extraSmall
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,11 +37,11 @@ ConnectedRect {
|
|||
sourceComponent: ColumnLayout {
|
||||
spacing: Tokens.spacing.extraSmall
|
||||
|
||||
NsIcon {
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
icon: root.placeholderIcon
|
||||
text: root.placeholderIcon
|
||||
color: Colours.palette.m3outline
|
||||
fontStyle: Tokens.font.icon.large
|
||||
font: Tokens.font.icon.large
|
||||
animate: true
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import qs.modules.nexus.common
|
|||
ConnectedRect {
|
||||
id: root
|
||||
|
||||
property alias icon: icon.icon
|
||||
property alias icon: icon.text
|
||||
property alias label: label.text
|
||||
property alias status: status.text
|
||||
|
||||
|
|
@ -32,11 +32,11 @@ ConnectedRect {
|
|||
anchors.rightMargin: Tokens.padding.largeIncreased
|
||||
spacing: Tokens.spacing.medium
|
||||
|
||||
NsIcon {
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
|
||||
color: Colours.palette.m3onSurfaceVariant
|
||||
fontStyle: Tokens.font.icon.medium
|
||||
font: Tokens.font.icon.medium
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
|
|
@ -63,10 +63,10 @@ ConnectedRect {
|
|||
}
|
||||
}
|
||||
|
||||
NsIcon {
|
||||
icon: "chevron_right"
|
||||
MaterialIcon {
|
||||
text: "chevron_right"
|
||||
color: Colours.palette.m3onSurfaceVariant
|
||||
fontStyle: Tokens.font.icon.medium
|
||||
font: Tokens.font.icon.medium
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import qs.modules.nexus.common
|
|||
ConnectedRect {
|
||||
id: root
|
||||
|
||||
property alias icon: icon.icon
|
||||
property alias icon: icon.text
|
||||
property alias label: label.text
|
||||
property alias valueLabel: valueLabel.text
|
||||
property real value
|
||||
|
|
@ -28,11 +28,11 @@ ConnectedRect {
|
|||
anchors.topMargin: Tokens.padding.large
|
||||
spacing: Tokens.spacing.medium
|
||||
|
||||
NsIcon {
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
|
||||
color: Colours.palette.m3onSurfaceVariant
|
||||
fontStyle: Tokens.font.icon.medium
|
||||
font: Tokens.font.icon.medium
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
|
|
|
|||
|
|
@ -1,102 +0,0 @@
|
|||
// SudoToggleRow.qml (NoSignal) — Settings -> Services toggle for time-boxed
|
||||
// passwordless sudo. Reflects live state by polling `nosignal-sudo-toggle
|
||||
// status` (no root needed); enabling opens a floating terminal for the ONE
|
||||
// password prompt, disabling runs passwordless inside the active window.
|
||||
//
|
||||
// Untracked file under modules/nexus/common — auto-discovered as the type
|
||||
// `SudoToggleRow` via `import qs.modules.nexus.common`. Survives caelestia
|
||||
// upgrades; only the one-line insert in ServicesPage.qml is re-applied by hook.
|
||||
import QtQuick
|
||||
import Quickshell.Io
|
||||
import qs.modules.nexus.common
|
||||
|
||||
ToggleRow {
|
||||
id: root
|
||||
|
||||
property bool active: false
|
||||
property int remaining: 0
|
||||
|
||||
text: qsTr("Passwordless sudo (15 min)")
|
||||
subtext: active
|
||||
? qsTr("On — %1 min left. Auto-reverts; a reboot also clears it.").arg(remaining)
|
||||
: qsTr("Run sudo without a password for 15 minutes, then it reverts")
|
||||
|
||||
onToggled: {
|
||||
if (checked) {
|
||||
enableProc.running = true; // needs a password -> floating terminal
|
||||
focusTimer.ticks = 0;
|
||||
focusTimer.restart(); // then pull keyboard focus to the prompt
|
||||
} else {
|
||||
disableProc.running = true; // no password inside the active window
|
||||
}
|
||||
reconcile.restart();
|
||||
}
|
||||
|
||||
// --- live state -----------------------------------------------------------
|
||||
Process {
|
||||
id: statusProc
|
||||
command: ["nosignal-sudo-toggle", "status"]
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
const p = text.trim().split(/\s+/);
|
||||
root.active = p[0] === "active";
|
||||
root.remaining = parseInt(p[1] || "0") || 0;
|
||||
root.checked = root.active; // drive switch from real state
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- actions --------------------------------------------------------------
|
||||
Process {
|
||||
id: enableProc
|
||||
// Dedicated window class (nosignal-sudo) so the shipped hypr windowrules
|
||||
// (float/center/pin/stayfocused) force this prompt to GRAB keyboard focus.
|
||||
// With the shared TUI.float class and no focus rule the prompt did not get
|
||||
// keystrokes -> three blank tries tripped pam_faillock and the switch just
|
||||
// snapped back with no feedback (hardware bug 2026-06-20). `enable-tui`
|
||||
// runs the sudo prompt as the user and keeps the terminal open on failure
|
||||
// so the reason is visible instead of vanishing.
|
||||
command: ["kitty", "--class", "nosignal-sudo", "-e", "nosignal-sudo-toggle", "enable-tui"]
|
||||
}
|
||||
Process {
|
||||
id: disableProc
|
||||
command: ["sudo", "-n", "nosignal-sudo-toggle", "disable"]
|
||||
}
|
||||
// Force keyboard focus onto the password prompt once it has mapped. Hyprland
|
||||
// 0.55's match: windowrule grammar has no working `stayfocused`, so we
|
||||
// "activate it" from here instead (the prompt uses the dedicated nosignal-sudo
|
||||
// class). Two nudges cover slow terminal startup.
|
||||
Process {
|
||||
id: focusProc
|
||||
command: ["hyprctl", "dispatch", "focuswindow", "class:^(nosignal-sudo)$"]
|
||||
}
|
||||
Timer {
|
||||
id: focusTimer
|
||||
interval: 400
|
||||
repeat: true
|
||||
triggeredOnStart: false
|
||||
property int ticks: 0
|
||||
onTriggered: {
|
||||
focusProc.running = true;
|
||||
ticks += 1;
|
||||
if (ticks >= 3) {
|
||||
ticks = 0;
|
||||
stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- polling --------------------------------------------------------------
|
||||
Timer {
|
||||
interval: 5000
|
||||
repeat: true
|
||||
running: true
|
||||
triggeredOnStart: true
|
||||
onTriggered: statusProc.running = true
|
||||
}
|
||||
Timer {
|
||||
id: reconcile
|
||||
interval: 2000
|
||||
onTriggered: statusProc.running = true
|
||||
}
|
||||
}
|
||||
|
|
@ -86,11 +86,11 @@ VerticalFadeFlickable {
|
|||
radius: Tokens.rounding.full
|
||||
color: item.isCurrentPage ? Colours.palette.m3primary : Colours.palette.m3secondaryContainer
|
||||
|
||||
NsIcon {
|
||||
MaterialIcon {
|
||||
anchors.centerIn: parent
|
||||
anchors.verticalCenterOffset: 1
|
||||
|
||||
icon: item.modelData.icon
|
||||
text: item.modelData.icon
|
||||
color: item.isCurrentPage ? Colours.palette.m3onPrimary : Colours.palette.m3onSecondaryContainer
|
||||
fontStyle: Tokens.font.icon.builders.medium.weight(Font.Medium).build()
|
||||
grade: 25
|
||||
|
|
|
|||
|
|
@ -37,8 +37,8 @@ StyledRect {
|
|||
|
||||
spacing: Tokens.spacing.small
|
||||
|
||||
NsIcon {
|
||||
icon: "search"
|
||||
MaterialIcon {
|
||||
text: "search"
|
||||
color: Colours.palette.m3onSurfaceVariant
|
||||
fontStyle: Tokens.font.icon.medium
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Caelestia
|
||||
import Caelestia.Config
|
||||
|
|
@ -62,27 +61,22 @@ PageBase {
|
|||
width: parent.width - Tokens.padding.largeIncreased * 2
|
||||
spacing: Tokens.spacing.small
|
||||
|
||||
Image {
|
||||
AnimatedLogo {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.preferredWidth: 104
|
||||
Layout.preferredHeight: 104
|
||||
source: Quickshell.shellDir + "/assets/nosignal-logo.png"
|
||||
sourceSize.width: 208
|
||||
sourceSize.height: 208
|
||||
fillMode: Image.PreserveAspectFit
|
||||
smooth: true
|
||||
Layout.preferredWidth: implicitWidth
|
||||
Layout.preferredHeight: implicitHeight
|
||||
}
|
||||
|
||||
StyledText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.topMargin: Tokens.spacing.small
|
||||
text: "NoSignal"
|
||||
text: "Caelestia"
|
||||
font: Tokens.font.headline.builders.large.width(110).build()
|
||||
}
|
||||
|
||||
StyledText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: "V1"
|
||||
text: CUtils.version ? `v${CUtils.version}` : "…"
|
||||
color: Colours.palette.m3onSurfaceVariant
|
||||
font: Tokens.font.body.medium
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,120 +0,0 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell.Io
|
||||
import Caelestia.Config
|
||||
import qs.components
|
||||
import qs.services
|
||||
import qs.modules.nexus.common
|
||||
|
||||
// NoSignal: the Additions settings page (replaces the upstream Plugins
|
||||
// placeholder). Optional software installed on demand from official sources
|
||||
// (pacman repos / upstream installers — no AUR, no Flatpak). Items come from
|
||||
// the additions.json manifest via the status cache written by
|
||||
// nosignal-additions; Install runs in a visible floating terminal so
|
||||
// git/sudo/pacman output and prompts stay in front of the user.
|
||||
PageBase {
|
||||
id: root
|
||||
|
||||
title: qsTr("Additions")
|
||||
|
||||
property var status: ({})
|
||||
readonly property var items: status.items || []
|
||||
|
||||
ColumnLayout {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
width: root.cappedWidth
|
||||
spacing: Tokens.spacing.extraSmall / 2
|
||||
|
||||
// The Process objects live INSIDE the layout (its `data` accepts
|
||||
// non-visual objects) — PageBase's default property is a single
|
||||
// `Item`, so declaring them at page level kills the whole shell.
|
||||
// Same pattern as UpdatesPage / the upstream AboutPage.
|
||||
|
||||
// Read the cached status (instant).
|
||||
Process {
|
||||
id: readProc
|
||||
|
||||
running: true
|
||||
command: ["sh", "-c", "cat \"${XDG_STATE_HOME:-$HOME/.local/state}/nosignal/additions-status.json\" 2>/dev/null"]
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
try {
|
||||
root.status = JSON.parse(text);
|
||||
} catch (e) {
|
||||
root.status = {};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh the cache on demand (re-runs every item's check).
|
||||
Process {
|
||||
id: checkProc
|
||||
|
||||
command: ["sh", "-c", "\"$HOME/.local/bin/nosignal-additions\" status >/dev/null 2>&1"]
|
||||
onExited: readProc.running = true
|
||||
}
|
||||
|
||||
// Run an installer in a visible floating terminal.
|
||||
Process {
|
||||
id: installProc
|
||||
|
||||
property string addId: ""
|
||||
|
||||
command: ["kitty", "--class", "TUI.float", "-e", "sh", "-c", "\"$HOME/.local/bin/nosignal-additions\" install " + addId + "; printf '\\nPress Enter to close...'; read _"]
|
||||
onExited: readProc.running = true
|
||||
}
|
||||
|
||||
SectionHeader {
|
||||
text: qsTr("Optional software")
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: root.items
|
||||
|
||||
NavRow {
|
||||
required property var modelData
|
||||
required property int index
|
||||
|
||||
first: index === 0
|
||||
last: index === root.items.length - 1
|
||||
icon: modelData.icon || "extension"
|
||||
label: modelData.name
|
||||
status: modelData.installed ? qsTr("Installed") : modelData.desc
|
||||
onClicked: {
|
||||
if (!modelData.installed && !installProc.running) {
|
||||
installProc.addId = modelData.id;
|
||||
installProc.running = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InfoRow {
|
||||
visible: root.items.length === 0
|
||||
first: true
|
||||
last: true
|
||||
label: qsTr("No additions manifest")
|
||||
value: "—"
|
||||
}
|
||||
|
||||
SectionHeader {
|
||||
text: qsTr("Actions")
|
||||
}
|
||||
|
||||
NavRow {
|
||||
first: true
|
||||
last: true
|
||||
icon: "refresh"
|
||||
label: qsTr("Re-check installed state")
|
||||
status: installProc.running ? qsTr("Install running in terminal…") : (checkProc.running ? qsTr("Checking…") : qsTr("Refreshes the list above"))
|
||||
onClicked: {
|
||||
if (!checkProc.running)
|
||||
checkProc.running = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -97,9 +97,9 @@ PageBase {
|
|||
anchors.rightMargin: Tokens.padding.largeIncreased
|
||||
spacing: Tokens.spacing.medium
|
||||
|
||||
NsIcon {
|
||||
icon: "tune"
|
||||
fontStyle: Tokens.font.icon.medium
|
||||
MaterialIcon {
|
||||
text: "tune"
|
||||
font: Tokens.font.icon.medium
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
|
|
@ -123,10 +123,10 @@ PageBase {
|
|||
}
|
||||
}
|
||||
|
||||
NsIcon {
|
||||
icon: "chevron_right"
|
||||
MaterialIcon {
|
||||
text: "chevron_right"
|
||||
color: Colours.palette.m3onSurfaceVariant
|
||||
fontStyle: Tokens.font.icon.medium
|
||||
font: Tokens.font.icon.medium
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,13 +93,13 @@ PageBase {
|
|||
radius: Tokens.rounding.full
|
||||
color: device.connected ? Colours.palette.m3primary : Colours.palette.m3secondaryContainer
|
||||
|
||||
NsIcon {
|
||||
MaterialIcon {
|
||||
id: deviceIcon
|
||||
|
||||
anchors.centerIn: parent
|
||||
icon: Icons.getBluetoothIcon(device.modelData?.icon ?? "")
|
||||
text: Icons.getBluetoothIcon(device.modelData?.icon ?? "")
|
||||
color: device.connected ? Colours.palette.m3onPrimary : Colours.palette.m3onSecondaryContainer
|
||||
fontStyle: Tokens.font.icon.medium
|
||||
font: Tokens.font.icon.medium
|
||||
fill: device.connected ? 1 : 0
|
||||
opacity: device.textOpacity
|
||||
|
||||
|
|
@ -193,9 +193,9 @@ PageBase {
|
|||
Anim {}
|
||||
}
|
||||
|
||||
NsIcon {
|
||||
icon: "add"
|
||||
fontStyle: Tokens.font.icon.medium
|
||||
MaterialIcon {
|
||||
text: "add"
|
||||
font: Tokens.font.icon.medium
|
||||
}
|
||||
|
||||
StyledText {
|
||||
|
|
|
|||
|
|
@ -92,71 +92,41 @@ PageBase {
|
|||
text: qsTr("Weather")
|
||||
}
|
||||
|
||||
// Location for the weather service. Weather.qml accepts a city name
|
||||
// (geocoded) or a "lat,lon" string, and auto-detects via IP when empty.
|
||||
// Placeholder until the map-based location picker lands
|
||||
ConnectedRect {
|
||||
Layout.fillWidth: true
|
||||
first: true
|
||||
last: true
|
||||
implicitHeight: weatherLayout.implicitHeight + weatherLayout.anchors.margins * 2
|
||||
implicitHeight: comingSoon.implicitHeight + Tokens.padding.extraLarge * 2
|
||||
|
||||
ColumnLayout {
|
||||
id: weatherLayout
|
||||
id: comingSoon
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Tokens.padding.medium
|
||||
anchors.leftMargin: Tokens.padding.largeIncreased
|
||||
anchors.rightMargin: Tokens.padding.largeIncreased
|
||||
spacing: Tokens.spacing.small
|
||||
anchors.centerIn: parent
|
||||
width: parent.width - Tokens.padding.largeIncreased * 2
|
||||
spacing: Tokens.padding.extraSmall
|
||||
|
||||
StyledText {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Location")
|
||||
font: Tokens.font.body.small
|
||||
elide: Text.ElideRight
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: "map"
|
||||
color: Colours.palette.m3outlineVariant
|
||||
font: Tokens.font.icon.extraLarge
|
||||
}
|
||||
|
||||
StyledRect {
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: locField.implicitHeight + Tokens.padding.medium * 2
|
||||
|
||||
radius: Tokens.rounding.small
|
||||
color: Colours.tPalette.m3surfaceContainerLowest
|
||||
border.color: Colours.palette.m3outlineVariant
|
||||
|
||||
Behavior on border.color {
|
||||
CAnim {}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.IBeamCursor
|
||||
onClicked: locField.focus = true
|
||||
}
|
||||
|
||||
StyledTextField {
|
||||
id: locField
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.leftMargin: Tokens.padding.large
|
||||
anchors.rightMargin: Tokens.padding.large
|
||||
|
||||
text: GlobalConfig.services.weatherLocation
|
||||
placeholderText: qsTr("City name or lat,long — empty = auto (IP)")
|
||||
placeholderTextColor: Colours.palette.m3onSurfaceVariant
|
||||
color: Colours.palette.m3onSurface
|
||||
onEditingFinished: GlobalConfig.services.weatherLocation = text
|
||||
}
|
||||
StyledText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: qsTr("Location picker coming soon")
|
||||
color: Colours.palette.m3outlineVariant
|
||||
font: Tokens.font.title.small
|
||||
}
|
||||
|
||||
StyledText {
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.WordWrap
|
||||
text: qsTr("Leave empty to detect your location automatically.")
|
||||
color: Colours.palette.m3outline
|
||||
font: Tokens.font.label.small
|
||||
text: qsTr("Choose your weather location on a map in a future update")
|
||||
color: Colours.palette.m3outlineVariant
|
||||
font: Tokens.font.body.small
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -131,10 +131,10 @@ PageBase {
|
|||
anchors.rightMargin: Tokens.padding.extraLarge
|
||||
spacing: Tokens.spacing.medium
|
||||
|
||||
NsIcon {
|
||||
icon: Icons.getNetworkIcon(network.modelData.strength)
|
||||
MaterialIcon {
|
||||
text: Icons.getNetworkIcon(network.modelData.strength)
|
||||
color: network.modelData.active ? Colours.palette.m3primary : Colours.palette.m3onSurfaceVariant
|
||||
fontStyle: Tokens.font.icon.medium
|
||||
font: Tokens.font.icon.medium
|
||||
opacity: network.textOpacity
|
||||
}
|
||||
|
||||
|
|
@ -165,10 +165,10 @@ PageBase {
|
|||
Component {
|
||||
id: iconComp
|
||||
|
||||
NsIcon {
|
||||
icon: network.modelData.active ? "settings" : "lock"
|
||||
MaterialIcon {
|
||||
text: network.modelData.active ? "settings" : "lock"
|
||||
color: network.modelData.active ? Colours.palette.m3primary : Colours.palette.m3onSurfaceVariant
|
||||
fontStyle: Tokens.font.icon.medium
|
||||
font: Tokens.font.icon.medium
|
||||
opacity: network.textOpacity
|
||||
}
|
||||
}
|
||||
|
|
@ -218,9 +218,9 @@ PageBase {
|
|||
|
||||
spacing: Tokens.spacing.medium
|
||||
|
||||
NsIcon {
|
||||
icon: "add"
|
||||
fontStyle: Tokens.font.icon.medium
|
||||
MaterialIcon {
|
||||
text: "add"
|
||||
font: Tokens.font.icon.medium
|
||||
}
|
||||
|
||||
StyledText {
|
||||
|
|
|
|||
|
|
@ -211,16 +211,6 @@ PageBase {
|
|||
onToggled: GlobalConfig.services.smartScheme = checked
|
||||
}
|
||||
|
||||
// NoSignal: time-boxed passwordless sudo (15 min) toggle
|
||||
SudoToggleRow {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
// NoSignal: CachyOS repos enable/disable (sudoless, auto-tier)
|
||||
CachyRepoToggleRow {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
SelectRow {
|
||||
Layout.fillWidth: true
|
||||
last: true
|
||||
|
|
|
|||
|
|
@ -1,139 +0,0 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell.Io
|
||||
import Caelestia.Config
|
||||
import qs.components
|
||||
import qs.services
|
||||
import qs.modules.nexus.common
|
||||
|
||||
// NoSignal: the Updates settings page (replaces the upstream placeholder).
|
||||
// Reads the JSON cache written by nosignal-update-check; "Update now" runs
|
||||
// nosignal-update in a floating terminal (sudo + pacman prompts stay visible).
|
||||
PageBase {
|
||||
id: root
|
||||
|
||||
title: qsTr("Updates")
|
||||
|
||||
property var status: ({})
|
||||
|
||||
function count(v) {
|
||||
return v === undefined ? "…" : String(v);
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
width: root.cappedWidth
|
||||
spacing: Tokens.spacing.extraSmall / 2
|
||||
|
||||
// The Process objects live INSIDE the layout (its `data` accepts
|
||||
// non-visual objects) — PageBase's default property is a single
|
||||
// `Item`, so declaring them at page level kills the whole shell
|
||||
// ("Cannot assign Process to QQuickItem*"). Same pattern as the
|
||||
// upstream AboutPage.
|
||||
|
||||
// Read the cached status (instant; the user timer keeps it fresh).
|
||||
Process {
|
||||
id: readProc
|
||||
|
||||
running: true
|
||||
command: ["sh", "-c", "cat \"${XDG_STATE_HOME:-$HOME/.local/state}/nosignal/update-status.json\" 2>/dev/null"]
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
try {
|
||||
root.status = JSON.parse(text);
|
||||
} catch (e) {
|
||||
root.status = {};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh the cache on demand.
|
||||
Process {
|
||||
id: checkProc
|
||||
|
||||
command: ["sh", "-c", "\"$HOME/.local/bin/nosignal-update-check\" >/dev/null 2>&1"]
|
||||
onExited: readProc.running = true
|
||||
}
|
||||
|
||||
// Run the real update in a visible floating terminal.
|
||||
Process {
|
||||
id: updateProc
|
||||
|
||||
command: ["kitty", "--class", "TUI.float", "-e", "sh", "-c", "\"$HOME/.local/bin/nosignal-update\"; \"$HOME/.local/bin/nosignal-update-check\" >/dev/null 2>&1; printf '\\nPress Enter to close...'; read _"]
|
||||
onExited: readProc.running = true
|
||||
}
|
||||
|
||||
SectionHeader {
|
||||
text: qsTr("Pending updates")
|
||||
}
|
||||
|
||||
InfoRow {
|
||||
first: true
|
||||
label: qsTr("Official packages")
|
||||
value: root.count(root.status.repo)
|
||||
}
|
||||
|
||||
InfoRow {
|
||||
label: qsTr("AUR packages")
|
||||
value: root.count(root.status.aur)
|
||||
}
|
||||
|
||||
InfoRow {
|
||||
label: qsTr("Flatpak")
|
||||
value: root.count(root.status.flatpak)
|
||||
}
|
||||
|
||||
InfoRow {
|
||||
last: true
|
||||
label: qsTr("NoSignal layer")
|
||||
subtext: qsTr("Idempotent migrations applied by nosignal-update")
|
||||
value: root.status.migrations_pending === undefined ? "…" : (root.status.migrations_pending > 0 ? qsTr("%1 migration(s) pending").arg(root.status.migrations_pending) : qsTr("up to date"))
|
||||
}
|
||||
|
||||
SectionHeader {
|
||||
text: qsTr("History")
|
||||
}
|
||||
|
||||
InfoRow {
|
||||
first: true
|
||||
label: qsTr("Last full upgrade")
|
||||
value: root.status.last_upgrade || "—"
|
||||
}
|
||||
|
||||
InfoRow {
|
||||
last: true
|
||||
label: qsTr("Last checked")
|
||||
value: root.status.checked ? root.status.checked.replace("T", " ").substring(0, 16) : "—"
|
||||
}
|
||||
|
||||
SectionHeader {
|
||||
text: qsTr("Actions")
|
||||
}
|
||||
|
||||
NavRow {
|
||||
first: true
|
||||
icon: "refresh"
|
||||
label: qsTr("Check for updates now")
|
||||
status: checkProc.running ? qsTr("Checking…") : qsTr("Refreshes the counts above")
|
||||
onClicked: {
|
||||
if (!checkProc.running)
|
||||
checkProc.running = true;
|
||||
}
|
||||
}
|
||||
|
||||
NavRow {
|
||||
last: true
|
||||
icon: "system_update_alt"
|
||||
label: qsTr("Update now")
|
||||
status: updateProc.running ? qsTr("Running in terminal…") : qsTr("Opens a terminal: snapshot, packages, NoSignal layer")
|
||||
onClicked: {
|
||||
if (!updateProc.running)
|
||||
updateProc.running = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -46,11 +46,11 @@ PageBase {
|
|||
sourceComponent: ColumnLayout {
|
||||
spacing: Tokens.spacing.extraSmall
|
||||
|
||||
NsIcon {
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
icon: "hide_image"
|
||||
text: "hide_image"
|
||||
color: Colours.palette.m3onSurfaceVariant
|
||||
fontStyle: Tokens.font.icon.extraLarge
|
||||
font: Tokens.font.icon.extraLarge
|
||||
}
|
||||
|
||||
StyledText {
|
||||
|
|
|
|||
|
|
@ -125,10 +125,10 @@ PageBase {
|
|||
anchors.rightMargin: Tokens.padding.largeIncreased
|
||||
spacing: Tokens.spacing.medium
|
||||
|
||||
NsIcon {
|
||||
icon: Icons.getBluetoothIcon(newDevice.modelData?.icon ?? "")
|
||||
MaterialIcon {
|
||||
text: Icons.getBluetoothIcon(newDevice.modelData?.icon ?? "")
|
||||
color: Colours.palette.m3onSurfaceVariant
|
||||
fontStyle: Tokens.font.icon.medium
|
||||
font: Tokens.font.icon.medium
|
||||
opacity: newDevice.textOpacity
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -72,11 +72,11 @@ PageBase {
|
|||
anchors.centerIn: parent
|
||||
spacing: 0
|
||||
|
||||
NsIcon {
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
icon: "delete"
|
||||
text: "delete"
|
||||
color: forgetBtn.onColour
|
||||
fontStyle: Tokens.font.icon.medium
|
||||
font: Tokens.font.icon.medium
|
||||
}
|
||||
|
||||
StyledText {
|
||||
|
|
@ -126,11 +126,11 @@ PageBase {
|
|||
ColumnLayout {
|
||||
spacing: 0
|
||||
|
||||
NsIcon {
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
icon: root.connected ? "close" : "add"
|
||||
text: root.connected ? "close" : "add"
|
||||
color: connectBtn.inactiveOnColour
|
||||
fontStyle: Tokens.font.icon.medium
|
||||
font: Tokens.font.icon.medium
|
||||
animate: true
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,11 +22,11 @@ PageBase {
|
|||
anchors.centerIn: parent
|
||||
spacing: Tokens.padding.extraSmall
|
||||
|
||||
NsIcon {
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
icon: "handyman"
|
||||
text: "handyman"
|
||||
color: Colours.palette.m3outlineVariant
|
||||
fontStyle: Tokens.font.icon.extraLarge
|
||||
font: Tokens.font.icon.extraLarge
|
||||
}
|
||||
|
||||
StyledText {
|
||||
|
|
|
|||
|
|
@ -168,9 +168,9 @@ PageBase {
|
|||
anchors.centerIn: parent
|
||||
spacing: Tokens.spacing.extraSmall
|
||||
|
||||
NsIcon {
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
icon: "hide_image"
|
||||
text: "hide_image"
|
||||
color: Colours.palette.m3outline
|
||||
fontStyle: Tokens.font.icon.extraLarge
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ Item {
|
|||
anchors.rightMargin: root.clampedPadding
|
||||
|
||||
color: "transparent"
|
||||
radius: 0
|
||||
radius: Tokens.rounding.large
|
||||
|
||||
StyledListView {
|
||||
id: list
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ StyledRect {
|
|||
property bool expanded: Config.notifs.openExpanded
|
||||
|
||||
color: root.modelData.urgency === NotificationUrgency.Critical ? Colours.palette.m3secondaryContainer : Colours.tPalette.m3surfaceContainer
|
||||
radius: 0
|
||||
radius: Tokens.rounding.large
|
||||
|
||||
implicitHeight: inner.implicitHeight
|
||||
|
||||
|
|
@ -116,7 +116,7 @@ StyledRect {
|
|||
visible: root.hasImage || root.hasAppIcon
|
||||
|
||||
sourceComponent: StyledClippingRect {
|
||||
radius: 0
|
||||
radius: Tokens.rounding.full
|
||||
color: root.modelData.urgency === NotificationUrgency.Critical ? Colours.palette.m3error : root.modelData.urgency === NotificationUrgency.Low ? Colours.layer(Colours.palette.m3surfaceContainerHighest, 2) : Colours.palette.m3secondaryContainer
|
||||
implicitWidth: TokenConfig.sizes.notifs.image
|
||||
implicitHeight: TokenConfig.sizes.notifs.image
|
||||
|
|
@ -147,7 +147,7 @@ StyledRect {
|
|||
anchors.bottom: root.hasImage ? image.bottom : undefined
|
||||
|
||||
sourceComponent: StyledRect {
|
||||
radius: 0
|
||||
radius: Tokens.rounding.full
|
||||
color: root.modelData.urgency === NotificationUrgency.Critical ? Colours.palette.m3error : root.modelData.urgency === NotificationUrgency.Low ? Colours.layer(Colours.palette.m3surfaceContainerHighest, 2) : Colours.palette.m3secondaryContainer
|
||||
implicitWidth: root.hasImage ? Tokens.sizes.notifs.badge : TokenConfig.sizes.notifs.image
|
||||
implicitHeight: root.hasImage ? Tokens.sizes.notifs.badge : TokenConfig.sizes.notifs.image
|
||||
|
|
@ -177,8 +177,8 @@ StyledRect {
|
|||
anchors.centerIn: parent
|
||||
anchors.verticalCenterOffset: 1
|
||||
|
||||
sourceComponent: NsIcon {
|
||||
icon: Icons.getNotifIcon(root.modelData.summary, root.modelData.urgency)
|
||||
sourceComponent: MaterialIcon {
|
||||
text: Icons.getNotifIcon(root.modelData.summary, root.modelData.urgency)
|
||||
color: root.modelData.urgency === NotificationUrgency.Critical ? Colours.palette.m3onError : root.modelData.urgency === NotificationUrgency.Low ? Colours.palette.m3onSurface : Colours.palette.m3onSecondaryContainer
|
||||
fontStyle: Tokens.font.icon.medium
|
||||
}
|
||||
|
|
@ -358,17 +358,17 @@ StyledRect {
|
|||
implicitHeight: expandIcon.implicitHeight
|
||||
|
||||
StateLayer {
|
||||
radius: 0
|
||||
radius: Tokens.rounding.full
|
||||
color: root.modelData.urgency === NotificationUrgency.Critical ? Colours.palette.m3onSecondaryContainer : Colours.palette.m3onSurface
|
||||
onClicked: root.expanded = !root.expanded
|
||||
}
|
||||
|
||||
NsIcon {
|
||||
MaterialIcon {
|
||||
id: expandIcon
|
||||
|
||||
anchors.centerIn: parent
|
||||
anchors.verticalCenterOffset: root.expanded ? -1 : 1
|
||||
icon: "expand_more"
|
||||
text: "expand_more"
|
||||
fontStyle: Tokens.font.icon.medium
|
||||
rotation: root.expanded ? 180 : 0
|
||||
|
||||
|
|
|
|||
|
|
@ -1,46 +0,0 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
|
||||
// NoSignal bar module pill: hover-highlightable (radius 5, hover bg); when its
|
||||
// panel is open it uses accent.soft bg + accent foreground. Children go in a
|
||||
// centered Row. `active` drives the open/highlight state; `triggered()` fires on
|
||||
// click (the bar wires this to toggle its openPanel string).
|
||||
|
||||
import QtQuick
|
||||
import qs.services
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property bool active: false
|
||||
property int hPad: 9
|
||||
signal triggered
|
||||
|
||||
default property alias content: inner.data
|
||||
|
||||
implicitWidth: inner.implicitWidth + hPad * 2
|
||||
implicitHeight: 26
|
||||
radius: Theme.radius.barModule
|
||||
color: active ? Theme.accentSoft : mouse.containsMouse ? Theme.hover : "transparent"
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Theme.hoverMs
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: inner
|
||||
|
||||
anchors.centerIn: parent
|
||||
spacing: 6
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouse
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: root.triggered()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,260 +0,0 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
|
||||
// NoSignal slim top bar (interface redesign 2026-06-14).
|
||||
// Flat 38px translucent-glass PanelWindow at the top edge. Three zones:
|
||||
// left : logo · | · workspaces · | · window-title
|
||||
// center: clock
|
||||
// right : media · bell · wifi · bt · volume · battery · | · power
|
||||
// Each interactive module is a BarPill (hover bg; active = accent.soft + accent
|
||||
// fg). `openPanel` is the single shell-level "one panel open at a time" string;
|
||||
// clicking a pill toggles it. The popup panels themselves are built next — for
|
||||
// now a click just highlights the pill. Reuses the existing service singletons.
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Bluetooth
|
||||
import Quickshell.Services.UPower
|
||||
import Quickshell.Services.SystemTray
|
||||
import qs.services
|
||||
import qs.utils
|
||||
import qs.components
|
||||
import qs.modules.bar.components as BC
|
||||
import qs.modules.bar.components.workspaces as WS
|
||||
|
||||
Variants {
|
||||
model: Screens.screens
|
||||
|
||||
PanelWindow {
|
||||
id: win
|
||||
|
||||
required property ShellScreen modelData
|
||||
|
||||
screen: modelData
|
||||
color: "transparent"
|
||||
WlrLayershell.namespace: "nsbar" // for Hyprland blur layerrule
|
||||
WlrLayershell.layer: WlrLayer.Top
|
||||
|
||||
anchors.top: true
|
||||
anchors.left: true
|
||||
anchors.right: true
|
||||
|
||||
implicitHeight: Theme.size.barHeight
|
||||
exclusiveZone: Theme.size.barHeight
|
||||
|
||||
readonly property int hPad: 12
|
||||
readonly property int gap: 6
|
||||
|
||||
// ── Glass surface + 1px bottom border ──────────────────────────────
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: Theme.barBg
|
||||
|
||||
Rectangle {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
height: 1
|
||||
color: Theme.barBorder
|
||||
}
|
||||
}
|
||||
|
||||
// ── Left zone ──────────────────────────────────────────────────────
|
||||
RowLayout {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: win.hPad
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: win.gap
|
||||
|
||||
BC.OsIcon {}
|
||||
|
||||
Divider {}
|
||||
|
||||
NsWorkspaces {}
|
||||
|
||||
Divider {}
|
||||
|
||||
BarPill {
|
||||
id: titlePill
|
||||
|
||||
active: NsShell.open === "overview"
|
||||
onTriggered: NsShell.toggle("overview", mapToItem(null, width / 2, 0).x, win.modelData.name)
|
||||
|
||||
NsIcon {
|
||||
icon: "desktop_windows"
|
||||
color: titlePill.active ? Theme.accent : Theme.textMuted
|
||||
}
|
||||
|
||||
StyledText {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: Hypr.activeToplevel?.title ?? "Desktop"
|
||||
elide: Text.ElideRight
|
||||
width: Math.min(implicitWidth, 220)
|
||||
color: titlePill.active ? Theme.accent : Theme.text
|
||||
font.family: Theme.font.family
|
||||
font.pixelSize: Theme.font.bar
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Center zone: clock ─────────────────────────────────────────────
|
||||
BarPill {
|
||||
id: clockPill
|
||||
|
||||
anchors.centerIn: parent
|
||||
active: NsShell.open === "calendar"
|
||||
onTriggered: NsShell.toggle("calendar", mapToItem(null, width / 2, 0).x, win.modelData.name)
|
||||
|
||||
BC.Clock {}
|
||||
}
|
||||
|
||||
// ── Right zone ─────────────────────────────────────────────────────
|
||||
RowLayout {
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: win.hPad
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: win.gap
|
||||
|
||||
// Media / now-playing
|
||||
BarPill {
|
||||
id: mediaPill
|
||||
|
||||
visible: Players.active !== null
|
||||
active: NsShell.open === "quicksettings"
|
||||
onTriggered: NsShell.toggle("quicksettings", mapToItem(null, width / 2, 0).x, win.modelData.name)
|
||||
|
||||
NsIcon {
|
||||
icon: Players.active?.isPlaying ? "pause" : "music_note"
|
||||
color: mediaPill.active ? Theme.accent : Theme.green
|
||||
}
|
||||
|
||||
StyledText {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: Players.active?.trackTitle || "Awake"
|
||||
elide: Text.ElideRight
|
||||
width: Math.min(implicitWidth, 90)
|
||||
color: mediaPill.active ? Theme.accent : Theme.text
|
||||
font.family: Theme.font.family
|
||||
font.pixelSize: Theme.font.bar
|
||||
}
|
||||
}
|
||||
|
||||
// System tray — only shown when an app actually registers a tray
|
||||
// icon, otherwise the pill just opened an empty panel (F-T1).
|
||||
IconPill {
|
||||
icon: "widgets"
|
||||
panel: "tray"
|
||||
visible: SystemTray.items.values.length > 0
|
||||
}
|
||||
|
||||
// Notifications bell (with unread dot overlaid on the icon)
|
||||
BarPill {
|
||||
id: bellPill
|
||||
|
||||
active: NsShell.open === "notifications"
|
||||
onTriggered: NsShell.toggle("notifications", mapToItem(null, width / 2, 0).x, win.modelData.name)
|
||||
|
||||
NsIcon {
|
||||
icon: Notifs.notClosed.length > 0 ? "notifications" : "notifications_none"
|
||||
color: bellPill.active ? Theme.accent : Theme.text
|
||||
|
||||
Rectangle {
|
||||
visible: Notifs.notClosed.length > 0
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.rightMargin: -1
|
||||
anchors.topMargin: 1
|
||||
implicitWidth: 6
|
||||
implicitHeight: 6
|
||||
radius: 0
|
||||
color: Theme.accent
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wi-Fi
|
||||
IconPill {
|
||||
icon: Nmcli.active ? Icons.getNetworkIcon(Nmcli.active.strength ?? 0) : "wifi_off"
|
||||
panel: "network"
|
||||
}
|
||||
|
||||
// Bluetooth
|
||||
IconPill {
|
||||
icon: {
|
||||
if (!Bluetooth.defaultAdapter?.enabled)
|
||||
return "bluetooth_disabled";
|
||||
if (Bluetooth.devices.values.some(d => d.connected))
|
||||
return "bluetooth_connected";
|
||||
return "bluetooth";
|
||||
}
|
||||
panel: "bluetooth"
|
||||
}
|
||||
|
||||
// Volume
|
||||
IconPill {
|
||||
icon: Icons.getVolumeIcon(Audio.volume, Audio.muted)
|
||||
panel: "audio"
|
||||
}
|
||||
|
||||
// Battery / Quick Settings — always present (control-centre trigger):
|
||||
// battery + % on laptops, a "tune" icon on desktops.
|
||||
BarPill {
|
||||
id: batteryPill
|
||||
|
||||
readonly property bool laptop: UPower.displayDevice.isLaptopBattery
|
||||
active: NsShell.open === "quicksettings"
|
||||
onTriggered: NsShell.toggle("quicksettings", mapToItem(null, width / 2, 0).x, win.modelData.name)
|
||||
|
||||
NsIcon {
|
||||
icon: batteryPill.laptop ? Icons.getBatteryIcon(UPower.displayDevice.percentage, [UPowerDeviceState.Charging, UPowerDeviceState.FullyCharged, UPowerDeviceState.PendingCharge].includes(UPower.displayDevice.state)) : "tune"
|
||||
color: batteryPill.active ? Theme.accent : !batteryPill.laptop || !UPower.onBattery || UPower.displayDevice.percentage > 0.2 ? Theme.text : Theme.danger
|
||||
fill: 1
|
||||
}
|
||||
|
||||
StyledText {
|
||||
visible: batteryPill.laptop
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: Math.round(UPower.displayDevice.percentage * 100) + "%"
|
||||
color: batteryPill.active ? Theme.accent : Theme.text
|
||||
font.family: Theme.font.family
|
||||
font.pixelSize: Theme.font.bar
|
||||
}
|
||||
}
|
||||
|
||||
Divider {}
|
||||
|
||||
// Power
|
||||
IconPill {
|
||||
icon: "power_settings_new"
|
||||
panel: "power"
|
||||
iconColour: Theme.danger
|
||||
}
|
||||
}
|
||||
|
||||
// ── Reusable bits ──────────────────────────────────────────────────
|
||||
component Divider: Rectangle {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
implicitWidth: 1
|
||||
implicitHeight: 16
|
||||
color: Theme.barBorder
|
||||
}
|
||||
|
||||
// Icon-only pill bound to the shell openPanel state
|
||||
component IconPill: BarPill {
|
||||
id: p
|
||||
|
||||
property string icon
|
||||
property string panel
|
||||
property color iconColour: Theme.text
|
||||
|
||||
active: NsShell.open === panel
|
||||
onTriggered: NsShell.toggle(panel, mapToItem(null, width / 2, 0).x, win.modelData.name)
|
||||
|
||||
NsIcon {
|
||||
icon: p.icon
|
||||
color: p.active ? Theme.accent : p.iconColour
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,166 +0,0 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
|
||||
// NoSignal popup overlay: one full-screen layer per screen that hosts the active
|
||||
// floating panel (driven by NsShell.open). A transparent click-catcher behind the
|
||||
// panel closes on outside-click; Escape also closes. Panels are positioned below
|
||||
// the bar, anchored center / right / under-trigger per their `anchorMode`.
|
||||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import qs.services
|
||||
import "panels" as Panels
|
||||
|
||||
Variants {
|
||||
model: Screens.screens
|
||||
|
||||
PanelWindow {
|
||||
id: ov
|
||||
|
||||
required property ShellScreen modelData
|
||||
|
||||
// This overlay owns the panel only when NsShell.screen names this output
|
||||
// (or is "" = unscoped, e.g. the global power modal → show on all screens).
|
||||
// Without this, every screen's overlay rendered the panel → mirrored popups.
|
||||
readonly property bool onThisScreen: NsShell.screen === "" || NsShell.screen === modelData.name
|
||||
|
||||
screen: modelData
|
||||
visible: NsShell.open !== "" && ov.onThisScreen
|
||||
color: "transparent"
|
||||
WlrLayershell.namespace: "nspanels"
|
||||
WlrLayershell.layer: WlrLayer.Overlay
|
||||
WlrLayershell.keyboardFocus: visible ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
|
||||
|
||||
anchors.top: true
|
||||
anchors.bottom: true
|
||||
anchors.left: true
|
||||
anchors.right: true
|
||||
exclusiveZone: 0
|
||||
|
||||
// outside-click closes
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: NsShell.close()
|
||||
}
|
||||
|
||||
// Escape closes
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
focus: ov.visible
|
||||
Keys.onEscapePressed: NsShell.close()
|
||||
}
|
||||
|
||||
// full-screen modal: Power Menu
|
||||
Loader {
|
||||
anchors.fill: parent
|
||||
active: NsShell.open === "power" && ov.onThisScreen
|
||||
sourceComponent: powerC
|
||||
|
||||
Component {
|
||||
id: powerC
|
||||
|
||||
Panels.NsPowerMenu {}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: loader
|
||||
|
||||
active: NsShell.open !== "" && NsShell.open !== "power" && ov.onThisScreen
|
||||
y: Theme.size.barHeight + 6
|
||||
x: {
|
||||
if (!item)
|
||||
return 0;
|
||||
const w = item.implicitWidth;
|
||||
const mode = item.anchorMode ?? "right";
|
||||
if (mode === "center")
|
||||
return Math.round((ov.width - w) / 2);
|
||||
if (mode === "trigger")
|
||||
return Math.max(8, Math.min(ov.width - w - 8, NsShell.anchorX - w / 2));
|
||||
return ov.width - w - 12; // right
|
||||
}
|
||||
|
||||
// small enter slide
|
||||
opacity: active ? 1 : 0
|
||||
transform: Translate {
|
||||
y: loader.active ? 0 : -5
|
||||
}
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: 120
|
||||
}
|
||||
}
|
||||
|
||||
sourceComponent: {
|
||||
switch (NsShell.open) {
|
||||
case "calendar":
|
||||
return calendarC;
|
||||
case "quicksettings":
|
||||
return quickSettingsC;
|
||||
case "network":
|
||||
return networkC;
|
||||
case "bluetooth":
|
||||
return bluetoothC;
|
||||
case "notifications":
|
||||
return notificationsC;
|
||||
case "audio":
|
||||
return audioC;
|
||||
case "tray":
|
||||
return trayC;
|
||||
case "overview":
|
||||
return overviewC;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: calendarC
|
||||
|
||||
Panels.NsCalendar {}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: quickSettingsC
|
||||
|
||||
Panels.NsQuickSettings {}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: networkC
|
||||
|
||||
Panels.NsNetwork {}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: bluetoothC
|
||||
|
||||
Panels.NsBluetooth {}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: notificationsC
|
||||
|
||||
Panels.NsNotifications {}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: audioC
|
||||
|
||||
Panels.NsAudio {}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: trayC
|
||||
|
||||
Panels.NsSystemTray {}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: overviewC
|
||||
|
||||
Panels.NsOverview {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,85 +0,0 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
|
||||
// NoSignal workspaces module: numbered 1..N, no capsule. Active = filled accent
|
||||
// pill with dark (onAccent) text + workspace name (e.g. "2 code"); occupied =
|
||||
// bright number; empty = faint number. Click switches workspace.
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Caelestia.Config
|
||||
import qs.services
|
||||
import qs.components
|
||||
|
||||
RowLayout {
|
||||
id: root
|
||||
|
||||
spacing: 4
|
||||
|
||||
Repeater {
|
||||
model: Config.bar.workspaces.shown
|
||||
|
||||
delegate: Rectangle {
|
||||
id: ws
|
||||
|
||||
required property int index
|
||||
readonly property int wsId: index + 1
|
||||
readonly property var hw: Hypr.workspaces.values.find(w => w.id === wsId) ?? null
|
||||
readonly property bool active: Hypr.activeWsId === wsId
|
||||
readonly property bool occupied: (hw?.lastIpcObject.windows ?? 0) > 0
|
||||
readonly property string wsName: {
|
||||
const n = hw?.name ?? "";
|
||||
return n && n !== String(wsId) && !n.startsWith("special") ? n : "";
|
||||
}
|
||||
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
implicitWidth: row.implicitWidth + (active ? 16 : 10)
|
||||
implicitHeight: 22
|
||||
radius: Theme.radius.barModule
|
||||
color: active ? Theme.accent : mouse.containsMouse ? Theme.hover : "transparent"
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Theme.hoverMs
|
||||
}
|
||||
}
|
||||
Behavior on implicitWidth {
|
||||
NumberAnimation {
|
||||
duration: 120
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: row
|
||||
|
||||
anchors.centerIn: parent
|
||||
spacing: 5
|
||||
|
||||
StyledText {
|
||||
text: ws.wsId
|
||||
color: ws.active ? Theme.onAccent : ws.occupied ? Theme.text : Theme.textFaint
|
||||
font.family: Theme.font.family
|
||||
font.pixelSize: Theme.font.bar
|
||||
font.weight: ws.active ? Font.DemiBold : Font.Normal
|
||||
}
|
||||
|
||||
StyledText {
|
||||
visible: ws.active && ws.wsName !== ""
|
||||
text: ws.wsName
|
||||
color: Theme.onAccent
|
||||
font.family: Theme.font.family
|
||||
font.pixelSize: Theme.font.bar
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouse
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: Hypr.dispatch(`workspace ${ws.wsId}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue