MaterialIcon opsz clamp, UPower popout and widget changes, improved SysTray hover detection logic
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
import QtQuick
|
||||
import qs.Config
|
||||
|
||||
AnchorAnimation {
|
||||
enum Type {
|
||||
StandardSmall = 0,
|
||||
Standard,
|
||||
StandardLarge,
|
||||
StandardExtraLarge,
|
||||
EmphasizedSmall,
|
||||
Emphasized,
|
||||
EmphasizedLarge,
|
||||
EmphasizedExtraLarge,
|
||||
FastSpatial,
|
||||
DefaultSpatial,
|
||||
SlowSpatial
|
||||
}
|
||||
|
||||
property int type: AnchorAnim.DefaultSpatial
|
||||
|
||||
duration: {
|
||||
if (type < AnchorAnim.StandardSmall || type > AnchorAnim.SlowSpatial)
|
||||
return Appearance.anim.durations.expressiveDefaultSpatial;
|
||||
|
||||
if (type == AnchorAnim.FastSpatial)
|
||||
return Appearance.anim.durations.expressiveFastSpatial;
|
||||
if (type == AnchorAnim.DefaultSpatial)
|
||||
return Appearance.anim.durations.expressiveDefaultSpatial;
|
||||
if (type == AnchorAnim.SlowSpatial)
|
||||
return Appearance.anim.durations.large;
|
||||
|
||||
const types = ["small", "normal", "large", "extraLarge"];
|
||||
const idx = type % 4;
|
||||
return Appearance.anim.durations[types[idx]];
|
||||
}
|
||||
easing.bezierCurve: {
|
||||
if (type == AnchorAnim.FastSpatial)
|
||||
return Appearance.anim.curves.expressiveFastSpatial;
|
||||
if (type == AnchorAnim.DefaultSpatial)
|
||||
return Appearance.anim.curves.expressiveDefaultSpatial;
|
||||
if (type == AnchorAnim.SlowSpatial)
|
||||
return Appearance.anim.curves.expressiveSlowSpatial;
|
||||
|
||||
if (type >= AnchorAnim.StandardSmall && type <= AnchorAnim.StandardExtraLarge)
|
||||
return Appearance.anim.curves.standard;
|
||||
if (type >= AnchorAnim.EmphasizedSmall && type <= AnchorAnim.EmphasizedExtraLarge)
|
||||
return Appearance.anim.curves.emphasized;
|
||||
|
||||
return Appearance.anim.curves.expressiveDefaultSpatial;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -39,7 +39,7 @@ NumberAnimation {
|
||||
return Appearance.anim.durations.expressiveSlowEffects;
|
||||
|
||||
const types = ["small", "normal", "large", "extraLarge"];
|
||||
const idx = type % 4; // 0-7 are the 4 standard types
|
||||
const idx = type % 4;
|
||||
return Appearance.anim.durations[types[idx]];
|
||||
}
|
||||
easing.bezierCurve: {
|
||||
|
||||
@@ -28,25 +28,21 @@ Text {
|
||||
|
||||
SequentialAnimation {
|
||||
Anim {
|
||||
easing.bezierCurve: MaterialEasing.standardAccel
|
||||
property: root.animateProp
|
||||
target: root
|
||||
to: root.animateFrom
|
||||
type: Anim.FastEffects
|
||||
}
|
||||
|
||||
PropertyAction {
|
||||
}
|
||||
|
||||
Anim {
|
||||
easing.bezierCurve: MaterialEasing.standardDecel
|
||||
property: root.animateProp
|
||||
target: root
|
||||
to: root.animateTo
|
||||
type: Anim.DefaultEffects
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component Anim: NumberAnimation {
|
||||
duration: root.animateDuration / 2
|
||||
easing.type: Easing.BezierSpline
|
||||
properties: root.animateProp.split(",").length > 1 ? root.animateProp : ""
|
||||
property: root.animateProp.split(",").length === 1 ? root.animateProp : ""
|
||||
target: root
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,26 @@
|
||||
import qs.Config
|
||||
|
||||
CustomText {
|
||||
property real fill
|
||||
id: root
|
||||
|
||||
readonly property list<int> allowed: [20, 24, 40, 48]
|
||||
property real fill: 0
|
||||
property int grade: DynamicColors.light ? 0 : -25
|
||||
readonly property int opsz: clampOpsz(optical)
|
||||
property int optical: fontInfo.pixelSize
|
||||
|
||||
function clampOpsz(value): int {
|
||||
return allowed.reduce((closest, current) => {
|
||||
return Math.abs(current - value) < Math.abs(closest - value) ? current : closest;
|
||||
});
|
||||
}
|
||||
|
||||
font.family: "Material Symbols Rounded"
|
||||
font.pointSize: Appearance.font.size.larger
|
||||
font.variableAxes: ({
|
||||
FILL: fill.toFixed(1),
|
||||
FILL: fill,
|
||||
GRAD: grade,
|
||||
opsz: fontInfo.pixelSize,
|
||||
opsz: opsz,
|
||||
wght: fontInfo.weight
|
||||
})
|
||||
}
|
||||
|
||||
+1
-1
@@ -254,7 +254,7 @@ CustomWindow {
|
||||
implicitHeight: panels.popouts.height * (1 + extraHeight)
|
||||
implicitWidth: panels.popouts.width
|
||||
panel: panels.popoutsWrapper
|
||||
radius: (panels.popouts.currentName.startsWith("audio") || panels.popouts.currentName.startsWith("updates")) ? Appearance.rounding.normal : 20 * Appearance.rounding.scale
|
||||
radius: panels.popouts.current?.panelRadius ?? Appearance.rounding.normal
|
||||
x: panels.popoutsWrapper.x + panels.popouts.x + Config.barConfig.border
|
||||
y: panels.popoutsWrapper.y + panels.popouts.y + bar.implicitHeight - panels.popouts.height * extraHeight
|
||||
|
||||
|
||||
@@ -29,4 +29,6 @@ Singleton {
|
||||
readonly property bool isLaptop: UPower.displayDevice.isLaptopBattery
|
||||
readonly property bool onBattery: UPower.onBattery
|
||||
readonly property bool ready: UPower.displayDevice.ready
|
||||
readonly property real timeToEmpty: UPower.displayDevice.timeToEmpty
|
||||
readonly property real timeToFull: UPower.displayDevice.timeToFull
|
||||
}
|
||||
|
||||
+3
-4
@@ -17,8 +17,8 @@ Item {
|
||||
readonly property Popout currentPopout: content.children.find(c => c.shouldBeActive) ?? null
|
||||
required property PopoutState popouts
|
||||
|
||||
implicitHeight: (currentPopout?.implicitHeight ?? 0) + 5 * 2
|
||||
implicitWidth: (currentPopout?.implicitWidth ?? 0) + 5 * 2
|
||||
implicitHeight: (currentPopout?.implicitHeight ?? 0) + Appearance.padding.small * 2
|
||||
implicitWidth: (currentPopout?.implicitWidth ?? 0) + Appearance.padding.small * 2
|
||||
|
||||
Item {
|
||||
id: content
|
||||
@@ -28,7 +28,7 @@ Item {
|
||||
Popout {
|
||||
name: "audio"
|
||||
|
||||
sourceComponent: AudioPopup {
|
||||
sourceComponent: AudioPopout {
|
||||
wrapper: root.wrapper
|
||||
}
|
||||
}
|
||||
@@ -73,7 +73,6 @@ Item {
|
||||
name: "upower"
|
||||
|
||||
sourceComponent: UPowerPopout {
|
||||
wrapper: root.popouts
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@ StackView {
|
||||
id: root
|
||||
|
||||
property int biggestWidth: 0
|
||||
readonly property int itemHeight: 30
|
||||
readonly property int panelRadius: ((itemHeight / 2) + Appearance.padding.small) * Appearance.rounding.scale
|
||||
required property PopoutState popouts
|
||||
property int rootWidth: 0
|
||||
required property QsMenuHandle trayItem
|
||||
@@ -100,7 +102,7 @@ StackView {
|
||||
asynchronous: true
|
||||
|
||||
sourceComponent: Item {
|
||||
implicitHeight: 30
|
||||
implicitHeight: root.itemHeight
|
||||
|
||||
StateLayer {
|
||||
enabled: item.modelData.enabled
|
||||
|
||||
@@ -3,16 +3,97 @@ pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import Quickshell.Services.UPower
|
||||
import qs.Config
|
||||
import qs.Helpers
|
||||
import qs.Components
|
||||
import qs.Modules
|
||||
|
||||
Item {
|
||||
Column {
|
||||
id: root
|
||||
|
||||
required property var wrapper
|
||||
readonly property int panelRadius: ((profiles.height / 2) + Appearance.padding.small) * Appearance.rounding.scale
|
||||
|
||||
implicitHeight: profiles.implicitHeight
|
||||
implicitWidth: profiles.implicitWidth
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
Loader {
|
||||
active: Battery.isLaptop
|
||||
|
||||
CustomText {
|
||||
text: qsTr("Remaining: %1%").arg(Math.round(UPower.displayDevice.percentage * 100))
|
||||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
function formatSeconds(s: int, fallback: string): string {
|
||||
const day = Math.floor(s / 86400);
|
||||
const hr = Math.floor(s / 3600) % 60;
|
||||
const min = Math.floor(s / 60) % 60;
|
||||
|
||||
let comps = [];
|
||||
if (day > 0)
|
||||
comps.push(`${day} days`);
|
||||
if (hr > 0)
|
||||
comps.push(`${hr} hours`);
|
||||
if (min > 0)
|
||||
comps.push(`${min} mins`);
|
||||
|
||||
return comps.join(", ") || fallback;
|
||||
}
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Appearance.padding.normal
|
||||
text: Battery.isLaptop ? qsTr("Time %1: %2").arg(Battery.onBattery ? "remaining" : "until charged").arg(Battery.onBattery ? formatSeconds(Battery.timeToEmpty, "Calculating...") : formatSeconds(Battery.timeToFull, "Fully charged!")) : qsTr("Power profile: %1").arg(PowerProfile.toString(PowerProfiles.profile))
|
||||
}
|
||||
|
||||
Loader {
|
||||
active: PowerProfiles.degradationReason !== PerformanceDegradationReason.None
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
asynchronous: true
|
||||
height: active ? ((item as Item)?.implicitHeight ?? 0) : 0
|
||||
|
||||
sourceComponent: CustomRect {
|
||||
color: DynamicColors.palette.m3error
|
||||
implicitHeight: child.implicitHeight + Appearance.padding.large
|
||||
implicitWidth: child.implicitWidth + Appearance.padding.larger * 2
|
||||
radius: Appearance.rounding.large
|
||||
|
||||
Column {
|
||||
id: child
|
||||
|
||||
anchors.centerIn: parent
|
||||
|
||||
Row {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
MaterialIcon {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.verticalCenterOffset: -font.pointSize / 10
|
||||
color: DynamicColors.palette.m3onError
|
||||
text: "warning"
|
||||
}
|
||||
|
||||
CustomText {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: DynamicColors.palette.m3onError
|
||||
font.family: Appearance.font.family.mono
|
||||
text: qsTr("Performance Degraded")
|
||||
}
|
||||
|
||||
MaterialIcon {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.verticalCenterOffset: -font.pointSize / 10
|
||||
color: DynamicColors.palette.m3onError
|
||||
text: "warning"
|
||||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
color: DynamicColors.palette.m3onError
|
||||
text: qsTr("Reason: %1").arg(PerformanceDegradationReason.toString(PowerProfiles.degradationReason))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CustomRect {
|
||||
id: profiles
|
||||
@@ -28,10 +109,9 @@ Item {
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
implicitHeight: Math.max(saver.implicitHeight, balance.implicitHeight, perf.implicitHeight) + 5 * 2 + saverLabel.contentHeight
|
||||
implicitWidth: saver.implicitHeight + balance.implicitHeight + perf.implicitHeight + 8 * 2 + saverLabel.contentWidth
|
||||
// color: "transparent"
|
||||
radius: (20 - Appearance.padding.small) * Appearance.rounding.scale
|
||||
implicitHeight: indicator.height + Appearance.padding.extraSmall * 2
|
||||
implicitWidth: saver.implicitHeight + balance.implicitHeight + perf.implicitHeight + Appearance.padding.larger * 2 + Appearance.spacing.small * 8
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
CustomRect {
|
||||
id: indicator
|
||||
@@ -64,10 +144,7 @@ Item {
|
||||
}
|
||||
]
|
||||
transitions: Transition {
|
||||
AnchorAnimation {
|
||||
duration: MaterialEasing.expressiveEffectsTime
|
||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
easing.type: Easing.BezierSpline
|
||||
AnchorAnim {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -76,62 +153,28 @@ Item {
|
||||
id: saver
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 25
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 8
|
||||
icon: "nest_eco_leaf"
|
||||
anchors.leftMargin: Appearance.padding.extraSmall
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
icon: "energy_savings_leaf"
|
||||
profile: PowerProfile.PowerSaver
|
||||
text: "Power Saver"
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: saverLabel
|
||||
|
||||
anchors.horizontalCenter: saver.horizontalCenter
|
||||
anchors.top: saver.bottom
|
||||
font.bold: true
|
||||
text: saver.text
|
||||
}
|
||||
|
||||
Profile {
|
||||
id: balance
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 8
|
||||
icon: "power_settings_new"
|
||||
anchors.centerIn: parent
|
||||
icon: "balance"
|
||||
profile: PowerProfile.Balanced
|
||||
text: "Balanced"
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: balanceLabel
|
||||
|
||||
anchors.horizontalCenter: balance.horizontalCenter
|
||||
anchors.top: balance.bottom
|
||||
font.bold: true
|
||||
text: balance.text
|
||||
}
|
||||
|
||||
Profile {
|
||||
id: perf
|
||||
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 25
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 8
|
||||
anchors.rightMargin: Appearance.padding.extraSmall
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
icon: "bolt"
|
||||
profile: PowerProfile.Performance
|
||||
text: "Performance"
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: perfLabel
|
||||
|
||||
anchors.horizontalCenter: perf.horizontalCenter
|
||||
anchors.top: perf.bottom
|
||||
font.bold: true
|
||||
text: perf.text
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,10 +190,9 @@ Item {
|
||||
component Profile: Item {
|
||||
required property string icon
|
||||
required property int profile
|
||||
required property string text
|
||||
|
||||
implicitHeight: icon.implicitHeight + 5 * 2
|
||||
implicitWidth: icon.implicitHeight + 5 * 2
|
||||
implicitHeight: icon.implicitHeight + Appearance.padding.small
|
||||
implicitWidth: icon.implicitHeight + Appearance.padding.small
|
||||
|
||||
StateLayer {
|
||||
color: profiles.current === parent.icon ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface
|
||||
@@ -168,10 +210,12 @@ Item {
|
||||
color: profiles.current === text ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface
|
||||
fill: profiles.current === text ? 1 : 0
|
||||
font.pointSize: Appearance.font.size.large * 2
|
||||
grade: DynamicColors.light ? 0 : (text === "balance" ? -25 : 0)
|
||||
text: parent.icon
|
||||
|
||||
Behavior on fill {
|
||||
Anim {
|
||||
type: Anim.DefaultEffects
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,12 +2,10 @@ pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Services.SystemTray
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
import qs.Modules.SysTray.Widgets
|
||||
import qs.Modules.SysTray.Popouts
|
||||
import qs.Modules
|
||||
|
||||
RowLayout {
|
||||
@@ -17,11 +15,37 @@ RowLayout {
|
||||
required property RowLayout loader
|
||||
required property Wrapper popouts
|
||||
|
||||
function closestRowChild(row, x) {
|
||||
let child = row.childAt(x, row.height / 2);
|
||||
if (child)
|
||||
return child;
|
||||
|
||||
let closest = null;
|
||||
let closestDistance = Infinity;
|
||||
|
||||
for (let i = 0; i < row.children.length; ++i) {
|
||||
let c = row.children[i];
|
||||
|
||||
if (!c.visible || c.width <= 0)
|
||||
continue;
|
||||
|
||||
let centerX = c.x + c.width / 2;
|
||||
let dist = Math.abs(x - centerX);
|
||||
|
||||
if (dist < closestDistance) {
|
||||
closestDistance = dist;
|
||||
closest = c;
|
||||
}
|
||||
}
|
||||
|
||||
return closest;
|
||||
}
|
||||
|
||||
function getHoveredSubItem(localX, localY) {
|
||||
let modPos = mapToItem(sysTrayMod, localX, localY);
|
||||
if (sysTrayMod.contains(Qt.point(modPos.x, modPos.y))) {
|
||||
let modRowPos = sysTrayMod.mapToItem(sysModRow, modPos.x, modPos.y);
|
||||
let child = sysModRow.childAt(modRowPos.x, modRowPos.y);
|
||||
let child = closestRowChild(sysModRow, modRowPos.x);
|
||||
if (child) {
|
||||
if (child.objectName === "audioWidget" && Config.barConfig.popouts.audio)
|
||||
return {
|
||||
|
||||
@@ -14,6 +14,8 @@ RowLayout {
|
||||
property color textColor: DynamicColors.palette.m3onSurface
|
||||
|
||||
MaterialIcon {
|
||||
id: mic
|
||||
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
animate: true
|
||||
color: (Audio.sourceMuted ?? false) ? DynamicColors.palette.m3error : root.textColor
|
||||
|
||||
@@ -102,20 +102,16 @@ Item {
|
||||
active: !Battery.isLaptop
|
||||
anchors.centerIn: parent
|
||||
|
||||
sourceComponent: RowLayout {
|
||||
id: upowerIcon
|
||||
|
||||
MaterialIcon {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
animate: true
|
||||
fill: 1
|
||||
text: {
|
||||
if (PowerProfiles.profile === PowerProfile.PowerSaver)
|
||||
return "nest_eco_leaf";
|
||||
if (PowerProfiles.profile === PowerProfile.Performance)
|
||||
return "bolt";
|
||||
return "power_settings_new";
|
||||
}
|
||||
sourceComponent: MaterialIcon {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
animate: true
|
||||
fill: 1
|
||||
text: {
|
||||
if (PowerProfiles.profile === PowerProfile.PowerSaver)
|
||||
return "energy_savings_leaf";
|
||||
if (PowerProfiles.profile === PowerProfile.Performance)
|
||||
return "bolt";
|
||||
return "balance";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user