mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-06-23 19:45:21 -04:00
Compare commits
5 Commits
ef5de19f6b
...
0b55fbcb15
| Author | SHA1 | Date | |
|---|---|---|---|
| 0b55fbcb15 | |||
| 7476a220b5 | |||
| aaff1ab61e | |||
| 39622eb62a | |||
| eea039f575 |
@@ -66,7 +66,7 @@ const DMS_ACTIONS = [
|
||||
{ id: "spawn dms ipc call mpris increment 5", label: "Player Volume Up (5%)" },
|
||||
{ id: "spawn dms ipc call mpris decrement 5", label: "Player Volume Down (5%)" },
|
||||
{ id: "spawn dms ipc call audio mute", label: "Volume Mute Toggle" },
|
||||
{ id: "spawn dms ipc call audio micmute", label: "Microphone Mute Toggle" },
|
||||
{ id: "spawn dms ipc call mic mute", label: "Microphone Mute Toggle" },
|
||||
{ id: "spawn dms ipc call audio cycleoutput", label: "Audio Output: Cycle" },
|
||||
{ id: "spawn dms ipc call brightness increment 5 \"\"", label: "Brightness Up" },
|
||||
{ id: "spawn dms ipc call brightness increment 1 \"\"", label: "Brightness Up (1%)" },
|
||||
|
||||
@@ -1179,6 +1179,12 @@ Singleton {
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
function getLauncherRestoreMode() {
|
||||
if (!SettingsData.rememberLastMode)
|
||||
return "all";
|
||||
return launcherLastMode || "all";
|
||||
}
|
||||
|
||||
function setLauncherLastFileSearchType(type) {
|
||||
launcherLastFileSearchType = type;
|
||||
saveSettings();
|
||||
|
||||
@@ -435,6 +435,7 @@ Singleton {
|
||||
property int appLauncherGridColumns: 4
|
||||
property bool spotlightCloseNiriOverview: true
|
||||
property bool rememberLastQuery: false
|
||||
property bool rememberLastMode: true
|
||||
property var spotlightSectionViewModes: ({})
|
||||
onSpotlightSectionViewModesChanged: saveSettings()
|
||||
property var appDrawerSectionViewModes: ({})
|
||||
@@ -707,6 +708,7 @@ Singleton {
|
||||
property bool osdBrightnessEnabled: true
|
||||
property bool osdIdleInhibitorEnabled: true
|
||||
property bool osdMicMuteEnabled: true
|
||||
property bool osdMicVolumeEnabled: true
|
||||
property bool osdCapsLockEnabled: true
|
||||
property bool osdPowerProfileEnabled: true
|
||||
property bool osdAudioOutputEnabled: true
|
||||
|
||||
@@ -203,6 +203,7 @@ var SPEC = {
|
||||
appLauncherGridColumns: { def: 4 },
|
||||
spotlightCloseNiriOverview: { def: true },
|
||||
rememberLastQuery: { def: false },
|
||||
rememberLastMode: { def: true },
|
||||
spotlightSectionViewModes: { def: {} },
|
||||
appDrawerSectionViewModes: { def: {} },
|
||||
niriOverviewOverlayEnabled: { def: true },
|
||||
|
||||
+112
-4
@@ -63,15 +63,27 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
property bool wallpaperSurfacesLoaded: true
|
||||
|
||||
Loader {
|
||||
id: blurredWallpaperBackgroundLoader
|
||||
active: SettingsData.blurredWallpaperLayer && CompositorService.isNiri
|
||||
active: root.wallpaperSurfacesLoaded && SettingsData.blurredWallpaperLayer && CompositorService.isNiri
|
||||
asynchronous: false
|
||||
|
||||
sourceComponent: BlurredWallpaperBackground {}
|
||||
}
|
||||
|
||||
WallpaperBackground {}
|
||||
DeferredAction {
|
||||
id: wallpaperSurfaceReloadAction
|
||||
onTriggered: root.wallpaperSurfacesLoaded = true
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: wallpaperBackgroundLoader
|
||||
active: root.wallpaperSurfacesLoaded
|
||||
asynchronous: false
|
||||
sourceComponent: WallpaperBackground {}
|
||||
}
|
||||
|
||||
DesktopWidgetLayer {}
|
||||
|
||||
@@ -168,6 +180,8 @@ Item {
|
||||
property bool barSurfacesLoaded: true
|
||||
|
||||
function recreateBarSurfaces() {
|
||||
log.info("Recreating bar surfaces, screens:", Quickshell.screens.length,
|
||||
Quickshell.screens.map(s => s.name).join(","));
|
||||
if (barSurfacesLoaded)
|
||||
barSurfacesLoaded = false;
|
||||
barSurfaceReloadAction.schedule();
|
||||
@@ -217,7 +231,18 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Frame {}
|
||||
property bool frameSurfacesLoaded: true
|
||||
|
||||
Loader {
|
||||
active: root.frameSurfacesLoaded
|
||||
asynchronous: false
|
||||
sourceComponent: Frame {}
|
||||
}
|
||||
|
||||
DeferredAction {
|
||||
id: frameSurfaceReloadAction
|
||||
onTriggered: root.frameSurfacesLoaded = true
|
||||
}
|
||||
|
||||
Repeater {
|
||||
id: dankBarRepeater
|
||||
@@ -301,6 +326,81 @@ Item {
|
||||
onTriggered: root.osdSurfacesLoaded = true
|
||||
}
|
||||
|
||||
property bool hadRealScreen: true
|
||||
|
||||
function _hasRealScreen() {
|
||||
for (let i = 0; i < Quickshell.screens.length; i++) {
|
||||
if (Quickshell.screens[i].name.length > 0)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function triggerSurfaceRecovery(source) {
|
||||
log.info("Surface recovery triggered by:", source,
|
||||
"screens:", Quickshell.screens.length,
|
||||
Quickshell.screens.map(s => s.name).join(","),
|
||||
"barLoaded:", root.barSurfacesLoaded,
|
||||
"frameLoaded:", root.frameSurfacesLoaded,
|
||||
"dockEnabled:", root.dockEnabled);
|
||||
surfaceResumeRecoveryTimer.pass = 0;
|
||||
surfaceResumeRecoveryTimer.interval = 800;
|
||||
surfaceResumeRecoveryTimer.restart();
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: Quickshell
|
||||
function onScreensChanged() {
|
||||
const hasReal = root._hasRealScreen();
|
||||
log.info("Screens changed:", Quickshell.screens.length,
|
||||
Quickshell.screens.map(s => "'" + s.name + "'").join(","),
|
||||
"hasReal:", hasReal, "hadReal:", root.hadRealScreen);
|
||||
if (!root.hadRealScreen && hasReal) {
|
||||
log.info("Real screen reappeared after placeholder state, triggering surface recovery");
|
||||
root.triggerSurfaceRecovery("screen-reconnect");
|
||||
}
|
||||
root.hadRealScreen = hasReal;
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: surfaceResumeRecoveryTimer
|
||||
interval: 800
|
||||
repeat: false
|
||||
property int pass: 0
|
||||
onTriggered: {
|
||||
pass++;
|
||||
log.info("Surface recovery pass", pass,
|
||||
"screens:", Quickshell.screens.length,
|
||||
Quickshell.screens.map(s => s.name).join(","));
|
||||
|
||||
root.recreateBarSurfaces();
|
||||
|
||||
if (root.frameSurfacesLoaded) {
|
||||
root.frameSurfacesLoaded = false;
|
||||
frameSurfaceReloadAction.schedule();
|
||||
}
|
||||
|
||||
if (root.wallpaperSurfacesLoaded) {
|
||||
root.wallpaperSurfacesLoaded = false;
|
||||
wallpaperSurfaceReloadAction.schedule();
|
||||
}
|
||||
|
||||
root.dockEnabled = false;
|
||||
Qt.callLater(() => {
|
||||
root.dockEnabled = true;
|
||||
});
|
||||
|
||||
if (pass < 2) {
|
||||
interval = 2000;
|
||||
restart();
|
||||
} else {
|
||||
pass = 0;
|
||||
interval = 800;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
dockRecreateDebounce.start();
|
||||
// Force PolkitService singleton to initialize
|
||||
@@ -887,9 +987,17 @@ Item {
|
||||
target: SessionService
|
||||
|
||||
function onSessionResumed() {
|
||||
log.info("Session resumed: screens:", Quickshell.screens.length,
|
||||
Quickshell.screens.map(s => s.name).join(","),
|
||||
"barLoaded:", root.barSurfacesLoaded,
|
||||
"frameLoaded:", root.frameSurfacesLoaded,
|
||||
"dockEnabled:", root.dockEnabled);
|
||||
|
||||
root.pendingOsdResumeReloads = 2;
|
||||
osdResumeRecreateTimer.interval = 400;
|
||||
osdResumeRecreateTimer.restart();
|
||||
|
||||
root.triggerSurfaceRecovery("sessionResumed");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1114,7 +1222,7 @@ Item {
|
||||
Variants {
|
||||
model: SettingsData.getFilteredScreens("osd")
|
||||
|
||||
delegate: MicMuteOSD {
|
||||
delegate: MicVolumeOSD {
|
||||
modelData: item
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1794,6 +1794,36 @@ Item {
|
||||
target: "outputs"
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
target: "mic"
|
||||
|
||||
function setvolume(percentage: string): string {
|
||||
return AudioService.setMicVolume(parseInt(percentage));
|
||||
}
|
||||
|
||||
function increment(step: string): string {
|
||||
return AudioService.incrementMicVolume(step);
|
||||
}
|
||||
|
||||
function decrement(step: string): string {
|
||||
return AudioService.decrementMicVolume(step);
|
||||
}
|
||||
|
||||
function mute(): string {
|
||||
return AudioService.toggleMicMute();
|
||||
}
|
||||
|
||||
function status(): string {
|
||||
if (!AudioService.source || !AudioService.source.audio) {
|
||||
return "No audio source available";
|
||||
}
|
||||
|
||||
const volume = Math.round(AudioService.source.audio.volume * 100);
|
||||
const muteStatus = AudioService.source.audio.muted ? " (muted)" : "";
|
||||
return `Microphone: ${volume}%${muteStatus}`;
|
||||
}
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
function findTrayItem(itemId: string): var {
|
||||
if (!itemId)
|
||||
|
||||
@@ -39,7 +39,7 @@ Item {
|
||||
|
||||
signal itemExecuted
|
||||
signal searchCompleted
|
||||
signal modeChanged(string mode)
|
||||
signal modeChanged(string mode, bool userInitiated)
|
||||
signal queryChanged(string query)
|
||||
signal viewModeChanged(string sectionId, string mode)
|
||||
signal searchQueryRequested(string query)
|
||||
@@ -440,7 +440,7 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
function setMode(mode, isAutoSwitch, fileTypeOverride) {
|
||||
function setMode(mode, isAutoSwitch, fileTypeOverride, notPersist) {
|
||||
if (searchMode === mode) {
|
||||
if (mode === "files" && fileTypeOverride !== undefined && fileSearchType !== fileTypeOverride) {
|
||||
fileSearchType = fileTypeOverride;
|
||||
@@ -458,7 +458,7 @@ Item {
|
||||
if (mode === "files") {
|
||||
fileSearchType = fileTypeOverride !== undefined ? fileTypeOverride : (SessionData.launcherLastFileSearchType || "all");
|
||||
}
|
||||
modeChanged(mode);
|
||||
modeChanged(mode, !isAutoSwitch && notPersist !== true);
|
||||
performSearch();
|
||||
var filesInAll = mode === "all" && (SettingsData.dankLauncherV2IncludeFilesInAll || SettingsData.dankLauncherV2IncludeFoldersInAll) && searchQuery.length > 0;
|
||||
if (mode === "files" || filesInAll) {
|
||||
@@ -471,7 +471,7 @@ Item {
|
||||
return;
|
||||
autoSwitchedToFiles = false;
|
||||
searchMode = previousSearchMode;
|
||||
modeChanged(previousSearchMode);
|
||||
modeChanged(previousSearchMode, false);
|
||||
performSearch();
|
||||
}
|
||||
|
||||
@@ -1897,7 +1897,7 @@ Item {
|
||||
if (browseTrigger && browseTrigger.length > 0) {
|
||||
searchQueryRequested(browseTrigger);
|
||||
} else {
|
||||
setMode("plugins");
|
||||
setMode("plugins", false, undefined, true);
|
||||
pluginFilter = browsePluginId;
|
||||
performSearch();
|
||||
}
|
||||
|
||||
@@ -396,7 +396,7 @@ Item {
|
||||
spotlightContent.searchField.text = query;
|
||||
}
|
||||
if (spotlightContent.controller) {
|
||||
var targetMode = mode || SessionData.launcherLastMode || "all";
|
||||
var targetMode = mode || SessionData.getLauncherRestoreMode();
|
||||
spotlightContent.controller.searchMode = targetMode;
|
||||
spotlightContent.controller.activePluginId = "";
|
||||
spotlightContent.controller.activePluginName = "";
|
||||
@@ -539,8 +539,8 @@ Item {
|
||||
|
||||
Connections {
|
||||
target: spotlightContent?.controller ?? null
|
||||
function onModeChanged(mode) {
|
||||
if (spotlightContent.controller.autoSwitchedToFiles)
|
||||
function onModeChanged(mode, userInitiated) {
|
||||
if (!userInitiated || !SettingsData.rememberLastMode)
|
||||
return;
|
||||
SessionData.setLauncherLastMode(mode);
|
||||
}
|
||||
@@ -928,8 +928,12 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onPressed: event => root.spotlightContent?.activeContextMenu?.handleKey(event)
|
||||
|
||||
Keys.onEscapePressed: event => {
|
||||
root.hide();
|
||||
root.spotlightContent?.activeContextMenu?.handleKey(event);
|
||||
if (!event.accepted)
|
||||
root.hide();
|
||||
event.accepted = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,7 +145,7 @@ Item {
|
||||
spotlightContent.closeTransientUi?.();
|
||||
|
||||
const targetQuery = query || (SettingsData.rememberLastQuery ? (SessionData.launcherLastQuery || "") : "");
|
||||
const targetMode = mode || SessionData.launcherLastMode || "all";
|
||||
const targetMode = mode || SessionData.getLauncherRestoreMode();
|
||||
|
||||
if (spotlightContent.searchField) {
|
||||
spotlightContent.searchField.text = targetQuery;
|
||||
@@ -489,8 +489,12 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onPressed: event => root.spotlightContent?.activeContextMenu?.handleKey(event)
|
||||
|
||||
Keys.onEscapePressed: event => {
|
||||
root.hide();
|
||||
root.spotlightContent?.activeContextMenu?.handleKey(event);
|
||||
if (!event.accepted)
|
||||
root.hide();
|
||||
event.accepted = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,7 +148,7 @@ Item {
|
||||
spotlightContent.searchField.text = targetQuery;
|
||||
}
|
||||
if (spotlightContent.controller) {
|
||||
var targetMode = mode || SessionData.launcherLastMode || "all";
|
||||
var targetMode = mode || SessionData.getLauncherRestoreMode();
|
||||
spotlightContent.controller.searchMode = targetMode;
|
||||
spotlightContent.controller.activePluginId = "";
|
||||
spotlightContent.controller.activePluginName = "";
|
||||
@@ -260,8 +260,8 @@ Item {
|
||||
Connections {
|
||||
target: spotlightContent?.controller ?? null
|
||||
|
||||
function onModeChanged(mode) {
|
||||
if (spotlightContent.controller.autoSwitchedToFiles)
|
||||
function onModeChanged(mode, userInitiated) {
|
||||
if (!userInitiated || !SettingsData.rememberLastMode || (mode !== "all" && mode !== "apps"))
|
||||
return;
|
||||
SessionData.setLauncherLastMode(mode);
|
||||
}
|
||||
@@ -536,8 +536,12 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onPressed: event => root.spotlightContent?.activeContextMenu?.handleKey(event)
|
||||
|
||||
Keys.onEscapePressed: event => {
|
||||
root.hide();
|
||||
root.spotlightContent?.activeContextMenu?.handleKey(event);
|
||||
if (!event.accepted)
|
||||
root.hide();
|
||||
event.accepted = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ FocusScope {
|
||||
property alias controller: controller
|
||||
property alias resultsList: resultsList
|
||||
property alias actionPanel: actionPanel
|
||||
readonly property alias activeContextMenu: contextMenu
|
||||
|
||||
property bool editMode: false
|
||||
property var editingApp: null
|
||||
|
||||
@@ -340,6 +340,31 @@ Item {
|
||||
return count;
|
||||
}
|
||||
|
||||
function handleKey(event) {
|
||||
if (!openState)
|
||||
return;
|
||||
switch (event.key) {
|
||||
case Qt.Key_Down:
|
||||
selectNext();
|
||||
event.accepted = true;
|
||||
return;
|
||||
case Qt.Key_Up:
|
||||
selectPrevious();
|
||||
event.accepted = true;
|
||||
return;
|
||||
case Qt.Key_Return:
|
||||
case Qt.Key_Enter:
|
||||
activateSelected();
|
||||
event.accepted = true;
|
||||
return;
|
||||
case Qt.Key_Left:
|
||||
case Qt.Key_Escape:
|
||||
hide();
|
||||
event.accepted = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function selectNext() {
|
||||
if (visibleItemCount > 0) {
|
||||
keyboardNavigation = true;
|
||||
|
||||
@@ -12,6 +12,7 @@ FocusScope {
|
||||
property var parentModal: null
|
||||
property alias searchField: searchInput
|
||||
property alias controller: searchController
|
||||
readonly property alias activeContextMenu: contextMenu
|
||||
|
||||
readonly property bool _hasQuery: searchInput.text.length > 0
|
||||
readonly property real _searchBarH: 56
|
||||
@@ -239,8 +240,8 @@ FocusScope {
|
||||
|
||||
Connections {
|
||||
target: searchController
|
||||
function onModeChanged(mode) {
|
||||
if (searchController.autoSwitchedToFiles)
|
||||
function onModeChanged(mode, userInitiated) {
|
||||
if (!userInitiated || !SettingsData.rememberLastMode)
|
||||
return;
|
||||
SessionData.setLauncherLastMode(mode);
|
||||
}
|
||||
|
||||
@@ -301,12 +301,22 @@ Column {
|
||||
property var widgetDef: root.model?.getWidgetForId(widgetData.id || "")
|
||||
width: parent.width
|
||||
height: 60
|
||||
iconBlinking: {
|
||||
const id = widgetData.id || "";
|
||||
if (id === "wifi")
|
||||
return NetworkService.isWifiConnecting;
|
||||
if (id === "bluetooth")
|
||||
return BluetoothService.connecting;
|
||||
return false;
|
||||
}
|
||||
iconName: {
|
||||
switch (widgetData.id || "") {
|
||||
case "wifi":
|
||||
{
|
||||
if (NetworkService.wifiToggling)
|
||||
return "sync";
|
||||
if (NetworkService.isConnecting && !NetworkService.ethernetConnected)
|
||||
return NetworkService.wifiSignalIcon;
|
||||
|
||||
const status = NetworkService.networkStatus;
|
||||
if (status === "ethernet")
|
||||
@@ -360,6 +370,8 @@ Column {
|
||||
{
|
||||
if (NetworkService.wifiToggling)
|
||||
return NetworkService.wifiEnabled ? I18n.tr("Disabling WiFi...", "network status") : I18n.tr("Enabling WiFi...", "network status");
|
||||
if (NetworkService.isConnecting && !NetworkService.ethernetConnected)
|
||||
return NetworkService.connectingSSID || I18n.tr("Connecting...", "network status");
|
||||
|
||||
const status = NetworkService.networkStatus;
|
||||
if (status === "ethernet")
|
||||
@@ -400,6 +412,8 @@ Column {
|
||||
{
|
||||
if (NetworkService.wifiToggling)
|
||||
return I18n.tr("Please wait...", "network status");
|
||||
if (NetworkService.isConnecting && !NetworkService.ethernetConnected)
|
||||
return I18n.tr("Connecting...", "network status");
|
||||
|
||||
const status = NetworkService.networkStatus;
|
||||
if (status === "ethernet")
|
||||
@@ -422,6 +436,8 @@ Column {
|
||||
return I18n.tr("No adapters", "bluetooth status");
|
||||
if (!BluetoothService.adapter || !BluetoothService.adapter.enabled)
|
||||
return I18n.tr("Off", "bluetooth status");
|
||||
if (BluetoothService.connecting)
|
||||
return I18n.tr("Connecting...", "bluetooth status");
|
||||
const primaryDevice = (() => {
|
||||
if (!BluetoothService.adapter || !BluetoothService.adapter.devices)
|
||||
return null;
|
||||
|
||||
@@ -10,6 +10,7 @@ Rectangle {
|
||||
|
||||
property string iconName: ""
|
||||
property color iconColor: Theme.surfaceText
|
||||
property bool iconBlinking: false
|
||||
property string primaryText: ""
|
||||
property string secondaryText: ""
|
||||
property bool expanded: false
|
||||
@@ -109,10 +110,16 @@ Rectangle {
|
||||
}
|
||||
|
||||
DankIcon {
|
||||
id: pillIcon
|
||||
anchors.centerIn: parent
|
||||
name: iconName
|
||||
size: Theme.iconSize
|
||||
color: isActive ? _tileIconActive : _tileIconInactive
|
||||
|
||||
DankBlink {
|
||||
target: pillIcon
|
||||
running: root.iconBlinking
|
||||
}
|
||||
}
|
||||
|
||||
DankRipple {
|
||||
|
||||
@@ -726,7 +726,7 @@ PanelWindow {
|
||||
item: clickThroughEnabled ? null : inputMask
|
||||
|
||||
Region {
|
||||
readonly property var r: barWindow.clickThroughEnabled ? barWindow.sectionRect(barWindow._leftSection, false, barWindow._revealProgress) : {
|
||||
readonly property var r: barWindow.clickThroughEnabled ? barWindow.sectionRect(barWindow._leftSection, false, barWindow._revealProgress + barWindow.width * 0) : {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 0,
|
||||
@@ -739,7 +739,7 @@ PanelWindow {
|
||||
}
|
||||
|
||||
Region {
|
||||
readonly property var r: barWindow.clickThroughEnabled ? barWindow.sectionRect(barWindow._centerSection, true, barWindow._revealProgress) : {
|
||||
readonly property var r: barWindow.clickThroughEnabled ? barWindow.sectionRect(barWindow._centerSection, true, barWindow._revealProgress + barWindow.width * 0) : {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 0,
|
||||
@@ -752,7 +752,7 @@ PanelWindow {
|
||||
}
|
||||
|
||||
Region {
|
||||
readonly property var r: barWindow.clickThroughEnabled ? barWindow.sectionRect(barWindow._rightSection, false, barWindow._revealProgress) : {
|
||||
readonly property var r: barWindow.clickThroughEnabled ? barWindow.sectionRect(barWindow._rightSection, false, barWindow._revealProgress + barWindow.width * 0) : {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 0,
|
||||
|
||||
@@ -131,9 +131,19 @@ BasePill {
|
||||
function getNetworkIconColor() {
|
||||
if (NetworkService.wifiToggling)
|
||||
return Theme.primary;
|
||||
if (NetworkService.isConnecting && !NetworkService.ethernetConnected)
|
||||
return Theme.primary;
|
||||
return NetworkService.networkStatus !== "disconnected" ? Theme.primary : Theme.surfaceText;
|
||||
}
|
||||
|
||||
function getIconBlinking(id) {
|
||||
if (id === "network")
|
||||
return NetworkService.isWifiConnecting;
|
||||
if (id === "bluetooth")
|
||||
return BluetoothService.connecting;
|
||||
return false;
|
||||
}
|
||||
|
||||
function getVolumeIconName() {
|
||||
if (!AudioService.sink?.audio)
|
||||
return "volume_up";
|
||||
@@ -485,6 +495,7 @@ BasePill {
|
||||
}
|
||||
|
||||
DankIcon {
|
||||
id: vIconOnlyItem
|
||||
anchors.centerIn: parent
|
||||
visible: !verticalGroupItem.modelData.composite
|
||||
name: {
|
||||
@@ -515,7 +526,7 @@ BasePill {
|
||||
case "vpn":
|
||||
return NetworkService.vpnConnected ? Theme.primary : Theme.surfaceText;
|
||||
case "bluetooth":
|
||||
return BluetoothService.connected ? Theme.primary : Theme.surfaceText;
|
||||
return (BluetoothService.connected || BluetoothService.connecting) ? Theme.primary : Theme.surfaceText;
|
||||
case "battery":
|
||||
return root.getBatteryIconColor();
|
||||
case "printer":
|
||||
@@ -524,6 +535,11 @@ BasePill {
|
||||
return Theme.widgetIconColor;
|
||||
}
|
||||
}
|
||||
|
||||
DankBlink {
|
||||
target: vIconOnlyItem
|
||||
running: root.getIconBlinking(verticalGroupItem.modelData.id)
|
||||
}
|
||||
}
|
||||
|
||||
DankIcon {
|
||||
@@ -687,7 +703,7 @@ BasePill {
|
||||
case "vpn":
|
||||
return NetworkService.vpnConnected ? Theme.primary : Theme.surfaceText;
|
||||
case "bluetooth":
|
||||
return BluetoothService.connected ? Theme.primary : Theme.surfaceText;
|
||||
return (BluetoothService.connected || BluetoothService.connecting) ? Theme.primary : Theme.surfaceText;
|
||||
case "battery":
|
||||
return root.getBatteryIconColor();
|
||||
case "printer":
|
||||
@@ -696,6 +712,11 @@ BasePill {
|
||||
return Theme.widgetIconColor;
|
||||
}
|
||||
}
|
||||
|
||||
DankBlink {
|
||||
target: iconOnlyItem
|
||||
running: root.getIconBlinking(horizontalGroupItem.modelData.id)
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
|
||||
@@ -73,6 +73,10 @@ Item {
|
||||
return pam && (pam.u2fState === "waiting" || pam.u2fState === "insert") && !pam.u2fPending;
|
||||
}
|
||||
|
||||
function canStartSecurityKeyUnlock() {
|
||||
return !demoMode && pam && pam.u2f && pam.u2f.available && SettingsData.enableU2f && SettingsData.u2fMode === "or" && !pam.passwd.active && !pam.u2f.active && !pam.u2fPending && !root.unlocking;
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
WeatherService.addRef();
|
||||
UserInfoService.getUserInfo();
|
||||
@@ -761,6 +765,9 @@ Item {
|
||||
if (enterButton.visible) {
|
||||
margin += enterButton.width + 2;
|
||||
}
|
||||
if (securityKeyButton.visible) {
|
||||
margin += securityKeyButton.width;
|
||||
}
|
||||
if (virtualKeyboardButton.visible) {
|
||||
margin += virtualKeyboardButton.width;
|
||||
}
|
||||
@@ -854,7 +861,7 @@ Item {
|
||||
|
||||
anchors.left: lockIconContainer.right
|
||||
anchors.leftMargin: Theme.spacingM
|
||||
anchors.right: (revealButton.visible ? revealButton.left : (virtualKeyboardButton.visible ? virtualKeyboardButton.left : (enterButton.visible ? enterButton.left : (loadingSpinner.visible ? loadingSpinner.left : parent.right))))
|
||||
anchors.right: (revealButton.visible ? revealButton.left : (virtualKeyboardButton.visible ? virtualKeyboardButton.left : (securityKeyButton.visible ? securityKeyButton.left : (enterButton.visible ? enterButton.left : (loadingSpinner.visible ? loadingSpinner.left : parent.right)))))
|
||||
anchors.rightMargin: 2
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: {
|
||||
@@ -896,7 +903,7 @@ Item {
|
||||
StyledText {
|
||||
anchors.left: lockIconContainer.right
|
||||
anchors.leftMargin: Theme.spacingM
|
||||
anchors.right: (revealButton.visible ? revealButton.left : (virtualKeyboardButton.visible ? virtualKeyboardButton.left : (enterButton.visible ? enterButton.left : (loadingSpinner.visible ? loadingSpinner.left : parent.right))))
|
||||
anchors.right: (revealButton.visible ? revealButton.left : (virtualKeyboardButton.visible ? virtualKeyboardButton.left : (securityKeyButton.visible ? securityKeyButton.left : (enterButton.visible ? enterButton.left : (loadingSpinner.visible ? loadingSpinner.left : parent.right)))))
|
||||
anchors.rightMargin: 2
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: {
|
||||
@@ -926,7 +933,7 @@ Item {
|
||||
DankActionButton {
|
||||
id: revealButton
|
||||
|
||||
anchors.right: virtualKeyboardButton.visible ? virtualKeyboardButton.left : (enterButton.visible ? enterButton.left : (loadingSpinner.visible ? loadingSpinner.left : parent.right))
|
||||
anchors.right: virtualKeyboardButton.visible ? virtualKeyboardButton.left : (securityKeyButton.visible ? securityKeyButton.left : (enterButton.visible ? enterButton.left : (loadingSpinner.visible ? loadingSpinner.left : parent.right)))
|
||||
anchors.rightMargin: 0
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
iconName: parent.showPassword ? "visibility_off" : "visibility"
|
||||
@@ -936,10 +943,26 @@ Item {
|
||||
onClicked: parent.showPassword = !parent.showPassword
|
||||
}
|
||||
DankActionButton {
|
||||
id: virtualKeyboardButton
|
||||
id: securityKeyButton
|
||||
|
||||
anchors.right: enterButton.visible ? enterButton.left : (loadingSpinner.visible ? loadingSpinner.left : parent.right)
|
||||
anchors.rightMargin: enterButton.visible ? 0 : Theme.spacingS
|
||||
anchors.rightMargin: 0
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
iconName: "passkey"
|
||||
buttonSize: 32
|
||||
visible: root.canStartSecurityKeyUnlock()
|
||||
enabled: visible
|
||||
onClicked: {
|
||||
passwordField.text = "";
|
||||
root.passwordBuffer = "";
|
||||
pam.u2f.startForAlternativeAuth();
|
||||
}
|
||||
}
|
||||
DankActionButton {
|
||||
id: virtualKeyboardButton
|
||||
|
||||
anchors.right: securityKeyButton.visible ? securityKeyButton.left : (enterButton.visible ? enterButton.left : (loadingSpinner.visible ? loadingSpinner.left : parent.right))
|
||||
anchors.rightMargin: securityKeyButton.visible || enterButton.visible ? 0 : Theme.spacingS
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
iconName: "keyboard"
|
||||
buttonSize: 32
|
||||
@@ -1438,6 +1461,7 @@ Item {
|
||||
}
|
||||
|
||||
DankIcon {
|
||||
id: lockNetworkIcon
|
||||
name: {
|
||||
if (NetworkService.wifiToggling)
|
||||
return "sync";
|
||||
@@ -1451,9 +1475,14 @@ Item {
|
||||
}
|
||||
}
|
||||
size: Theme.iconSize - 2
|
||||
color: NetworkService.networkStatus !== "disconnected" ? "white" : Qt.rgba(255, 255, 255, 0.5)
|
||||
color: (NetworkService.networkStatus !== "disconnected" || NetworkService.isConnecting) ? "white" : Qt.rgba(255, 255, 255, 0.5)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: NetworkService.networkAvailable
|
||||
|
||||
DankBlink {
|
||||
target: lockNetworkIcon
|
||||
running: NetworkService.isWifiConnecting
|
||||
}
|
||||
}
|
||||
|
||||
DankIcon {
|
||||
@@ -1465,11 +1494,17 @@ Item {
|
||||
}
|
||||
|
||||
DankIcon {
|
||||
id: lockBluetoothIcon
|
||||
name: "bluetooth"
|
||||
size: Theme.iconSize - 2
|
||||
color: "white"
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: BluetoothService.available && BluetoothService.enabled
|
||||
|
||||
DankBlink {
|
||||
target: lockBluetoothIcon
|
||||
running: BluetoothService.connecting
|
||||
}
|
||||
}
|
||||
|
||||
DankIcon {
|
||||
|
||||
@@ -20,6 +20,7 @@ Scope {
|
||||
property string fprintState
|
||||
property string u2fState
|
||||
property bool u2fPending: false
|
||||
property string u2fPendingMode
|
||||
property string buffer
|
||||
|
||||
signal flashMsg
|
||||
@@ -35,6 +36,7 @@ Scope {
|
||||
passwdActiveTimeout.running = false;
|
||||
unlockRequestTimeout.running = false;
|
||||
root.u2fPending = false;
|
||||
root.u2fPendingMode = "";
|
||||
root.u2fState = "";
|
||||
root.unlockInProgress = false;
|
||||
}
|
||||
@@ -58,6 +60,7 @@ Scope {
|
||||
u2fErrorRetry.running = false;
|
||||
u2fPendingTimeout.running = false;
|
||||
root.u2fPending = false;
|
||||
root.u2fPendingMode = "";
|
||||
root.u2fState = "";
|
||||
unlockRequestTimeout.restart();
|
||||
unlockRequested();
|
||||
@@ -79,6 +82,7 @@ Scope {
|
||||
u2fErrorRetry.running = false;
|
||||
u2fPendingTimeout.running = false;
|
||||
root.u2fPending = false;
|
||||
root.u2fPendingMode = "";
|
||||
root.u2fState = "";
|
||||
fprint.checkAvail();
|
||||
}
|
||||
@@ -142,6 +146,7 @@ Scope {
|
||||
unlockRequestTimeout.running = false;
|
||||
root.unlockInProgress = false;
|
||||
root.u2fPending = false;
|
||||
root.u2fPendingMode = "";
|
||||
root.u2fState = "";
|
||||
u2fPendingTimeout.running = false;
|
||||
u2f.abort();
|
||||
@@ -243,9 +248,8 @@ Scope {
|
||||
return;
|
||||
}
|
||||
|
||||
if (SettingsData.u2fMode === "or") {
|
||||
start();
|
||||
}
|
||||
if (SettingsData.u2fMode === "or")
|
||||
abort();
|
||||
}
|
||||
|
||||
function startForSecondFactor(): void {
|
||||
@@ -255,6 +259,18 @@ Scope {
|
||||
}
|
||||
abort();
|
||||
root.u2fPending = true;
|
||||
root.u2fPendingMode = "and";
|
||||
root.u2fState = "";
|
||||
u2fPendingTimeout.restart();
|
||||
start();
|
||||
}
|
||||
|
||||
function startForAlternativeAuth(): void {
|
||||
if (!available || !SettingsData.enableU2f || SettingsData.u2fMode !== "or" || root.unlockInProgress || passwd.active || active)
|
||||
return;
|
||||
abort();
|
||||
root.u2fPending = true;
|
||||
root.u2fPendingMode = "or";
|
||||
root.u2fState = "";
|
||||
u2fPendingTimeout.restart();
|
||||
start();
|
||||
@@ -281,9 +297,19 @@ Scope {
|
||||
abort();
|
||||
|
||||
if (root.u2fPending) {
|
||||
if (root.u2fPendingMode === "or") {
|
||||
root.u2fPending = false;
|
||||
root.u2fPendingMode = "";
|
||||
root.u2fState = root.u2fState === "waiting" ? "" : "insert";
|
||||
u2fPendingTimeout.running = false;
|
||||
fprint.checkAvail();
|
||||
return;
|
||||
}
|
||||
|
||||
if (root.u2fState === "waiting") {
|
||||
// AND mode: device was found but auth failed → back to password
|
||||
root.u2fPending = false;
|
||||
root.u2fPendingMode = "";
|
||||
root.u2fState = "";
|
||||
fprint.checkAvail();
|
||||
} else {
|
||||
@@ -292,9 +318,7 @@ Scope {
|
||||
u2fErrorRetry.restart();
|
||||
}
|
||||
} else {
|
||||
// OR mode: prompt to insert key, silently retry
|
||||
root.u2fState = "insert";
|
||||
u2fErrorRetry.restart();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -367,6 +391,7 @@ Scope {
|
||||
root.fprintState = "";
|
||||
root.u2fState = "";
|
||||
root.u2fPending = false;
|
||||
root.u2fPendingMode = "";
|
||||
root.lockMessage = "";
|
||||
root.resetAuthFlows();
|
||||
fprint.checkAvail();
|
||||
@@ -399,6 +424,7 @@ Scope {
|
||||
u2fPendingTimeout.running = false;
|
||||
unlockRequestTimeout.running = false;
|
||||
root.u2fPending = false;
|
||||
root.u2fPendingMode = "";
|
||||
root.u2fState = "";
|
||||
u2f.checkAvail();
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
DankOSD {
|
||||
id: root
|
||||
|
||||
osdWidth: Theme.iconSize + Theme.spacingS * 2
|
||||
osdHeight: Theme.iconSize + Theme.spacingS * 2
|
||||
autoHideInterval: 2000
|
||||
enableMouseInteraction: false
|
||||
|
||||
Connections {
|
||||
target: AudioService
|
||||
function onMicMuteChanged() {
|
||||
if (SettingsData.osdMicMuteEnabled) {
|
||||
root.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
content: DankIcon {
|
||||
anchors.centerIn: parent
|
||||
name: AudioService.source && AudioService.source.audio && AudioService.source.audio.muted ? "mic_off" : "mic"
|
||||
size: Theme.iconSize
|
||||
color: AudioService.source && AudioService.source.audio && AudioService.source.audio.muted ? Theme.error : Theme.primary
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
DankOSD {
|
||||
id: root
|
||||
|
||||
readonly property bool useVertical: isVerticalLayout
|
||||
property int _displayVolume: 0
|
||||
|
||||
function _syncVolume() {
|
||||
if (!AudioService.source?.audio)
|
||||
return;
|
||||
_displayVolume = Math.round(AudioService.source.audio.volume * 100);
|
||||
}
|
||||
|
||||
osdWidth: useVertical ? (40 + Theme.spacingS * 2) : Math.min(260, Screen.width - Theme.spacingM * 2)
|
||||
osdHeight: useVertical ? Math.min(260, Screen.height - Theme.spacingM * 2) : (40 + Theme.spacingS * 2)
|
||||
autoHideInterval: 3000
|
||||
enableMouseInteraction: true
|
||||
|
||||
Connections {
|
||||
target: AudioService.source?.audio ?? null
|
||||
|
||||
function onVolumeChanged() {
|
||||
root._syncVolume();
|
||||
if (SettingsData.osdMicVolumeEnabled)
|
||||
root.show();
|
||||
}
|
||||
|
||||
function onMutedChanged() {
|
||||
if (SettingsData.osdMicMuteEnabled)
|
||||
root.show();
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: AudioService
|
||||
|
||||
function onSourceChanged() {
|
||||
root._syncVolume();
|
||||
if (root.shouldBeVisible && SettingsData.osdMicVolumeEnabled)
|
||||
root.show();
|
||||
}
|
||||
}
|
||||
|
||||
content: Loader {
|
||||
anchors.fill: parent
|
||||
sourceComponent: useVertical ? verticalContent : horizontalContent
|
||||
}
|
||||
|
||||
Component {
|
||||
id: horizontalContent
|
||||
|
||||
Item {
|
||||
property int gap: Theme.spacingS
|
||||
|
||||
anchors.centerIn: parent
|
||||
width: parent.width - Theme.spacingS * 2
|
||||
height: 40
|
||||
|
||||
Rectangle {
|
||||
width: Theme.iconSize
|
||||
height: Theme.iconSize
|
||||
radius: Theme.iconSize / 2
|
||||
color: "transparent"
|
||||
x: parent.gap
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
name: AudioService.source?.audio?.muted ? "mic_off" : "mic"
|
||||
size: Theme.iconSize
|
||||
color: muteButton.containsMouse ? Theme.primary : (AudioService.source?.audio?.muted ? Theme.error : Theme.surfaceText)
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: muteButton
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: AudioService.toggleMicMute()
|
||||
onContainsMouseChanged: setChildHovered(containsMouse || volumeSlider.containsMouse)
|
||||
}
|
||||
}
|
||||
|
||||
DankSlider {
|
||||
id: volumeSlider
|
||||
|
||||
width: parent.width - Theme.iconSize - parent.gap * 3
|
||||
height: 40
|
||||
x: parent.gap * 2 + Theme.iconSize
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
minimum: 0
|
||||
maximum: 100
|
||||
enabled: AudioService.source?.audio ?? false
|
||||
showValue: true
|
||||
unit: "%"
|
||||
thumbOutlineColor: Theme.surfaceContainer
|
||||
valueOverride: root._displayVolume
|
||||
alwaysShowValue: SettingsData.osdAlwaysShowValue
|
||||
|
||||
Component.onCompleted: {
|
||||
root._syncVolume();
|
||||
value = root._displayVolume;
|
||||
}
|
||||
|
||||
onSliderValueChanged: newValue => {
|
||||
if (!AudioService.source?.audio)
|
||||
return;
|
||||
SessionData.suppressOSDTemporarily();
|
||||
AudioService.source.audio.volume = newValue / 100;
|
||||
resetHideTimer();
|
||||
}
|
||||
|
||||
onContainsMouseChanged: setChildHovered(containsMouse || muteButton.containsMouse)
|
||||
|
||||
Binding on value {
|
||||
value: root._displayVolume
|
||||
when: !volumeSlider.pressed
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: verticalContent
|
||||
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
property int gap: Theme.spacingS
|
||||
|
||||
Rectangle {
|
||||
width: Theme.iconSize
|
||||
height: Theme.iconSize
|
||||
radius: Theme.iconSize / 2
|
||||
color: "transparent"
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
y: gap
|
||||
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
name: AudioService.source?.audio?.muted ? "mic_off" : "mic"
|
||||
size: Theme.iconSize
|
||||
color: muteButtonVert.containsMouse ? Theme.primary : (AudioService.source?.audio?.muted ? Theme.error : Theme.surfaceText)
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: muteButtonVert
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: AudioService.toggleMicMute()
|
||||
onContainsMouseChanged: setChildHovered(containsMouse || vertSliderArea.containsMouse)
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: vertSlider
|
||||
width: 12
|
||||
height: parent.height - Theme.iconSize - gap * 3 - 24
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
y: gap * 2 + Theme.iconSize
|
||||
|
||||
property bool dragging: false
|
||||
property int value: root._displayVolume
|
||||
|
||||
Rectangle {
|
||||
id: vertTrack
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
anchors.centerIn: parent
|
||||
color: Theme.outline
|
||||
radius: Theme.cornerRadius
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: vertFill
|
||||
width: parent.width
|
||||
height: (vertSlider.value / 100) * parent.height
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
color: AudioService.source?.audio?.muted ? Theme.error : Theme.primary
|
||||
radius: Theme.cornerRadius
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: vertHandle
|
||||
width: 24
|
||||
height: 8
|
||||
radius: Theme.cornerRadius
|
||||
y: {
|
||||
const ratio = vertSlider.value / 100;
|
||||
const travel = parent.height - height;
|
||||
return Math.max(0, Math.min(travel, travel * (1 - ratio)));
|
||||
}
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
color: AudioService.source?.audio?.muted ? Theme.error : Theme.primary
|
||||
border.width: 3
|
||||
border.color: Theme.surfaceContainer
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: vertSliderArea
|
||||
anchors.fill: parent
|
||||
anchors.margins: -12
|
||||
enabled: AudioService.source?.audio ?? false
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
|
||||
onContainsMouseChanged: setChildHovered(containsMouse || muteButtonVert.containsMouse)
|
||||
|
||||
onPressed: mouse => {
|
||||
vertSlider.dragging = true;
|
||||
updateVolume(mouse);
|
||||
}
|
||||
|
||||
onReleased: vertSlider.dragging = false
|
||||
|
||||
onPositionChanged: mouse => {
|
||||
if (pressed)
|
||||
updateVolume(mouse);
|
||||
}
|
||||
|
||||
onClicked: mouse => updateVolume(mouse)
|
||||
|
||||
function updateVolume(mouse) {
|
||||
if (!AudioService.source?.audio)
|
||||
return;
|
||||
const ratio = 1.0 - (mouse.y / height);
|
||||
const volume = Math.max(0, Math.min(100, Math.round(ratio * 100)));
|
||||
SessionData.suppressOSDTemporarily();
|
||||
AudioService.source.audio.volume = volume / 100;
|
||||
resetHideTimer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottomMargin: gap
|
||||
text: vertSlider.value + "%"
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
visible: SettingsData.osdAlwaysShowValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1121,6 +1121,15 @@ Item {
|
||||
onToggled: checked => SessionData.setSearchAppActions(checked)
|
||||
}
|
||||
|
||||
SettingsToggleRow {
|
||||
settingKey: "rememberLastMode"
|
||||
tags: ["launcher", "remember", "last", "mode", "tab"]
|
||||
text: I18n.tr("Remember Last Mode")
|
||||
description: I18n.tr("Restore the last selected mode (tab) when the launcher is opened")
|
||||
checked: SettingsData.rememberLastMode
|
||||
onToggled: checked => SettingsData.set("rememberLastMode", checked)
|
||||
}
|
||||
|
||||
SettingsToggleRow {
|
||||
settingKey: "rememberLastQuery"
|
||||
tags: ["launcher", "remember", "last", "search", "query"]
|
||||
|
||||
@@ -338,45 +338,61 @@ Scope {
|
||||
border.width: 1
|
||||
}
|
||||
|
||||
LauncherContent {
|
||||
id: launcherContent
|
||||
FocusScope {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 0
|
||||
focus: true
|
||||
|
||||
property var fakeParentModal: QtObject {
|
||||
property bool spotlightOpen: spotlightContainer.visible
|
||||
property bool isClosing: niriOverviewScope.isClosing
|
||||
function hide() {
|
||||
if (niriOverviewScope.searchActive) {
|
||||
niriOverviewScope.hideSpotlight();
|
||||
return;
|
||||
Keys.onPressed: event => launcherContent.activeContextMenu?.handleKey(event)
|
||||
|
||||
Keys.onEscapePressed: event => {
|
||||
launcherContent.activeContextMenu?.handleKey(event);
|
||||
if (!event.accepted)
|
||||
launcherContent.parentModal?.hide();
|
||||
event.accepted = true;
|
||||
}
|
||||
|
||||
LauncherContent {
|
||||
id: launcherContent
|
||||
anchors.fill: parent
|
||||
anchors.margins: 0
|
||||
|
||||
property var fakeParentModal: QtObject {
|
||||
property bool spotlightOpen: spotlightContainer.visible
|
||||
property bool isClosing: niriOverviewScope.isClosing
|
||||
property real alignedX: spotlightContainer.x
|
||||
property real alignedY: spotlightContainer.y
|
||||
function hide() {
|
||||
if (niriOverviewScope.searchActive) {
|
||||
niriOverviewScope.hideSpotlight();
|
||||
return;
|
||||
}
|
||||
NiriService.toggleOverview();
|
||||
}
|
||||
NiriService.toggleOverview();
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: launcherContent.searchField
|
||||
function onTextChanged() {
|
||||
if (launcherContent.searchField.text.length > 0 || !niriOverviewScope.searchActive)
|
||||
return;
|
||||
niriOverviewScope.hideSpotlight();
|
||||
Connections {
|
||||
target: launcherContent.searchField
|
||||
function onTextChanged() {
|
||||
if (launcherContent.searchField.text.length > 0 || !niriOverviewScope.searchActive)
|
||||
return;
|
||||
niriOverviewScope.hideSpotlight();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
parentModal = fakeParentModal;
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: launcherContent.controller
|
||||
function onItemExecuted() {
|
||||
niriOverviewScope.releaseKeyboard = true;
|
||||
Component.onCompleted: {
|
||||
parentModal = fakeParentModal;
|
||||
}
|
||||
function onModeChanged(mode) {
|
||||
if (launcherContent.controller.autoSwitchedToFiles)
|
||||
return;
|
||||
SessionData.setNiriOverviewLastMode(mode);
|
||||
|
||||
Connections {
|
||||
target: launcherContent.controller
|
||||
function onItemExecuted() {
|
||||
niriOverviewScope.releaseKeyboard = true;
|
||||
}
|
||||
function onModeChanged(mode) {
|
||||
if (launcherContent.controller.autoSwitchedToFiles)
|
||||
return;
|
||||
SessionData.setNiriOverviewLastMode(mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -397,6 +397,14 @@ EOFCONFIG
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root.source?.audio ?? null
|
||||
|
||||
function onMutedChanged() {
|
||||
root.micMuteChanged();
|
||||
}
|
||||
}
|
||||
|
||||
function checkGsettings() {
|
||||
Proc.runCommand("checkGsettings", ["sh", "-c", "gsettings get org.gnome.desktop.sound theme-name 2>/dev/null"], (output, exitCode) => {
|
||||
gsettingsAvailable = (exitCode === 0);
|
||||
@@ -844,6 +852,36 @@ EOFCONFIG
|
||||
return root.source.audio.muted ? "Microphone muted" : "Microphone unmuted";
|
||||
}
|
||||
|
||||
function incrementMicVolume(step) {
|
||||
if (!root.source?.audio)
|
||||
return "No audio source available";
|
||||
|
||||
if (root.source.audio.muted)
|
||||
root.source.audio.muted = false;
|
||||
|
||||
const currentVolume = Math.round(root.source.audio.volume * 100);
|
||||
const stepValue = parseInt(step || "5");
|
||||
const newVolume = Math.max(0, Math.min(100, currentVolume + stepValue));
|
||||
|
||||
root.source.audio.volume = newVolume / 100;
|
||||
return `Microphone volume increased to ${newVolume}%`;
|
||||
}
|
||||
|
||||
function decrementMicVolume(step) {
|
||||
if (!root.source?.audio)
|
||||
return "No audio source available";
|
||||
|
||||
if (root.source.audio.muted)
|
||||
root.source.audio.muted = false;
|
||||
|
||||
const currentVolume = Math.round(root.source.audio.volume * 100);
|
||||
const stepValue = parseInt(step || "5");
|
||||
const newVolume = Math.max(0, Math.min(100, currentVolume - stepValue));
|
||||
|
||||
root.source.audio.volume = newVolume / 100;
|
||||
return `Microphone volume decreased to ${newVolume}%`;
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
target: "audio"
|
||||
|
||||
@@ -892,9 +930,7 @@ EOFCONFIG
|
||||
}
|
||||
|
||||
function micmute(): string {
|
||||
const result = root.toggleMicMute();
|
||||
root.micMuteChanged();
|
||||
return result;
|
||||
return root.toggleMicMute();
|
||||
}
|
||||
|
||||
function status(): string {
|
||||
@@ -957,7 +993,6 @@ EOFCONFIG
|
||||
return `Switched to: ${result}`;
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: SettingsData
|
||||
function onUseSystemSoundThemeChanged() {
|
||||
|
||||
@@ -28,6 +28,20 @@ Singleton {
|
||||
});
|
||||
return isConnected;
|
||||
}
|
||||
readonly property bool connecting: {
|
||||
if (!adapter || !adapter.devices) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let busy = false;
|
||||
adapter.devices.values.forEach(dev => {
|
||||
if (!dev)
|
||||
return;
|
||||
if (dev.pairing || dev.state === BluetoothDeviceState.Connecting)
|
||||
busy = true;
|
||||
});
|
||||
return busy;
|
||||
}
|
||||
readonly property var pairedDevices: {
|
||||
if (!adapter || !adapter.devices) {
|
||||
return [];
|
||||
|
||||
@@ -41,6 +41,9 @@ Singleton {
|
||||
property var savedConnections: []
|
||||
property var ssidToConnectionName: ({})
|
||||
property var wifiSignalIcon: {
|
||||
if (isConnecting) {
|
||||
return "wifi";
|
||||
}
|
||||
if (!wifiConnected) {
|
||||
return "wifi_off";
|
||||
}
|
||||
|
||||
@@ -99,6 +99,9 @@ Singleton {
|
||||
}
|
||||
|
||||
readonly property string wifiSignalIcon: {
|
||||
if (isConnecting) {
|
||||
return "wifi";
|
||||
}
|
||||
if (!wifiConnected || networkStatus !== "wifi") {
|
||||
return "wifi_off";
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ Singleton {
|
||||
|
||||
property string userPreference: activeService?.userPreference ?? "auto"
|
||||
property bool isConnecting: activeService?.isConnecting ?? false
|
||||
readonly property bool isWifiConnecting: isConnecting && !ethernetConnected && !wifiToggling
|
||||
property string connectingSSID: activeService?.connectingSSID ?? ""
|
||||
property string connectionError: activeService?.connectionError ?? ""
|
||||
|
||||
|
||||
@@ -345,4 +345,13 @@ Singleton {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: SessionService
|
||||
|
||||
function onSessionResumed() {
|
||||
log.info("Session resumed, re-requesting output state, current outputs:", outputs.length);
|
||||
requestState();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import QtQuick
|
||||
|
||||
SequentialAnimation {
|
||||
id: root
|
||||
|
||||
property Item target
|
||||
property real minOpacity: 0.3
|
||||
property int pulseDuration: 600
|
||||
|
||||
loops: Animation.Infinite
|
||||
|
||||
NumberAnimation {
|
||||
target: root.target
|
||||
property: "opacity"
|
||||
to: root.minOpacity
|
||||
duration: root.pulseDuration
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
NumberAnimation {
|
||||
target: root.target
|
||||
property: "opacity"
|
||||
to: 1.0
|
||||
duration: root.pulseDuration
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
|
||||
onStopped: if (root.target) root.target.opacity = 1.0
|
||||
}
|
||||
Reference in New Issue
Block a user