mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-10-21 18:54:01 -04:00
* island: open media and the power menu in one step, fix dash edits - Clicking media on the home face opens the player. Closing a sheet goes back to the face the user picked, and an opt-in setting switches to media when playback starts. - The power menu can open in the island (Super+X, the dock button), with its own route row in the island settings. - An island collapses when another popout opens on its screen. - The widgets page hides the center section the island replaces. - Dragging or resizing a dashboard card no longer blanks the overview: Qt hands the committed list over as a non-array, which read as empty. - The island blur region stays inside the rounded fill. * fix(island): address review feedback on switching faces by mouse Opening the player with one click removed the only mouse path from the home face to the media face. A right-click on the collapsed island now swaps between the two while media is available. The swap resizes the island under the pointer, so hover-open stays off until the pointer leaves. Without a face to swap to, the press is not accepted and reaches the bar's own right-click action as before.
493 lines
18 KiB
QML
493 lines
18 KiB
QML
pragma ComponentBehavior: Bound
|
|
|
|
import QtQuick
|
|
import Quickshell
|
|
import Quickshell.Io
|
|
import qs.Common
|
|
import qs.Services
|
|
|
|
Item {
|
|
id: root
|
|
|
|
property var hyprlandOverviewLoader: null
|
|
|
|
readonly property var hostSlots: ShellLayout.islandKeys
|
|
|
|
readonly property bool launcherOpen: root.activityOpen("launcher")
|
|
readonly property bool controlCenterOpen: root.activityOpen("controlcenter")
|
|
|
|
function hosts() {
|
|
return (islandVariants.instances || []).concat(freeIslandVariants.instances || []);
|
|
}
|
|
|
|
function freeHostForScreen(screenName, kind) {
|
|
const requested = (screenName || "").trim() || CompositorService.getFocusedScreenName();
|
|
const instance = (ShellLayout.forScreen(requested)?.instances ?? []).find(instance => instance.free && root.matchesKind(instance, kind));
|
|
if (!instance)
|
|
return null;
|
|
return hosts().find(host => host?.free && host.screen?.name === requested && host.barId === instance.barId) ?? null;
|
|
}
|
|
|
|
function ipcMove(x, y, screen, kind) {
|
|
const host = root.freeHostForScreen(screen, kind);
|
|
if (!host)
|
|
return `${root.ipcTag(kind)}_NOT_FREE`;
|
|
if (!isFinite(x) || !isFinite(y))
|
|
return `${root.ipcTag(kind)}_INVALID_POSITION`;
|
|
host.moveTo(x, y);
|
|
return `${root.ipcTag(kind)}_MOVED: ${Math.round(host.anchorX)}\t${Math.round(host.anchorY)}\t${host.screen?.name ?? ""}`;
|
|
}
|
|
|
|
function ipcCenter(screen, kind) {
|
|
const host = root.freeHostForScreen(screen, kind);
|
|
if (!host)
|
|
return `${root.ipcTag(kind)}_NOT_FREE`;
|
|
return root.ipcMove(host.screenWidth / 2, host.screenHeight / 2, screen, kind);
|
|
}
|
|
|
|
function hostWithActivity(activityId, screenName) {
|
|
for (const host of hosts()) {
|
|
if (host?.islandController?.activeActivity === activityId && host.islandController.expanded && (!screenName || host.screen?.name === screenName))
|
|
return host;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function activityOpen(activityId) {
|
|
return root.hostWithActivity(activityId) !== null;
|
|
}
|
|
|
|
function hostForExactScreen(screen, barId, kind) {
|
|
return screen ? hostForScreenName(screen.name, barId, kind) : null;
|
|
}
|
|
|
|
function matchesKind(instance, kind) {
|
|
return !kind || (kind === "dot") === !!instance.dot;
|
|
}
|
|
|
|
function hostForScreenName(screenName, barId, kind) {
|
|
const instances = ShellLayout.forScreen(screenName)?.instances ?? [];
|
|
const ordered = instances.filter(instance => instance.hostsIsland && (!barId || instance.barId === barId) && root.matchesKind(instance, kind)).sort((a, b) => Number(!!a.free) - Number(!!b.free) || Number(["left", "right"].includes(a.edge)) - Number(["left", "right"].includes(b.edge)) || a.configOrder - b.configOrder);
|
|
for (const instance of ordered) {
|
|
const host = hosts().find(host => host?.screen?.name === screenName && host.barId === instance.barId && host.islandController);
|
|
if (host)
|
|
return host;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function focusedHost(kind) {
|
|
const focused = hostForScreenName(CompositorService.getFocusedScreenName(), "", kind);
|
|
if (focused)
|
|
return focused;
|
|
for (const screen of Quickshell.screens) {
|
|
const host = hostForScreenName(screen.name, "", kind);
|
|
if (host)
|
|
return host;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function focusedIslandScreen() {
|
|
return focusedHost()?.screen ?? null;
|
|
}
|
|
|
|
function hasHostForScreen(screen, barId) {
|
|
return screen ? hostForExactScreen(screen, barId) !== null : focusedHost() !== null;
|
|
}
|
|
|
|
function hostForScreenOrFocused(screen, barId) {
|
|
if (barId && !screen)
|
|
return null;
|
|
return screen ? hostForExactScreen(screen, barId) : focusedHost();
|
|
}
|
|
|
|
function hostForScreen(screenName, barId, kind) {
|
|
const requested = (screenName || "").trim();
|
|
return requested ? hostForScreenName(requested, barId, kind) : barId ? null : focusedHost(kind);
|
|
}
|
|
|
|
function ipcTag(kind) {
|
|
return kind === "dot" ? "DANK_DOT" : "DANK_ISLAND";
|
|
}
|
|
|
|
function activityName(activity) {
|
|
const requested = (activity || "home").trim().toLowerCase();
|
|
switch (requested) {
|
|
case "media":
|
|
case "launcher":
|
|
case "controlcenter":
|
|
case "wallpaper":
|
|
case "weather":
|
|
case "notificationcenter":
|
|
case "clipboard":
|
|
case "powermenu":
|
|
return requested;
|
|
case "power-menu":
|
|
return "powermenu";
|
|
case "control-center":
|
|
case "cc":
|
|
return "controlcenter";
|
|
case "notifications":
|
|
case "notification-center":
|
|
case "notification":
|
|
case "nc":
|
|
return "notificationcenter";
|
|
}
|
|
return "home";
|
|
}
|
|
|
|
function openActivityOn(host, activityId, section) {
|
|
switch (activityId) {
|
|
case "media":
|
|
if (!host.islandController.mediaAvailable)
|
|
return host.islandController.requestActivity("home", true, true);
|
|
return host.islandController.requestActivity("media", true, true);
|
|
case "launcher":
|
|
return host.islandController.requestLauncher("", "", false);
|
|
case "controlcenter":
|
|
return host.islandController.requestControlCenter(section || "", false);
|
|
case "wallpaper":
|
|
return host.islandController.requestWallpaper(false);
|
|
case "weather":
|
|
return host.islandController.requestWeather(false);
|
|
case "notificationcenter":
|
|
return host.islandController.requestNotificationCenter(false);
|
|
case "clipboard":
|
|
return host.islandController.requestClipboard(false);
|
|
case "powermenu":
|
|
return host.islandController.requestPowerMenu(false);
|
|
}
|
|
return host.islandController.requestActivity(activityId, true, true);
|
|
}
|
|
|
|
function openActivity(activityId, screen, section, barId): bool {
|
|
const host = root.hostForScreenOrFocused(screen, barId);
|
|
return host ? root.openActivityOn(host, activityId, section) === true : false;
|
|
}
|
|
|
|
function toggleActivity(activityId, screen, section, barId): bool {
|
|
const host = root.hostForScreenOrFocused(screen, barId);
|
|
if (!host)
|
|
return false;
|
|
const resolved = activityId === "media" && !host.islandController.mediaAvailable ? "home" : activityId;
|
|
const openHost = barId ? (host.islandController.activeActivity === resolved && host.islandController.expanded ? host : null) : root.hostWithActivity(resolved);
|
|
if (openHost) {
|
|
openHost.islandController.requestCollapse();
|
|
return true;
|
|
}
|
|
return root.openActivity(activityId, screen, section, barId);
|
|
}
|
|
|
|
// An IPC close takes down everything that shows the activity, on every monitor.
|
|
function closeActivity(activityId): bool {
|
|
let closed = false;
|
|
for (const host of hosts()) {
|
|
if (host?.islandController?.activeActivity !== activityId || !host.islandController.expanded)
|
|
continue;
|
|
host.islandController.requestCollapse();
|
|
closed = true;
|
|
}
|
|
return closed;
|
|
}
|
|
|
|
function openLauncher(query, mode, screen, barId): bool {
|
|
const target = screen ?? CompositorService.getFocusedScreen();
|
|
const config = barId ? null : SettingsData.islandLauncherHostConfig(target);
|
|
const host = barId ? root.hostForExactScreen(target, barId) : config ? root.hostForExactScreen(target, config.id) : null;
|
|
return host ? host.islandController.requestLauncher(query || "", mode || "", false) : false;
|
|
}
|
|
|
|
function toggleLauncher(query, mode, screen, barId): bool {
|
|
const target = screen ?? CompositorService.getFocusedScreen();
|
|
const host = barId ? root.hostForExactScreen(target, barId) : root.hostWithActivity("launcher", target?.name);
|
|
if (host?.islandController.expanded && host.islandController.activeActivity === "launcher") {
|
|
host.islandController.requestCollapse();
|
|
return true;
|
|
}
|
|
return root.openLauncher(query, mode, screen, barId);
|
|
}
|
|
|
|
function ipcOpen(activity, screen, barId, kind) {
|
|
const host = root.hostForScreen(screen, barId, kind);
|
|
if (!host)
|
|
return `${root.ipcTag(kind)}_UNAVAILABLE`;
|
|
const requested = root.activityName(activity);
|
|
if (!root.openActivityOn(host, requested, ""))
|
|
return `${root.ipcTag(kind)}_ACTIVITY_UNAVAILABLE: ${requested}`;
|
|
return `${root.ipcTag(kind)}_OPEN: ${requested}\t${host.screen?.name ?? ""}`;
|
|
}
|
|
|
|
function ipcToggle(activity, screen, barId, kind) {
|
|
const host = root.hostForScreen(screen, barId, kind);
|
|
if (!host)
|
|
return `${root.ipcTag(kind)}_UNAVAILABLE`;
|
|
if (host.islandController.expanded && !host.islandController.notificationActive) {
|
|
host.islandController.requestCollapse();
|
|
return `${root.ipcTag(kind)}_CLOSED: ${host.screen?.name ?? ""}`;
|
|
}
|
|
return ipcOpen(activity, screen, barId, kind);
|
|
}
|
|
|
|
function ipcShow(activity, screen, barId, kind) {
|
|
const host = root.hostForScreen(screen, barId, kind);
|
|
if (!host)
|
|
return `${root.ipcTag(kind)}_UNAVAILABLE`;
|
|
const requested = root.activityName(activity);
|
|
if (!host.islandController.requestActivity(requested, false, false))
|
|
return `${root.ipcTag(kind)}_ACTIVITY_UNAVAILABLE: ${requested}`;
|
|
return `${root.ipcTag(kind)}_SHOW: ${requested}\t${host.screen?.name ?? ""}`;
|
|
}
|
|
|
|
function ipcClose(screen, barId, kind) {
|
|
const host = root.hostForScreen(screen, barId, kind);
|
|
if (!host)
|
|
return `${root.ipcTag(kind)}_UNAVAILABLE`;
|
|
host.islandController.requestCollapse();
|
|
return `${root.ipcTag(kind)}_CLOSED: ${host.screen?.name ?? ""}`;
|
|
}
|
|
|
|
function ipcCycle(screen, barId, kind) {
|
|
const host = root.hostForScreen(screen, barId, kind);
|
|
if (!host)
|
|
return `${root.ipcTag(kind)}_UNAVAILABLE`;
|
|
host.islandController.cycleActivity(1, host.islandController.expanded);
|
|
return `${root.ipcTag(kind)}_ACTIVITY: ${host.islandController.activeActivity}\t${host.screen?.name ?? ""}`;
|
|
}
|
|
|
|
function ipcStatus(screen, barId, kind) {
|
|
const host = root.hostForScreen(screen, barId, kind);
|
|
if (!host)
|
|
return JSON.stringify({
|
|
"available": false,
|
|
"enabled": true,
|
|
"launcherAvailable": false
|
|
});
|
|
return JSON.stringify({
|
|
"available": true,
|
|
"enabled": true,
|
|
"screen": host.screen?.name ?? "",
|
|
"activity": host.islandController.activeActivity,
|
|
"expanded": host.islandController.expanded,
|
|
"mediaAvailable": host.islandController.mediaAvailable,
|
|
"launcherAvailable": true,
|
|
"controlCenterAvailable": true,
|
|
"wallpaperAvailable": true,
|
|
"weatherAvailable": true,
|
|
"notificationCenterAvailable": true,
|
|
"clipboardAvailable": true,
|
|
"powerMenuAvailable": true,
|
|
"launcherInputFocused": host.islandController.launcherInputFocused,
|
|
"launcherResultCount": host.launcherResultCount,
|
|
"compactHeight": host.islandController.compactThickness,
|
|
"free": !!host.free,
|
|
"idle": host.free ? host.idle : false,
|
|
"x": host.free ? Math.round(host.anchorX) : null,
|
|
"y": host.free ? Math.round(host.anchorY) : null
|
|
});
|
|
}
|
|
|
|
Component.onCompleted: PopoutService.dankIslandRouter = root
|
|
Component.onDestruction: {
|
|
if (PopoutService.dankIslandRouter === root)
|
|
PopoutService.dankIslandRouter = null;
|
|
}
|
|
|
|
Variants {
|
|
id: islandVariants
|
|
|
|
model: root.hostSlots
|
|
|
|
delegate: DIslandHostWindow {
|
|
required property var modelData
|
|
|
|
readonly property var identity: JSON.parse(modelData)
|
|
screen: ShellLayout.screenForName(identity[0])
|
|
barId: identity[1]
|
|
hyprlandOverviewLoader: root.hyprlandOverviewLoader
|
|
}
|
|
}
|
|
|
|
Variants {
|
|
id: freeIslandVariants
|
|
|
|
model: ShellLayout.freeIslandKeys
|
|
|
|
delegate: IslandFreeHostWindow {
|
|
required property var modelData
|
|
|
|
readonly property var identity: JSON.parse(modelData)
|
|
screen: ShellLayout.screenForName(identity[0])
|
|
barId: identity[1]
|
|
}
|
|
}
|
|
|
|
IpcHandler {
|
|
target: "island"
|
|
|
|
function open(activity: string): string {
|
|
return root.ipcOpen(activity, "", "", "island");
|
|
}
|
|
|
|
function toggle(activity: string): string {
|
|
return root.ipcToggle(activity, "", "", "island");
|
|
}
|
|
|
|
function show(activity: string): string {
|
|
return root.ipcShow(activity, "", "", "island");
|
|
}
|
|
|
|
function close(): string {
|
|
return root.ipcClose("", "", "island");
|
|
}
|
|
|
|
function cycle(): string {
|
|
return root.ipcCycle("", "", "island");
|
|
}
|
|
|
|
function status(): string {
|
|
return root.ipcStatus("", "", "island");
|
|
}
|
|
|
|
function openOn(activity: string, screen: string): string {
|
|
return root.ipcOpen(activity, screen, "", "island");
|
|
}
|
|
|
|
function toggleOn(activity: string, screen: string): string {
|
|
return root.ipcToggle(activity, screen, "", "island");
|
|
}
|
|
|
|
function showOn(activity: string, screen: string): string {
|
|
return root.ipcShow(activity, screen, "", "island");
|
|
}
|
|
|
|
function closeOn(screen: string): string {
|
|
return root.ipcClose(screen, "", "island");
|
|
}
|
|
|
|
function cycleOn(screen: string): string {
|
|
return root.ipcCycle(screen, "", "island");
|
|
}
|
|
|
|
function statusOn(screen: string): string {
|
|
return root.ipcStatus(screen, "", "island");
|
|
}
|
|
|
|
function openInstance(activity: string, screen: string, barId: string): string {
|
|
return root.ipcOpen(activity, screen, barId);
|
|
}
|
|
|
|
function toggleInstance(activity: string, screen: string, barId: string): string {
|
|
return root.ipcToggle(activity, screen, barId);
|
|
}
|
|
|
|
function notifications(): string {
|
|
return root.ipcToggle("notificationcenter", "", "", "island");
|
|
}
|
|
|
|
function notificationsOn(screen: string): string {
|
|
return root.ipcToggle("notificationcenter", screen, "", "island");
|
|
}
|
|
|
|
function move(x: string, y: string): string {
|
|
return root.ipcMove(parseFloat(x), parseFloat(y), "", "island");
|
|
}
|
|
|
|
function moveOn(screen: string, x: string, y: string): string {
|
|
return root.ipcMove(parseFloat(x), parseFloat(y), screen, "island");
|
|
}
|
|
|
|
function center(): string {
|
|
return root.ipcCenter("", "island");
|
|
}
|
|
|
|
function centerOn(screen: string): string {
|
|
return root.ipcCenter(screen, "island");
|
|
}
|
|
}
|
|
|
|
IpcHandler {
|
|
target: "dot"
|
|
|
|
function open(activity: string): string {
|
|
return root.ipcOpen(activity, "", "", "dot");
|
|
}
|
|
|
|
function toggle(activity: string): string {
|
|
return root.ipcToggle(activity, "", "", "dot");
|
|
}
|
|
|
|
function show(activity: string): string {
|
|
return root.ipcShow(activity, "", "", "dot");
|
|
}
|
|
|
|
function close(): string {
|
|
return root.ipcClose("", "", "dot");
|
|
}
|
|
|
|
function cycle(): string {
|
|
return root.ipcCycle("", "", "dot");
|
|
}
|
|
|
|
function status(): string {
|
|
return root.ipcStatus("", "", "dot");
|
|
}
|
|
|
|
function notifications(): string {
|
|
return root.ipcToggle("notificationcenter", "", "", "dot");
|
|
}
|
|
|
|
function openOn(activity: string, screen: string): string {
|
|
return root.ipcOpen(activity, screen, "", "dot");
|
|
}
|
|
|
|
function toggleOn(activity: string, screen: string): string {
|
|
return root.ipcToggle(activity, screen, "", "dot");
|
|
}
|
|
|
|
function showOn(activity: string, screen: string): string {
|
|
return root.ipcShow(activity, screen, "", "dot");
|
|
}
|
|
|
|
function closeOn(screen: string): string {
|
|
return root.ipcClose(screen, "", "dot");
|
|
}
|
|
|
|
function cycleOn(screen: string): string {
|
|
return root.ipcCycle(screen, "", "dot");
|
|
}
|
|
|
|
function statusOn(screen: string): string {
|
|
return root.ipcStatus(screen, "", "dot");
|
|
}
|
|
|
|
function notificationsOn(screen: string): string {
|
|
return root.ipcToggle("notificationcenter", screen, "", "dot");
|
|
}
|
|
|
|
function openInstance(activity: string, screen: string, barId: string): string {
|
|
return root.ipcOpen(activity, screen, barId, "dot");
|
|
}
|
|
|
|
function toggleInstance(activity: string, screen: string, barId: string): string {
|
|
return root.ipcToggle(activity, screen, barId, "dot");
|
|
}
|
|
|
|
function move(x: string, y: string): string {
|
|
return root.ipcMove(parseFloat(x), parseFloat(y), "", "dot");
|
|
}
|
|
|
|
function moveOn(screen: string, x: string, y: string): string {
|
|
return root.ipcMove(parseFloat(x), parseFloat(y), screen, "dot");
|
|
}
|
|
|
|
function center(): string {
|
|
return root.ipcCenter("", "dot");
|
|
}
|
|
|
|
function centerOn(screen: string): string {
|
|
return root.ipcCenter(screen, "dot");
|
|
}
|
|
}
|
|
}
|