Merge tag 'v2.8.0'
Release v2.8.0 We've been busy squashing bugs and adding some nice improvements based on your feedback. What's New New Icon Set - Swapped out Material Symbols for Tabler icons. They look great and load faster since they're built right in. Works on Any Linux Distro - Dropped the Arch-specific update checker so this works properly on whatever distro you're running. You can build your own update notifications with Custom Buttons if you want. Icon Picker - Added a proper icon picker for custom button widgets. No more guessing icon names. Smarter Audio Visualizer - The Cava visualizer actually pays attention now - it only kicks in when you're playing music or videos instead of running all the time. Better Notifications - Notifications now show actual app names like "Firefox" instead of cryptic IDs like "org.mozilla.firefox". Less Noise - Turned a bunch of those persistent notification popups into toast notifications so they don't stick around cluttering your screen. Fixes Active Window widget finally shows the right app icon and title consistently Fixed a nasty crash on Hyprland Screen recorder button disables itself if the recording software isn't installed Added a force-enable option for Night Light so you can turn it on manually whenever
This commit is contained in:
commit
9792f401f7
102 changed files with 7930 additions and 1626 deletions
|
|
@ -82,9 +82,9 @@ Rectangle {
|
|||
// Icon (optional)
|
||||
NIcon {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
layoutTopMargin: 1 * scaling
|
||||
visible: root.icon !== ""
|
||||
text: root.icon
|
||||
|
||||
icon: root.icon
|
||||
font.pointSize: root.iconSize
|
||||
color: {
|
||||
if (!root.enabled)
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ RowLayout {
|
|||
NIcon {
|
||||
visible: root.checked
|
||||
anchors.centerIn: parent
|
||||
text: "check"
|
||||
icon: "check"
|
||||
color: root.activeOnColor
|
||||
font.pointSize: Math.max(Style.fontSizeS, root.baseSize * 0.7) * scaling
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,20 +88,21 @@ Rectangle {
|
|||
// Tiny circular badge for the icon, positioned using anchors within the gauge
|
||||
Rectangle {
|
||||
id: iconBadge
|
||||
width: 28 * scaling * contentScale
|
||||
width: iconText.implicitWidth + Style.marginXS * scaling
|
||||
height: width
|
||||
radius: width / 2
|
||||
color: Color.mSurface
|
||||
color: Color.mPrimary
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.rightMargin: -6 * scaling * contentScale
|
||||
anchors.topMargin: Style.marginXXS * scaling * contentScale
|
||||
anchors.rightMargin: -2 * scaling
|
||||
anchors.topMargin: -2 * scaling
|
||||
|
||||
NIcon {
|
||||
id: iconText
|
||||
anchors.centerIn: parent
|
||||
text: root.icon
|
||||
font.pointSize: Style.fontSizeLargeXL * scaling * contentScale
|
||||
color: Color.mOnSurface
|
||||
icon: root.icon
|
||||
color: Color.mOnPrimary
|
||||
font.pointSize: Style.fontSizeM * scaling
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import QtQuick
|
|||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import qs.Commons
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
Rectangle {
|
||||
|
|
@ -58,7 +59,7 @@ Rectangle {
|
|||
}
|
||||
|
||||
NIcon {
|
||||
text: "palette"
|
||||
icon: "color-picker"
|
||||
color: Color.mOnSurfaceVariant
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import QtQuick
|
|||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import qs.Commons
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
Popup {
|
||||
|
|
@ -129,7 +130,7 @@ Popup {
|
|||
spacing: Style.marginS * scaling
|
||||
|
||||
NIcon {
|
||||
text: "palette"
|
||||
icon: "color-picker"
|
||||
font.pointSize: Style.fontSizeXXL * scaling
|
||||
color: Color.mPrimary
|
||||
}
|
||||
|
|
@ -491,7 +492,6 @@ Popup {
|
|||
NButton {
|
||||
id: cancelButton
|
||||
text: "Cancel"
|
||||
icon: "close"
|
||||
outlined: cancelButton.hovered ? false : true
|
||||
customHeight: 36 * scaling
|
||||
customWidth: 100 * scaling
|
||||
|
|
|
|||
|
|
@ -85,8 +85,8 @@ RowLayout {
|
|||
indicator: NIcon {
|
||||
x: combo.width - width - Style.marginM * scaling
|
||||
y: combo.topPadding + (combo.availableHeight - height) / 2
|
||||
text: "arrow_drop_down"
|
||||
font.pointSize: Style.fontSizeXXL * scaling
|
||||
icon: "caret-down"
|
||||
font.pointSize: Style.fontSizeL * scaling
|
||||
}
|
||||
|
||||
popup: Popup {
|
||||
|
|
|
|||
|
|
@ -1,19 +1,27 @@
|
|||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Commons
|
||||
import qs.Widgets
|
||||
import QtQuick.Layouts
|
||||
|
||||
Text {
|
||||
// Optional layout nudge for optical alignment when used inside Layouts
|
||||
property real layoutTopMargin: 0
|
||||
text: "question_mark"
|
||||
font.family: "Material Symbols Rounded"
|
||||
font.pointSize: Style.fontSizeL * scaling
|
||||
font.variableAxes: {
|
||||
"wght"// slightly bold to ensure all lines looks good
|
||||
: (Font.Normal + Font.Bold) / 2.5
|
||||
id: root
|
||||
|
||||
property string icon: Icons.defaultIcon
|
||||
|
||||
visible: (icon !== undefined) && (icon !== "")
|
||||
text: {
|
||||
if ((icon === undefined) || (icon === "")) {
|
||||
return ""
|
||||
}
|
||||
if (Icons.get(icon) === undefined) {
|
||||
Logger.warn("Icon", `"${icon}"`, "doesn't exist in the icons font")
|
||||
Logger.callStack()
|
||||
return Icons.get(Icons.defaultIcon)
|
||||
}
|
||||
return Icons.get(icon)
|
||||
}
|
||||
font.family: Icons.fontFamily
|
||||
font.pointSize: Style.fontSizeL * scaling
|
||||
color: Color.mOnSurface
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
Layout.topMargin: layoutTopMargin
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ Rectangle {
|
|||
property string icon
|
||||
property string tooltipText
|
||||
property bool enabled: true
|
||||
property bool allowClickWhenDisabled: false
|
||||
property bool hovering: false
|
||||
|
||||
property color colorBg: Color.mSurfaceVariant
|
||||
|
|
@ -35,17 +36,31 @@ Rectangle {
|
|||
opacity: root.enabled ? Style.opacityFull : Style.opacityMedium
|
||||
color: root.enabled && root.hovering ? colorBgHover : colorBg
|
||||
radius: width * 0.5
|
||||
border.color: root.hovering ? colorBorderHover : colorBorder
|
||||
border.color: root.enabled && root.hovering ? colorBorderHover : colorBorder
|
||||
border.width: Math.max(1, Style.borderS * scaling)
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Style.animationNormal
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
}
|
||||
|
||||
NIcon {
|
||||
text: root.icon
|
||||
font.pointSize: Style.fontSizeM * scaling
|
||||
color: root.hovering ? colorFgHover : colorFg
|
||||
icon: root.icon
|
||||
font.pointSize: Math.max(1, root.width * 0.47)
|
||||
color: root.enabled && root.hovering ? colorFgHover : colorFg
|
||||
// Center horizontally
|
||||
x: (root.width - width) / 2
|
||||
// Center vertically accounting for font metrics
|
||||
y: (root.height - height) / 2 + (height - contentHeight) / 2
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Style.animationFast
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NTooltip {
|
||||
|
|
@ -56,13 +71,14 @@ Rectangle {
|
|||
}
|
||||
|
||||
MouseArea {
|
||||
enabled: root.enabled
|
||||
// Always enabled to allow hover/tooltip even when the button is disabled
|
||||
enabled: true
|
||||
anchors.fill: parent
|
||||
cursorShape: root.enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
|
||||
hoverEnabled: true
|
||||
onEntered: {
|
||||
hovering = true
|
||||
hovering = root.enabled ? true : false
|
||||
if (tooltipText) {
|
||||
tooltip.show()
|
||||
}
|
||||
|
|
@ -79,6 +95,9 @@ Rectangle {
|
|||
if (tooltipText) {
|
||||
tooltip.hide()
|
||||
}
|
||||
if (!root.enabled && !allowClickWhenDisabled) {
|
||||
return
|
||||
}
|
||||
if (mouse.button === Qt.LeftButton) {
|
||||
root.clicked()
|
||||
} else if (mouse.button === Qt.RightButton) {
|
||||
|
|
|
|||
|
|
@ -9,9 +9,10 @@ Rectangle {
|
|||
id: root
|
||||
|
||||
property string imagePath: ""
|
||||
property string fallbackIcon: ""
|
||||
property color borderColor: Color.transparent
|
||||
property real borderWidth: 0
|
||||
property string fallbackIcon: ""
|
||||
property real fallbackIconSize: Style.fontSizeXXL * scaling
|
||||
|
||||
color: Color.transparent
|
||||
radius: parent.width * 0.5
|
||||
|
|
@ -45,18 +46,20 @@ Rectangle {
|
|||
}
|
||||
|
||||
property real imageOpacity: root.opacity
|
||||
fragmentShader: Qt.resolvedUrl("../Shaders/qsb/circled_image.frag.qsb")
|
||||
fragmentShader: Qt.resolvedUrl(Quickshell.shellDir + "/Shaders/qsb/circled_image.frag.qsb")
|
||||
supportsAtlasTextures: false
|
||||
blending: true
|
||||
}
|
||||
|
||||
// Fallback icon
|
||||
NIcon {
|
||||
anchors.centerIn: parent
|
||||
text: fallbackIcon
|
||||
font.pointSize: Style.fontSizeXXL * scaling
|
||||
visible: fallbackIcon !== undefined && fallbackIcon !== "" && (imagePath === undefined || imagePath === "")
|
||||
z: 0
|
||||
Loader {
|
||||
active: fallbackIcon !== undefined && fallbackIcon !== "" && (imagePath === undefined || imagePath === "")
|
||||
sourceComponent: NIcon {
|
||||
anchors.centerIn: parent
|
||||
icon: fallbackIcon
|
||||
font.pointSize: fallbackIconSize
|
||||
z: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,10 +9,11 @@ Rectangle {
|
|||
id: root
|
||||
|
||||
property string imagePath: ""
|
||||
property string fallbackIcon: ""
|
||||
property color borderColor: Color.transparent
|
||||
property real borderWidth: 0
|
||||
property real imageRadius: width * 0.5
|
||||
property string fallbackIcon: ""
|
||||
property real fallbackIconSize: Style.fontSizeXXL * scaling
|
||||
|
||||
property real scaledRadius: imageRadius * Settings.data.general.radiusRatio
|
||||
|
||||
|
|
@ -56,7 +57,7 @@ Rectangle {
|
|||
property real itemHeight: root.height
|
||||
property real cornerRadius: root.radius
|
||||
property real imageOpacity: root.opacity
|
||||
fragmentShader: Qt.resolvedUrl("../Shaders/qsb/rounded_image.frag.qsb")
|
||||
fragmentShader: Qt.resolvedUrl(Quickshell.shellDir + "/Shaders/qsb/rounded_image.frag.qsb")
|
||||
|
||||
// Qt6 specific properties - ensure proper blending
|
||||
supportsAtlasTextures: false
|
||||
|
|
@ -71,12 +72,14 @@ Rectangle {
|
|||
}
|
||||
|
||||
// Fallback icon
|
||||
NIcon {
|
||||
anchors.centerIn: parent
|
||||
text: fallbackIcon
|
||||
font.pointSize: Style.fontSizeXXL * scaling
|
||||
visible: fallbackIcon !== undefined && fallbackIcon !== "" && (imagePath === undefined || imagePath === "")
|
||||
z: 0
|
||||
Loader {
|
||||
active: fallbackIcon !== undefined && fallbackIcon !== "" && (imagePath === undefined || imagePath === "")
|
||||
sourceComponent: NIcon {
|
||||
anchors.centerIn: parent
|
||||
icon: fallbackIcon
|
||||
font.pointSize: fallbackIconSize
|
||||
z: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import QtQuick
|
|||
import QtQuick.Layouts
|
||||
import qs.Commons
|
||||
import qs.Widgets
|
||||
import qs.Services
|
||||
|
||||
// Input and button row
|
||||
RowLayout {
|
||||
|
|
@ -13,7 +14,7 @@ RowLayout {
|
|||
property string placeholderText: ""
|
||||
property string text: ""
|
||||
property string actionButtonText: "Test"
|
||||
property string actionButtonIcon: "play_arrow"
|
||||
property string actionButtonIcon: "media-play"
|
||||
property bool actionButtonEnabled: text !== ""
|
||||
|
||||
// Signals
|
||||
|
|
|
|||
|
|
@ -40,8 +40,8 @@ Loader {
|
|||
property int buttonWidth: 0
|
||||
property int buttonHeight: 0
|
||||
|
||||
// Whether this panel should accept keyboard focus
|
||||
property bool panelKeyboardFocus: false
|
||||
property bool backgroundClickEnabled: true
|
||||
|
||||
// Animation properties
|
||||
readonly property real originalScale: 0.7
|
||||
|
|
@ -62,6 +62,24 @@ Loader {
|
|||
PanelService.registerPanel(root)
|
||||
}
|
||||
|
||||
// -----------------------------------------
|
||||
// Functions to control background click behavior
|
||||
function disableBackgroundClick() {
|
||||
backgroundClickEnabled = false
|
||||
}
|
||||
|
||||
function enableBackgroundClick() {
|
||||
// Add a small delay to prevent immediate close after drag release
|
||||
enableBackgroundClickTimer.restart()
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: enableBackgroundClickTimer
|
||||
interval: 100
|
||||
repeat: false
|
||||
onTriggered: backgroundClickEnabled = true
|
||||
}
|
||||
|
||||
// -----------------------------------------
|
||||
function toggle(aScreen, buttonItem) {
|
||||
// Don't toggle if screen is null or invalid
|
||||
|
|
@ -110,6 +128,7 @@ Loader {
|
|||
|
||||
PanelService.willOpenPanel(root)
|
||||
|
||||
backgroundClickEnabled = true
|
||||
active = true
|
||||
root.opened()
|
||||
}
|
||||
|
|
@ -125,7 +144,8 @@ Loader {
|
|||
function closeCompleted() {
|
||||
root.closed()
|
||||
active = false
|
||||
useButtonPosition = false // Reset button position usage
|
||||
useButtonPosition = false
|
||||
backgroundClickEnabled = true
|
||||
PanelService.closedPanel(root)
|
||||
}
|
||||
|
||||
|
|
@ -179,6 +199,7 @@ Loader {
|
|||
// Clicking outside of the rectangle to close
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
enabled: root.backgroundClickEnabled
|
||||
onClicked: root.close()
|
||||
}
|
||||
|
||||
|
|
@ -208,7 +229,7 @@ Loader {
|
|||
|
||||
return Math.round(Math.max(minX, Math.min(targetX, maxX)))
|
||||
} else if (!panelAnchorHorizontalCenter && panelAnchorLeft) {
|
||||
return Math.round(marginS * scaling)
|
||||
return Math.round(Style.marginS * scaling)
|
||||
} else if (!panelAnchorHorizontalCenter && panelAnchorRight) {
|
||||
return Math.round(panelWindow.width - panelWidth - (Style.marginS * scaling))
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -9,21 +9,15 @@ Item {
|
|||
property string icon: ""
|
||||
property string text: ""
|
||||
property string tooltipText: ""
|
||||
property color pillColor: Color.mSurfaceVariant
|
||||
property color textColor: Color.mOnSurface
|
||||
property color iconCircleColor: Color.mPrimary
|
||||
property color iconTextColor: Color.mSurface
|
||||
property color collapsedIconColor: Color.mOnSurface
|
||||
|
||||
property real iconRotation: 0
|
||||
property real sizeRatio: 0.8
|
||||
property bool autoHide: false
|
||||
property bool forceOpen: false
|
||||
property bool disableOpen: false
|
||||
property bool rightOpen: false
|
||||
property bool hovered: false
|
||||
|
||||
// Effective shown state (true if hovered/animated open or forced)
|
||||
readonly property bool effectiveShown: forceOpen || showPill
|
||||
readonly property bool revealed: forceOpen || showPill
|
||||
|
||||
signal shown
|
||||
signal hidden
|
||||
|
|
@ -50,14 +44,14 @@ Item {
|
|||
|
||||
Rectangle {
|
||||
id: pill
|
||||
width: effectiveShown ? maxPillWidth : 1
|
||||
width: revealed ? maxPillWidth : 1
|
||||
height: pillHeight
|
||||
|
||||
x: rightOpen ? (iconCircle.x + iconCircle.width / 2) : // Opens right
|
||||
(iconCircle.x + iconCircle.width / 2) - width // Opens left
|
||||
|
||||
opacity: effectiveShown ? Style.opacityFull : Style.opacityNone
|
||||
color: pillColor
|
||||
opacity: revealed ? Style.opacityFull : Style.opacityNone
|
||||
color: Color.mSurfaceVariant
|
||||
|
||||
topLeftRadius: rightOpen ? 0 : pillHeight * 0.5
|
||||
bottomLeftRadius: rightOpen ? 0 : pillHeight * 0.5
|
||||
|
|
@ -77,8 +71,8 @@ Item {
|
|||
text: root.text
|
||||
font.pointSize: Style.fontSizeXS * scaling
|
||||
font.weight: Style.fontWeightBold
|
||||
color: textColor
|
||||
visible: effectiveShown
|
||||
color: Color.mPrimary
|
||||
visible: revealed
|
||||
}
|
||||
|
||||
Behavior on width {
|
||||
|
|
@ -102,11 +96,8 @@ Item {
|
|||
width: iconSize
|
||||
height: iconSize
|
||||
radius: width * 0.5
|
||||
// When forced shown, match pill background; otherwise use accent when hovered
|
||||
color: forceOpen ? pillColor : (showPill ? iconCircleColor : Color.mSurfaceVariant)
|
||||
color: hovered && !forceOpen ? Color.mPrimary : Color.mSurfaceVariant
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
border.width: Math.max(1, Style.borderS * scaling)
|
||||
border.color: forceOpen ? Qt.alpha(Color.mOutline, 0.5) : Color.transparent
|
||||
|
||||
x: rightOpen ? 0 : (parent.width - width)
|
||||
|
||||
|
|
@ -118,11 +109,9 @@ Item {
|
|||
}
|
||||
|
||||
NIcon {
|
||||
text: root.icon
|
||||
rotation: root.iconRotation
|
||||
icon: root.icon
|
||||
font.pointSize: Style.fontSizeM * scaling
|
||||
// When forced shown, use pill text color; otherwise accent color when hovered
|
||||
color: forceOpen ? textColor : (showPill ? iconTextColor : Color.mOnSurface)
|
||||
color: hovered && !forceOpen ? Color.mOnPrimary : Color.mOnSurface
|
||||
// Center horizontally
|
||||
x: (iconCircle.width - width) / 2
|
||||
// Center vertically accounting for font metrics
|
||||
|
|
@ -220,6 +209,7 @@ Item {
|
|||
hoverEnabled: true
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
|
||||
onEntered: {
|
||||
hovered = true
|
||||
root.entered()
|
||||
tooltip.show()
|
||||
if (disableOpen) {
|
||||
|
|
@ -230,6 +220,7 @@ Item {
|
|||
}
|
||||
}
|
||||
onExited: {
|
||||
hovered = false
|
||||
root.exited()
|
||||
if (!forceOpen) {
|
||||
hide()
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ RowLayout {
|
|||
|
||||
NIcon {
|
||||
anchors.centerIn: parent
|
||||
text: "remove"
|
||||
icon: "chevron-left"
|
||||
font.pointSize: Style.fontSizeS * scaling
|
||||
color: decreaseArea.containsMouse ? Color.mOnPrimary : Color.mPrimary
|
||||
}
|
||||
|
|
@ -130,7 +130,7 @@ RowLayout {
|
|||
|
||||
NIcon {
|
||||
anchors.centerIn: parent
|
||||
text: "add"
|
||||
icon: "chevron-right"
|
||||
font.pointSize: Style.fontSizeS * scaling
|
||||
color: increaseArea.containsMouse ? Color.mOnPrimary : Color.mPrimary
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,23 +112,13 @@ Item {
|
|||
RowLayout {
|
||||
id: contentLayout
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginM * scaling
|
||||
spacing: Style.marginS * scaling
|
||||
anchors.margins: Style.marginL * scaling
|
||||
spacing: Style.marginL * scaling
|
||||
|
||||
// Icon
|
||||
NIcon {
|
||||
id: icon
|
||||
text: {
|
||||
switch (root.type) {
|
||||
case "warning":
|
||||
return "warning"
|
||||
case "notice":
|
||||
return "info"
|
||||
default:
|
||||
return "info"
|
||||
}
|
||||
}
|
||||
|
||||
icon: (root.type == "warning") ? "toast-warning" : "toast-notice"
|
||||
color: {
|
||||
switch (root.type) {
|
||||
case "warning":
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue