Not only is precedence used to choose which window is used to represent the workspace, it is also now used to choose which category of each window is used to represent the app itself.
282 lines
8.6 KiB
QML
282 lines
8.6 KiB
QML
pragma Singleton
|
|
|
|
import qs.config
|
|
import Quickshell
|
|
import Quickshell.Hyprland
|
|
import Quickshell.Io
|
|
import Quickshell.Services.Notifications
|
|
|
|
Singleton {
|
|
id: root
|
|
|
|
readonly property var weatherIcons: ({
|
|
"113": "clear_day",
|
|
"116": "partly_cloudy_day",
|
|
"119": "cloud",
|
|
"122": "cloud",
|
|
"143": "foggy",
|
|
"176": "rainy_light",
|
|
"179": "rainy_snow",
|
|
"182": "rainy_snow",
|
|
"185": "rainy_snow",
|
|
"200": "electric_bolt",
|
|
"227": "snowing",
|
|
"230": "snowing_heavy",
|
|
"248": "foggy",
|
|
"260": "foggy",
|
|
"263": "rainy_light",
|
|
"266": "rainy_light",
|
|
"281": "rainy_snow",
|
|
"284": "rainy_snow",
|
|
"293": "rainy_light",
|
|
"296": "rainy_light",
|
|
"299": "rainy_heavy",
|
|
"302": "rainy_heavy",
|
|
"305": "rainy_heavy",
|
|
"308": "rainy_heavy",
|
|
"311": "rainy_snow",
|
|
"314": "rainy_snow",
|
|
"317": "rainy_snow",
|
|
"320": "snowing",
|
|
"323": "rainy_snow",
|
|
"326": "rainy_snow",
|
|
"329": "snowing_heavy",
|
|
"332": "snowing_heavy",
|
|
"335": "snowing",
|
|
"338": "snowing_heavy",
|
|
"350": "rainy_snow",
|
|
"353": "rainy_light",
|
|
"356": "rainy_heavy",
|
|
"359": "rainy_heavy",
|
|
"362": "rainy_snow",
|
|
"365": "rainy_snow",
|
|
"368": "snowing",
|
|
"371": "snowing_heavy",
|
|
"374": "rainy_snow",
|
|
"377": "rainy_snow",
|
|
"386": "electric_bolt",
|
|
"389": "electric_bolt",
|
|
"392": "electric_bolt",
|
|
"395": "snowing_heavy"
|
|
})
|
|
|
|
readonly property var weatherIconColors: ({
|
|
"clear_day": Config.colors.yellow,
|
|
"partly_cloudy_day": Config.colors.primary,
|
|
"cloud": Config.colors.tertiary,
|
|
"foggy": Config.colors.tertiary,
|
|
"electric_bolt": Config.colors.yellow,
|
|
"rainy_light": Config.colors.blue,
|
|
"rainy_heavy": Config.colors.blue,
|
|
"rainy_snow": Config.colors.blue,
|
|
"snowing": Config.colors.cyan,
|
|
"snowing_heavy": Config.colors.cyan,
|
|
"air": Config.colors.primary
|
|
})
|
|
|
|
readonly property var desktopEntrySubs: ({
|
|
"gimp-3.0": ["gimp"],
|
|
"discord": ["discord", "discord-canary"],
|
|
// Libreoffice
|
|
"libreoffice-startcenter": ["libreoffice-startcenter", "startcenter"],
|
|
"libreoffice-writer": ["libreoffice-writer", "writer"],
|
|
"libreoffice-draw": ["libreoffice-draw", "draw"],
|
|
"libreoffice-impress": ["libreoffice-impress", "impress"],
|
|
"libreoffice-math": ["libreoffice-math", "math"],
|
|
"libreoffice-base": ["libreoffice-base", "base"],
|
|
"libreoffice-calc": ["libreoffice-calc", "calc"]
|
|
})
|
|
|
|
readonly property var categoryIcons: ({
|
|
WebBrowser: "language",
|
|
Printing: "print",
|
|
Security: "security",
|
|
Network: "business_messages",
|
|
Archiving: "archive",
|
|
Compression: "archive",
|
|
Development: "code",
|
|
IDE: "code",
|
|
TextEditor: "edit_note",
|
|
Audio: "music_note",
|
|
Music: "music_note",
|
|
Player: "music_note",
|
|
Recorder: "mic",
|
|
Game: "sports_esports",
|
|
FileTools: "files",
|
|
FileManager: "files",
|
|
Filesystem: "files",
|
|
FileTransfer: "files",
|
|
Settings: "settings",
|
|
DesktopSettings: "settings",
|
|
HardwareSettings: "settings",
|
|
TerminalEmulator: "terminal",
|
|
ConsoleOnly: "terminal",
|
|
Utility: "build",
|
|
Monitor: "monitor_heart",
|
|
Midi: "graphic_eq",
|
|
Mixer: "graphic_eq",
|
|
AudioVideoEditing: "video_settings",
|
|
AudioVideo: "music_video",
|
|
Video: "videocam",
|
|
Building: "construction",
|
|
Graphics: "photo_library",
|
|
"2DGraphics": "photo_library",
|
|
RasterGraphics: "photo_library",
|
|
TV: "tv",
|
|
System: "host",
|
|
Office: "content_paste"
|
|
})
|
|
|
|
// App icon precedence
|
|
// Used to preferentially pick app icon for display
|
|
readonly property var appIconPrec: ({
|
|
sports_esports: 10,
|
|
code: 9,
|
|
music_note: 9,
|
|
content_paste: 9,
|
|
graphic_eq: 8,
|
|
tv: 8,
|
|
edit_note: 6,
|
|
language: 5,
|
|
business_messages: 5,
|
|
files: 4,
|
|
mic: 4,
|
|
construction: 4,
|
|
terminal: 3,
|
|
monitor_heart: 3,
|
|
security: 3,
|
|
photo_library: 3,
|
|
archive: 2,
|
|
settings: 2,
|
|
build: 1,
|
|
host: 0
|
|
})
|
|
|
|
function getDesktopEntry(name: string): DesktopEntry {
|
|
name = name.toLowerCase().replace(/ /g, "-");
|
|
|
|
let names = [];
|
|
if (desktopEntrySubs.hasOwnProperty(name))
|
|
names = desktopEntrySubs[name];
|
|
else
|
|
names = [name];
|
|
|
|
return DesktopEntries.applications.values.find(a => names.includes(a.id.toLowerCase())) ?? null;
|
|
}
|
|
|
|
function getAppIcon(name: string): string {
|
|
return Quickshell.iconPath(getDesktopEntry(name)?.icon, "icon-missing");
|
|
}
|
|
|
|
function getAppCategoryIcon(name: string, fallback: string): string {
|
|
const categories = getDesktopEntry(name)?.categories;
|
|
|
|
let values = [];
|
|
if (categories)
|
|
for (const [key, value] of Object.entries(categoryIcons))
|
|
if (categories.includes(key) && value in appIconPrec)
|
|
values.push(value);
|
|
|
|
if (values.length !== 0) {
|
|
return values.reduce((a, b) => appIconPrec[a] > appIconPrec[b] ? a : b);
|
|
} else {
|
|
return fallback;
|
|
}
|
|
}
|
|
|
|
function getWorkspaceIcon(workspace: HyprlandWorkspace): string {
|
|
if (!workspace || workspace.toplevels.values.length === 0) return "add";
|
|
return [...workspace.toplevels.values]
|
|
.map(tl => getAppCategoryIcon(tl.lastIpcObject.class, "terminal"))
|
|
.reduce((a, b) => appIconPrec[a] > appIconPrec[b] ? a : b);
|
|
}
|
|
|
|
function getNetworkIcon(strength: int): string {
|
|
if (strength >= 80)
|
|
return "signal_wifi_4_bar";
|
|
if (strength >= 60)
|
|
return "network_wifi_3_bar";
|
|
if (strength >= 40)
|
|
return "network_wifi_2_bar";
|
|
if (strength >= 20)
|
|
return "network_wifi_1_bar";
|
|
return "signal_wifi_0_bar";
|
|
}
|
|
|
|
function getBluetoothIcon(icon: string): string {
|
|
if (icon.includes("headset") || icon.includes("headphones"))
|
|
return "headphones";
|
|
if (icon.includes("audio"))
|
|
return "speaker";
|
|
if (icon.includes("phone"))
|
|
return "smartphone";
|
|
if (icon.includes("mouse"))
|
|
return "mouse";
|
|
return "bluetooth";
|
|
}
|
|
|
|
function getWeatherIcon(code: string): string {
|
|
if (weatherIcons.hasOwnProperty(code))
|
|
return weatherIcons[code];
|
|
return "air";
|
|
}
|
|
|
|
function getHumidityIcon(humidity: real): string {
|
|
if (humidity >= 0.66)
|
|
return "humidity_high";
|
|
if (humidity >= 0.33)
|
|
return "humidity_mid";
|
|
return "humidity_low";
|
|
}
|
|
|
|
function getVolumeIcon(volume: real, isMuted: bool): string {
|
|
if (isMuted)
|
|
return "no_sound";
|
|
if (volume >= 0.5)
|
|
return "volume_up";
|
|
if (volume > 0)
|
|
return "volume_down";
|
|
return "volume_mute";
|
|
}
|
|
|
|
function getMicVolumeIcon(volume: real, isMuted: bool): string {
|
|
if (!isMuted && volume > 0)
|
|
return "mic";
|
|
return "mic_off";
|
|
}
|
|
|
|
function getBrightnessIcon(brightness: real): string {
|
|
return `brightness_${(Math.round(brightness * 6) + 1)}`;
|
|
}
|
|
|
|
function getNotifIcon(summary: string, urgency: int): string {
|
|
summary = summary.toLowerCase();
|
|
if (summary.includes("reboot"))
|
|
return "restart_alt";
|
|
if (summary.includes("record"))
|
|
return "screen_record";
|
|
if (summary.includes("battery"))
|
|
return "power";
|
|
if (summary.includes("screenshot"))
|
|
return "screenshot_monitor";
|
|
if (summary.includes("welcome"))
|
|
return "waving_hand";
|
|
if (summary.includes("time") || summary.includes("a break"))
|
|
return "schedule";
|
|
if (summary.includes("installed"))
|
|
return "download";
|
|
if (summary.includes("update"))
|
|
return "update";
|
|
if (summary.includes("unable to"))
|
|
return "deployed_code_alert";
|
|
if (summary.includes("profile"))
|
|
return "person";
|
|
if (summary.includes("image"))
|
|
return "image";
|
|
if (summary.includes("file"))
|
|
return "folder_copy";
|
|
if (urgency === NotificationUrgency.Critical)
|
|
return "release_alert";
|
|
return "chat";
|
|
}
|
|
}
|