diff --git a/assets/icon-missing.png b/assets/icon-missing.png
new file mode 100644
index 0000000..434f071
Binary files /dev/null and b/assets/icon-missing.png differ
diff --git a/assets/icon-missing.svg b/assets/icon-missing.svg
new file mode 100644
index 0000000..71f1e2e
--- /dev/null
+++ b/assets/icon-missing.svg
@@ -0,0 +1,4 @@
+
+
diff --git a/config/Config.qml b/config/Config.qml
index d8b9e22..16481c8 100644
--- a/config/Config.qml
+++ b/config/Config.qml
@@ -161,6 +161,8 @@ Singleton {
}
readonly property QtObject services: QtObject {
+ readonly property real batteryWarning: 0.15
+
readonly property string weatherLocation: ""
readonly property bool useFahrenheit: [Locale.ImperialUSSystem, Locale.ImperialSystem].includes(Qt.locale().measurementSystem)
@@ -169,6 +171,8 @@ Singleton {
readonly property string sunsetFrom: "21:00"
readonly property string sunsetTo: "9:00"
readonly property int sunsetTemperature: 4500
+
+ readonly property real brightnessExp: 4.0
}
readonly property QtObject session: QtObject {
diff --git a/modules/bar/modules/StatusIcons.qml b/modules/bar/modules/StatusIcons.qml
index 59a9c17..f5bff04 100644
--- a/modules/bar/modules/StatusIcons.qml
+++ b/modules/bar/modules/StatusIcons.qml
@@ -95,7 +95,7 @@ Container {
readonly property bool hasBattery: UPower.displayDevice.isLaptopBattery
readonly property real percentage: UPower.displayDevice.percentage
readonly property bool charging: !UPower.onBattery && batteryText.text !== "100"
- readonly property bool warning: UPower.onBattery && percentage < 0.15
+ readonly property bool warning: UPower.onBattery && percentage < Config.services.batteryWarning + 0.01
text: {
if (!hasBattery) {
@@ -142,7 +142,7 @@ Container {
anchors.verticalCenter: parent.verticalCenter
anchors.verticalCenterOffset: 0.5
- text: Math.round(battery.percentage * 100)
+ text: Math.floor(battery.percentage * 100)
color: battery.warning ? Config.colors.batteryWarning : Config.colors.bg
font.family: Config.font.family.mono
font.pointSize: 6
diff --git a/modules/bar/popouts/ActiveWindow.qml b/modules/bar/popouts/ActiveWindow.qml
index bb1e625..673e52f 100644
--- a/modules/bar/popouts/ActiveWindow.qml
+++ b/modules/bar/popouts/ActiveWindow.qml
@@ -73,6 +73,13 @@ Item {
property: "scale"
to: 1
}
+ // Fixes really weird transition bug
+ NumberAnimation {
+ targets: header
+ property: "height"
+ to: Config.font.size.normal
+ duration: 20
+ }
}
// Reveal on window title change
@@ -111,6 +118,7 @@ Item {
anchors.right: infobox.right
anchors.top: infobox.top
anchors.margins: 12
+ anchors.topMargin: 11
}
}
diff --git a/modules/bar/popouts/Battery.qml b/modules/bar/popouts/Battery.qml
index 5553f32..ff1fafa 100644
--- a/modules/bar/popouts/Battery.qml
+++ b/modules/bar/popouts/Battery.qml
@@ -13,14 +13,15 @@ ColumnLayout {
spacing: 4
- readonly property color color: UPower.onBattery && UPower.displayDevice.percentage < 0.15 ?
- Config.colors.batteryWarning :
- Config.colors.battery
+ readonly property bool hasBattery: UPower.displayDevice.isLaptopBattery
+ readonly property real percentage: UPower.displayDevice.percentage
+ readonly property bool warning: UPower.onBattery && percentage < Config.services.batteryWarning + 0.01
+ readonly property color color: warning ? Config.colors.batteryWarning : Config.colors.battery
Loader {
Layout.alignment: Qt.AlignHCenter
- active: UPower.displayDevice.isLaptopBattery
+ active: root.hasBattery
asynchronous: true
height: active ? (item?.implicitHeight ?? 0) : 0
@@ -76,7 +77,7 @@ ColumnLayout {
radiusX: (meter.size + meter.thickness) / 2 + meter.padding
radiusY: radiusX
startAngle: -90 - meter.angle / 2
- sweepAngle: meter.angle * UPower.displayDevice.percentage
+ sweepAngle: meter.angle * root.percentage
}
Behavior on strokeColor {
@@ -98,7 +99,7 @@ ColumnLayout {
CustomText {
anchors.horizontalCenter: parent.horizontalCenter
- text: Math.round(UPower.displayDevice.percentage * 100) + "%"
+ text: Math.floor(root.percentage * 100) + "%"
font.pointSize: Config.font.size.largest
}
diff --git a/modules/bar/popouts/Content.qml b/modules/bar/popouts/Content.qml
index 1ab7152..18e50ae 100644
--- a/modules/bar/popouts/Content.qml
+++ b/modules/bar/popouts/Content.qml
@@ -67,7 +67,7 @@ Item {
name: "battery"
source: "Battery.qml"
color: UPower.displayDevice.isLaptopBattery &&
- UPower.onBattery && UPower.displayDevice.percentage < 0.15 ?
+ UPower.onBattery && UPower.displayDevice.percentage < Config.services.batteryWarning + 0.01 ?
Config.colors.batteryWarning :
Config.colors.battery
}
diff --git a/modules/bar/popouts/Wrapper.qml b/modules/bar/popouts/Wrapper.qml
index 305f17f..df5184f 100644
--- a/modules/bar/popouts/Wrapper.qml
+++ b/modules/bar/popouts/Wrapper.qml
@@ -13,7 +13,6 @@ Item {
id: root
required property PersistentProperties uiState
- required property ShellScreen screen
readonly property real nonAnimWidth: content.implicitWidth
readonly property real nonAnimHeight: y > 0 || hasCurrent ? content.implicitHeight : 0
diff --git a/modules/dashboard/Mixer.qml b/modules/dashboard/Mixer.qml
index 7ca119e..a83ae58 100644
--- a/modules/dashboard/Mixer.qml
+++ b/modules/dashboard/Mixer.qml
@@ -110,8 +110,8 @@ Item {
source: {
const icon = entry.modelData.properties["application.icon-name"];
if (icon)
- return Icons.getAppIcon(icon, "image-missing");
- Icons.getAppIcon(entry.modelData.name, "image-missing")
+ return Icons.getAppIcon(icon, "icon-missing");
+ Icons.getAppIcon(entry.modelData.name, "icon-missing")
}
}
diff --git a/modules/dashboard/Workspaces.qml b/modules/dashboard/Workspaces.qml
index 4feeef9..6f5db42 100644
--- a/modules/dashboard/Workspaces.qml
+++ b/modules/dashboard/Workspaces.qml
@@ -290,6 +290,7 @@ Item {
property var ipc: modelData.lastIpcObject
opacity: ipc && ipc.at && !ipc.hidden ? 1 : 0
+ visible: opacity > 0
property real nonAnimX: ipc?.at ? (ipc.at[0] - preview.monX) * preview.sizeRatio : 0
property real nonAnimY: ipc?.at ? (ipc.at[1] - preview.monY) * preview.sizeRatio : 0
diff --git a/modules/dashboard/dash/Weather.qml b/modules/dashboard/dash/Weather.qml
index af520fc..ee61b91 100644
--- a/modules/dashboard/dash/Weather.qml
+++ b/modules/dashboard/dash/Weather.qml
@@ -83,7 +83,6 @@ Item {
anchors.left: parent.right
anchors.bottom: parent.bottom
anchors.leftMargin: 12
- anchors.bottomMargin: 1
animate: true
text: Weather.feelsLike
diff --git a/modules/launcher/items/AppItem.qml b/modules/launcher/items/AppItem.qml
index dea01c2..3d8b4e6 100644
--- a/modules/launcher/items/AppItem.qml
+++ b/modules/launcher/items/AppItem.qml
@@ -37,7 +37,7 @@ Item {
IconImage {
id: icon
- source: Quickshell.iconPath(root.modelData?.icon, "image-missing")
+ source: Quickshell.iconPath(root.modelData?.icon, "icon-missing")
implicitSize: parent.height * 0.9
anchors.verticalCenter: parent.verticalCenter
diff --git a/modules/osd/Content.qml b/modules/osd/Content.qml
index 3495b96..1449135 100644
--- a/modules/osd/Content.qml
+++ b/modules/osd/Content.qml
@@ -11,7 +11,6 @@ Item {
id: root
required property var uiState
- required property Brightness.Monitor monitor
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
@@ -95,13 +94,10 @@ Item {
implicitHeight: Config.osd.sliderLength
function onWheel(event: WheelEvent) {
- const monitor = root.monitor;
- if (!monitor)
- return;
if (event.angleDelta.y > 0)
- monitor.setBrightness(monitor.brightness + 0.1);
+ Brightness.setBrightness(Brightness.brightness + 0.1);
else if (event.angleDelta.y < 0)
- monitor.setBrightness(monitor.brightness - 0.1);
+ Brightness.setBrightness(Brightness.brightness - 0.1);
}
CustomFilledSlider {
@@ -109,8 +105,8 @@ Item {
color: Config.colors.brightness
icon: Icons.getBrightnessIcon(value)
- value: root.monitor?.brightness ?? 0
- onMoved: root.monitor?.setBrightness(value)
+ value: Brightness.brightness
+ onMoved: Brightness.setBrightness(value)
}
}
}
diff --git a/modules/osd/Interactions.qml b/modules/osd/Interactions.qml
index 3036a35..4c2f05f 100644
--- a/modules/osd/Interactions.qml
+++ b/modules/osd/Interactions.qml
@@ -7,10 +7,8 @@ Scope {
id: root
required property PersistentProperties uiState
- required property ShellScreen screen
required property bool hovered
required property bool suppressed
- readonly property Brightness.Monitor monitor: Brightness.getMonitorForScreen(screen)
function show(): void {
if (!root.suppressed) {
@@ -34,7 +32,7 @@ Scope {
}
Connections {
- target: root.monitor
+ target: Brightness
function onBrightnessChanged(): void {
if (root.uiState.osdBrightnessReact)
diff --git a/modules/osd/Wrapper.qml b/modules/osd/Wrapper.qml
index 109909c..a764f19 100644
--- a/modules/osd/Wrapper.qml
+++ b/modules/osd/Wrapper.qml
@@ -8,7 +8,6 @@ Item {
id: root
required property var uiState
- required property ShellScreen screen
visible: width > 0
implicitWidth: 0
@@ -61,6 +60,5 @@ Item {
id: content
uiState: root.uiState
- monitor: Brightness.getMonitorForScreen(root.screen)
}
}
diff --git a/modules/ui/Interactions.qml b/modules/ui/Interactions.qml
index f3d601e..3e08345 100644
--- a/modules/ui/Interactions.qml
+++ b/modules/ui/Interactions.qml
@@ -62,6 +62,10 @@ CustomMouseArea {
if (y < Config.bar.height && !popoutsSuppressed && !popouts.persistent) {
bar.checkPopout(x);
}
+ // Hide bar popouts (if user moves mouse along edge)
+ if (y > popouts.nonAnimHeight + Config.bar.height) {
+ popouts.hasCurrent = false;
+ }
// Show osd on hover
const showOsd = inRightPanel(panels.osd, x, y);
@@ -116,7 +120,6 @@ CustomMouseArea {
Osd.Interactions {
uiState: root.uiState
- screen: root.screen
hovered: root.osdHovered
suppressed: root.osdSuppressed
}
diff --git a/modules/ui/Panels.qml b/modules/ui/Panels.qml
index f808fa2..b1b39c1 100644
--- a/modules/ui/Panels.qml
+++ b/modules/ui/Panels.qml
@@ -13,7 +13,6 @@ Item {
id: root
required property PersistentProperties uiState
- required property ShellScreen screen
required property Item bar
readonly property alias popouts: popouts
@@ -31,14 +30,12 @@ Item {
id: popouts
uiState: root.uiState
- screen: root.screen
}
Osd.Wrapper {
id: osd
uiState: root.uiState
- screen: root.screen
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
diff --git a/modules/ui/UI.qml b/modules/ui/UI.qml
index 896cdc4..e988ab0 100644
--- a/modules/ui/UI.qml
+++ b/modules/ui/UI.qml
@@ -110,7 +110,6 @@ Variants {
visible: !uiState.uiState.hidden
uiState: uiState.uiState
- screen: scope.modelData
bar: bar
}
}
diff --git a/package.nix b/package.nix
index e1e3c4d..79feb46 100644
--- a/package.nix
+++ b/package.nix
@@ -55,6 +55,9 @@ in stdenv.mkDerivation {
cp -R . $out/share/${pname}
+ mkdir -p $out/share/icons/hicolor/512x512/apps/
+ cp assets/icon-missing.png $out/share/icons/hicolor/512x512/apps/
+
makeWrapper ${quickshell}/bin/qs $out/bin/${pname} \
--prefix PATH : "${lib.makeBinPath runtimeDeps}" \
--set FONTCONFIG_FILE "${fontconfig}" \
diff --git a/services/Brightness.qml b/services/Brightness.qml
index 856b0c7..b7f8bc9 100644
--- a/services/Brightness.qml
+++ b/services/Brightness.qml
@@ -12,58 +12,38 @@ Singleton {
reloadableId: "brightness"
- property list ddcMonitors: []
- readonly property list monitors: variants.instances
- property bool appleDisplayPresent: false
-
- function getMonitorForScreen(screen: ShellScreen): var {
- return monitors.find(m => m.modelData === screen);
- }
+ property real brightness: 1.0
+ property real maxBrightness: 0.0
function increaseBrightness(): void {
- const focusedName = Hypr.focusedMonitor.name;
- const monitor = monitors.find(m => focusedName === m.modelData.name);
- if (monitor)
- monitor.setBrightness(monitor.brightness + Config.osd.brightnessIncrement);
+ setBrightness(brightness + Config.osd.brightnessIncrement);
}
function decreaseBrightness(): void {
- const focusedName = Hypr.focusedMonitor.name;
- const monitor = monitors.find(m => focusedName === m.modelData.name);
- if (monitor)
- monitor.setBrightness(monitor.brightness - Config.osd.brightnessIncrement);
+ setBrightness(brightness - Config.osd.brightnessIncrement);
}
- onMonitorsChanged: {
- ddcMonitors = [];
- ddcProc.running = true;
- }
-
- Variants {
- id: variants
-
- model: Quickshell.screens
-
- Monitor {}
+ function setBrightness(value: real): void {
+ value = Math.max(0, Math.min(1, value));
+ if (Math.abs(brightness - value) < 0.01) return;
+ brightness = value;
+
+ const exp = Config.services.brightnessExp;
+ const raw = Math.round((value ** exp) * maxBrightness);
+ Quickshell.execDetached(["brightnessctl", "s", `${raw}`]);
}
+ Component.onCompleted: initProc.running = true
Process {
- running: true
- command: ["sh", "-c", "asdbctl get"] // To avoid warnings if asdbctl is not installed
+ id: initProc
+ command: ["sh", "-c", "echo $(brightnessctl g) $(brightnessctl m)"]
stdout: StdioCollector {
- onStreamFinished: root.appleDisplayPresent = text.trim().length > 0
- }
- }
-
- Process {
- id: ddcProc
-
- command: ["ddcutil", "detect", "--brief"]
- stdout: StdioCollector {
- onStreamFinished: root.ddcMonitors = text.trim().split("\n\n").filter(d => d.startsWith("Display ")).map(d => ({
- model: d.match(/Monitor:.*:(.*):.*/)[1],
- busNum: d.match(/I2C bus:[ ]*\/dev\/i2c-([0-9]+)/)[1]
- }))
+ onStreamFinished: {
+ const exp = Config.services.brightnessExp;
+ const [cur, max] = text.split(" ");
+ root.maxBrightness = parseInt(max);
+ root.brightness = (parseInt(cur) / root.maxBrightness) ** (1 / exp);
+ }
}
}
@@ -110,77 +90,4 @@ Singleton {
root.decreaseBrightness();
}
}
-
- component Monitor: QtObject {
- id: monitor
-
- required property ShellScreen modelData
- readonly property bool isDdc: root.ddcMonitors.some(m => m.model === modelData.model)
- readonly property string busNum: root.ddcMonitors.find(m => m.model === modelData.model)?.busNum ?? ""
- readonly property bool isAppleDisplay: root.appleDisplayPresent && modelData.model.startsWith("StudioDisplay")
- property real brightness
- property real queuedBrightness: NaN
-
- readonly property Process initProc: Process {
- stdout: StdioCollector {
- onStreamFinished: {
- if (monitor.isAppleDisplay) {
- const val = parseInt(text.trim());
- monitor.brightness = val / 101;
- } else {
- const [, , , cur, max] = text.split(" ");
- monitor.brightness = parseInt(cur) / parseInt(max);
- }
- }
- }
- }
-
- readonly property Timer timer: Timer {
- interval: 500
- onTriggered: {
- if (!isNaN(monitor.queuedBrightness)) {
- monitor.setBrightness(monitor.queuedBrightness);
- monitor.queuedBrightness = NaN;
- }
- }
- }
-
- function setBrightness(value: real): void {
- value = Math.max(0, Math.min(1, value));
- const rounded = Math.round(value * 100);
- if (Math.round(brightness * 100) === rounded)
- return;
-
- if (isDdc && timer.running) {
- queuedBrightness = value;
- return;
- }
-
- brightness = value;
-
- if (isAppleDisplay)
- Quickshell.execDetached(["asdbctl", "set", rounded]);
- else if (isDdc)
- Quickshell.execDetached(["ddcutil", "-b", busNum, "setvcp", "10", rounded]);
- else
- Quickshell.execDetached(["brightnessctl", "s", `${rounded}%`]);
-
- if (isDdc)
- timer.restart();
- }
-
- function initBrightness(): void {
- if (isAppleDisplay)
- initProc.command = ["asdbctl", "get"];
- else if (isDdc)
- initProc.command = ["ddcutil", "-b", busNum, "getvcp", "10", "--brief"];
- else
- initProc.command = ["sh", "-c", "echo a b c $(brightnessctl g) $(brightnessctl m)"];
-
- initProc.running = true;
- }
-
- onBusNumChanged: initBrightness()
- Component.onCompleted: initBrightness()
- }
}