mirror of
https://github.com/zoriya/noctalia-shell.git
synced 2025-12-06 06:36:15 +00:00
- All cards now live in Modules/Cards - CalendarPanel is now called ClockPanel - Added a way to ease settings migration in separate QML files
465 lines
13 KiB
QML
465 lines
13 KiB
QML
import QtQuick
|
|
import Quickshell
|
|
import Quickshell.Io
|
|
import Quickshell.Wayland
|
|
import Quickshell.Widgets
|
|
|
|
import qs.Commons
|
|
import qs.Services.Compositor
|
|
import qs.Services.Hardware
|
|
import qs.Services.Media
|
|
import qs.Services.Power
|
|
import qs.Services.System
|
|
import qs.Services.Theming
|
|
import qs.Services.UI
|
|
|
|
Item {
|
|
id: root
|
|
|
|
IpcHandler {
|
|
target: "bar"
|
|
function toggle() {
|
|
BarService.isVisible = !BarService.isVisible;
|
|
}
|
|
}
|
|
|
|
IpcHandler {
|
|
target: "screenRecorder"
|
|
function toggle() {
|
|
if (ScreenRecorderService.isAvailable) {
|
|
ScreenRecorderService.toggleRecording();
|
|
}
|
|
}
|
|
}
|
|
|
|
IpcHandler {
|
|
target: "settings"
|
|
function toggle() {
|
|
root.withTargetScreen(screen => {
|
|
var settingsPanel = PanelService.getPanel("settingsPanel", screen);
|
|
settingsPanel?.toggle();
|
|
});
|
|
}
|
|
}
|
|
|
|
IpcHandler {
|
|
target: "calendar"
|
|
function toggle() {
|
|
root.withTargetScreen(screen => {
|
|
var clockPanel = PanelService.getPanel("clockPanel", screen);
|
|
clockPanel?.toggle(null, "Clock");
|
|
});
|
|
}
|
|
}
|
|
|
|
IpcHandler {
|
|
target: "notifications"
|
|
function toggleHistory() {
|
|
// Will attempt to open the panel next to the bar button if any.
|
|
root.withTargetScreen(screen => {
|
|
var notificationHistoryPanel = PanelService.getPanel("notificationHistoryPanel", screen);
|
|
notificationHistoryPanel.toggle(null, "NotificationHistory");
|
|
});
|
|
}
|
|
function toggleDND() {
|
|
NotificationService.doNotDisturb = !NotificationService.doNotDisturb;
|
|
}
|
|
function enableDND() {
|
|
NotificationService.doNotDisturb = true;
|
|
}
|
|
function disableDND() {
|
|
NotificationService.doNotDisturb = false;
|
|
}
|
|
function clear() {
|
|
NotificationService.clearHistory();
|
|
}
|
|
|
|
function dismissOldest() {
|
|
NotificationService.dismissOldestActive();
|
|
}
|
|
|
|
function removeOldestHistory() {
|
|
NotificationService.removeOldestHistory();
|
|
}
|
|
|
|
function dismissAll() {
|
|
NotificationService.dismissAllActive();
|
|
}
|
|
}
|
|
|
|
IpcHandler {
|
|
target: "idleInhibitor"
|
|
function toggle() {
|
|
return IdleInhibitorService.manualToggle();
|
|
}
|
|
}
|
|
|
|
IpcHandler {
|
|
target: "launcher"
|
|
function toggle() {
|
|
root.withTargetScreen(screen => {
|
|
var launcherPanel = PanelService.getPanel("launcherPanel", screen);
|
|
if (!launcherPanel?.windowActive || (launcherPanel?.windowActive && !launcherPanel?.activePlugin))
|
|
launcherPanel?.toggle();
|
|
launcherPanel?.setSearchText("");
|
|
});
|
|
}
|
|
function clipboard() {
|
|
root.withTargetScreen(screen => {
|
|
var launcherPanel = PanelService.getPanel("launcherPanel", screen);
|
|
if (!launcherPanel?.windowActive || (launcherPanel?.windowActive && launcherPanel?.searchText.startsWith(">clip")))
|
|
launcherPanel?.toggle();
|
|
launcherPanel?.setSearchText(">clip ");
|
|
});
|
|
}
|
|
function calculator() {
|
|
root.withTargetScreen(screen => {
|
|
var launcherPanel = PanelService.getPanel("launcherPanel", screen);
|
|
if (!launcherPanel?.windowActive || (launcherPanel?.windowActive && launcherPanel?.searchText.startsWith(">calc")))
|
|
launcherPanel?.toggle();
|
|
launcherPanel?.setSearchText(">calc ");
|
|
});
|
|
}
|
|
function emoji() {
|
|
root.withTargetScreen(screen => {
|
|
var launcherPanel = PanelService.getPanel("launcherPanel", screen);
|
|
if (!launcherPanel?.windowActive || (launcherPanel?.windowActive && launcherPanel?.searchText.startsWith(">emoji")))
|
|
launcherPanel?.toggle();
|
|
launcherPanel?.setSearchText(">emoji ");
|
|
});
|
|
}
|
|
}
|
|
|
|
IpcHandler {
|
|
target: "lockScreen"
|
|
|
|
// New preferred method - lock the screen
|
|
function lock() {
|
|
// Only lock if not already locked (prevents the red screen issue)
|
|
if (!PanelService.lockScreen.active) {
|
|
PanelService.lockScreen.active = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
IpcHandler {
|
|
target: "brightness"
|
|
function increase() {
|
|
BrightnessService.increaseBrightness();
|
|
}
|
|
function decrease() {
|
|
BrightnessService.decreaseBrightness();
|
|
}
|
|
}
|
|
|
|
IpcHandler {
|
|
target: "darkMode"
|
|
function toggle() {
|
|
Settings.data.colorSchemes.darkMode = !Settings.data.colorSchemes.darkMode;
|
|
}
|
|
function setDark() {
|
|
Settings.data.colorSchemes.darkMode = true;
|
|
}
|
|
function setLight() {
|
|
Settings.data.colorSchemes.darkMode = false;
|
|
}
|
|
}
|
|
|
|
IpcHandler {
|
|
target: "colorScheme"
|
|
function set(schemeName: string) {
|
|
ColorSchemeService.setPredefinedScheme(schemeName);
|
|
}
|
|
}
|
|
|
|
IpcHandler {
|
|
target: "volume"
|
|
function increase() {
|
|
AudioService.increaseVolume();
|
|
}
|
|
function decrease() {
|
|
AudioService.decreaseVolume();
|
|
}
|
|
function muteOutput() {
|
|
AudioService.setOutputMuted(!AudioService.muted);
|
|
}
|
|
function increaseInput() {
|
|
AudioService.increaseInputVolume();
|
|
}
|
|
function decreaseInput() {
|
|
AudioService.decreaseInputVolume();
|
|
}
|
|
function muteInput() {
|
|
AudioService.setInputMuted(!AudioService.inputMuted);
|
|
}
|
|
}
|
|
|
|
IpcHandler {
|
|
target: "sessionMenu"
|
|
function toggle() {
|
|
root.withTargetScreen(screen => {
|
|
var sessionMenuPanel = PanelService.getPanel("sessionMenuPanel", screen);
|
|
sessionMenuPanel?.toggle();
|
|
});
|
|
}
|
|
|
|
function lockAndSuspend() {
|
|
CompositorService.lockAndSuspend();
|
|
}
|
|
}
|
|
|
|
IpcHandler {
|
|
target: "controlCenter"
|
|
function toggle() {
|
|
root.withTargetScreen(screen => {
|
|
var controlCenterPanel = PanelService.getPanel("controlCenterPanel", screen);
|
|
if (Settings.data.controlCenter.position === "close_to_bar_button") {
|
|
// Will attempt to open the panel next to the bar button if any.
|
|
controlCenterPanel?.toggle(null, "ControlCenter");
|
|
} else {
|
|
controlCenterPanel?.toggle();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
IpcHandler {
|
|
target: "dock"
|
|
function toggle() {
|
|
Settings.data.dock.enabled = !Settings.data.dock.enabled;
|
|
}
|
|
}
|
|
|
|
// Wallpaper IPC: trigger a new random wallpaper
|
|
IpcHandler {
|
|
target: "wallpaper"
|
|
function toggle() {
|
|
if (Settings.data.wallpaper.enabled) {
|
|
root.withTargetScreen(screen => {
|
|
var wallpaperPanel = PanelService.getPanel("wallpaperPanel", screen);
|
|
wallpaperPanel?.toggle();
|
|
});
|
|
}
|
|
}
|
|
|
|
function random() {
|
|
if (Settings.data.wallpaper.enabled) {
|
|
WallpaperService.setRandomWallpaper();
|
|
}
|
|
}
|
|
|
|
function set(path: string, screen: string) {
|
|
if (screen === "all" || screen === "") {
|
|
screen = undefined;
|
|
}
|
|
WallpaperService.changeWallpaper(path, screen);
|
|
}
|
|
|
|
function toggleAutomation() {
|
|
Settings.data.wallpaper.randomEnabled = !Settings.data.wallpaper.randomEnabled;
|
|
}
|
|
function disableAutomation() {
|
|
Settings.data.wallpaper.randomEnabled = false;
|
|
}
|
|
function enableAutomation() {
|
|
Settings.data.wallpaper.randomEnabled = true;
|
|
}
|
|
}
|
|
|
|
IpcHandler {
|
|
target: "batteryManager"
|
|
|
|
function cycle() {
|
|
BatteryService.cycleModes();
|
|
}
|
|
|
|
function set(mode: string) {
|
|
switch (mode) {
|
|
case "full":
|
|
BatteryService.setChargingMode(BatteryService.ChargingMode.Full);
|
|
break;
|
|
case "balanced":
|
|
BatteryService.setChargingMode(BatteryService.ChargingMode.Balanced);
|
|
break;
|
|
case "lifespan":
|
|
BatteryService.setChargingMode(BatteryService.ChargingMode.Lifespan);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
IpcHandler {
|
|
target: "powerProfile"
|
|
function cycle() {
|
|
PowerProfileService.cycleProfile();
|
|
}
|
|
|
|
function set(mode: string) {
|
|
switch (mode) {
|
|
case "performance":
|
|
PowerProfileService.setProfile(2);
|
|
break;
|
|
case "balanced":
|
|
PowerProfileService.setProfile(1);
|
|
break;
|
|
case "powersaver":
|
|
PowerProfileService.setProfile(0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
function toggleNoctaliaPerformance() {
|
|
PowerProfileService.toggleNoctaliaPerformance();
|
|
}
|
|
|
|
function enableNoctaliaPerformance() {
|
|
PowerProfileService.setNoctaliaPerformance(true);
|
|
}
|
|
|
|
function disableNoctaliaPerformance() {
|
|
PowerProfileService.setNoctaliaPerformance(false);
|
|
}
|
|
}
|
|
|
|
IpcHandler {
|
|
target: "media"
|
|
function playPause() {
|
|
MediaService.playPause();
|
|
}
|
|
|
|
function play() {
|
|
MediaService.play();
|
|
}
|
|
|
|
function stop() {
|
|
MediaService.stop();
|
|
}
|
|
|
|
function pause() {
|
|
MediaService.pause();
|
|
}
|
|
|
|
function next() {
|
|
MediaService.next();
|
|
}
|
|
|
|
function previous() {
|
|
MediaService.previous();
|
|
}
|
|
|
|
function seekRelative(offset: string) {
|
|
var offsetVal = parseFloat(offset);
|
|
if (Number.isNaN(offsetVal)) {
|
|
Logger.w("Media", "Argument to ipc call 'media seekRelative' must be a number");
|
|
return;
|
|
}
|
|
MediaService.seekRelative(offsetVal);
|
|
}
|
|
|
|
function seekByRatio(position: string) {
|
|
var positionVal = parseFloat(position);
|
|
if (Number.isNaN(positionVal)) {
|
|
Logger.w("Media", "Argument to ipc call 'media seekByRatio' must be a number");
|
|
return;
|
|
}
|
|
MediaService.seekByRatio(positionVal);
|
|
}
|
|
}
|
|
|
|
IpcHandler {
|
|
target: "state"
|
|
|
|
// Returns all settings and shell state as JSON
|
|
function all(): string {
|
|
try {
|
|
var snapshot = ShellState.buildStateSnapshot();
|
|
if (!snapshot) {
|
|
throw new Error("State snapshot unavailable");
|
|
}
|
|
return JSON.stringify(snapshot, null, 2);
|
|
} catch (error) {
|
|
Logger.e("IPC", "Failed to serialize state:", error);
|
|
return JSON.stringify({
|
|
"error": "Failed to serialize state: " + error
|
|
}, null, 2);
|
|
}
|
|
}
|
|
}
|
|
|
|
// -------------------------------------------------------------------
|
|
// Queue an IPC panel operation - will execute when screen is detected
|
|
// -------------------------------------------------------------------
|
|
function withTargetScreen(callback) {
|
|
if (pendingCallback) {
|
|
Logger.w("IPC", "Another IPC call is pending, ignoring new call");
|
|
return;
|
|
}
|
|
|
|
// Single monitor setup can execute immediately
|
|
if (Quickshell.screens.length === 1) {
|
|
callback(Quickshell.screens[0]);
|
|
} else {
|
|
// Multi-monitors setup needs to start async detection
|
|
detectedScreen = null;
|
|
pendingCallback = callback;
|
|
screenDetectorLoader.active = true;
|
|
}
|
|
}
|
|
/**
|
|
* For IPC calls on multi-monitors setup that will open panels on screen,
|
|
* we need to open a QS PanelWindow and wait for it's "screen" property to stabilize.
|
|
*/
|
|
property ShellScreen detectedScreen: null
|
|
property var pendingCallback: null
|
|
|
|
Timer {
|
|
id: screenDetectorDebounce
|
|
running: false
|
|
interval: 20
|
|
onTriggered: {
|
|
Logger.d("IPC", "Screen debounced to:", detectedScreen?.name || "null");
|
|
|
|
// Execute pending callback if any
|
|
if (pendingCallback) {
|
|
if (!Settings.data.general.allowPanelsOnScreenWithoutBar) {
|
|
// If we explicitely disabled panels on screen without bar, check if bar is configured
|
|
// for this screen, and fallback to primary screen if necessary
|
|
var monitors = Settings.data.bar.monitors || [];
|
|
const hasBar = monitors.length === 0 || monitors.includes(detectedScreen?.name);
|
|
if (!hasBar) {
|
|
detectedScreen = Quickshell.screens[0];
|
|
}
|
|
}
|
|
|
|
Logger.d("IPC", "Executing pending IPC callback on screen:", detectedScreen.name);
|
|
pendingCallback(detectedScreen);
|
|
pendingCallback = null;
|
|
}
|
|
|
|
// Clean up
|
|
screenDetectorLoader.active = false;
|
|
}
|
|
}
|
|
|
|
// Invisible dummy PanelWindow to detect which screen should receive IPC calls
|
|
Loader {
|
|
id: screenDetectorLoader
|
|
active: false
|
|
|
|
sourceComponent: PanelWindow {
|
|
implicitWidth: 0
|
|
implicitHeight: 0
|
|
color: Color.transparent
|
|
WlrLayershell.exclusionMode: ExclusionMode.Ignore
|
|
mask: Region {}
|
|
|
|
onScreenChanged: {
|
|
detectedScreen = screen;
|
|
screenDetectorDebounce.restart();
|
|
}
|
|
}
|
|
}
|
|
// -------------------------------------------------------------------
|
|
// -------------------------------------------------------------------
|
|
}
|