Fix audio in/output, add app pinning to applauncher
This commit is contained in:
parent
57bd28e326
commit
7d568694bd
3 changed files with 94 additions and 10 deletions
|
|
@ -11,6 +11,23 @@ import "../../Helpers/Fuzzysort.js" as Fuzzysort
|
||||||
PanelWithOverlay {
|
PanelWithOverlay {
|
||||||
id: appLauncherPanel
|
id: appLauncherPanel
|
||||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
|
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
|
||||||
|
|
||||||
|
function isPinned(app) {
|
||||||
|
return app && app.execString && Settings.settings.pinnedExecs.indexOf(app.execString) !== -1;
|
||||||
|
}
|
||||||
|
function togglePin(app) {
|
||||||
|
if (!app || !app.execString) return;
|
||||||
|
var arr = Settings.settings.pinnedExecs ? Settings.settings.pinnedExecs.slice() : [];
|
||||||
|
var idx = arr.indexOf(app.execString);
|
||||||
|
if (idx === -1) {
|
||||||
|
arr.push(app.execString);
|
||||||
|
} else {
|
||||||
|
arr.splice(idx, 1);
|
||||||
|
}
|
||||||
|
Settings.settings.pinnedExecs = arr;
|
||||||
|
root.updateFilter();
|
||||||
|
}
|
||||||
|
|
||||||
function showAt() {
|
function showAt() {
|
||||||
appLauncherPanelRect.showAt();
|
appLauncherPanelRect.showAt();
|
||||||
}
|
}
|
||||||
|
|
@ -126,7 +143,22 @@ PanelWithOverlay {
|
||||||
return r.obj;
|
return r.obj;
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
root.filteredApps = results;
|
// Pinning logic: split into pinned and unpinned
|
||||||
|
var pinned = [];
|
||||||
|
var unpinned = [];
|
||||||
|
for (var i = 0; i < results.length; ++i) {
|
||||||
|
var app = results[i];
|
||||||
|
if (app.execString && Settings.settings.pinnedExecs.indexOf(app.execString) !== -1) {
|
||||||
|
pinned.push(app);
|
||||||
|
} else {
|
||||||
|
unpinned.push(app);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Sort pinned alphabetically
|
||||||
|
pinned.sort(function(a, b) {
|
||||||
|
return a.name.toLowerCase().localeCompare(b.name.toLowerCase());
|
||||||
|
});
|
||||||
|
root.filteredApps = pinned.concat(unpinned);
|
||||||
root.selectedIndex = 0;
|
root.selectedIndex = 0;
|
||||||
}
|
}
|
||||||
function selectNext() {
|
function selectNext() {
|
||||||
|
|
@ -276,10 +308,14 @@ PanelWithOverlay {
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: hovered || isSelected ? Theme.accentPrimary : "transparent"
|
color: (hovered || isSelected)
|
||||||
|
? Theme.accentPrimary
|
||||||
|
: (appLauncherPanel.isPinned(modelData) ? Theme.surfaceVariant : "transparent")
|
||||||
radius: 12
|
radius: 12
|
||||||
border.color: hovered || isSelected ? Theme.accentPrimary : "transparent"
|
border.color: appLauncherPanel.isPinned(modelData)
|
||||||
border.width: hovered || isSelected ? 2 : 0
|
? "transparent"
|
||||||
|
: (hovered || isSelected ? Theme.accentPrimary : "transparent")
|
||||||
|
border.width: appLauncherPanel.isPinned(modelData) ? 0 : (hovered || isSelected ? 2 : 0)
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
ColorAnimation {
|
ColorAnimation {
|
||||||
duration: 120
|
duration: 120
|
||||||
|
|
@ -331,7 +367,7 @@ PanelWithOverlay {
|
||||||
spacing: 1
|
spacing: 1
|
||||||
Text {
|
Text {
|
||||||
text: modelData.name
|
text: modelData.name
|
||||||
color: hovered || isSelected ? Theme.onAccent : Theme.textPrimary
|
color: (hovered || isSelected) ? Theme.onAccent : (appLauncherPanel.isPinned(modelData) ? Theme.textPrimary : Theme.textPrimary)
|
||||||
font.family: Theme.fontFamily
|
font.family: Theme.fontFamily
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
font.bold: hovered || isSelected
|
font.bold: hovered || isSelected
|
||||||
|
|
@ -341,7 +377,7 @@ PanelWithOverlay {
|
||||||
}
|
}
|
||||||
Text {
|
Text {
|
||||||
text: modelData.isCalculator ? (modelData.expr + " = " + modelData.result) : (modelData.comment || modelData.genericName || "No description available")
|
text: modelData.isCalculator ? (modelData.expr + " = " + modelData.result) : (modelData.comment || modelData.genericName || "No description available")
|
||||||
color: hovered || isSelected ? Theme.onAccent : Theme.textSecondary
|
color: (hovered || isSelected) ? Theme.onAccent : (appLauncherPanel.isPinned(modelData) ? Theme.textSecondary : Theme.textSecondary)
|
||||||
font.family: Theme.fontFamily
|
font.family: Theme.fontFamily
|
||||||
font.pixelSize: Theme.fontSizeCaption
|
font.pixelSize: Theme.fontSizeCaption
|
||||||
font.italic: !(modelData.comment || modelData.genericName)
|
font.italic: !(modelData.comment || modelData.genericName)
|
||||||
|
|
@ -358,9 +394,14 @@ PanelWithOverlay {
|
||||||
text: modelData.isCalculator ? "content_copy" : "chevron_right"
|
text: modelData.isCalculator ? "content_copy" : "chevron_right"
|
||||||
font.family: "Material Symbols Outlined"
|
font.family: "Material Symbols Outlined"
|
||||||
font.pixelSize: Theme.fontSizeBody
|
font.pixelSize: Theme.fontSizeBody
|
||||||
color: hovered || isSelected ? Theme.onAccent : Theme.textSecondary
|
color: (hovered || isSelected)
|
||||||
|
? Theme.onAccent
|
||||||
|
: (appLauncherPanel.isPinned(modelData) ? Theme.textPrimary : Theme.textSecondary)
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
Layout.rightMargin: 8 // Add margin to separate from star
|
||||||
}
|
}
|
||||||
|
// Add a spacing item between chevron and star
|
||||||
|
Item { width: 8; height: 1 }
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
|
@ -374,7 +415,14 @@ PanelWithOverlay {
|
||||||
id: mouseArea
|
id: mouseArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
onClicked: {
|
onClicked: {
|
||||||
|
// Prevent app launch if click is inside pinArea
|
||||||
|
if (pinArea.containsMouse) return;
|
||||||
|
if (mouse.button === Qt.RightButton) {
|
||||||
|
appLauncherPanel.togglePin(modelData);
|
||||||
|
return;
|
||||||
|
}
|
||||||
ripple.opacity = 0.18;
|
ripple.opacity = 0.18;
|
||||||
rippleNumberAnimation.start();
|
rippleNumberAnimation.start();
|
||||||
root.selectedIndex = index;
|
root.selectedIndex = index;
|
||||||
|
|
@ -401,6 +449,37 @@ PanelWithOverlay {
|
||||||
color: Theme.outline
|
color: Theme.outline
|
||||||
opacity: index === appList.count - 1 ? 0 : 0.10
|
opacity: index === appList.count - 1 ? 0 : 0.10
|
||||||
}
|
}
|
||||||
|
// Pin/Unpin button (move to last child for stacking)
|
||||||
|
Item {
|
||||||
|
id: pinArea
|
||||||
|
width: 28; height: 28
|
||||||
|
z: 100 // Ensure above everything else
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
preventStealing: true
|
||||||
|
z: 100
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
|
propagateComposedEvents: false
|
||||||
|
onClicked: {
|
||||||
|
appLauncherPanel.togglePin(modelData);
|
||||||
|
event.accepted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Text {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: "star"
|
||||||
|
font.family: "Material Symbols Outlined"
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: (parent.MouseArea.containsMouse || hovered || isSelected)
|
||||||
|
? Theme.onAccent
|
||||||
|
: (appLauncherPanel.isPinned(modelData) ? Theme.textPrimary : Theme.textDisabled)
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -226,7 +226,9 @@ PanelWithOverlay {
|
||||||
|
|
||||||
function sinkNodes() {
|
function sinkNodes() {
|
||||||
let nodes = Pipewire.nodes && Pipewire.nodes.values
|
let nodes = Pipewire.nodes && Pipewire.nodes.values
|
||||||
? Pipewire.nodes.values.filter(function(n) { return n.isSink && n.audio })
|
? Pipewire.nodes.values.filter(function(n) {
|
||||||
|
return n.isSink && n.audio && n.isStream === false;
|
||||||
|
})
|
||||||
: [];
|
: [];
|
||||||
if (Pipewire.defaultAudioSink) {
|
if (Pipewire.defaultAudioSink) {
|
||||||
nodes = nodes.slice().sort(function(a, b) {
|
nodes = nodes.slice().sort(function(a, b) {
|
||||||
|
|
@ -239,7 +241,9 @@ PanelWithOverlay {
|
||||||
}
|
}
|
||||||
function sourceNodes() {
|
function sourceNodes() {
|
||||||
let nodes = Pipewire.nodes && Pipewire.nodes.values
|
let nodes = Pipewire.nodes && Pipewire.nodes.values
|
||||||
? Pipewire.nodes.values.filter(function(n) { return !n.isSink && n.audio })
|
? Pipewire.nodes.values.filter(function(n) {
|
||||||
|
return !n.isSink && n.audio && n.isStream === false;
|
||||||
|
})
|
||||||
: [];
|
: [];
|
||||||
if (Pipewire.defaultAudioSource) {
|
if (Pipewire.defaultAudioSource) {
|
||||||
nodes = nodes.slice().sort(function(a, b) {
|
nodes = nodes.slice().sort(function(a, b) {
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import qs.Services
|
||||||
Singleton {
|
Singleton {
|
||||||
|
|
||||||
property string shellName: "Noctalia"
|
property string shellName: "Noctalia"
|
||||||
property string settingsDir: Quickshell.env("HOME") + "/.config/" + shellName + "/"
|
property string settingsDir: (Quickshell.env("XDG_CONFIG_HOME") || Quickshell.env("HOME") + "/.config") + "/" + shellName + "/"
|
||||||
property string settingsFile: settingsDir + "Settings.json"
|
property string settingsFile: settingsDir + "Settings.json"
|
||||||
property var settings: settingAdapter
|
property var settings: settingAdapter
|
||||||
|
|
||||||
|
|
@ -57,6 +57,7 @@ Singleton {
|
||||||
property bool reverseDayMonth: false
|
property bool reverseDayMonth: false
|
||||||
property bool use12HourClock: false
|
property bool use12HourClock: false
|
||||||
property bool dimPanels: true
|
property bool dimPanels: true
|
||||||
|
property var pinnedExecs: [] // Added for AppLauncher pinned apps
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue