mirror of
https://github.com/noctalia-dev/noctalia-shell.git
synced 2026-05-11 17:08:27 +08:00
Settings: added option to open settings in a separate (tiled) window + Fixed migrations/upgrades by parsing the rawJson
This commit is contained in:
@@ -705,6 +705,11 @@
|
||||
"hover": "Beim Hover Scrollen",
|
||||
"never": "Nie Scrollen"
|
||||
},
|
||||
"settings-panel-mode": {
|
||||
"attached": "An Platte an einer Stange befestigt",
|
||||
"centered": "Zentriertes Feld",
|
||||
"window": "Eigenes Fenster"
|
||||
},
|
||||
"shadow-direction": {
|
||||
"bottom": "Unten",
|
||||
"bottom_left": "Unten links",
|
||||
@@ -2176,9 +2181,9 @@
|
||||
"description": "Passen Sie das Aussehen, die Haptik und das Verhalten der Benutzeroberfläche an.",
|
||||
"label": "Aussehen"
|
||||
},
|
||||
"settings-panel-attached-to-bar": {
|
||||
"description": "Richten Sie das Einstellungsfenster an der Leiste aus, um ein einheitliches Erscheinungsbild zu erhalten.",
|
||||
"label": "Einstellungsfenster an Leiste ausrichten"
|
||||
"settings-panel-mode": {
|
||||
"description": "Wähle das Layout der Einstellungen (möglicherweise ist ein Neustart erforderlich).",
|
||||
"label": "Einstellungsfeldmodus"
|
||||
},
|
||||
"shadows": {
|
||||
"description": "Aktiviert Schlagschatten unter Balken und Panels.",
|
||||
|
||||
@@ -705,6 +705,11 @@
|
||||
"hover": "Scroll on hover",
|
||||
"never": "Never scroll"
|
||||
},
|
||||
"settings-panel-mode": {
|
||||
"attached": "Panel attached to bar",
|
||||
"centered": "Centered panel",
|
||||
"window": "Separate window"
|
||||
},
|
||||
"shadow-direction": {
|
||||
"bottom": "Below",
|
||||
"bottom_left": "Bottom left",
|
||||
@@ -2176,9 +2181,9 @@
|
||||
"description": "Customize the look, feel, and behavior of the interface.",
|
||||
"label": "Appearance"
|
||||
},
|
||||
"settings-panel-attached-to-bar": {
|
||||
"description": "Keep the settings window aligned with the bar for a unified look.",
|
||||
"label": "Snap settings window to bar"
|
||||
"settings-panel-mode": {
|
||||
"description": "Choose settings layout (may require reopening).",
|
||||
"label": "Settings panel mode"
|
||||
},
|
||||
"shadows": {
|
||||
"description": "Enables drop shadows under bars and panels.",
|
||||
|
||||
@@ -705,6 +705,11 @@
|
||||
"hover": "Desplazar al Pasar",
|
||||
"never": "Nunca Desplazar"
|
||||
},
|
||||
"settings-panel-mode": {
|
||||
"attached": "Panel adjunto a la barra",
|
||||
"centered": "Panel centrado",
|
||||
"window": "Ventana separada"
|
||||
},
|
||||
"shadow-direction": {
|
||||
"bottom": "Inferior",
|
||||
"bottom_left": "Inferior izquierda",
|
||||
@@ -2176,9 +2181,9 @@
|
||||
"description": "Personaliza la apariencia, el ambiente y el comportamiento de la interfaz.",
|
||||
"label": "Apariencia"
|
||||
},
|
||||
"settings-panel-attached-to-bar": {
|
||||
"description": "Mantén la ventana de Configuración alineada con la barra para un aspecto unificado.",
|
||||
"label": "Ajustar Configuración a la barra"
|
||||
"settings-panel-mode": {
|
||||
"description": "Elegir diseño de configuración (puede requerir reapertura).",
|
||||
"label": "Modo panel de configuración"
|
||||
},
|
||||
"shadows": {
|
||||
"description": "Habilita sombras paralelas debajo de las barras y los paneles.",
|
||||
|
||||
@@ -705,6 +705,11 @@
|
||||
"hover": "Défiler au Survol",
|
||||
"never": "Ne Jamais Défiler"
|
||||
},
|
||||
"settings-panel-mode": {
|
||||
"attached": "Panneau fixé à une barre",
|
||||
"centered": "Panneau centré",
|
||||
"window": "Fenêtre séparée"
|
||||
},
|
||||
"shadow-direction": {
|
||||
"bottom": "En bas",
|
||||
"bottom_left": "En bas à gauche",
|
||||
@@ -2176,9 +2181,9 @@
|
||||
"description": "Personnaliser l'apparence, l'ergonomie et le comportement de l'interface.",
|
||||
"label": "Apparence"
|
||||
},
|
||||
"settings-panel-attached-to-bar": {
|
||||
"description": "Alignez la fenêtre des paramètres sur la barre pour un rendu cohérent.",
|
||||
"label": "Aligner la fenêtre des paramètres sur la barre"
|
||||
"settings-panel-mode": {
|
||||
"description": "Choisir la disposition des paramètres (peut nécessiter une réouverture).",
|
||||
"label": "Mode du panneau de configuration"
|
||||
},
|
||||
"shadows": {
|
||||
"description": "Active les ombres portées sous les barres et les panneaux.",
|
||||
|
||||
@@ -705,6 +705,11 @@
|
||||
"hover": "ホバー時にスクロール",
|
||||
"never": "スクロールしない"
|
||||
},
|
||||
"settings-panel-mode": {
|
||||
"attached": "棒に取り付けられたパネル",
|
||||
"centered": "中央パネル",
|
||||
"window": "別ウィンドウ"
|
||||
},
|
||||
"shadow-direction": {
|
||||
"bottom": "下",
|
||||
"bottom_left": "左下",
|
||||
@@ -2176,9 +2181,9 @@
|
||||
"description": "インターフェースの外観や操作感、挙動をカスタマイズします。",
|
||||
"label": "外観"
|
||||
},
|
||||
"settings-panel-attached-to-bar": {
|
||||
"description": "設定ウィンドウをバーに合わせて配置し、統一感のある外観にします。",
|
||||
"label": "設定ウィンドウをバーに吸着"
|
||||
"settings-panel-mode": {
|
||||
"description": "設定レイアウトを選択 (再起動が必要な場合があります)",
|
||||
"label": "設定パネルモード"
|
||||
},
|
||||
"shadows": {
|
||||
"description": "バーやパネルの下にドロップシャドウ(影)を表示します。",
|
||||
|
||||
@@ -705,6 +705,11 @@
|
||||
"hover": "Scrollen bij hover",
|
||||
"never": "Nooit scrollen"
|
||||
},
|
||||
"settings-panel-mode": {
|
||||
"attached": "Paneel bevestigd aan staaf",
|
||||
"centered": "Gecentreerd paneel",
|
||||
"window": "Apart venster"
|
||||
},
|
||||
"shadow-direction": {
|
||||
"bottom": "Onder",
|
||||
"bottom_left": "Linksonder",
|
||||
@@ -2176,9 +2181,9 @@
|
||||
"description": "Pas de look, feel en het gedrag van de interface aan.",
|
||||
"label": "Uiterlijk"
|
||||
},
|
||||
"settings-panel-attached-to-bar": {
|
||||
"description": "Houd het instellingenvenster uitgelijnd met de balk voor een uniforme uitstraling.",
|
||||
"label": "Instellingenvenster aan balk vastklikken"
|
||||
"settings-panel-mode": {
|
||||
"description": "Kies lay-out voor instellingen (mogelijk opnieuw openen vereist).",
|
||||
"label": "Instellingenpaneelmodus"
|
||||
},
|
||||
"shadows": {
|
||||
"description": "Schakelt slagschaduwen onder balken en panelen in.",
|
||||
|
||||
@@ -705,6 +705,11 @@
|
||||
"hover": "Rolar ao Passar o Mouse",
|
||||
"never": "Nunca Rolar"
|
||||
},
|
||||
"settings-panel-mode": {
|
||||
"attached": "Painel anexado à barra",
|
||||
"centered": "Painel centralizado",
|
||||
"window": "Janela separada"
|
||||
},
|
||||
"shadow-direction": {
|
||||
"bottom": "Inferior",
|
||||
"bottom_left": "Inferior esquerda",
|
||||
@@ -2176,9 +2181,9 @@
|
||||
"description": "Personalize a aparência, a sensação e o comportamento da interface.",
|
||||
"label": "Aparência"
|
||||
},
|
||||
"settings-panel-attached-to-bar": {
|
||||
"description": "Mantenha a janela de Configurações alinhada com a barra para um visual uniforme.",
|
||||
"label": "Ajustar Configurações à barra"
|
||||
"settings-panel-mode": {
|
||||
"description": "Escolha o layout das configurações (pode ser necessário reabrir).",
|
||||
"label": "Modo do painel de configurações"
|
||||
},
|
||||
"shadows": {
|
||||
"description": "Ativa sombras projetadas sob barras e painéis.",
|
||||
|
||||
@@ -705,6 +705,11 @@
|
||||
"hover": "Прокручивать при наведении",
|
||||
"never": "Никогда не прокручивать"
|
||||
},
|
||||
"settings-panel-mode": {
|
||||
"attached": "Панель, прикреплённая к перекладине",
|
||||
"centered": "Центрированная панель",
|
||||
"window": "Отдельное окно"
|
||||
},
|
||||
"shadow-direction": {
|
||||
"bottom": "Внизу",
|
||||
"bottom_left": "Внизу слева",
|
||||
@@ -2176,9 +2181,9 @@
|
||||
"description": "Настройка внешнего вида, ощущений и поведения интерфейса.",
|
||||
"label": "Внешний вид"
|
||||
},
|
||||
"settings-panel-attached-to-bar": {
|
||||
"description": "Держать окно настроек выровненным с панелью для единого вида.",
|
||||
"label": "Прикрепить окно настроек к панели"
|
||||
"settings-panel-mode": {
|
||||
"description": "Выберите раскладку настроек (может потребоваться перезапуск).",
|
||||
"label": "Режим панели настроек"
|
||||
},
|
||||
"shadows": {
|
||||
"description": "Включает отбрасываемые тени под панелями и панелью задач.",
|
||||
|
||||
@@ -705,6 +705,11 @@
|
||||
"hover": "Üzerine Gelince Kaydır",
|
||||
"never": "Asla Kaydırma"
|
||||
},
|
||||
"settings-panel-mode": {
|
||||
"attached": "Çubuğa bağlı panel",
|
||||
"centered": "Ortalanmış panel",
|
||||
"window": "Ayrı pencere"
|
||||
},
|
||||
"shadow-direction": {
|
||||
"bottom": "Alt",
|
||||
"bottom_left": "Sol alt",
|
||||
@@ -2176,9 +2181,9 @@
|
||||
"description": "Arayüzün görünümünü, hissini ve davranışını özelleştirin.",
|
||||
"label": "Görünüm"
|
||||
},
|
||||
"settings-panel-attached-to-bar": {
|
||||
"description": "Ayarlar penceresini çubukla hizalayarak tutarlı bir görünüm sağlayın.",
|
||||
"label": "Ayarlar penceresini çubuğa hizala"
|
||||
"settings-panel-mode": {
|
||||
"description": "Ayarlar düzenini seçin (yeniden açılması gerekebilir).",
|
||||
"label": "Ayarlar paneli modu"
|
||||
},
|
||||
"shadows": {
|
||||
"description": "Çubukların ve panellerin altında gölgelerin etkinleştirilmesini sağlar.",
|
||||
|
||||
@@ -705,6 +705,11 @@
|
||||
"hover": "Прокручувати при наведенні",
|
||||
"never": "Ніколи не прокручувати"
|
||||
},
|
||||
"settings-panel-mode": {
|
||||
"attached": "Панель, прикріплена до стійки",
|
||||
"centered": "Центрована панель",
|
||||
"window": "Окреме вікно"
|
||||
},
|
||||
"shadow-direction": {
|
||||
"bottom": "Знизу",
|
||||
"bottom_left": "Знизу ліворуч",
|
||||
@@ -2176,9 +2181,9 @@
|
||||
"description": "Налаштуйте вигляд, відчуття та поведінку інтерфейсу.",
|
||||
"label": "Зовнішній вигляд"
|
||||
},
|
||||
"settings-panel-attached-to-bar": {
|
||||
"description": "Вирівнюйте вікно налаштувань відносно панелі, щоб зберегти цілісний вигляд.",
|
||||
"label": "Прив'язувати вікно налаштувань до панелі"
|
||||
"settings-panel-mode": {
|
||||
"description": "Виберіть макет налаштувань (може знадобитися перезапуск).",
|
||||
"label": "Режим панелі налаштувань"
|
||||
},
|
||||
"shadows": {
|
||||
"description": "Увімкнути тіні під панелями та смугами.",
|
||||
|
||||
@@ -705,6 +705,11 @@
|
||||
"hover": "悬停时滚动",
|
||||
"never": "从不滚动"
|
||||
},
|
||||
"settings-panel-mode": {
|
||||
"attached": "连接到杆的面板",
|
||||
"centered": "居中面板",
|
||||
"window": "分离窗口"
|
||||
},
|
||||
"shadow-direction": {
|
||||
"bottom": "下方",
|
||||
"bottom_left": "左下",
|
||||
@@ -2176,9 +2181,9 @@
|
||||
"description": "自定义界面的外观、感觉和行为。",
|
||||
"label": "外观"
|
||||
},
|
||||
"settings-panel-attached-to-bar": {
|
||||
"description": "使设置窗口与边栏保持对齐,实现统一的外观。",
|
||||
"label": "将设置窗口贴合边栏"
|
||||
"settings-panel-mode": {
|
||||
"description": "选择设置布局(可能需要重新打开)。",
|
||||
"label": "设置面板模式"
|
||||
},
|
||||
"shadows": {
|
||||
"description": "启用条形图和面板下的阴影。",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"settingsVersion": 26,
|
||||
"settingsVersion": 0,
|
||||
"bar": {
|
||||
"position": "top",
|
||||
"backgroundOpacity": 1,
|
||||
@@ -94,7 +94,7 @@
|
||||
"tooltipsEnabled": true,
|
||||
"panelBackgroundOpacity": 1,
|
||||
"panelsAttachedToBar": true,
|
||||
"settingsPanelAttachToBar": false
|
||||
"settingsPanelMode": "attached"
|
||||
},
|
||||
"location": {
|
||||
"name": "Tokyo",
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
import QtQuick
|
||||
|
||||
QtObject {
|
||||
id: root
|
||||
|
||||
// Migrate from version < 26 to version 26
|
||||
// Replaces old calendar-card and banner-card with calendar-header-card and calendar-month-card
|
||||
function migrate(adapter, logger) {
|
||||
logger.i("Settings", "Migrating settings to v26");
|
||||
|
||||
// Replace old calendar-card and banner-card with calendar-header-card and calendar-month-card
|
||||
if (adapter.calendar !== undefined && adapter.calendar.cards !== undefined) {
|
||||
const oldCards = adapter.calendar.cards;
|
||||
const newCards = [];
|
||||
let anyCalendarEnabled = false;
|
||||
|
||||
// Check if any calendar-related card was enabled
|
||||
for (var i = 0; i < oldCards.length; i++) {
|
||||
const card = oldCards[i];
|
||||
if ((card.id === "banner-card" || card.id === "calendar-card") && card.enabled) {
|
||||
anyCalendarEnabled = true;
|
||||
} else if (card.id !== "banner-card" && card.id !== "calendar-card" && card.id !== 'calendar-month-card' && card.id !== 'calendar-header-card') {
|
||||
// Keep other cards as-is (timer, weather)
|
||||
newCards.push(card);
|
||||
}
|
||||
}
|
||||
|
||||
// Add new split cards at the beginning (enabled if any old calendar card was enabled)
|
||||
newCards.unshift({
|
||||
"id": "calendar-month-card",
|
||||
"enabled": anyCalendarEnabled
|
||||
});
|
||||
newCards.unshift({
|
||||
"id": "calendar-header-card",
|
||||
"enabled": anyCalendarEnabled
|
||||
});
|
||||
|
||||
adapter.calendar.cards = newCards;
|
||||
logger.i("Settings", "Replaced old calendar cards with calendar-header-card + calendar-month-card");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import QtQuick
|
||||
|
||||
QtObject {
|
||||
id: root
|
||||
|
||||
// Migrate from version < 27 to version 27
|
||||
// Converts settingsPanelAttachToBar boolean to settingsPanelMode string
|
||||
function migrate(adapter, logger, rawJson) {
|
||||
logger.i("Settings", "Migrating settings to v27");
|
||||
|
||||
// Check rawJson for old property (adapter doesn't expose removed properties)
|
||||
if (rawJson?.ui?.settingsPanelAttachToBar !== undefined) {
|
||||
if (rawJson.ui.settingsPanelAttachToBar === true) {
|
||||
adapter.ui.settingsPanelMode = "attached";
|
||||
} else {
|
||||
adapter.ui.settingsPanelMode = "centered";
|
||||
}
|
||||
logger.i("Settings", "Migrated settingsPanelAttachToBar to settingsPanelMode: " + adapter.ui.settingsPanelMode);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -7,9 +7,9 @@ QtObject {
|
||||
|
||||
// Map of version number to migration component
|
||||
readonly property var migrations: ({
|
||||
26: migration26Component
|
||||
27: migration27Component
|
||||
})
|
||||
|
||||
// Migration components
|
||||
property Component migration26Component: Migration26 {}
|
||||
property Component migration27Component: Migration27 {}
|
||||
}
|
||||
|
||||
+23
-14
@@ -23,7 +23,7 @@ Singleton {
|
||||
- Default cache directory: ~/.cache/noctalia
|
||||
*/
|
||||
readonly property alias data: adapter // Used to access via Settings.data.xxx.yyy
|
||||
readonly property int settingsVersion: 26
|
||||
readonly property int settingsVersion: 27
|
||||
readonly property bool isDebug: Quickshell.env("NOCTALIA_DEBUG") === "1"
|
||||
readonly property string shellName: "noctalia"
|
||||
readonly property string configDir: Quickshell.env("NOCTALIA_CONFIG_DIR") || (Quickshell.env("XDG_CONFIG_HOME") || Quickshell.env("HOME") + "/.config") + "/" + shellName + "/"
|
||||
@@ -102,15 +102,25 @@ Singleton {
|
||||
if (!isLoaded) {
|
||||
Logger.i("Settings", "Settings loaded");
|
||||
|
||||
upgradeSettings();
|
||||
// Load raw JSON for migrations (adapter doesn't expose removed properties)
|
||||
var rawJson = null;
|
||||
try {
|
||||
rawJson = JSON.parse(settingsFileView.text());
|
||||
} catch (e) {
|
||||
Logger.w("Settings", "Could not parse raw JSON for migrations");
|
||||
}
|
||||
|
||||
root.isLoaded = true;
|
||||
|
||||
// Emit the signal
|
||||
root.settingsLoaded();
|
||||
// Run versioned migrations immediately, don't move it in upgradeSettings
|
||||
runVersionedMigrations(rawJson);
|
||||
|
||||
// Finally, update our local settings version
|
||||
adapter.settingsVersion = settingsVersion;
|
||||
|
||||
// Emit the signal
|
||||
root.isLoaded = true;
|
||||
root.settingsLoaded();
|
||||
|
||||
upgradeSettings();
|
||||
}
|
||||
}
|
||||
onLoadFailed: function (error) {
|
||||
@@ -141,7 +151,7 @@ Singleton {
|
||||
JsonAdapter {
|
||||
id: adapter
|
||||
|
||||
property int settingsVersion: root.settingsVersion
|
||||
property int settingsVersion: 0
|
||||
|
||||
// bar
|
||||
property JsonObject bar: JsonObject {
|
||||
@@ -251,7 +261,7 @@ Singleton {
|
||||
property bool tooltipsEnabled: true
|
||||
property real panelBackgroundOpacity: 1.0
|
||||
property bool panelsAttachedToBar: true
|
||||
property bool settingsPanelAttachToBar: false
|
||||
property string settingsPanelMode: "attached" // "centered", "attached", "window"
|
||||
}
|
||||
|
||||
// location
|
||||
@@ -643,10 +653,13 @@ Singleton {
|
||||
|
||||
// -----------------------------------------------------
|
||||
// Run versioned migrations using MigrationRegistry
|
||||
function runVersionedMigrations() {
|
||||
// rawJson is the parsed JSON file content (before adapter filtering)
|
||||
function runVersionedMigrations(rawJson) {
|
||||
const currentVersion = adapter.settingsVersion;
|
||||
const migrations = MigrationRegistry.migrations;
|
||||
|
||||
Logger.i("Settings", "adapter.settingsVersion:", adapter.settingsVersion);
|
||||
|
||||
// Get all migration versions and sort them
|
||||
const versions = Object.keys(migrations).map(v => parseInt(v)).sort((a, b) => a - b);
|
||||
|
||||
@@ -660,7 +673,7 @@ Singleton {
|
||||
const migration = migrationComponent.createObject(root);
|
||||
|
||||
if (migration && typeof migration.migrate === "function") {
|
||||
const success = migration.migrate(adapter, Logger);
|
||||
const success = migration.migrate(adapter, Logger, rawJson);
|
||||
if (!success) {
|
||||
Logger.e("Settings", "Migration to v" + version + " failed");
|
||||
}
|
||||
@@ -695,10 +708,6 @@ Singleton {
|
||||
return;
|
||||
}
|
||||
|
||||
// -----------------
|
||||
// Run versioned migrations from MigrationRegistry
|
||||
runVersionedMigrations();
|
||||
|
||||
// -----------------
|
||||
const sections = ["left", "center", "right"];
|
||||
|
||||
|
||||
@@ -100,9 +100,13 @@ Item {
|
||||
}
|
||||
|
||||
if (action === "open-display-settings") {
|
||||
var settingsPanel = PanelService.getPanel("settingsPanel", screen);
|
||||
settingsPanel.requestedTab = SettingsPanel.Tab.Display;
|
||||
settingsPanel.open();
|
||||
if (Settings.data.ui.settingsPanelMode === "window") {
|
||||
SettingsPanelService.openWindow(SettingsPanel.Tab.Display);
|
||||
} else {
|
||||
var settingsPanel = PanelService.getPanel("settingsPanel", screen);
|
||||
settingsPanel.requestedTab = SettingsPanel.Tab.Display;
|
||||
settingsPanel.open();
|
||||
}
|
||||
} else if (action === "widget-settings") {
|
||||
BarService.openWidgetSettings(screen, section, sectionWidgetIndex, widgetId, widgetSettings);
|
||||
}
|
||||
|
||||
@@ -123,7 +123,11 @@ NIconButton {
|
||||
if (action === "open-launcher") {
|
||||
PanelService.getPanel("launcherPanel", screen)?.toggle();
|
||||
} else if (action === "open-settings") {
|
||||
PanelService.getPanel("settingsPanel", screen)?.toggle();
|
||||
if (Settings.data.ui.settingsPanelMode === "window") {
|
||||
SettingsPanelService.toggleWindow();
|
||||
} else {
|
||||
PanelService.getPanel("settingsPanel", screen)?.toggle();
|
||||
}
|
||||
} else if (action === "widget-settings") {
|
||||
BarService.openWidgetSettings(screen, section, sectionWidgetIndex, widgetId, widgetSettings);
|
||||
}
|
||||
|
||||
@@ -417,9 +417,13 @@ Item {
|
||||
Logger.i("CustomButton", `Executing command: ${leftClickExec}`);
|
||||
} else if (!leftClickUpdateText) {
|
||||
// No left click script was defined, open settings
|
||||
var settingsPanel = PanelService.getPanel("settingsPanel", screen);
|
||||
settingsPanel.requestedTab = SettingsPanel.Tab.Bar;
|
||||
settingsPanel.open();
|
||||
if (Settings.data.ui.settingsPanelMode === "window") {
|
||||
SettingsPanelService.openWindow(SettingsPanel.Tab.Bar);
|
||||
} else {
|
||||
var settingsPanel = PanelService.getPanel("settingsPanel", screen);
|
||||
settingsPanel.requestedTab = SettingsPanel.Tab.Bar;
|
||||
settingsPanel.open();
|
||||
}
|
||||
}
|
||||
if (!textStream && leftClickUpdateText) {
|
||||
runTextCommand();
|
||||
|
||||
@@ -45,8 +45,12 @@ NIconButton {
|
||||
}
|
||||
|
||||
onRightClicked: {
|
||||
var settingsPanel = PanelService.getPanel("settingsPanel", screen);
|
||||
settingsPanel.requestedTab = SettingsPanel.Tab.Display;
|
||||
settingsPanel.open();
|
||||
if (Settings.data.ui.settingsPanelMode === "window") {
|
||||
SettingsPanelService.openWindow(SettingsPanel.Tab.Display);
|
||||
} else {
|
||||
var settingsPanel = PanelService.getPanel("settingsPanel", screen);
|
||||
settingsPanel.requestedTab = SettingsPanel.Tab.Display;
|
||||
settingsPanel.open();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,9 +60,14 @@ NBox {
|
||||
icon: "settings"
|
||||
tooltipText: I18n.tr("tooltips.open-settings")
|
||||
onClicked: {
|
||||
var panel = PanelService.getPanel("settingsPanel", screen);
|
||||
panel.requestedTab = SettingsPanel.Tab.General;
|
||||
panel.open();
|
||||
if (Settings.data.ui.settingsPanelMode === "window") {
|
||||
SettingsPanelService.openWindow(SettingsPanel.Tab.General);
|
||||
PanelService.openedPanel?.close();
|
||||
} else {
|
||||
var panel = PanelService.getPanel("settingsPanel", screen);
|
||||
panel.requestedTab = SettingsPanel.Tab.General;
|
||||
panel.open();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,8 +27,12 @@ NIconButtonHot {
|
||||
}
|
||||
|
||||
onRightClicked: {
|
||||
var settingsPanel = PanelService.getPanel("settingsPanel", screen);
|
||||
settingsPanel.requestedTab = SettingsPanel.Tab.Display;
|
||||
settingsPanel.open();
|
||||
if (Settings.data.ui.settingsPanelMode === "window") {
|
||||
SettingsPanelService.openWindow(SettingsPanel.Tab.Display);
|
||||
} else {
|
||||
var settingsPanel = PanelService.getPanel("settingsPanel", screen);
|
||||
settingsPanel.requestedTab = SettingsPanel.Tab.Display;
|
||||
settingsPanel.open();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,568 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs.Commons
|
||||
import qs.Modules.Panels.Settings.Tabs
|
||||
import qs.Modules.Panels.Settings.Tabs.ColorScheme
|
||||
import qs.Modules.Panels.Settings.Tabs.SessionMenu
|
||||
import qs.Services.System
|
||||
import qs.Widgets
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
// Input: which tab to show initially
|
||||
property int requestedTab: 0
|
||||
|
||||
// Exposed state for parent to access
|
||||
property int currentTabIndex: 0
|
||||
property var tabsModel: []
|
||||
property var activeScrollView: null
|
||||
|
||||
// Signal when close button is clicked
|
||||
signal closeRequested
|
||||
|
||||
Component.onCompleted: {
|
||||
updateTabsModel();
|
||||
}
|
||||
|
||||
// Tab components
|
||||
Component {
|
||||
id: generalTab
|
||||
GeneralTab {}
|
||||
}
|
||||
Component {
|
||||
id: launcherTab
|
||||
LauncherTab {}
|
||||
}
|
||||
Component {
|
||||
id: barTab
|
||||
BarTab {}
|
||||
}
|
||||
Component {
|
||||
id: audioTab
|
||||
AudioTab {}
|
||||
}
|
||||
Component {
|
||||
id: displayTab
|
||||
DisplayTab {}
|
||||
}
|
||||
Component {
|
||||
id: osdTab
|
||||
OsdTab {}
|
||||
}
|
||||
Component {
|
||||
id: networkTab
|
||||
NetworkTab {}
|
||||
}
|
||||
Component {
|
||||
id: locationTab
|
||||
LocationTab {}
|
||||
}
|
||||
Component {
|
||||
id: colorSchemeTab
|
||||
ColorSchemeTab {}
|
||||
}
|
||||
Component {
|
||||
id: wallpaperTab
|
||||
WallpaperTab {}
|
||||
}
|
||||
Component {
|
||||
id: screenRecorderTab
|
||||
ScreenRecorderTab {}
|
||||
}
|
||||
Component {
|
||||
id: aboutTab
|
||||
AboutTab {}
|
||||
}
|
||||
Component {
|
||||
id: hooksTab
|
||||
HooksTab {}
|
||||
}
|
||||
Component {
|
||||
id: dockTab
|
||||
DockTab {}
|
||||
}
|
||||
Component {
|
||||
id: notificationsTab
|
||||
NotificationsTab {}
|
||||
}
|
||||
Component {
|
||||
id: controlCenterTab
|
||||
ControlCenterTab {}
|
||||
}
|
||||
Component {
|
||||
id: userInterfaceTab
|
||||
UserInterfaceTab {}
|
||||
}
|
||||
Component {
|
||||
id: lockScreenTab
|
||||
LockScreenTab {}
|
||||
}
|
||||
Component {
|
||||
id: sessionMenuTab
|
||||
SessionMenuTab {}
|
||||
}
|
||||
Component {
|
||||
id: systemMonitorTab
|
||||
SystemMonitorTab {}
|
||||
}
|
||||
Component {
|
||||
id: pluginsTab
|
||||
PluginsTab {}
|
||||
}
|
||||
|
||||
function updateTabsModel() {
|
||||
let newTabs = [
|
||||
{
|
||||
"id": SettingsPanel.Tab.General,
|
||||
"label": "settings.general.title",
|
||||
"icon": "settings-general",
|
||||
"source": generalTab
|
||||
},
|
||||
{
|
||||
"id": SettingsPanel.Tab.UserInterface,
|
||||
"label": "settings.user-interface.title",
|
||||
"icon": "settings-user-interface",
|
||||
"source": userInterfaceTab
|
||||
},
|
||||
{
|
||||
"id": SettingsPanel.Tab.ColorScheme,
|
||||
"label": "settings.color-scheme.title",
|
||||
"icon": "settings-color-scheme",
|
||||
"source": colorSchemeTab
|
||||
},
|
||||
{
|
||||
"id": SettingsPanel.Tab.Wallpaper,
|
||||
"label": "settings.wallpaper.title",
|
||||
"icon": "settings-wallpaper",
|
||||
"source": wallpaperTab
|
||||
},
|
||||
{
|
||||
"id": SettingsPanel.Tab.Bar,
|
||||
"label": "settings.bar.title",
|
||||
"icon": "settings-bar",
|
||||
"source": barTab
|
||||
},
|
||||
{
|
||||
"id": SettingsPanel.Tab.Dock,
|
||||
"label": "settings.dock.title",
|
||||
"icon": "settings-dock",
|
||||
"source": dockTab
|
||||
},
|
||||
{
|
||||
"id": SettingsPanel.Tab.ControlCenter,
|
||||
"label": "settings.control-center.title",
|
||||
"icon": "settings-control-center",
|
||||
"source": controlCenterTab
|
||||
},
|
||||
{
|
||||
"id": SettingsPanel.Tab.Launcher,
|
||||
"label": "settings.launcher.title",
|
||||
"icon": "settings-launcher",
|
||||
"source": launcherTab
|
||||
},
|
||||
{
|
||||
"id": SettingsPanel.Tab.Notifications,
|
||||
"label": "settings.notifications.title",
|
||||
"icon": "settings-notifications",
|
||||
"source": notificationsTab
|
||||
},
|
||||
{
|
||||
"id": SettingsPanel.Tab.OSD,
|
||||
"label": "settings.osd.title",
|
||||
"icon": "settings-osd",
|
||||
"source": osdTab
|
||||
},
|
||||
{
|
||||
"id": SettingsPanel.Tab.LockScreen,
|
||||
"label": "settings.lock-screen.title",
|
||||
"icon": "settings-lock-screen",
|
||||
"source": lockScreenTab
|
||||
},
|
||||
{
|
||||
"id": SettingsPanel.Tab.SessionMenu,
|
||||
"label": "settings.session-menu.title",
|
||||
"icon": "settings-session-menu",
|
||||
"source": sessionMenuTab
|
||||
},
|
||||
{
|
||||
"id": SettingsPanel.Tab.Audio,
|
||||
"label": "settings.audio.title",
|
||||
"icon": "settings-audio",
|
||||
"source": audioTab
|
||||
},
|
||||
{
|
||||
"id": SettingsPanel.Tab.Display,
|
||||
"label": "settings.display.title",
|
||||
"icon": "settings-display",
|
||||
"source": displayTab
|
||||
},
|
||||
{
|
||||
"id": SettingsPanel.Tab.Network,
|
||||
"label": "settings.network.title",
|
||||
"icon": "settings-network",
|
||||
"source": networkTab
|
||||
},
|
||||
{
|
||||
"id": SettingsPanel.Tab.Location,
|
||||
"label": "settings.location.title",
|
||||
"icon": "settings-location",
|
||||
"source": locationTab
|
||||
},
|
||||
{
|
||||
"id": SettingsPanel.Tab.ScreenRecorder,
|
||||
"label": "settings.screen-recorder.title",
|
||||
"icon": "settings-screen-recorder",
|
||||
"source": screenRecorderTab
|
||||
},
|
||||
{
|
||||
"id": SettingsPanel.Tab.SystemMonitor,
|
||||
"label": "settings.system-monitor.title",
|
||||
"icon": "settings-system-monitor",
|
||||
"source": systemMonitorTab
|
||||
},
|
||||
{
|
||||
"id": SettingsPanel.Tab.Plugins,
|
||||
"label": "settings.plugins.title",
|
||||
"icon": "plugin",
|
||||
"source": pluginsTab
|
||||
},
|
||||
{
|
||||
"id": SettingsPanel.Tab.Hooks,
|
||||
"label": "settings.hooks.title",
|
||||
"icon": "settings-hooks",
|
||||
"source": hooksTab
|
||||
},
|
||||
{
|
||||
"id": SettingsPanel.Tab.About,
|
||||
"label": "settings.about.title",
|
||||
"icon": "settings-about",
|
||||
"source": aboutTab
|
||||
}
|
||||
];
|
||||
|
||||
root.tabsModel = newTabs;
|
||||
}
|
||||
|
||||
function selectTabById(tabId) {
|
||||
for (var i = 0; i < tabsModel.length; i++) {
|
||||
if (tabsModel[i].id === tabId) {
|
||||
currentTabIndex = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
currentTabIndex = 0;
|
||||
}
|
||||
|
||||
function initialize() {
|
||||
ProgramCheckerService.checkAllPrograms();
|
||||
updateTabsModel();
|
||||
selectTabById(requestedTab);
|
||||
}
|
||||
|
||||
// Scroll functions
|
||||
function scrollDown() {
|
||||
if (activeScrollView && activeScrollView.ScrollBar.vertical) {
|
||||
const scrollBar = activeScrollView.ScrollBar.vertical;
|
||||
const stepSize = activeScrollView.height * 0.1;
|
||||
scrollBar.position = Math.min(scrollBar.position + stepSize / activeScrollView.contentHeight, 1.0 - scrollBar.size);
|
||||
}
|
||||
}
|
||||
|
||||
function scrollUp() {
|
||||
if (activeScrollView && activeScrollView.ScrollBar.vertical) {
|
||||
const scrollBar = activeScrollView.ScrollBar.vertical;
|
||||
const stepSize = activeScrollView.height * 0.1;
|
||||
scrollBar.position = Math.max(scrollBar.position - stepSize / activeScrollView.contentHeight, 0);
|
||||
}
|
||||
}
|
||||
|
||||
function scrollPageDown() {
|
||||
if (activeScrollView && activeScrollView.ScrollBar.vertical) {
|
||||
const scrollBar = activeScrollView.ScrollBar.vertical;
|
||||
const pageSize = activeScrollView.height * 0.9;
|
||||
scrollBar.position = Math.min(scrollBar.position + pageSize / activeScrollView.contentHeight, 1.0 - scrollBar.size);
|
||||
}
|
||||
}
|
||||
|
||||
function scrollPageUp() {
|
||||
if (activeScrollView && activeScrollView.ScrollBar.vertical) {
|
||||
const scrollBar = activeScrollView.ScrollBar.vertical;
|
||||
const pageSize = activeScrollView.height * 0.9;
|
||||
scrollBar.position = Math.max(scrollBar.position - pageSize / activeScrollView.contentHeight, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Tab navigation functions
|
||||
function selectNextTab() {
|
||||
if (tabsModel.length > 0) {
|
||||
currentTabIndex = (currentTabIndex + 1) % tabsModel.length;
|
||||
}
|
||||
}
|
||||
|
||||
function selectPreviousTab() {
|
||||
if (tabsModel.length > 0) {
|
||||
currentTabIndex = (currentTabIndex - 1 + tabsModel.length) % tabsModel.length;
|
||||
}
|
||||
}
|
||||
|
||||
// Main UI
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginL
|
||||
spacing: 0
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
spacing: Style.marginL
|
||||
|
||||
// Sidebar
|
||||
Rectangle {
|
||||
id: sidebar
|
||||
clip: true
|
||||
Layout.preferredWidth: 220 * Style.uiScaleRatio
|
||||
Layout.fillHeight: true
|
||||
Layout.alignment: Qt.AlignTop
|
||||
color: Color.transparent
|
||||
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
|
||||
NListView {
|
||||
id: sidebarList
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginS
|
||||
model: root.tabsModel
|
||||
spacing: Style.marginXS
|
||||
currentIndex: root.currentTabIndex
|
||||
verticalPolicy: ScrollBar.AsNeeded
|
||||
|
||||
delegate: Rectangle {
|
||||
id: tabItem
|
||||
width: sidebarList.verticalScrollBarActive ? sidebarList.width - sidebarList.scrollBarWidth - Style.marginXS : sidebarList.width
|
||||
height: tabEntryRow.implicitHeight + Style.marginS * 2
|
||||
radius: Style.radiusS
|
||||
color: selected ? Color.mPrimary : (tabItem.hovering ? Color.mHover : Color.transparent)
|
||||
readonly property bool selected: index === root.currentTabIndex
|
||||
property bool hovering: false
|
||||
property color tabTextColor: selected ? Color.mOnPrimary : (tabItem.hovering ? Color.mOnHover : Color.mOnSurface)
|
||||
|
||||
Behavior on width {
|
||||
NumberAnimation {
|
||||
duration: Style.animationFast
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Style.animationFast
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on tabTextColor {
|
||||
ColorAnimation {
|
||||
duration: Style.animationFast
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: tabEntryRow
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Style.marginS
|
||||
anchors.rightMargin: Style.marginS
|
||||
spacing: Style.marginM
|
||||
|
||||
NIcon {
|
||||
icon: modelData.icon
|
||||
color: tabTextColor
|
||||
pointSize: Style.fontSizeXL
|
||||
}
|
||||
|
||||
NText {
|
||||
text: I18n.tr(modelData.label)
|
||||
color: tabTextColor
|
||||
pointSize: Style.fontSizeM
|
||||
font.weight: Style.fontWeightBold
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onEntered: tabItem.hovering = true
|
||||
onExited: tabItem.hovering = false
|
||||
onCanceled: tabItem.hovering = false
|
||||
onClicked: root.currentTabIndex = index
|
||||
}
|
||||
}
|
||||
|
||||
onCurrentIndexChanged: {
|
||||
if (currentIndex !== root.currentTabIndex) {
|
||||
root.currentTabIndex = currentIndex;
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root
|
||||
function onCurrentTabIndexChanged() {
|
||||
if (sidebarList.currentIndex !== root.currentTabIndex) {
|
||||
sidebarList.currentIndex = root.currentTabIndex;
|
||||
sidebarList.positionViewAtIndex(root.currentTabIndex, ListView.Contain);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Overlay gradient for sidebar scrolling
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.borderS
|
||||
radius: Style.radiusM
|
||||
color: Color.transparent
|
||||
visible: sidebarList.verticalScrollBarActive
|
||||
gradient: Gradient {
|
||||
GradientStop {
|
||||
position: 0.0
|
||||
color: Color.transparent
|
||||
}
|
||||
GradientStop {
|
||||
position: 0.95
|
||||
color: Color.transparent
|
||||
}
|
||||
GradientStop {
|
||||
position: 1.0
|
||||
color: Color.mSurfaceVariant
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Content pane
|
||||
Rectangle {
|
||||
id: contentPane
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Layout.alignment: Qt.AlignTop
|
||||
radius: Style.radiusM
|
||||
color: Color.mSurfaceVariant
|
||||
border.color: Color.mOutline
|
||||
border.width: Style.borderS
|
||||
|
||||
ColumnLayout {
|
||||
id: contentLayout
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginL
|
||||
spacing: Style.marginS
|
||||
|
||||
// Header row
|
||||
RowLayout {
|
||||
id: headerRow
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginS
|
||||
|
||||
NIcon {
|
||||
icon: root.tabsModel[currentTabIndex]?.icon
|
||||
color: Color.mPrimary
|
||||
pointSize: Style.fontSizeXXL
|
||||
}
|
||||
|
||||
NText {
|
||||
text: I18n.tr(root.tabsModel[currentTabIndex]?.label) || ""
|
||||
pointSize: Style.fontSizeXL
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Color.mPrimary
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
|
||||
NIconButton {
|
||||
icon: "close"
|
||||
tooltipText: I18n.tr("tooltips.close")
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
onClicked: root.closeRequested()
|
||||
}
|
||||
}
|
||||
|
||||
NDivider {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
// Tab content area
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
color: Color.transparent
|
||||
|
||||
Repeater {
|
||||
model: root.tabsModel
|
||||
delegate: Loader {
|
||||
anchors.fill: parent
|
||||
active: index === root.currentTabIndex
|
||||
|
||||
onStatusChanged: {
|
||||
if (status === Loader.Ready && item) {
|
||||
const scrollView = item.children[0];
|
||||
if (scrollView && scrollView.toString().includes("ScrollView")) {
|
||||
root.activeScrollView = scrollView;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sourceComponent: Flickable {
|
||||
id: flickable
|
||||
anchors.fill: parent
|
||||
pressDelay: 200
|
||||
|
||||
NScrollView {
|
||||
id: scrollView
|
||||
anchors.fill: parent
|
||||
horizontalPolicy: ScrollBar.AlwaysOff
|
||||
verticalPolicy: ScrollBar.AsNeeded
|
||||
padding: Style.marginL
|
||||
Component.onCompleted: {
|
||||
root.activeScrollView = scrollView;
|
||||
}
|
||||
|
||||
Loader {
|
||||
active: true
|
||||
sourceComponent: root.tabsModel[index]?.source
|
||||
width: scrollView.availableWidth
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Overlay gradient for content scrolling
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: Color.transparent
|
||||
visible: root.activeScrollView && root.activeScrollView.ScrollBar.vertical && root.activeScrollView.ScrollBar.vertical.size < 1.0
|
||||
gradient: Gradient {
|
||||
GradientStop {
|
||||
position: 0.0
|
||||
color: Color.transparent
|
||||
}
|
||||
GradientStop {
|
||||
position: 0.95
|
||||
color: Color.transparent
|
||||
}
|
||||
GradientStop {
|
||||
position: 1.0
|
||||
color: Qt.alpha(Color.mSurfaceVariant, 0.95)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,10 +4,7 @@ import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs.Commons
|
||||
import qs.Modules.MainScreen
|
||||
import qs.Modules.Panels.Settings.Tabs
|
||||
import qs.Modules.Panels.Settings.Tabs.ColorScheme
|
||||
import qs.Modules.Panels.Settings.Tabs.SessionMenu
|
||||
import qs.Services.System
|
||||
import qs.Services.UI
|
||||
import qs.Widgets
|
||||
|
||||
SmartPanel {
|
||||
@@ -16,7 +13,11 @@ SmartPanel {
|
||||
preferredWidth: Math.round(820 * Style.uiScaleRatio)
|
||||
preferredHeight: Math.round(910 * Style.uiScaleRatio)
|
||||
|
||||
readonly property bool attachToBar: Settings.data.ui.settingsPanelAttachToBar
|
||||
// Settings panel mode: "centered", "attached", "window"
|
||||
readonly property string settingsPanelMode: Settings.data.ui.settingsPanelMode
|
||||
readonly property bool isWindowMode: settingsPanelMode === "window"
|
||||
readonly property bool attachToBar: settingsPanelMode === "attached"
|
||||
|
||||
readonly property string barPosition: Settings.data.bar.position
|
||||
readonly property bool barFloating: Settings.data.bar.floating
|
||||
readonly property real barMarginH: barFloating ? Math.ceil(Settings.data.bar.marginHorizontal * Style.marginXL) : 0
|
||||
@@ -86,297 +87,95 @@ SmartPanel {
|
||||
}
|
||||
|
||||
property int requestedTab: SettingsPanel.Tab.General
|
||||
|
||||
// Content state - these are synced with SettingsContent when panel opens
|
||||
property int currentTabIndex: 0
|
||||
property var tabsModel: []
|
||||
property var activeScrollView: null
|
||||
|
||||
Component.onCompleted: {
|
||||
updateTabsModel();
|
||||
// Internal reference to the content (set when panel content loads)
|
||||
property var _settingsContent: null
|
||||
|
||||
// Override toggle to handle window mode
|
||||
function toggle(buttonItem, buttonName) {
|
||||
if (isWindowMode) {
|
||||
SettingsPanelService.toggleWindow(requestedTab);
|
||||
return;
|
||||
}
|
||||
// Call parent toggle
|
||||
if (isPanelOpen) {
|
||||
close();
|
||||
} else {
|
||||
open(buttonItem, buttonName);
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: generalTab
|
||||
GeneralTab {}
|
||||
}
|
||||
Component {
|
||||
id: launcherTab
|
||||
LauncherTab {}
|
||||
}
|
||||
Component {
|
||||
id: barTab
|
||||
BarTab {}
|
||||
}
|
||||
Component {
|
||||
id: audioTab
|
||||
AudioTab {}
|
||||
}
|
||||
Component {
|
||||
id: displayTab
|
||||
DisplayTab {}
|
||||
}
|
||||
Component {
|
||||
id: osdTab
|
||||
OsdTab {}
|
||||
}
|
||||
Component {
|
||||
id: networkTab
|
||||
NetworkTab {}
|
||||
}
|
||||
Component {
|
||||
id: locationTab
|
||||
LocationTab {}
|
||||
}
|
||||
Component {
|
||||
id: colorSchemeTab
|
||||
ColorSchemeTab {}
|
||||
}
|
||||
Component {
|
||||
id: wallpaperTab
|
||||
WallpaperTab {}
|
||||
}
|
||||
Component {
|
||||
id: screenRecorderTab
|
||||
ScreenRecorderTab {}
|
||||
}
|
||||
Component {
|
||||
id: aboutTab
|
||||
AboutTab {}
|
||||
}
|
||||
Component {
|
||||
id: hooksTab
|
||||
HooksTab {}
|
||||
}
|
||||
Component {
|
||||
id: dockTab
|
||||
DockTab {}
|
||||
}
|
||||
Component {
|
||||
id: notificationsTab
|
||||
NotificationsTab {}
|
||||
}
|
||||
Component {
|
||||
id: controlCenterTab
|
||||
ControlCenterTab {}
|
||||
}
|
||||
Component {
|
||||
id: userInterfaceTab
|
||||
UserInterfaceTab {}
|
||||
}
|
||||
Component {
|
||||
id: lockScreenTab
|
||||
LockScreenTab {}
|
||||
}
|
||||
Component {
|
||||
id: sessionMenuTab
|
||||
SessionMenuTab {}
|
||||
}
|
||||
Component {
|
||||
id: systemMonitorTab
|
||||
SystemMonitorTab {}
|
||||
}
|
||||
Component {
|
||||
id: pluginsTab
|
||||
PluginsTab {}
|
||||
// Override open to handle window mode
|
||||
function open(buttonItem, buttonName) {
|
||||
if (isWindowMode) {
|
||||
SettingsPanelService.openWindow(requestedTab);
|
||||
return;
|
||||
}
|
||||
|
||||
// Panel mode: replicate SmartPanel.open() logic
|
||||
if (!buttonItem && buttonName) {
|
||||
buttonItem = BarService.lookupWidget(buttonName, screen.name);
|
||||
}
|
||||
|
||||
if (buttonItem) {
|
||||
root.buttonItem = buttonItem;
|
||||
var buttonPos = buttonItem.mapToItem(null, 0, 0);
|
||||
root.buttonPosition = Qt.point(buttonPos.x, buttonPos.y);
|
||||
root.buttonWidth = buttonItem.width;
|
||||
root.buttonHeight = buttonItem.height;
|
||||
root.useButtonPosition = true;
|
||||
} else {
|
||||
root.buttonItem = null;
|
||||
root.useButtonPosition = false;
|
||||
}
|
||||
|
||||
isPanelOpen = true;
|
||||
PanelService.willOpenPanel(root);
|
||||
}
|
||||
|
||||
// Order *DOES* matter
|
||||
function updateTabsModel() {
|
||||
let newTabs = [
|
||||
{
|
||||
"id": SettingsPanel.Tab.General,
|
||||
"label": "settings.general.title",
|
||||
"icon": "settings-general",
|
||||
"source": generalTab
|
||||
},
|
||||
{
|
||||
"id": SettingsPanel.Tab.UserInterface,
|
||||
"label": "settings.user-interface.title",
|
||||
"icon": "settings-user-interface",
|
||||
"source": userInterfaceTab
|
||||
},
|
||||
{
|
||||
"id": SettingsPanel.Tab.ColorScheme,
|
||||
"label": "settings.color-scheme.title",
|
||||
"icon": "settings-color-scheme",
|
||||
"source": colorSchemeTab
|
||||
},
|
||||
{
|
||||
"id": SettingsPanel.Tab.Wallpaper,
|
||||
"label": "settings.wallpaper.title",
|
||||
"icon": "settings-wallpaper",
|
||||
"source": wallpaperTab
|
||||
},
|
||||
{
|
||||
"id": SettingsPanel.Tab.Bar,
|
||||
"label": "settings.bar.title",
|
||||
"icon": "settings-bar",
|
||||
"source": barTab
|
||||
},
|
||||
{
|
||||
"id": SettingsPanel.Tab.Dock,
|
||||
"label": "settings.dock.title",
|
||||
"icon": "settings-dock",
|
||||
"source": dockTab
|
||||
},
|
||||
{
|
||||
"id": SettingsPanel.Tab.ControlCenter,
|
||||
"label": "settings.control-center.title",
|
||||
"icon": "settings-control-center",
|
||||
"source": controlCenterTab
|
||||
},
|
||||
{
|
||||
"id": SettingsPanel.Tab.Launcher,
|
||||
"label": "settings.launcher.title",
|
||||
"icon": "settings-launcher",
|
||||
"source": launcherTab
|
||||
},
|
||||
{
|
||||
"id": SettingsPanel.Tab.Notifications,
|
||||
"label": "settings.notifications.title",
|
||||
"icon": "settings-notifications",
|
||||
"source": notificationsTab
|
||||
},
|
||||
{
|
||||
"id": SettingsPanel.Tab.OSD,
|
||||
"label": "settings.osd.title",
|
||||
"icon": "settings-osd",
|
||||
"source": osdTab
|
||||
},
|
||||
{
|
||||
"id": SettingsPanel.Tab.LockScreen,
|
||||
"label": "settings.lock-screen.title",
|
||||
"icon": "settings-lock-screen",
|
||||
"source": lockScreenTab
|
||||
},
|
||||
{
|
||||
"id": SettingsPanel.Tab.SessionMenu,
|
||||
"label": "settings.session-menu.title",
|
||||
"icon": "settings-session-menu",
|
||||
"source": sessionMenuTab
|
||||
},
|
||||
{
|
||||
"id": SettingsPanel.Tab.Audio,
|
||||
"label": "settings.audio.title",
|
||||
"icon": "settings-audio",
|
||||
"source": audioTab
|
||||
},
|
||||
{
|
||||
"id": SettingsPanel.Tab.Display,
|
||||
"label": "settings.display.title",
|
||||
"icon": "settings-display",
|
||||
"source": displayTab
|
||||
},
|
||||
{
|
||||
"id": SettingsPanel.Tab.Network,
|
||||
"label": "settings.network.title",
|
||||
"icon": "settings-network",
|
||||
"source": networkTab
|
||||
},
|
||||
{
|
||||
"id": SettingsPanel.Tab.Location,
|
||||
"label": "settings.location.title",
|
||||
"icon": "settings-location",
|
||||
"source": locationTab
|
||||
},
|
||||
{
|
||||
"id": SettingsPanel.Tab.ScreenRecorder,
|
||||
"label": "settings.screen-recorder.title",
|
||||
"icon": "settings-screen-recorder",
|
||||
"source": screenRecorderTab
|
||||
},
|
||||
{
|
||||
"id": SettingsPanel.Tab.SystemMonitor,
|
||||
"label": "settings.system-monitor.title",
|
||||
"icon": "settings-system-monitor",
|
||||
"source": systemMonitorTab
|
||||
},
|
||||
{
|
||||
"id": SettingsPanel.Tab.Plugins,
|
||||
"label": "settings.plugins.title",
|
||||
"icon": "plugin",
|
||||
"source": pluginsTab
|
||||
},
|
||||
{
|
||||
"id": SettingsPanel.Tab.Hooks,
|
||||
"label": "settings.hooks.title",
|
||||
"icon": "settings-hooks",
|
||||
"source": hooksTab
|
||||
},
|
||||
{
|
||||
"id": SettingsPanel.Tab.About,
|
||||
"label": "settings.about.title",
|
||||
"icon": "settings-about",
|
||||
"source": aboutTab
|
||||
}
|
||||
];
|
||||
|
||||
root.tabsModel = newTabs; // Assign the generated list to the model
|
||||
}
|
||||
|
||||
// When the panel opens, choose the appropriate tab
|
||||
// When the panel opens, initialize content
|
||||
onOpened: {
|
||||
// Run program availability checks every time settings opens
|
||||
ProgramCheckerService.checkAllPrograms();
|
||||
updateTabsModel();
|
||||
|
||||
var initialIndex = SettingsPanel.Tab.General;
|
||||
if (root.requestedTab !== null) {
|
||||
for (var i = 0; i < root.tabsModel.length; i++) {
|
||||
if (root.tabsModel[i].id === root.requestedTab) {
|
||||
initialIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (_settingsContent) {
|
||||
_settingsContent.requestedTab = requestedTab;
|
||||
_settingsContent.initialize();
|
||||
}
|
||||
|
||||
// Now that the UI is settled, set the current tab index.
|
||||
root.currentTabIndex = initialIndex;
|
||||
}
|
||||
|
||||
// Add scroll functions
|
||||
// Scroll functions - delegate to content
|
||||
function scrollDown() {
|
||||
if (activeScrollView && activeScrollView.ScrollBar.vertical) {
|
||||
const scrollBar = activeScrollView.ScrollBar.vertical;
|
||||
const stepSize = activeScrollView.height * 0.1; // Scroll 10% of viewport
|
||||
scrollBar.position = Math.min(scrollBar.position + stepSize / activeScrollView.contentHeight, 1.0 - scrollBar.size);
|
||||
}
|
||||
if (_settingsContent)
|
||||
_settingsContent.scrollDown();
|
||||
}
|
||||
|
||||
function scrollUp() {
|
||||
if (activeScrollView && activeScrollView.ScrollBar.vertical) {
|
||||
const scrollBar = activeScrollView.ScrollBar.vertical;
|
||||
const stepSize = activeScrollView.height * 0.1; // Scroll 10% of viewport
|
||||
scrollBar.position = Math.max(scrollBar.position - stepSize / activeScrollView.contentHeight, 0);
|
||||
}
|
||||
if (_settingsContent)
|
||||
_settingsContent.scrollUp();
|
||||
}
|
||||
|
||||
function scrollPageDown() {
|
||||
if (activeScrollView && activeScrollView.ScrollBar.vertical) {
|
||||
const scrollBar = activeScrollView.ScrollBar.vertical;
|
||||
const pageSize = activeScrollView.height * 0.9; // Scroll 90% of viewport
|
||||
scrollBar.position = Math.min(scrollBar.position + pageSize / activeScrollView.contentHeight, 1.0 - scrollBar.size);
|
||||
}
|
||||
if (_settingsContent)
|
||||
_settingsContent.scrollPageDown();
|
||||
}
|
||||
|
||||
function scrollPageUp() {
|
||||
if (activeScrollView && activeScrollView.ScrollBar.vertical) {
|
||||
const scrollBar = activeScrollView.ScrollBar.vertical;
|
||||
const pageSize = activeScrollView.height * 0.9; // Scroll 90% of viewport
|
||||
scrollBar.position = Math.max(scrollBar.position - pageSize / activeScrollView.contentHeight, 0);
|
||||
}
|
||||
if (_settingsContent)
|
||||
_settingsContent.scrollPageUp();
|
||||
}
|
||||
|
||||
// Add navigation functions
|
||||
// Navigation functions - delegate to content
|
||||
function selectNextTab() {
|
||||
if (tabsModel.length > 0) {
|
||||
currentTabIndex = (currentTabIndex + 1) % tabsModel.length;
|
||||
}
|
||||
if (_settingsContent)
|
||||
_settingsContent.selectNextTab();
|
||||
}
|
||||
|
||||
function selectPreviousTab() {
|
||||
if (tabsModel.length > 0) {
|
||||
currentTabIndex = (currentTabIndex - 1 + tabsModel.length) % tabsModel.length;
|
||||
}
|
||||
if (_settingsContent)
|
||||
_settingsContent.selectPreviousTab();
|
||||
}
|
||||
|
||||
// Override keyboard handlers from SmartPanel
|
||||
@@ -415,275 +214,21 @@ SmartPanel {
|
||||
panelContent: Rectangle {
|
||||
color: Color.transparent
|
||||
|
||||
// Main layout container that fills the panel
|
||||
ColumnLayout {
|
||||
SettingsContent {
|
||||
id: settingsContent
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginL
|
||||
spacing: 0
|
||||
|
||||
// Main content area
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
spacing: Style.marginL
|
||||
|
||||
// Sidebar
|
||||
Rectangle {
|
||||
id: sidebar
|
||||
|
||||
clip: true
|
||||
Layout.preferredWidth: 220 * Style.uiScaleRatio
|
||||
Layout.fillHeight: true
|
||||
Layout.alignment: Qt.AlignTop
|
||||
color: Color.mSurfaceVariant
|
||||
border.color: Color.mOutline
|
||||
border.width: Style.borderS
|
||||
radius: Style.radiusM
|
||||
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
|
||||
NListView {
|
||||
id: sidebarList
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginS
|
||||
model: root.tabsModel
|
||||
spacing: Style.marginXS
|
||||
currentIndex: root.currentTabIndex
|
||||
verticalPolicy: ScrollBar.AsNeeded
|
||||
|
||||
delegate: Rectangle {
|
||||
id: tabItem
|
||||
width: sidebarList.verticalScrollBarActive ? sidebarList.width - sidebarList.scrollBarWidth - Style.marginXS : sidebarList.width
|
||||
height: tabEntryRow.implicitHeight + Style.marginS * 2
|
||||
radius: Style.radiusS
|
||||
color: selected ? Color.mPrimary : (tabItem.hovering ? Color.mHover : Color.transparent)
|
||||
readonly property bool selected: index === root.currentTabIndex
|
||||
property bool hovering: false
|
||||
property color tabTextColor: selected ? Color.mOnPrimary : (tabItem.hovering ? Color.mOnHover : Color.mOnSurface)
|
||||
|
||||
Behavior on width {
|
||||
NumberAnimation {
|
||||
duration: Style.animationFast
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Style.animationFast
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on tabTextColor {
|
||||
ColorAnimation {
|
||||
duration: Style.animationFast
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: tabEntryRow
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Style.marginS
|
||||
anchors.rightMargin: Style.marginS
|
||||
spacing: Style.marginM
|
||||
|
||||
// Tab icon
|
||||
NIcon {
|
||||
icon: modelData.icon
|
||||
color: tabTextColor
|
||||
pointSize: Style.fontSizeXL
|
||||
}
|
||||
|
||||
// Tab label
|
||||
NText {
|
||||
text: I18n.tr(modelData.label)
|
||||
color: tabTextColor
|
||||
pointSize: Style.fontSizeM
|
||||
font.weight: Style.fontWeightBold
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onEntered: tabItem.hovering = true
|
||||
onExited: tabItem.hovering = false
|
||||
onCanceled: tabItem.hovering = false
|
||||
onClicked: root.currentTabIndex = index
|
||||
}
|
||||
}
|
||||
|
||||
onCurrentIndexChanged: {
|
||||
if (currentIndex !== root.currentTabIndex) {
|
||||
root.currentTabIndex = currentIndex;
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root
|
||||
function onCurrentTabIndexChanged() {
|
||||
if (sidebarList.currentIndex !== root.currentTabIndex) {
|
||||
sidebarList.currentIndex = root.currentTabIndex;
|
||||
sidebarList.positionViewAtIndex(root.currentTabIndex, ListView.Contain);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Overlay gradient for sidebar scrolling (only visible when scrollable)
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.borderS
|
||||
radius: Style.radiusM
|
||||
color: Color.transparent
|
||||
visible: sidebarList.verticalScrollBarActive
|
||||
gradient: Gradient {
|
||||
GradientStop {
|
||||
position: 0.0
|
||||
color: Color.transparent
|
||||
}
|
||||
GradientStop {
|
||||
position: 0.95
|
||||
color: Color.transparent
|
||||
}
|
||||
GradientStop {
|
||||
position: 1.0
|
||||
color: Color.mSurfaceVariant
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Content pane
|
||||
Rectangle {
|
||||
id: contentPane
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Layout.alignment: Qt.AlignTop
|
||||
radius: Style.radiusM
|
||||
color: Color.mSurfaceVariant
|
||||
border.color: Color.mOutline
|
||||
border.width: Style.borderS
|
||||
|
||||
ColumnLayout {
|
||||
id: contentLayout
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginL
|
||||
spacing: Style.marginS
|
||||
|
||||
// Header row
|
||||
RowLayout {
|
||||
id: headerRow
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginS
|
||||
|
||||
// Main icon
|
||||
NIcon {
|
||||
icon: root.tabsModel[currentTabIndex]?.icon
|
||||
color: Color.mPrimary
|
||||
pointSize: Style.fontSizeXXL
|
||||
}
|
||||
|
||||
// Main title
|
||||
NText {
|
||||
text: I18n.tr(root.tabsModel[currentTabIndex]?.label) || ""
|
||||
pointSize: Style.fontSizeXL
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Color.mPrimary
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
|
||||
// Close button
|
||||
NIconButton {
|
||||
icon: "close"
|
||||
tooltipText: I18n.tr("tooltips.close")
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
onClicked: root.close()
|
||||
}
|
||||
}
|
||||
|
||||
// Divider
|
||||
NDivider {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
// Tab content area
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
color: Color.transparent
|
||||
|
||||
Repeater {
|
||||
model: root.tabsModel
|
||||
delegate: Loader {
|
||||
anchors.fill: parent
|
||||
active: index === root.currentTabIndex
|
||||
|
||||
onStatusChanged: {
|
||||
if (status === Loader.Ready && item) {
|
||||
// Find and store reference to the ScrollView
|
||||
const scrollView = item.children[0];
|
||||
if (scrollView && scrollView.toString().includes("ScrollView")) {
|
||||
root.activeScrollView = scrollView;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sourceComponent: Flickable {
|
||||
// Using a Flickable here with a pressDelay to fix conflict between
|
||||
// ScrollView and NTextInput. This fixes the weird text selection issue.
|
||||
id: flickable
|
||||
anchors.fill: parent
|
||||
pressDelay: 200
|
||||
|
||||
NScrollView {
|
||||
id: scrollView
|
||||
anchors.fill: parent
|
||||
horizontalPolicy: ScrollBar.AlwaysOff
|
||||
verticalPolicy: ScrollBar.AsNeeded
|
||||
padding: Style.marginL
|
||||
Component.onCompleted: {
|
||||
root.activeScrollView = scrollView;
|
||||
}
|
||||
|
||||
Loader {
|
||||
active: true
|
||||
sourceComponent: root.tabsModel[index]?.source
|
||||
width: scrollView.availableWidth
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Overlay gradient for content scrolling (only visible when scrollable)
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: Color.transparent
|
||||
visible: root.activeScrollView && root.activeScrollView.ScrollBar.vertical && root.activeScrollView.ScrollBar.vertical.size < 1.0
|
||||
gradient: Gradient {
|
||||
GradientStop {
|
||||
position: 0.0
|
||||
color: Color.transparent
|
||||
}
|
||||
GradientStop {
|
||||
position: 0.95
|
||||
color: Color.transparent
|
||||
}
|
||||
GradientStop {
|
||||
position: 1.0
|
||||
color: Qt.alpha(Color.mSurfaceVariant, 0.95)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
onCloseRequested: root.close()
|
||||
Component.onCompleted: {
|
||||
root._settingsContent = settingsContent;
|
||||
root.tabsModel = Qt.binding(function () {
|
||||
return settingsContent.tabsModel;
|
||||
});
|
||||
root.currentTabIndex = Qt.binding(function () {
|
||||
return settingsContent.currentTabIndex;
|
||||
});
|
||||
root.activeScrollView = Qt.binding(function () {
|
||||
return settingsContent.activeScrollView;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import qs.Commons
|
||||
import qs.Services.UI
|
||||
import qs.Widgets
|
||||
|
||||
FloatingWindow {
|
||||
id: root
|
||||
|
||||
minimumSize: Qt.size(820 * Style.uiScaleRatio, 910 * Style.uiScaleRatio)
|
||||
implicitWidth: Math.round(820 * Style.uiScaleRatio)
|
||||
implicitHeight: Math.round(910 * Style.uiScaleRatio)
|
||||
color: Color.mSurface
|
||||
|
||||
visible: false
|
||||
|
||||
// Register with SettingsPanelService
|
||||
Component.onCompleted: {
|
||||
SettingsPanelService.settingsWindow = root;
|
||||
}
|
||||
|
||||
// Sync visibility with service
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
settingsContent.requestedTab = SettingsPanelService.requestedTab;
|
||||
settingsContent.initialize();
|
||||
SettingsPanelService.isWindowOpen = true;
|
||||
} else {
|
||||
SettingsPanelService.isWindowOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Keyboard shortcuts
|
||||
Shortcut {
|
||||
sequence: "Escape"
|
||||
onActivated: SettingsPanelService.closeWindow()
|
||||
}
|
||||
|
||||
Shortcut {
|
||||
sequence: "Tab"
|
||||
onActivated: settingsContent.selectNextTab()
|
||||
}
|
||||
|
||||
Shortcut {
|
||||
sequence: "Backtab"
|
||||
onActivated: settingsContent.selectPreviousTab()
|
||||
}
|
||||
|
||||
Shortcut {
|
||||
sequence: "Up"
|
||||
onActivated: settingsContent.scrollUp()
|
||||
}
|
||||
|
||||
Shortcut {
|
||||
sequence: "Down"
|
||||
onActivated: settingsContent.scrollDown()
|
||||
}
|
||||
|
||||
// Main content
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginL
|
||||
color: Color.transparent
|
||||
radius: Style.radiusL
|
||||
|
||||
SettingsContent {
|
||||
id: settingsContent
|
||||
anchors.fill: parent
|
||||
onCloseRequested: SettingsPanelService.closeWindow()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,6 +46,7 @@ ColumnLayout {
|
||||
}
|
||||
}
|
||||
|
||||
// Panels attached to bar and screen edges
|
||||
NToggle {
|
||||
label: I18n.tr("settings.user-interface.panels-attached-to-bar.label")
|
||||
description: I18n.tr("settings.user-interface.panels-attached-to-bar.description")
|
||||
@@ -53,12 +54,27 @@ ColumnLayout {
|
||||
onToggled: checked => Settings.data.ui.panelsAttachedToBar = checked
|
||||
}
|
||||
|
||||
NToggle {
|
||||
label: I18n.tr("settings.user-interface.settings-panel-attached-to-bar.label")
|
||||
description: I18n.tr("settings.user-interface.settings-panel-attached-to-bar.description")
|
||||
checked: Settings.data.ui.settingsPanelAttachToBar
|
||||
enabled: Settings.data.ui.panelsAttachedToBar
|
||||
onToggled: checked => Settings.data.ui.settingsPanelAttachToBar = checked
|
||||
// Settings panel display mode
|
||||
NComboBox {
|
||||
label: I18n.tr("settings.user-interface.settings-panel-mode.label")
|
||||
description: I18n.tr("settings.user-interface.settings-panel-mode.description")
|
||||
Layout.fillWidth: true
|
||||
model: [
|
||||
{
|
||||
"key": "attached",
|
||||
"name": I18n.tr("options.settings-panel-mode.attached")
|
||||
},
|
||||
{
|
||||
"key": "centered",
|
||||
"name": I18n.tr("options.settings-panel-mode.centered")
|
||||
},
|
||||
{
|
||||
"key": "window",
|
||||
"name": I18n.tr("options.settings-panel-mode.window")
|
||||
}
|
||||
]
|
||||
currentKey: Settings.data.ui.settingsPanelMode
|
||||
onSelected: key => Settings.data.ui.settingsPanelMode = key
|
||||
}
|
||||
|
||||
NToggle {
|
||||
|
||||
@@ -253,9 +253,13 @@ SmartPanel {
|
||||
tooltipText: I18n.tr("settings.wallpaper.settings.section.label")
|
||||
baseSize: Style.baseWidgetSize * 0.8
|
||||
onClicked: {
|
||||
var settingsPanel = PanelService.getPanel("settingsPanel", screen);
|
||||
settingsPanel.requestedTab = SettingsPanel.Tab.Wallpaper;
|
||||
settingsPanel.open();
|
||||
if (Settings.data.ui.settingsPanelMode === "window") {
|
||||
SettingsPanelService.openWindow(SettingsPanel.Tab.Wallpaper);
|
||||
} else {
|
||||
var settingsPanel = PanelService.getPanel("settingsPanel", screen);
|
||||
settingsPanel.requestedTab = SettingsPanel.Tab.Wallpaper;
|
||||
settingsPanel.open();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,10 +37,14 @@ Item {
|
||||
IpcHandler {
|
||||
target: "settings"
|
||||
function toggle() {
|
||||
root.withTargetScreen(screen => {
|
||||
var settingsPanel = PanelService.getPanel("settingsPanel", screen);
|
||||
settingsPanel?.toggle();
|
||||
});
|
||||
if (Settings.data.ui.settingsPanelMode === "window") {
|
||||
SettingsPanelService.toggleWindow();
|
||||
} else {
|
||||
root.withTargetScreen(screen => {
|
||||
var settingsPanel = PanelService.getPanel("settingsPanel", screen);
|
||||
settingsPanel?.toggle();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
pragma Singleton
|
||||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import qs.Commons
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
// Track if the settings window is open
|
||||
property bool isWindowOpen: false
|
||||
|
||||
// Reference to the window (set by SettingsPanelWindow)
|
||||
property var settingsWindow: null
|
||||
|
||||
// Requested tab when opening
|
||||
property int requestedTab: 0
|
||||
|
||||
signal windowOpened
|
||||
signal windowClosed
|
||||
|
||||
function openWindow(tab) {
|
||||
requestedTab = tab !== undefined ? tab : 0;
|
||||
if (settingsWindow) {
|
||||
settingsWindow.visible = true;
|
||||
isWindowOpen = true;
|
||||
windowOpened();
|
||||
}
|
||||
}
|
||||
|
||||
function closeWindow() {
|
||||
if (settingsWindow) {
|
||||
settingsWindow.visible = false;
|
||||
isWindowOpen = false;
|
||||
windowClosed();
|
||||
}
|
||||
}
|
||||
|
||||
function toggleWindow(tab) {
|
||||
if (isWindowOpen) {
|
||||
closeWindow();
|
||||
} else {
|
||||
openWindow(tab);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import qs.Modules.LockScreen
|
||||
import qs.Modules.MainScreen
|
||||
import qs.Modules.Notification
|
||||
import qs.Modules.OSD
|
||||
import qs.Modules.Panels.Settings
|
||||
import qs.Modules.Toast
|
||||
import qs.Services.Control
|
||||
import qs.Services.Hardware
|
||||
@@ -117,6 +118,9 @@ ShellRoot {
|
||||
|
||||
LockScreen {}
|
||||
|
||||
// Settings window mode (single window across all monitors)
|
||||
SettingsPanelWindow {}
|
||||
|
||||
// IPCService is treated as a service but it must be in graphics scene.
|
||||
IPCService {}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user