mirror of
https://github.com/noctalia-dev/noctalia-shell.git
synced 2026-05-11 17:08:27 +08:00
332 lines
11 KiB
QML
332 lines
11 KiB
QML
import QtQuick
|
|
import QtQuick.Controls
|
|
import QtQuick.Layouts
|
|
import Quickshell
|
|
import qs.Commons
|
|
import qs.Services.Compositor
|
|
import qs.Services.Noctalia
|
|
import qs.Services.UI
|
|
import qs.Widgets
|
|
|
|
ColumnLayout {
|
|
id: root
|
|
|
|
spacing: Style.marginL
|
|
|
|
// Available widgets model - declared early so Repeater delegates can access it
|
|
property alias availableWidgetsModel: availableWidgets
|
|
ListModel {
|
|
id: availableWidgets
|
|
}
|
|
|
|
// Listen for plugin widget registration/unregistration
|
|
Connections {
|
|
target: DesktopWidgetRegistry
|
|
function onPluginWidgetRegistryUpdated() {
|
|
updateAvailableWidgetsModel();
|
|
}
|
|
}
|
|
|
|
NToggle {
|
|
Layout.fillWidth: true
|
|
label: I18n.tr("panels.desktop-widgets.enabled-label")
|
|
description: I18n.tr("panels.desktop-widgets.enabled-description")
|
|
checked: Settings.data.desktopWidgets.enabled
|
|
defaultValue: Settings.getDefaultValue("desktopWidgets.enabled")
|
|
onToggled: checked => Settings.data.desktopWidgets.enabled = checked
|
|
}
|
|
|
|
NToggle {
|
|
Layout.fillWidth: true
|
|
enabled: Settings.data.desktopWidgets.enabled
|
|
label: I18n.tr("panels.desktop-widgets.overview-enabled-label")
|
|
description: I18n.tr("panels.desktop-widgets.overview-enabled-description")
|
|
checked: Settings.data.desktopWidgets.overviewEnabled
|
|
defaultValue: Settings.getDefaultValue("desktopWidgets.overviewEnabled")
|
|
onToggled: checked => Settings.data.desktopWidgets.overviewEnabled = checked
|
|
}
|
|
|
|
ColumnLayout {
|
|
enabled: Settings.data.desktopWidgets.enabled
|
|
|
|
NLabel {
|
|
description: I18n.tr("panels.desktop-widgets.cpu-intensive-note")
|
|
}
|
|
|
|
NButton {
|
|
Layout.fillWidth: true
|
|
Layout.topMargin: Style.marginM
|
|
Layout.bottomMargin: Style.marginM
|
|
text: DesktopWidgetRegistry.editMode ? I18n.tr("panels.desktop-widgets.edit-mode-exit-button") : I18n.tr("panels.desktop-widgets.edit-mode-button-label")
|
|
icon: "edit"
|
|
onClicked: {
|
|
DesktopWidgetRegistry.editMode = !DesktopWidgetRegistry.editMode;
|
|
if (DesktopWidgetRegistry.editMode && Settings.data.ui.settingsPanelMode !== "window") {
|
|
var item = root.parent;
|
|
while (item) {
|
|
if (item.closeRequested !== undefined) {
|
|
item.closeRequested();
|
|
break;
|
|
}
|
|
item = item.parent;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// One NSectionEditor per monitor
|
|
Repeater {
|
|
model: Quickshell.screens
|
|
|
|
NSectionEditor {
|
|
required property var modelData
|
|
readonly property real compositorScale: {
|
|
const info = CompositorService.displayScales[modelData.name];
|
|
return (info && info.scale) ? info.scale : 1.0;
|
|
}
|
|
|
|
Layout.fillWidth: true
|
|
sectionName: modelData.name
|
|
sectionSubtitle: {
|
|
// Format scale to 2 decimal places to prevent overly long text
|
|
var formattedScale = compositorScale.toFixed(2);
|
|
return "(" + modelData.width + "x" + modelData.height + " @ " + formattedScale + "x)";
|
|
}
|
|
|
|
sectionId: modelData.name
|
|
screen: modelData
|
|
settingsDialogComponent: Qt.resolvedUrl(Quickshell.shellDir + "/Modules/Panels/Settings/DesktopWidgets/DesktopWidgetSettingsDialog.qml")
|
|
widgetRegistry: DesktopWidgetRegistry
|
|
widgetModel: getWidgetsForMonitor(modelData.name)
|
|
availableWidgets: root.availableWidgetsModel
|
|
availableSections: root.getScreenNames()
|
|
sectionLabels: root.getScreenLabels()
|
|
sectionIcons: root.getScreenIcons()
|
|
draggable: false // Desktop widgets are positioned by X,Y, not list order
|
|
crossSectionDraggable: true
|
|
maxWidgets: -1
|
|
onAddWidget: (widgetId, section) => _addWidgetToMonitor(modelData.name, widgetId)
|
|
onRemoveWidget: (section, index) => _removeWidgetFromMonitor(modelData.name, index)
|
|
onMoveWidget: (fromSection, index, toSection) => _moveWidgetToMonitor(fromSection, index, toSection)
|
|
onUpdateWidgetSettings: (section, index, settings) => _updateWidgetSettingsForMonitor(modelData.name, index, settings)
|
|
onOpenPluginSettingsRequested: manifest => pluginSettingsDialog.openPluginSettings(manifest)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Shared Plugin Settings Popup
|
|
NPluginSettingsPopup {
|
|
id: pluginSettingsDialog
|
|
parent: Overlay.overlay
|
|
showToastOnSave: false
|
|
}
|
|
|
|
Component.onCompleted: {
|
|
// Use Qt.callLater to ensure DesktopWidgetRegistry is ready
|
|
Qt.callLater(updateAvailableWidgetsModel);
|
|
}
|
|
|
|
// Helper to get screen names array
|
|
function getScreenNames() {
|
|
var names = [];
|
|
for (var i = 0; i < Quickshell.screens.length; i++) {
|
|
names.push(Quickshell.screens[i].name);
|
|
}
|
|
return names;
|
|
}
|
|
|
|
// Helper to get screen labels map (just screen names, NSectionEditor adds "Move to" prefix)
|
|
function getScreenLabels() {
|
|
var labels = {};
|
|
for (var i = 0; i < Quickshell.screens.length; i++) {
|
|
var screen = Quickshell.screens[i];
|
|
labels[screen.name] = screen.name;
|
|
}
|
|
return labels;
|
|
}
|
|
|
|
// Helper to get screen icons map
|
|
function getScreenIcons() {
|
|
var icons = {};
|
|
for (var i = 0; i < Quickshell.screens.length; i++) {
|
|
icons[Quickshell.screens[i].name] = "device-desktop";
|
|
}
|
|
return icons;
|
|
}
|
|
|
|
function updateAvailableWidgetsModel() {
|
|
availableWidgets.clear();
|
|
try {
|
|
if (typeof DesktopWidgetRegistry === "undefined" || !DesktopWidgetRegistry) {
|
|
Logger.e("DesktopWidgetsTab", "DesktopWidgetRegistry is not available");
|
|
// Retry after a short delay
|
|
Qt.callLater(function () {
|
|
if (typeof DesktopWidgetRegistry !== "undefined" && DesktopWidgetRegistry) {
|
|
updateAvailableWidgetsModel();
|
|
}
|
|
});
|
|
return;
|
|
}
|
|
var widgetIds = DesktopWidgetRegistry.getAvailableWidgets();
|
|
Logger.d("DesktopWidgetsTab", "Found widgets:", widgetIds, "count:", widgetIds ? widgetIds.length : 0);
|
|
if (!widgetIds || widgetIds.length === 0) {
|
|
Logger.w("DesktopWidgetsTab", "No widgets found in registry");
|
|
return;
|
|
}
|
|
for (var i = 0; i < widgetIds.length; i++) {
|
|
var widgetId = widgetIds[i];
|
|
var displayName = widgetId;
|
|
|
|
// Get plugin name for plugin widgets
|
|
var isPlugin = false;
|
|
if (DesktopWidgetRegistry.isPluginWidget(widgetId)) {
|
|
isPlugin = true;
|
|
var pluginId = widgetId.replace("plugin:", "");
|
|
var manifest = PluginRegistry.getPluginManifest(pluginId);
|
|
if (manifest && manifest.name) {
|
|
displayName = manifest.name;
|
|
}
|
|
}
|
|
|
|
// Add plugin badge first (with custom color)
|
|
const badges = [];
|
|
if (isPlugin) {
|
|
badges.push({
|
|
"icon": "plugin",
|
|
"color": Color.mSecondary
|
|
});
|
|
}
|
|
if (DesktopWidgetRegistry.isCpuIntensive(widgetId)) {
|
|
badges.push({
|
|
"icon": "cpu-intensive",
|
|
"color": Color.mSecondary
|
|
});
|
|
}
|
|
|
|
availableWidgets.append({
|
|
"key": widgetId,
|
|
"name": displayName,
|
|
"badges": badges
|
|
});
|
|
}
|
|
Logger.d("DesktopWidgetsTab", "Available widgets model count:", availableWidgets.count);
|
|
} catch (e) {
|
|
Logger.e("DesktopWidgetsTab", "Error updating available widgets:", e, e.stack);
|
|
}
|
|
}
|
|
|
|
// Get widgets for a specific monitor
|
|
function getWidgetsForMonitor(monitorName) {
|
|
var monitorWidgets = Settings.data.desktopWidgets.monitorWidgets || [];
|
|
for (var i = 0; i < monitorWidgets.length; i++) {
|
|
if (monitorWidgets[i].name === monitorName) {
|
|
return monitorWidgets[i].widgets || [];
|
|
}
|
|
}
|
|
return [];
|
|
}
|
|
|
|
// Set widgets for a specific monitor
|
|
function setWidgetsForMonitor(monitorName, widgets) {
|
|
var monitorWidgets = Settings.data.desktopWidgets.monitorWidgets || [];
|
|
var newMonitorWidgets = monitorWidgets.slice();
|
|
var found = false;
|
|
for (var i = 0; i < newMonitorWidgets.length; i++) {
|
|
if (newMonitorWidgets[i].name === monitorName) {
|
|
newMonitorWidgets[i] = {
|
|
"name": monitorName,
|
|
"widgets": widgets
|
|
};
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
newMonitorWidgets.push({
|
|
"name": monitorName,
|
|
"widgets": widgets
|
|
});
|
|
}
|
|
Settings.data.desktopWidgets.monitorWidgets = newMonitorWidgets;
|
|
}
|
|
|
|
function _addWidgetToMonitor(monitorName, widgetId) {
|
|
var newWidget = {
|
|
"id": widgetId
|
|
};
|
|
if (DesktopWidgetRegistry.widgetHasUserSettings(widgetId)) {
|
|
var metadata = DesktopWidgetRegistry.widgetMetadata[widgetId];
|
|
if (metadata) {
|
|
Object.keys(metadata).forEach(function (key) {
|
|
newWidget[key] = metadata[key];
|
|
});
|
|
}
|
|
}
|
|
// Set default positions
|
|
if (widgetId === "Clock") {
|
|
newWidget.x = 50;
|
|
newWidget.y = 50;
|
|
} else if (widgetId === "MediaPlayer") {
|
|
newWidget.x = 100;
|
|
newWidget.y = 200;
|
|
} else if (widgetId === "AudioVisualizer") {
|
|
newWidget.x = 120;
|
|
newWidget.y = 280;
|
|
} else if (widgetId === "Weather") {
|
|
newWidget.x = 100;
|
|
newWidget.y = 300;
|
|
} else {
|
|
// Default position for plugin widgets
|
|
newWidget.x = 150;
|
|
newWidget.y = 150;
|
|
}
|
|
var widgets = getWidgetsForMonitor(monitorName).slice();
|
|
widgets.push(newWidget);
|
|
setWidgetsForMonitor(monitorName, widgets);
|
|
}
|
|
|
|
function _removeWidgetFromMonitor(monitorName, index) {
|
|
var widgets = getWidgetsForMonitor(monitorName);
|
|
if (index >= 0 && index < widgets.length) {
|
|
var newArray = widgets.slice();
|
|
newArray.splice(index, 1);
|
|
setWidgetsForMonitor(monitorName, newArray);
|
|
}
|
|
}
|
|
|
|
function _updateWidgetSettingsForMonitor(monitorName, index, settings) {
|
|
var widgets = getWidgetsForMonitor(monitorName);
|
|
if (index >= 0 && index < widgets.length) {
|
|
var newArray = widgets.slice();
|
|
newArray[index] = Object.assign({}, newArray[index], settings);
|
|
setWidgetsForMonitor(monitorName, newArray);
|
|
}
|
|
}
|
|
|
|
function _moveWidgetToMonitor(fromMonitor, index, toMonitor) {
|
|
Logger.i("DesktopWidgetsTab", "Moving widget from", fromMonitor, "index", index, "to", toMonitor);
|
|
var sourceWidgets = getWidgetsForMonitor(fromMonitor);
|
|
if (index < 0 || index >= sourceWidgets.length) {
|
|
Logger.e("DesktopWidgetsTab", "Invalid index", index, "for monitor", fromMonitor);
|
|
return;
|
|
}
|
|
|
|
// Get the widget to move
|
|
var newSourceWidgets = sourceWidgets.slice();
|
|
var widget = Object.assign({}, newSourceWidgets.splice(index, 1)[0]);
|
|
|
|
// Reset position and scale to ensure widget is accessible on new screen
|
|
widget.x = 100;
|
|
widget.y = 100;
|
|
widget.scale = 1.0;
|
|
|
|
// Add to destination monitor
|
|
var destWidgets = getWidgetsForMonitor(toMonitor).slice();
|
|
destWidgets.push(widget);
|
|
|
|
// Update both monitors
|
|
setWidgetsForMonitor(fromMonitor, newSourceWidgets);
|
|
setWidgetsForMonitor(toMonitor, destWidgets);
|
|
}
|
|
}
|