Fix text centering

Sort applauncher entries alphabetical
Use global fontFamily and font size (Theme.qml)
Tons of other small fixes
This commit is contained in:
ly-sec 2025-07-12 13:58:18 +02:00
parent ac456fa9a3
commit a1a9060111
27 changed files with 535 additions and 1060 deletions

View file

@ -34,7 +34,8 @@ Item {
text: panel.displayedWindowTitle && panel.displayedWindowTitle.length > 60
? panel.displayedWindowTitle.substring(0, 60) + "..."
: panel.displayedWindowTitle
font.pixelSize: 12
font.family: Theme.fontFamily
font.pixelSize: Theme.fontSizeCaption
color: Theme.textSecondary
elide: Text.ElideRight
horizontalAlignment: Text.AlignHCenter

View file

@ -75,7 +75,10 @@ PanelWindow {
}
function updateFilter() {
var query = searchField.text ? searchField.text.toLowerCase() : "";
var apps = root.appModel;
// Sort apps alphabetically by name (case-insensitive)
var apps = root.appModel.slice().sort(function(a, b) {
return a.name.toLowerCase().localeCompare(b.name.toLowerCase());
});
var results = [];
// Calculator mode: starts with '='
if (query.startsWith("=")) {
@ -98,7 +101,11 @@ PanelWindow {
results = results.concat(apps);
} else {
var fuzzyResults = Fuzzysort.go(query, apps, { keys: ["name", "comment", "genericName"] });
results = results.concat(fuzzyResults.map(function(r) { return r.obj; }));
// Sort fuzzy results alphabetically by name as well
var sortedFuzzy = fuzzyResults.map(function(r) { return r.obj; }).sort(function(a, b) {
return a.name.toLowerCase().localeCompare(b.name.toLowerCase());
});
results = results.concat(sortedFuzzy);
}
root.filteredApps = results;
root.selectedIndex = 0;
@ -158,18 +165,22 @@ PanelWindow {
height: 48
Layout.fillWidth: true
border.color: searchField.activeFocus ? Theme.accentPrimary : Theme.outline
border.width: 2
border.width: searchField.activeFocus ? 2 : 1
RowLayout {
anchors.fill: parent
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: 14
anchors.rightMargin: 14
spacing: 10
anchors.margins: 14
Text {
text: "search"
font.family: "Material Symbols Outlined"
font.pixelSize: 22
font.pixelSize: Theme.fontSizeHeader
color: searchField.activeFocus ? Theme.accentPrimary : Theme.textSecondary
verticalAlignment: Text.AlignVCenter
Layout.alignment: Qt.AlignVCenter
}
TextField {
id: searchField
@ -177,15 +188,19 @@ PanelWindow {
color: Theme.textPrimary
placeholderTextColor: Theme.textSecondary
background: null
font.pixelSize: 17
font.family: Theme.fontFamily
font.pixelSize: Theme.fontSizeBody
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
onTextChanged: root.updateFilter()
selectedTextColor: Theme.onAccent
selectionColor: Theme.accentPrimary
padding: 2
padding: 0
verticalAlignment: TextInput.AlignVCenter
leftPadding: 0
rightPadding: 0
topPadding: 0
bottomPadding: 0
font.bold: true
Component.onCompleted: contentItem.cursorColor = Theme.textPrimary
onActiveFocusChanged: contentItem.cursorColor = Theme.textPrimary
@ -265,7 +280,7 @@ PanelWindow {
visible: !modelData.isCalculator && !parent.iconLoaded
text: "broken_image"
font.family: "Material Symbols Outlined"
font.pixelSize: 22
font.pixelSize: Theme.fontSizeHeader
color: Theme.accentPrimary
}
}
@ -275,7 +290,8 @@ PanelWindow {
Text {
text: modelData.name
color: hovered || isSelected ? Theme.onAccent : Theme.textPrimary
font.pixelSize: 14
font.family: Theme.fontFamily
font.pixelSize: Theme.fontSizeSmall
font.bold: hovered || isSelected
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
@ -284,7 +300,8 @@ PanelWindow {
Text {
text: modelData.isCalculator ? (modelData.expr + " = " + modelData.result) : (modelData.comment || modelData.genericName || "")
color: hovered || isSelected ? Theme.onAccent : Theme.textSecondary
font.pixelSize: 11
font.family: Theme.fontFamily
font.pixelSize: Theme.fontSizeCaption
elide: Text.ElideRight
Layout.fillWidth: true
}
@ -293,7 +310,7 @@ PanelWindow {
Text {
text: modelData.isCalculator ? "content_copy" : "chevron_right"
font.family: "Material Symbols Outlined"
font.pixelSize: 16
font.pixelSize: Theme.fontSizeBody
color: hovered || isSelected ? Theme.onAccent : Theme.textSecondary
verticalAlignment: Text.AlignVCenter
}

View file

@ -1,221 +0,0 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Bluetooth
import qs.Settings
import qs.Components
Item {
id: bluetoothDisplay
width: 22
height: 22
property color hoverColor: Theme.rippleEffect
property real hoverOpacity: 0.0
property bool isActive: mouseArea.containsMouse || (bluetoothPopup && bluetoothPopup.visible)
// Show the Bluetooth popup when clicked
MouseArea {
id: mouseArea
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
onClicked: {
if (bluetoothPopup.visible) {
bluetoothPopup.hidePopup();
} else {
bluetoothPopup.showAt(this, 0, parent.height);
}
}
onEntered: bluetoothDisplay.hoverOpacity = 0.18
onExited: bluetoothDisplay.hoverOpacity = 0.0
}
Rectangle {
anchors.fill: parent
anchors.margins: -4 // Make hover area larger than icon
color: hoverColor
opacity: isActive ? 0.18 : hoverOpacity
radius: height / 2
z: 0
visible: opacity > 0.01
}
Text {
anchors.centerIn: parent
text: "bluetooth"
font.family: isActive ? "Material Symbols Rounded" : "Material Symbols Outlined"
font.pixelSize: 18
color: bluetoothPopup.visible ? Theme.accentPrimary : Theme.textPrimary
z: 1
}
Behavior on hoverOpacity {
NumberAnimation {
duration: 120
easing.type: Easing.OutQuad
}
}
// The popup window for device list
PopupWindow {
id: bluetoothPopup
implicitWidth: 350
//property int deviceCount: (typeof Bluetooth.devices.count === 'number' && Bluetooth.devices.count >= 0) ? Bluetooth.devices.count : 0
//implicitHeight: Math.max(100, Math.min(420, 56 + (deviceCount * 36) + 24))
implicitHeight: 400
visible: false
color: "transparent"
property var anchorItem: null
property real anchorX
property real anchorY
anchor.item: anchorItem ? anchorItem : null
anchor.rect.x: anchorX - (implicitWidth / 2) + (anchorItem ? anchorItem.width / 2 : 0)
anchor.rect.y: anchorY + 8 // Move popup further down
function showAt(item, x, y) {
if (!item) {
console.warn("Bluetooth: anchorItem is undefined, not showing popup.")
return
}
anchorItem = item
anchorX = x
anchorY = y
visible = true
forceActiveFocus()
}
function hidePopup() {
visible = false
}
Item {
anchors.fill: parent
Keys.onEscapePressed: bluetoothPopup.hidePopup()
}
Rectangle {
id: bg
anchors.fill: parent
color: Theme.backgroundPrimary
radius: 12
border.width: 1
border.color: Theme.surfaceVariant
z: 0
}
// Header
Rectangle {
id: header
width: parent.width
height: 56
color: "transparent"
Text {
text: "Bluetooth"
font.pixelSize: 18
font.bold: true
color: Theme.textPrimary
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 16
}
}
// Device list container with proper margins
Rectangle {
id: listContainer
anchors.top: header.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.margins: 8
color: "transparent"
clip: true
ListView {
id: deviceListView
anchors.fill: parent
spacing: 4
boundsBehavior: Flickable.StopAtBounds
model: Bluetooth.devices
delegate: Rectangle {
width: parent.width
height: 42
color: "transparent"
radius: 8
Rectangle {
anchors.fill: parent
radius: 8
color: modelData.connected ? Qt.rgba(Theme.accentPrimary.r, Theme.accentPrimary.g, Theme.accentPrimary.b, 0.18)
: (deviceMouseArea.containsMouse ? Theme.highlight : "transparent")
}
RowLayout {
anchors.fill: parent
anchors.leftMargin: 12
anchors.rightMargin: 12
spacing: 12
Text {
text: modelData.connected ? "bluetooth" : "bluetooth_disabled"
font.family: "Material Symbols Outlined"
font.pixelSize: 20
color: modelData.connected ? Theme.accentPrimary : Theme.textSecondary
verticalAlignment: Text.AlignVCenter
}
ColumnLayout {
Layout.fillWidth: true
spacing: 2
Text {
text: modelData.name || "Unknown Device"
color: modelData.connected ? Theme.accentPrimary : Theme.textPrimary
font.pixelSize: 14
elide: Text.ElideRight
}
Text {
text: modelData.address
color: modelData.connected ? Theme.accentPrimary : Theme.textSecondary
font.pixelSize: 11
elide: Text.ElideRight
}
}
}
MouseArea {
id: deviceMouseArea
anchors.fill: parent
hoverEnabled: true
onClicked: {
if (modelData.connected) {
modelData.disconnect()
} else {
modelData.connect()
}
}
}
}
}
}
// Scroll indicator when needed
Rectangle {
anchors.right: parent.right
anchors.rightMargin: 2
anchors.top: listContainer.top
anchors.bottom: listContainer.bottom
width: 4
radius: 2
color: Theme.textSecondary
opacity: deviceListView.contentHeight > deviceListView.height ? 0.3 : 0
visible: opacity > 0
}
}
}

View file

@ -9,9 +9,9 @@ Rectangle {
Text {
id: textItem
text: Time.time
font.family: "Roboto"
font.family: Theme.fontFamily
font.weight: Font.Bold
font.pixelSize: 14
font.pixelSize: Theme.fontSizeSmall
color: Theme.textPrimary
anchors.centerIn: parent
}

View file

@ -104,7 +104,8 @@ PopupWindow {
Layout.fillWidth: true
color: (modelData?.enabled ?? true) ? Theme.textPrimary : Theme.textDisabled
text: modelData?.text ?? ""
font.pixelSize: 13
font.family: Theme.fontFamily
font.pixelSize: Theme.fontSizeSmall
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
}

View file

@ -1,430 +0,0 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import Quickshell.Wayland
import qs.Settings
Item {
id: wifiDisplay
width: 22
height: 22
property color hoverColor: Theme.rippleEffect
property real hoverOpacity: 0.0
property bool isActive: mouseArea.containsMouse || (wifiPanel && wifiPanel.visible)
MouseArea {
id: mouseArea
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
onClicked: {
if (wifiPanel.visible) {
wifiPanel.hidePopup();
} else {
wifiPanel.showAt(this, 0, parent.height);
}
}
onEntered: wifiDisplay.hoverOpacity = 0.18
onExited: wifiDisplay.hoverOpacity = 0.0
}
Rectangle {
anchors.fill: parent
anchors.margins: -4
color: hoverColor
opacity: isActive ? 0.18 : hoverOpacity
radius: height / 2
z: 0
visible: opacity > 0.01
}
Text {
anchors.centerIn: parent
text: "wifi"
font.family: isActive ? "Material Symbols Rounded" : "Material Symbols Outlined"
font.pixelSize: 18
color: wifiPanel.visible ? Theme.accentPrimary : Theme.textPrimary
z: 1
}
PanelWindow {
id: wifiPanel
implicitWidth: 350
implicitHeight: 400
color: "transparent"
visible: false
property var anchorItem: null
property real anchorX
property real anchorY
property var networks: [] // { ssid, signal, security, connected }
property string connectingSsid: ""
property string errorMsg: ""
property string passwordPromptSsid: ""
property string passwordInput: ""
property bool showPasswordPrompt: false
WlrLayershell.keyboardFocus: visible ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
function showAt(item, x, y) {
if (!item) {
console.warn("Wifi: anchorItem is undefined, not showing panel.");
return;
}
anchorItem = item
anchorX = x
anchorY = y
visible = true
refreshNetworks()
Qt.callLater(() => {
wifiPanel.x = anchorX - (wifiPanel.width / 2) + (anchorItem ? anchorItem.width / 2 : 0)
wifiPanel.y = anchorY + 8
})
}
function hidePopup() {
visible = false
showPasswordPrompt = false
errorMsg = ""
}
// Scan for networks
Process {
id: scanProcess
running: false
command: ["nmcli", "-t", "-f", "SSID,SECURITY,SIGNAL,IN-USE", "device", "wifi", "list"]
stdout: StdioCollector {
onStreamFinished: {
var lines = text.split("\n");
var nets = [];
var seen = {};
for (var i = 0; i < lines.length; ++i) {
var line = lines[i].trim();
if (!line) continue;
var parts = line.split(":");
var ssid = parts[0];
var security = parts[1];
var signal = parseInt(parts[2]);
var inUse = parts[3] === "*";
if (ssid && !seen[ssid]) {
nets.push({ ssid: ssid, security: security, signal: signal, connected: inUse });
seen[ssid] = true;
}
}
wifiPanel.networks = nets;
}
}
}
function refreshNetworks() {
scanProcess.running = true;
}
// Connect to a network
Process {
id: connectProcess
property string ssid: ""
property string password: ""
running: false
command: password ? ["nmcli", "device", "wifi", "connect", ssid, "password", password] : ["nmcli", "device", "wifi", "connect", ssid]
stdout: StdioCollector {
onStreamFinished: {
wifiPanel.connectingSsid = "";
wifiPanel.showPasswordPrompt = false;
wifiPanel.errorMsg = "";
refreshNetworks();
}
}
stderr: StdioCollector {
onStreamFinished: {
wifiPanel.connectingSsid = "";
wifiPanel.errorMsg = text;
wifiPanel.showPasswordPrompt = false;
}
}
}
function connectNetwork(ssid, security) {
if (security && security !== "--") {
// Prompt for password
passwordPromptSsid = ssid;
passwordInput = "";
showPasswordPrompt = true;
} else {
connectingSsid = ssid;
connectProcess.ssid = ssid;
connectProcess.password = "";
connectProcess.running = true;
}
}
function submitPassword() {
connectingSsid = passwordPromptSsid;
connectProcess.ssid = passwordPromptSsid;
connectProcess.password = passwordInput;
connectProcess.running = true;
}
// Disconnect
Process {
id: disconnectProcess
property string ssid: ""
running: false
command: ["nmcli", "connection", "down", "id", ssid]
onRunningChanged: {
if (!running) {
refreshNetworks();
}
}
}
function disconnectNetwork(ssid) {
disconnectProcess.ssid = ssid;
disconnectProcess.running = true;
}
// UI
Rectangle {
id: bg
anchors.fill: parent
radius: 12
border.width: 1
border.color: Theme.surfaceVariant
color: Theme.backgroundPrimary
z: 0
}
// Header
Rectangle {
id: header
width: parent.width
height: 56
color: "transparent"
Text {
text: "Wi-Fi"
font.pixelSize: 18
font.bold: true
color: Theme.textPrimary
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 16
}
// Refresh button
Rectangle {
id: refreshBtn
width: 36
height: 36
radius: 18
color: Theme.surfaceVariant
border.color: refreshMouseArea.containsMouse ? Theme.accentPrimary : Theme.outline
border.width: 1
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
anchors.rightMargin: 12
MouseArea {
id: refreshMouseArea
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: wifiPanel.refreshNetworks()
}
Text {
anchors.centerIn: parent
text: "refresh"
font.family: "Material Symbols Outlined"
font.pixelSize: 20
color: Theme.accentPrimary
}
}
}
// Error message
Text {
visible: wifiPanel.errorMsg.length > 0
text: wifiPanel.errorMsg
color: Theme.error
font.pixelSize: 12
anchors.top: header.bottom
anchors.left: parent.left
anchors.leftMargin: 16
anchors.topMargin: 2
}
// Device list container
Rectangle {
id: listContainer
anchors.top: header.bottom
anchors.topMargin: wifiPanel.showPasswordPrompt ? 68 : 0
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.margins: 8
color: "transparent"
clip: true
ListView {
id: networkListView
anchors.fill: parent
spacing: 4
boundsBehavior: Flickable.StopAtBounds
model: wifiPanel.networks
delegate: Rectangle {
id: networkEntry
width: parent.width
height: modelData.ssid === wifiPanel.passwordPromptSsid && wifiPanel.showPasswordPrompt ? 110 : 42
color: "transparent"
radius: 8
Rectangle {
anchors.fill: parent
radius: 8
color: modelData.connected ? Qt.rgba(Theme.accentPrimary.r, Theme.accentPrimary.g, Theme.accentPrimary.b, 0.18)
: ((networkMouseArea.containsMouse || (modelData.ssid === wifiPanel.passwordPromptSsid && wifiPanel.showPasswordPrompt)) ? Theme.highlight : "transparent")
z: 0
}
RowLayout {
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.margins: 0
anchors.bottomMargin: 0
anchors.topMargin: 0
height: 42
spacing: 12
// Signal icon
Text {
text: signalIcon(modelData.signal)
font.family: "Material Symbols Outlined"
font.pixelSize: 20
color: hovered ? Theme.backgroundPrimary : (modelData.connected ? Theme.accentPrimary : Theme.textSecondary)
verticalAlignment: Text.AlignVCenter
}
ColumnLayout {
Layout.fillWidth: true
spacing: 2
Text {
text: modelData.ssid || "Unknown Network"
color: hovered ? Theme.backgroundPrimary : (modelData.connected ? Theme.accentPrimary : Theme.textPrimary)
font.pixelSize: 14
elide: Text.ElideRight
Layout.fillWidth: true
}
Text {
text: modelData.security && modelData.security !== "--" ? modelData.security : "Open"
color: hovered ? Theme.backgroundPrimary : (modelData.connected ? Theme.accentPrimary : Theme.textSecondary)
font.pixelSize: 11
elide: Text.ElideRight
Layout.fillWidth: true
}
}
// Status
Text {
visible: modelData.connected
text: "connected"
color: hovered ? Theme.backgroundPrimary : Theme.accentPrimary
font.pixelSize: 11
verticalAlignment: Text.AlignVCenter
}
}
// Password prompt dropdown (only for selected network)
Rectangle {
visible: modelData.ssid === wifiPanel.passwordPromptSsid && wifiPanel.showPasswordPrompt
width: parent.width
height: 60
radius: 8
color: Theme.surfaceVariant
border.color: passwordField.activeFocus ? Theme.accentPrimary : Theme.outline
border.width: 1
anchors.bottom: parent.bottom
z: 2
RowLayout {
anchors.fill: parent
anchors.margins: 12
spacing: 10
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 36
radius: 8
color: Theme.surfaceVariant
border.color: passwordField.activeFocus ? Theme.accentPrimary : Theme.outline
border.width: 1
TextInput {
id: passwordField
anchors.fill: parent
anchors.margins: 12
text: wifiPanel.passwordInput
font.pixelSize: 13
color: Theme.textPrimary
verticalAlignment: TextInput.AlignVCenter
clip: true
focus: true
selectByMouse: true
activeFocusOnTab: true
inputMethodHints: Qt.ImhNone
echoMode: TextInput.Password
onTextChanged: wifiPanel.passwordInput = text
onAccepted: wifiPanel.submitPassword()
MouseArea {
id: passwordMouseArea
anchors.fill: parent
onClicked: passwordField.forceActiveFocus()
}
}
}
Rectangle {
width: 80
height: 36
radius: 18
color: Theme.accentPrimary
border.color: Theme.accentPrimary
border.width: 0
MouseArea {
anchors.fill: parent
onClicked: wifiPanel.submitPassword()
cursorShape: Qt.PointingHandCursor
}
Text {
anchors.centerIn: parent
text: "Connect"
color: Theme.backgroundPrimary
font.pixelSize: 14
font.bold: true
}
}
}
}
MouseArea {
id: networkMouseArea
anchors.fill: parent
hoverEnabled: true
onClicked: {
if (modelData.connected) {
wifiPanel.disconnectNetwork(modelData.ssid)
} else if (modelData.security && modelData.security !== "--") {
wifiPanel.passwordPromptSsid = modelData.ssid;
wifiPanel.passwordInput = "";
wifiPanel.showPasswordPrompt = true;
} else {
wifiPanel.connectNetwork(modelData.ssid, modelData.security)
}
}
}
// Helper for hover text color
property bool hovered: networkMouseArea.containsMouse || (modelData.ssid === wifiPanel.passwordPromptSsid && wifiPanel.showPasswordPrompt)
}
}
}
// Scroll indicator
Rectangle {
anchors.right: parent.right
anchors.rightMargin: 2
anchors.top: listContainer.top
anchors.bottom: listContainer.bottom
width: 4
radius: 2
color: Theme.textSecondary
opacity: networkListView.contentHeight > networkListView.height ? 0.3 : 0
visible: opacity > 0
}
}
// Helper for signal icon
function signalIcon(signal) {
if (signal >= 80) return "network_wifi_4_bar";
if (signal >= 60) return "network_wifi_3_bar";
if (signal >= 40) return "network_wifi_2_bar";
if (signal >= 20) return "network_wifi_1_bar";
return "wifi_0_bar";
}
}