Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 0 additions & 13 deletions barstyles/imi/GlobalStates.qml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import "modules/common"
import "modules/common/functions"
import "services"
import shell.services as ShellState
import shell.services as HostServices
import QtQuick
import Quickshell
import Quickshell.Io
Expand Down Expand Up @@ -1629,17 +1628,6 @@ Singleton {
// and the close grace, exactly as StyledPopup does for the other popups.
property bool mediaWidgetHovered: false

// background.wallpaperPath follows the host Session wallpaper.
function syncWallpaperPath() {
const live = HostServices.Session.wallpaper || "";
if (live !== "" && Config.ready && Config.options.background.wallpaperPath !== live)
Config.options.background.wallpaperPath = live;
}
Connections {
target: HostServices.Session
function onWallpaperChanged() { root.syncWallpaperPath(); }
}

function setMediaWidgetHovered(hovered) {
root.mediaWidgetHovered = hovered;
}
Expand Down Expand Up @@ -2581,7 +2569,6 @@ Singleton {
}

Component.onCompleted: {
Qt.callLater(root.syncWallpaperPath);
animatedLeftSidebarWidth = leftSidebarTargetWidth;
animatedRightSidebarWidth = rightSidebarTargetWidth;
root.enforceSidebarStyle();
Expand Down
14 changes: 14 additions & 0 deletions barstyles/imi/Scene.qml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ pragma ComponentBehavior: Bound

import QtQuick
import Quickshell
import shell.services as HostServices
import Quickshell.Io
import "modules/common"
import "modules/ii/bar"
Expand Down Expand Up @@ -119,6 +120,19 @@ Scope {
sourceComponent: SettingsWindow {}
}

// background.wallpaperPath follows the host Session wallpaper (used by
// the sidebar banner and search previews).
function syncWallpaperPath() {
const live = HostServices.Session.wallpaper || "";
if (live !== "" && Config.ready && Config.options.background.wallpaperPath !== live)
Config.options.background.wallpaperPath = live;
}
Component.onCompleted: Qt.callLater(scene.syncWallpaperPath)
Connections {
target: HostServices.Session
function onWallpaperChanged() { scene.syncWallpaperPath(); }
}

// Handoff for Ryoku Hub > Bar Studio.
IpcHandler {
target: "imi"
Expand Down
2 changes: 1 addition & 1 deletion barstyles/imi/VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2.0.0
2.0.1
Binary file modified barstyles/imi/assets/preview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 13 additions & 13 deletions barstyles/imi/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"schema": 1,
"id": "imi",
"category": "barstyles",
"version": "2.0.0",
"version": "2.0.1",
"destination": "ryoku/barstyles/imi",
"files": [
{
Expand All @@ -17,8 +17,8 @@
"source": "GlobalStates.qml",
"destination": "GlobalStates.qml",
"mode": "0644",
"size": 153864,
"sha256": "b8955110b9dea3e14e72608ddf19bce8ecccf602a618b89f42f0783af361e3d5",
"size": 153329,
"sha256": "3defad1dd43b947c86cc78bb41dc398268d3b4715e3b3d85b5f1eb3a20c58d72",
"install": true
},
{
Expand Down Expand Up @@ -49,8 +49,8 @@
"source": "Scene.qml",
"destination": "Scene.qml",
"mode": "0644",
"size": 4003,
"sha256": "59a0f4b31e81e99ae841f1579b3f4cfb5bb0baabc68189c1e4c59cbc5b6f786c",
"size": 4614,
"sha256": "fcb5c5829557c186a4fe2c052b385c57653ff745548b1a68ff3ba015be97bb5c",
"install": true
},
{
Expand All @@ -66,7 +66,7 @@
"destination": "VERSION",
"mode": "0644",
"size": 6,
"sha256": "c28fcca53637bc88e124af1725df13cb98c69dedefd62fb3cdbe1cdb6b760624",
"sha256": "fe840f52fb5578d3f77eb497115eb02c6036601c3a53e60df7995c7276baa77a",
"install": true
},
{
Expand Down Expand Up @@ -2281,8 +2281,8 @@
"source": "assets/preview.png",
"destination": "assets/preview.png",
"mode": "0644",
"size": 1048017,
"sha256": "621dc0329d095ee25edebdb9e8070ef64c9a6a4f62a6fb2107dccedda63b2969",
"size": 659831,
"sha256": "d8a626ca7ab84d9913e0634ddce8fe7d4a6b18d17e749d48e128394571a9a9a4",
"install": false
},
{
Expand Down Expand Up @@ -7761,8 +7761,8 @@
"source": "modules/ii/bar/popups/bluetooth/ExpressiveBluetoothDevicesPopup.qml",
"destination": "modules/ii/bar/popups/bluetooth/ExpressiveBluetoothDevicesPopup.qml",
"mode": "0644",
"size": 23495,
"sha256": "4152a3fed6a92a958608f477a36a5ad90c7096dd31fa2fca50cfe472064b1ec7",
"size": 23520,
"sha256": "5458730faea12d15433180b4d34f76e480eb65b8be23f984ccb2b16bd05f51ea",
"install": true
},
{
Expand Down Expand Up @@ -12770,7 +12770,7 @@
"destination": "modules/settings/configs/AboutConfig.qml",
"mode": "0644",
"size": 31950,
"sha256": "ee016658e0614ed502566f028c06ffce5aa8e41f15010de0bfe751fc8ffa7d97",
"sha256": "f2dc2dee7f54adb471d883f6001bcec7b61f69d17de8a3f1cf09cea7fd188960",
"install": true
},
{
Expand Down Expand Up @@ -14665,8 +14665,8 @@
"source": "services/Notifications.qml",
"destination": "services/Notifications.qml",
"mode": "0644",
"size": 21334,
"sha256": "65a75705253ad6b85b512eebe8a930b8c281295202d1fdd3da485e4b54c3b43e",
"size": 24097,
"sha256": "4fd91d21be039ec2d2e0e42c3e2124c010afc576318ad4ed231495723a380804",
"install": true
},
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import "../../../../../"
import "../../shared"
import "../../../../common"
import "../../../../common/functions"
Expand Down
2 changes: 1 addition & 1 deletion barstyles/imi/modules/settings/configs/AboutConfig.qml
Original file line number Diff line number Diff line change
Expand Up @@ -566,7 +566,7 @@ Item {

StyledText {
Layout.fillWidth: true
text: "Imi 2.0.0 - maintained by Dodo986"
text: "Imi 2.0.1 - maintained by Dodo986"
color: Appearance.colors.colSubtext
font.pixelSize: Appearance.font.pixelSize.small
}
Expand Down
109 changes: 90 additions & 19 deletions barstyles/imi/services/Notifications.qml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ Singleton {
// null entries ("Cannot append Notif(...) to a QML list").
property list<QtObject> list: []
property var _livePopups: []
property var _mirrored: []
property bool _releasingHostPopups: false
// The sidebar covering the bar holds the banners back, exactly as the
// upstream popups were never armed while it was open.
readonly property var popupList: root.popupInhibited ? [] : root._livePopups
Expand Down Expand Up @@ -128,6 +130,7 @@ Singleton {
if (!wrapper)
return null;
root._serverWrappers[key] = wrapper;
raw.closed.connect(function() { root.forgetPopup(raw.id); });
root._handleShellNotification(wrapper);
}
return wrapper;
Expand Down Expand Up @@ -169,6 +172,10 @@ Singleton {
if (notif && !notif.internal)
alive[String(notif.notificationId)] = true;
});
root._mirrored.forEach((notif) => {
if (notif && !notif.internal)
alive[String(notif.notificationId)] = true;
});
for (const id in root._serverWrappers) {
if (!alive[id]) {
const gone = root._serverWrappers[id];
Expand All @@ -180,32 +187,81 @@ Singleton {

function rebuildPopups() {
const popups = HostServices.Notifs.popups ?? [];
const out = [];
for (let i = 0; i < popups.length; i++) {
if (!popups[i])
const raw = popups[i];
if (!raw)
continue;
const wrapper = root.ensureWrapper(popups[i]);
const wrapper = root.ensureWrapper(raw);
if (!wrapper)
continue;
for (const entry of root._mirrored) {
if (entry && entry !== wrapper && entry.popup && root.sameContent(entry.notification ?? {}, raw)) {
root.stopTimeout(entry);
entry.popup = false;
}
}
if (root._mirrored.indexOf(wrapper) === -1)
root._mirrored = root._mirrored.concat([wrapper]);
wrapper.popup = true;
out.push(wrapper);
root.armNotificationTimeout(wrapper);
// Count each popup once, no matter how often the host re-fronts it.
const key = String(popups[i].id);
const key = String(raw.id);
if (!(key in root._countedPopupIds)) {
root._countedPopupIds[key] = true;
root.unread++;
}
}

const out = root._mirrored.filter(entry => entry && entry.popup);
root._mirrored = out.filter(entry => !entry.internal);
for (let j = 0; j < root._internalList.length; j++) {
if (root._internalList[j].popup)
out.push(root._internalList[j]);
}
out.sort((a, b) => (b?.time ?? 0) - (a?.time ?? 0));
root._livePopups = out;
for (const id in root._serverWrappers) {
const wrapper = root._serverWrappers[id];
if (wrapper.popup && out.indexOf(wrapper) === -1)
wrapper.popup = false;
}

// The host banner window draws from this list.
if (!root._releasingHostPopups) {
root._releasingHostPopups = true;
for (let k = 0; k < popups.length; k++) {
if (popups[k])
HostServices.Notifs.removePopup(popups[k]);
}
root._releasingHostPopups = false;
}
}

function stopTimeout(notifObject) {
if (!notifObject)
return;
const timer = notifObject.timer ?? null;
notifObject.timer = null;
if (timer) {
timer.stop();
timer.destroy();
}
}

function sameContent(a, b) {
return (a?.appName || "") === (b?.appName || "")
&& (a?.summary || "") === (b?.summary || "")
&& (a?.body || "") === (b?.body || "");
}

function forgetPopup(id) {
const wrapper = root._serverWrappers[String(id)];
if (!wrapper || !wrapper.popup)
return;
root.stopTimeout(wrapper);
wrapper.popup = false;
root._mirrored = root._mirrored.filter(entry => entry !== wrapper);
root.rebuildPopups();
}

function releaseNotificationObject(notifObject) {
Expand Down Expand Up @@ -308,14 +364,23 @@ Singleton {
}

function armNotificationTimeout(notifObject) {
if (root.popupInhibited)
if (notifObject.internal && root.popupInhibited)
return;
const expireTimeout = notifObject.internalExpireTimeout;
const fallback = Config?.options.notifications.timeout ?? 7000;
let expireTimeout;
if (notifObject.internal) {
expireTimeout = notifObject.internalExpireTimeout;
} else {
const raw = notifObject.notification;
if (!raw)
return;
expireTimeout = raw.urgency === NotificationUrgency.Critical ? 0 : raw.expireTimeout;
}
if (expireTimeout === 0)
return;
const interval = expireTimeout < 0
? (Config?.options.notifications.timeout ?? 7000)
: expireTimeout;
? fallback
: (notifObject.internal ? expireTimeout : Math.min(fallback, expireTimeout));
notifObject.popup = true;
if (notifObject.timer) {
notifObject.timer.interval = interval;
Expand Down Expand Up @@ -407,6 +472,8 @@ Singleton {
const internals = root._internalList.slice();
root._internalList = [];
internals.forEach(notif => root.releaseNotificationObject(notif));
root._mirrored.forEach(notif => root.stopTimeout(notif));
root._mirrored = [];
// History items first (the host marks them seen), then whatever
// transient popups are still up - they never entered history.
HostServices.Notifs.clearAll();
Expand All @@ -421,24 +488,28 @@ Singleton {

function cancelTimeout(id) {
const wrapper = root.findNotification(id);
if (wrapper && wrapper.internal) {
// The timer is gone once it has fired.
wrapper.timer?.stop();
} else {
HostServices.Notifs.cancelPopupTimeout(id);
if (!wrapper)
return;
// The timer is gone once it has fired.
if (wrapper.timer) {
wrapper.timer.stop();
wrapper.timer.destroy();
wrapper.timer = null;
}
if (!wrapper.internal)
HostServices.Notifs.cancelPopupTimeout(id);
}

function timeoutNotification(id) {
const wrapper = root.findNotification(id);
if (!wrapper)
return;
if (wrapper.internal) {
wrapper.popup = false;
} else if (wrapper.notification) {
root.stopTimeout(wrapper);
wrapper.popup = false;
if (!wrapper.internal && wrapper.notification)
HostServices.Notifs.removePopup(wrapper.notification);
wrapper.popup = false;
}
root._mirrored = root._mirrored.filter(entry => entry !== wrapper);
root.rebuildPopups();
root.timeout(id);
}

Expand Down
6 changes: 3 additions & 3 deletions barstyles/registry.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
{
"id": "imi",
"name": "Imi",
"version": "2.0.0",
"version": "2.0.1",
"path": "barstyles/imi",
"author": "dodo986",
"upstream": "https://github.com/ryoku-dev/ryostore/tree/main/barstyles/imi",
Expand All @@ -69,8 +69,8 @@
"preview": "assets/preview.png",
"screenshots": [],
"manifest": "manifest.json",
"manifestSha256": "c97045cef32156f8e3f419e304559ecc3f3a579ca4576a60315f9a7d51487da9",
"lastUpdated": "2026-11-03",
"manifestSha256": "b78d4b81cd186d049c426c8414a3cd2ca0f90882de0abb314d586165c017744e",
"lastUpdated": "2026-11-05",
"official": false,
"license": "GPL-3.0-only"
},
Expand Down