Merge remote-tracking branch 'origin/main' into addon-i18n

This commit is contained in:
月月
2025-06-21 09:10:40 +08:00
71 changed files with 2040 additions and 671 deletions
+29 -15
View File
@@ -1,7 +1,7 @@
import "root:/"
import "root:/modules/common"
import "root:/modules/common/widgets"
import "root:/services"
import "root:/modules/common/"
import "root:/modules/common/widgets"
import "root:/modules/common/functions/color_utils.js" as ColorUtils
import QtQuick
import QtQuick.Controls
@@ -240,25 +240,36 @@ Scope {
VerticalBarSeparator {visible: ConfigOptions?.bar.borderless}
BarGroup {
MouseArea {
id: rightCenterGroup
implicitWidth: rightCenterGroupContent.implicitWidth
implicitHeight: rightCenterGroupContent.implicitHeight
Layout.preferredWidth: barRoot.centerSideModuleWidth
Layout.fillHeight: true
ClockWidget {
showDate: (ConfigOptions.bar.verbose && barRoot.useShortenedForm < 2)
Layout.alignment: Qt.AlignVCenter
Layout.fillWidth: true
onPressed: {
Hyprland.dispatch('global quickshell:sidebarRightToggle')
}
UtilButtons {
visible: (ConfigOptions.bar.verbose && barRoot.useShortenedForm === 0)
Layout.alignment: Qt.AlignVCenter
}
BarGroup {
id: rightCenterGroupContent
anchors.fill: parent
ClockWidget {
showDate: (ConfigOptions.bar.verbose && barRoot.useShortenedForm < 2)
Layout.alignment: Qt.AlignVCenter
Layout.fillWidth: true
}
BatteryIndicator {
visible: (barRoot.useShortenedForm < 2 && UPower.displayDevice.isLaptopBattery)
Layout.alignment: Qt.AlignVCenter
UtilButtons {
visible: (ConfigOptions.bar.verbose && barRoot.useShortenedForm === 0)
Layout.alignment: Qt.AlignVCenter
}
BatteryIndicator {
visible: (barRoot.useShortenedForm < 2 && UPower.displayDevice.isLaptopBattery)
Layout.alignment: Qt.AlignVCenter
}
}
}
@@ -445,6 +456,7 @@ Scope {
bottom: ConfigOptions.bar.bottom ? barContent.top : undefined
}
height: Appearance.rounding.screenRounding
visible: showBarBackground
RoundCorner {
anchors.top: parent.top
@@ -452,6 +464,7 @@ Scope {
size: Appearance.rounding.screenRounding
corner: ConfigOptions.bar.bottom ? cornerEnum.bottomLeft : cornerEnum.topLeft
color: showBarBackground ? Appearance.colors.colLayer0 : "transparent"
opacity: 1.0 - Appearance.transparency
}
RoundCorner {
anchors.top: parent.top
@@ -459,6 +472,7 @@ Scope {
size: Appearance.rounding.screenRounding
corner: ConfigOptions.bar.bottom ? cornerEnum.bottomRight : cornerEnum.topRight
color: showBarBackground ? Appearance.colors.colLayer0 : "transparent"
opacity: 1.0 - Appearance.transparency
}
}
@@ -16,8 +16,8 @@ Singleton {
property string syntaxHighlightingTheme
// Extremely conservative transparency values for consistency and readability
property real transparency: ConfigOptions?.appearance.transparency ? (m3colors.darkmode ? 0.1 : 0) : 0
property real contentTransparency: ConfigOptions?.appearance.transparency ? (m3colors.darkmode ? 0.55 : 0) : 0
property real transparency: ConfigOptions?.appearance.transparency ? (m3colors.darkmode ? 0.1 : 0.07) : 0
property real contentTransparency: ConfigOptions?.appearance.transparency ? (m3colors.darkmode ? 0.55 : 0.55) : 0
m3colors: QtObject {
property bool darkmode: false
@@ -126,10 +126,11 @@ Singleton {
property color colPrimaryContainer: m3colors.m3primaryContainer
property color colPrimaryContainerHover: ColorUtils.mix(colors.colPrimaryContainer, colLayer1Hover, 0.7)
property color colPrimaryContainerActive: ColorUtils.mix(colors.colPrimaryContainer, colLayer1Active, 0.6)
property color colOnPrimaryContainer: m3colors.m3onPrimaryContainer
property color colSecondary: m3colors.m3secondary
property color colSecondaryHover: ColorUtils.mix(m3colors.m3secondary, colLayer1Hover, 0.85)
property color colSecondaryActive: ColorUtils.mix(m3colors.m3secondary, colLayer1Active, 0.4)
property color colSecondaryContainer: ColorUtils.transparentize(m3colors.m3secondaryContainer, root.contentTransparency)
property color colSecondaryContainer: m3colors.m3secondaryContainer
property color colSecondaryContainerHover: ColorUtils.mix(m3colors.m3secondaryContainer, colLayer1Hover, 0.6)
property color colSecondaryContainerActive: ColorUtils.mix(m3colors.m3secondaryContainer, colLayer1Active, 0.54)
property color colOnSecondaryContainer: m3colors.m3onSecondaryContainer
@@ -177,7 +178,7 @@ Singleton {
property int larger: 19
property int huge: 22
property int hugeass: 23
property int title: 28
property int title: huge
}
}
@@ -187,11 +188,17 @@ Singleton {
readonly property list<real> expressiveSlowSpatial: [0.39, 1.29, 0.35, 0.98, 1, 1] // Default, 650ms
readonly property list<real> expressiveEffects: [0.34, 0.80, 0.34, 1.00, 1, 1] // Default, 200ms
readonly property list<real> emphasized: [0.05, 0, 2 / 15, 0.06, 1 / 6, 0.4, 5 / 24, 0.82, 0.25, 1, 1, 1]
readonly property list<real> emphasizedFirstHalf: [0.05, 0, 2 / 15, 0.06, 1 / 6, 0.4, 5 / 24, 0.82]
readonly property list<real> emphasizedLastHalf: [5 / 24, 0.82, 0.25, 1, 1, 1]
readonly property list<real> emphasizedAccel: [0.3, 0, 0.8, 0.15, 1, 1]
readonly property list<real> emphasizedDecel: [0.05, 0.7, 0.1, 1, 1, 1]
readonly property list<real> standard: [0.2, 0, 0, 1, 1, 1]
readonly property list<real> standardAccel: [0.3, 0, 1, 1, 1, 1]
readonly property list<real> standardDecel: [0, 0, 0, 1, 1, 1]
readonly property real expressiveFastSpatialDuration: 350
readonly property real expressiveDefaultSpatialDuration: 500
readonly property real expressiveSlowSpatialDuration: 650
readonly property real expressiveEffectsDuration: 200
}
animation: QtObject {
@@ -31,10 +31,8 @@ Singleton {
property QtObject apps: QtObject {
property string bluetooth: "kcmshell6 kcm_bluetooth"
property string imageViewer: "loupe"
property string network: "plasmawindowed org.kde.plasma.networkmanagement"
property string networkEthernet: "kcmshell6 kcm_networkmanagement"
property string settings: "systemsettings"
property string taskManager: "plasma-systemmonitor --page-name Processes"
property string terminal: "kitty -1" // This is only for shell actions
}
@@ -157,6 +155,7 @@ Singleton {
property QtObject windows: QtObject {
property bool showTitlebar: true // Client-side decoration for shell apps
property bool centerTitle: true
}
property QtObject hacks: QtObject {
@@ -32,12 +32,12 @@ Singleton {
property string wallpaperSwitchScriptPath: FileUtils.trimFileProtocol(`${Directories.config}/quickshell/scripts/colors/switchwall.sh`)
// Cleanup on init
Component.onCompleted: {
Hyprland.dispatch(`exec mkdir -p '${shellConfig}'`)
Hyprland.dispatch(`exec mkdir -p '${favicons}'`)
Hyprland.dispatch(`exec rm -rf '${coverArt}'; mkdir -p '${coverArt}'`)
Hyprland.dispatch(`exec rm -rf '${booruPreviews}'; mkdir -p '${booruPreviews}'`)
Hyprland.dispatch(`exec mkdir -p '${booruDownloads}' && mkdir -p '${booruDownloadsNsfw}'`)
Hyprland.dispatch(`exec rm -rf '${latexOutput}'; mkdir -p '${latexOutput}'`)
Hyprland.dispatch(`exec rm -rf '${cliphistDecode}'; mkdir -p '${cliphistDecode}'`)
Quickshell.execDetached(["bash", "-c", `mkdir -p '${shellConfig}'`])
Quickshell.execDetached(["bash", "-c", `mkdir -p '${favicons}'`])
Quickshell.execDetached(["bash", "-c", `rm -rf '${coverArt}'; mkdir -p '${coverArt}'`])
Quickshell.execDetached(["bash", "-c", `rm -rf '${booruPreviews}'; mkdir -p '${booruPreviews}'`])
Quickshell.execDetached(["bash", "-c", `mkdir -p '${booruDownloads}' && mkdir -p '${booruDownloadsNsfw}'`])
Quickshell.execDetached(["bash", "-c", `rm -rf '${latexOutput}'; mkdir -p '${latexOutput}'`])
Quickshell.execDetached(["bash", "-c", `rm -rf '${cliphistDecode}'; mkdir -p '${cliphistDecode}'`])
}
}
@@ -8,6 +8,7 @@ import Qt.labs.platform
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import Quickshell.Widgets
import Quickshell.Hyprland
@@ -71,7 +72,7 @@ Rectangle {
}
Component.onDestruction: {
Hyprland.dispatch(`exec bash -c "[ -f '${imageDecodeFilePath}' ] && rm -f '${imageDecodeFilePath}'"`)
Quickshell.execDetached(["bash", "-c", `[ -f '${imageDecodeFilePath}' ] && rm -f '${imageDecodeFilePath}'`])
}
Image {
@@ -0,0 +1,8 @@
import QtQuick
import QtQuick.Layouts
RowLayout {
property bool uniform: false
spacing: 10
uniformCellSizes: uniform
}
@@ -0,0 +1,48 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import Quickshell.Hyprland
import "root:/services/"
import "root:/modules/common/"
import "root:/modules/common/widgets/"
import "root:/modules/common/functions/color_utils.js" as ColorUtils
import "root:/modules/common/functions/file_utils.js" as FileUtils
Flow {
id: root
Layout.fillWidth: true
spacing: 2
property list<var> options: []
property string configOptionName: ""
property var currentValue: null
signal selected(var newValue)
Repeater {
model: root.options
delegate: SelectionGroupButton {
id: paletteButton
required property var modelData
required property int index
onYChanged: {
if (index === 0) {
paletteButton.leftmost = true
} else {
var prev = root.children[index - 1]
var thisIsOnNewLine = prev && prev.y !== paletteButton.y
paletteButton.leftmost = thisIsOnNewLine
prev.rightmost = thisIsOnNewLine
}
}
leftmost: index === 0
rightmost: index === root.options.length - 1
buttonText: modelData.displayName;
toggled: root.currentValue === modelData.value
onClicked: {
root.selected(modelData.value);
}
}
}
}
@@ -0,0 +1,31 @@
import "root:/modules/common/widgets/"
import "root:/modules/common/"
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
RippleButton {
id: root
Layout.fillWidth: true
implicitHeight: contentItem.implicitHeight + 8 * 2
contentItem: RowLayout {
spacing: 10
StyledText {
id: labelWidget
Layout.fillWidth: true
text: root.text
font.pixelSize: Appearance.font.pixelSize.small
color: Appearance.colors.colOnSecondaryContainer
}
StyledSwitch {
id: switchWidget
down: root.down
scale: 0.6
Layout.fillWidth: false
checked: root.checked
onClicked: root.clicked()
}
}
}
@@ -0,0 +1,29 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import "root:/modules/common/"
import "root:/modules/common/widgets/"
Flickable {
id: root
property real baseWidth: 500
property bool forceWidth: false
property real bottomContentPadding: 100
default property alias data: contentColumn.data
clip: true
contentHeight: contentColumn.implicitHeight + root.bottomContentPadding // Add some padding at the bottom
implicitWidth: contentColumn.implicitWidth
ColumnLayout {
id: contentColumn
width: root.forceWidth ? root.baseWidth : Math.max(root.baseWidth, implicitWidth)
anchors {
top: parent.top
horizontalCenter: parent.horizontalCenter
margins: 10
}
spacing: 20
}
}
@@ -0,0 +1,22 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import "root:/modules/common/"
import "root:/modules/common/widgets/"
ColumnLayout {
id: root
property string title
default property alias data: sectionContent.data
Layout.fillWidth: true
spacing: 8
StyledText {
text: root.title
font.pixelSize: Appearance.font.pixelSize.larger
}
ColumnLayout {
id: sectionContent
spacing: 4
}
}
@@ -0,0 +1,60 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import "root:/modules/common/"
import "root:/modules/common/widgets/"
/**
* Material 3 FAB.
*/
RippleButton {
id: root
property string iconText: "add"
property bool expanded: false
property real baseSize: 56
property real elementSpacing: 5
implicitWidth: Math.max(contentRowLayout.implicitWidth + 10 * 2, baseSize)
implicitHeight: baseSize
buttonRadius: Appearance.rounding.small
colBackground: Appearance.colors.colPrimaryContainer
colBackgroundHover: Appearance.colors.colPrimaryContainerHover
colRipple: Appearance.colors.colPrimaryContainerActive
contentItem: RowLayout {
id: contentRowLayout
property real horizontalMargins: (root.baseSize - icon.width) / 2
anchors {
verticalCenter: parent?.verticalCenter
left: parent?.left
leftMargin: contentRowLayout.horizontalMargins
}
spacing: 0
MaterialSymbol {
id: icon
Layout.fillWidth: true
horizontalAlignment: Text.AlignHCenter
iconSize: 24
color: Appearance.colors.colOnPrimaryContainer
text: root.iconText
}
Loader {
active: true
sourceComponent: Revealer {
visible: root.expanded || implicitWidth > 0
reveal: root.expanded
implicitWidth: reveal ? (buttonText.implicitWidth + root.elementSpacing + contentRowLayout.horizontalMargins) : 0
StyledText {
id: buttonText
anchors {
left: parent.left
leftMargin: root.elementSpacing
}
text: root.buttonText
color: Appearance.colors.colOnPrimaryContainer
font.pixelSize: 14
font.weight: 450
}
}
}
}
}
@@ -15,7 +15,7 @@ Rectangle {
property real extraBottomBorderWidth: 2
property color borderColor: Appearance.colors.colOnLayer0
property real borderRadius: 5
property color keyColor: Appearance.colors.colSurfaceContainerLow
property color keyColor: Appearance.m3colors.m3surfaceContainerLow
implicitWidth: keyFace.implicitWidth + borderWidth * 2
implicitHeight: keyFace.implicitHeight + borderWidth * 2 + extraBottomBorderWidth
radius: borderRadius
@@ -0,0 +1,124 @@
import "root:/modules/common"
import "root:/modules/common/widgets"
import "root:/modules/common/functions/color_utils.js" as ColorUtils
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import Quickshell.Hyprland
GroupButton {
id: lightDarkButtonRoot
required property bool dark
property color previewBg: dark ? ColorUtils.colorWithHueOf("#3f3838", Appearance.m3colors.m3primary) :
ColorUtils.colorWithHueOf("#F7F9FF", Appearance.m3colors.m3primary)
property color previewFg: dark ? Qt.lighter(previewBg, 2.2) : ColorUtils.mix(previewBg, "#292929", 0.85)
padding: 5
Layout.fillWidth: true
colBackground: Appearance.colors.colLayer2
toggled: Appearance.m3colors.darkmode === dark
onClicked: {
Quickshell.execDetached(["bash", "-c", `${Directories.wallpaperSwitchScriptPath} --mode ${dark ? "dark" : "light"} --noswitch`])
}
contentItem: Item {
anchors.centerIn: parent
implicitWidth: buttonContentLayout.implicitWidth
implicitHeight: buttonContentLayout.implicitHeight
ColumnLayout {
id: buttonContentLayout
anchors.centerIn: parent
Rectangle {
Layout.alignment: Qt.AlignHCenter
implicitWidth: 250
implicitHeight: skeletonColumnLayout.implicitHeight + 10 * 2
radius: lightDarkButtonRoot.buttonRadius - lightDarkButtonRoot.padding
color: lightDarkButtonRoot.previewBg
border {
width: 1
color: Appearance.m3colors.m3outlineVariant
}
// Some skeleton items
ColumnLayout {
id: skeletonColumnLayout
anchors.fill: parent
anchors.margins: 10
spacing: 10
RowLayout {
Rectangle {
radius: Appearance.rounding.full
color: lightDarkButtonRoot.previewFg
implicitWidth: 50
implicitHeight: 50
}
ColumnLayout {
spacing: 4
Rectangle {
radius: Appearance.rounding.unsharpenmore
color: lightDarkButtonRoot.previewFg
Layout.fillWidth: true
implicitHeight: 22
}
Rectangle {
radius: Appearance.rounding.unsharpenmore
color: lightDarkButtonRoot.previewFg
Layout.fillWidth: true
Layout.rightMargin: 45
implicitHeight: 18
}
}
}
StyledProgressBar {
Layout.topMargin: 5
Layout.bottomMargin: 5
Layout.fillWidth: true
value: 0.7
sperm: true
animateSperm: lightDarkButtonRoot.toggled
highlightColor: lightDarkButtonRoot.toggled ? Appearance.m3colors.m3primary : lightDarkButtonRoot.previewFg
trackColor: ColorUtils.mix(lightDarkButtonRoot.previewBg, lightDarkButtonRoot.previewFg, 0.5)
}
RowLayout {
spacing: 2
Rectangle {
radius: Appearance.rounding.full
color: lightDarkButtonRoot.toggled ? Appearance.m3colors.m3primary : lightDarkButtonRoot.previewFg
Layout.fillWidth: true
implicitHeight: 30
MaterialSymbol {
visible: lightDarkButtonRoot.toggled
anchors.centerIn: parent
horizontalAlignment: Text.AlignHCenter
text: "check"
iconSize: 20
color: lightDarkButtonRoot.toggled ? Appearance.m3colors.m3onPrimary : lightDarkButtonRoot.previewBg
}
}
Rectangle {
radius: Appearance.rounding.unsharpenmore
color: lightDarkButtonRoot.toggled ? Appearance.m3colors.m3secondaryContainer : lightDarkButtonRoot.previewFg
Layout.fillWidth: true
implicitHeight: 30
}
Rectangle {
topLeftRadius: Appearance.rounding.unsharpenmore
bottomLeftRadius: Appearance.rounding.unsharpenmore
topRightRadius: Appearance.rounding.full
bottomRightRadius: Appearance.rounding.full
color: lightDarkButtonRoot.toggled ? Appearance.m3colors.m3secondaryContainer : lightDarkButtonRoot.previewFg
Layout.fillWidth: true
implicitHeight: 30
}
}
}
}
StyledText {
Layout.fillWidth: true
text: dark ? "Dark" : "Light"
color: lightDarkButtonRoot.toggled ? Appearance.m3colors.m3onPrimary : Appearance.colors.colOnLayer2
horizontalAlignment: Text.AlignHCenter
}
}
}
}
@@ -12,6 +12,7 @@ Text {
hintingPreference: Font.PreferFullHinting
family: Appearance?.font.family.iconMaterial ?? "Material Symbols Rounded"
pixelSize: iconSize
weight: Font.Normal + (Font.DemiBold - Font.Normal) * fill
}
verticalAlignment: Text.AlignVCenter
color: Appearance.m3colors.m3onBackground
@@ -1,60 +0,0 @@
import "root:/modules/common"
import "root:/modules/common/widgets"
import "root:/modules/common/functions/color_utils.js" as ColorUtils
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell.Io
Button {
id: button
property bool toggled
property string buttonIcon
property string buttonText
Layout.alignment: Qt.AlignHCenter
implicitHeight: columnLayout.implicitHeight
implicitWidth: columnLayout.implicitWidth
background: null
PointingHandInteraction {}
// Real stuff
ColumnLayout {
id: columnLayout
spacing: 5
Rectangle {
width: 62
implicitHeight: navRailButtonIcon.height + 2 * 2
Layout.alignment: Qt.AlignHCenter
radius: Appearance.rounding.full
color: toggled ?
(button.down ? Appearance.colors.colSecondaryContainerActive : button.hovered ? Appearance.colors.colSecondaryContainerHover : Appearance.colors.colSecondaryContainer) :
(button.down ? Appearance.colors.colLayer1Active : button.hovered ? Appearance.colors.colLayer1Hover : ColorUtils.transparentize(Appearance.colors.colLayer1Hover, 1))
Behavior on color {
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
}
MaterialSymbol {
id: navRailButtonIcon
anchors.centerIn: parent
iconSize: Appearance.font.pixelSize.hugeass
fill: toggled ? 1 : 0
text: buttonIcon
color: toggled ? Appearance.m3colors.m3onSecondaryContainer : Appearance.colors.colOnLayer1
Behavior on color {
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
}
}
}
StyledText {
Layout.alignment: Qt.AlignHCenter
text: buttonText
color: Appearance.colors.colOnLayer1
}
}
}
@@ -0,0 +1,12 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import "root:/modules/common/"
import "root:/modules/common/widgets/"
ColumnLayout { // Window content with navigation rail and content pane
id: root
property bool expanded: true
property int currentIndex: 0
spacing: 5
}
@@ -0,0 +1,149 @@
import "root:/modules/common"
import "root:/modules/common/widgets"
import "root:/modules/common/functions/color_utils.js" as ColorUtils
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell.Io
TabButton {
id: root
property bool toggled: TabBar.tabBar.currentIndex === TabBar.index
property string buttonIcon
property string buttonText
property bool expanded: false
property bool showToggledHighlight: true
readonly property real visualWidth: root.expanded ? root.baseSize + 20 + itemText.implicitWidth : root.baseSize
property real baseSize: 56
property real baseHighlightHeight: 32
property real highlightCollapsedTopMargin: 8
padding: 0
// The navigation items target area always spans the full width of the
// nav rail, even if the item container hugs its contents.
Layout.fillWidth: true
// implicitWidth: contentItem.implicitWidth
implicitHeight: baseSize
background: null
PointingHandInteraction {}
// Real stuff
contentItem: Item {
id: buttonContent
anchors {
top: parent.top
bottom: parent.bottom
left: parent.left
right: undefined
}
implicitWidth: root.visualWidth
implicitHeight: root.expanded ? itemIconBackground.implicitHeight : itemIconBackground.implicitHeight + itemText.implicitHeight
Rectangle {
id: itemBackground
anchors.top: itemIconBackground.top
anchors.left: itemIconBackground.left
anchors.bottom: itemIconBackground.bottom
implicitWidth: root.visualWidth
radius: Appearance.rounding.full
color: toggled ?
root.showToggledHighlight ?
(root.down ? Appearance.colors.colSecondaryContainerActive : root.hovered ? Appearance.colors.colSecondaryContainerHover : Appearance.colors.colSecondaryContainer)
: ColorUtils.transparentize(Appearance.colors.colSecondaryContainer) :
(root.down ? Appearance.colors.colLayer1Active : root.hovered ? Appearance.colors.colLayer1Hover : ColorUtils.transparentize(Appearance.colors.colLayer1Hover, 1))
states: State {
name: "expanded"
when: root.expanded
AnchorChanges {
target: itemBackground
anchors.top: buttonContent.top
anchors.left: buttonContent.left
anchors.bottom: buttonContent.bottom
}
PropertyChanges {
target: itemBackground
implicitWidth: root.visualWidth
}
}
transitions: Transition {
AnchorAnimation {
duration: Appearance.animation.elementMoveFast.duration
easing.type: Appearance.animation.elementMoveFast.type
easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve
}
PropertyAnimation {
target: itemBackground
property: "implicitWidth"
duration: Appearance.animation.elementMove.duration
easing.type: Appearance.animation.elementMove.type
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
}
}
Behavior on color {
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
}
}
Item {
id: itemIconBackground
implicitWidth: root.baseSize
implicitHeight: root.baseHighlightHeight
anchors {
left: parent.left
verticalCenter: parent.verticalCenter
}
MaterialSymbol {
id: navRailButtonIcon
anchors.centerIn: parent
iconSize: 24
fill: toggled ? 1 : 0
font.weight: (toggled || root.hovered) ? Font.DemiBold : Font.Normal
text: buttonIcon
color: toggled ? Appearance.m3colors.m3onSecondaryContainer : Appearance.colors.colOnLayer1
Behavior on color {
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
}
}
}
StyledText {
id: itemText
anchors {
top: itemIconBackground.bottom
topMargin: 2
horizontalCenter: itemIconBackground.horizontalCenter
}
states: State {
name: "expanded"
when: root.expanded
AnchorChanges {
target: itemText
anchors {
top: undefined
horizontalCenter: undefined
left: itemIconBackground.right
verticalCenter: itemIconBackground.verticalCenter
}
}
}
transitions: Transition {
AnchorAnimation {
duration: Appearance.animation.elementMoveFast.duration
easing.type: Appearance.animation.elementMoveFast.type
easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve
}
}
text: buttonText
font.pixelSize: 14
color: Appearance.colors.colOnLayer1
}
}
}
@@ -0,0 +1,25 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import "root:/modules/common/"
import "root:/modules/common/widgets/"
RippleButton {
id: root
Layout.alignment: Qt.AlignLeft
implicitWidth: 40
implicitHeight: 40
Layout.leftMargin: 8
onClicked: {
parent.expanded = !parent.expanded;
}
buttonRadius: Appearance.rounding.full
contentItem: MaterialSymbol {
id: icon
anchors.centerIn: parent
horizontalAlignment: Text.AlignHCenter
iconSize: 24
color: Appearance.colors.colOnLayer1
text: root.parent.expanded ? "menu_open" : "menu"
}
}
@@ -0,0 +1,44 @@
import "root:/modules/common"
import "root:/modules/common/widgets"
import "root:/modules/common/functions/color_utils.js" as ColorUtils
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell.Io
Item {
id: root
property int currentIndex: 0
property bool expanded: false
default property alias data: tabBarColumn.data
implicitHeight: tabBarColumn.implicitHeight
implicitWidth: tabBarColumn.implicitWidth
Layout.topMargin: 25
Rectangle {
property real itemHeight: tabBarColumn.children[0].baseSize
property real baseHighlightHeight: tabBarColumn.children[0].baseHighlightHeight
anchors {
top: tabBarColumn.top
left: tabBarColumn.left
topMargin: itemHeight * root.currentIndex + (root.expanded ? 0 : ((itemHeight - baseHighlightHeight) / 2))
}
radius: Appearance.rounding.full
color: Appearance.colors.colSecondaryContainer
implicitHeight: root.expanded ? itemHeight : baseHighlightHeight
implicitWidth: tabBarColumn.children[root.currentIndex].visualWidth
Behavior on anchors.topMargin {
NumberAnimation {
duration: Appearance.animationCurves.expressiveFastSpatialDuration
easing.type: Appearance.animation.elementMove.type
easing.bezierCurve: Appearance.animationCurves.expressiveFastSpatial
}
}
}
ColumnLayout {
id: tabBarColumn
anchors.fill: parent
spacing: 0
}
}
@@ -94,12 +94,6 @@ Item { // Notification item area
}
}
onPressAndHold: (mouse) => {
if (mouse.button === Qt.LeftButton) {
Hyprland.dispatch(`exec wl-copy '${StringUtils.shellSingleQuoteEscape(notificationObject.body)}'`)
notificationSummaryText.text = String.format(Translation.tr("{0} (copied)"), notificationObject.summary)
}
}
onDraggingChanged: () => {
if (dragging) {
root.qmlParent.dragIndex = root.index ?? root.parent.children.indexOf(root);
@@ -226,12 +220,8 @@ Item { // Notification item area
Qt.openUrlExternally(link)
Hyprland.dispatch("global quickshell:sidebarRightClose")
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.NoButton // Only for hover
hoverEnabled: true
cursorShape: parent.hoveredLink !== "" ? Qt.PointingHandCursor : Qt.ArrowCursor
}
PointingHandLinkHover {}
}
Flickable { // Notification actions
@@ -295,7 +285,7 @@ Item { // Notification item area
(contentItem.implicitWidth + leftPadding + rightPadding)
onClicked: {
Hyprland.dispatch(`exec wl-copy '${StringUtils.shellSingleQuoteEscape(notificationObject.body)}'`)
Quickshell.clipboardText = notificationObject.body
copyIcon.text = "inventory"
copyIconTimer.restart()
}
@@ -0,0 +1,8 @@
import QtQuick
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.NoButton // Only for hover
hoverEnabled: true
cursorShape: parent.hoveredLink !== "" ? Qt.PointingHandCursor : Qt.ArrowCursor
}
@@ -13,7 +13,7 @@ Item {
implicitWidth: (reveal || vertical) ? childrenRect.width : 0
implicitHeight: (reveal || !vertical) ? childrenRect.height : 0
visible: reveal && width > 0 && height > 0
visible: reveal || (width > 0 && height > 0)
Behavior on implicitWidth {
enabled: !vertical
@@ -0,0 +1,56 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import "root:/modules/common/"
import "root:/modules/common/widgets/"
RippleButton {
id: buttonWithIconRoot
property string nerdIcon
property string materialIcon
property bool materialIconFill: true
property string mainText: "Button text"
property Component mainContentComponent: Component {
StyledText {
text: buttonWithIconRoot.mainText
font.pixelSize: Appearance.font.pixelSize.small
color: Appearance.colors.colOnSecondaryContainer
}
}
implicitHeight: 35
horizontalPadding: 15
buttonRadius: Appearance.rounding.small
colBackground: Appearance.colors.colLayer2
contentItem: RowLayout {
Item {
implicitWidth: Math.max(materialIconLoader.implicitWidth, nerdIconLoader.implicitWidth)
Loader {
id: materialIconLoader
anchors.centerIn: parent
active: !nerdIcon
sourceComponent: MaterialSymbol {
text: buttonWithIconRoot.materialIcon
iconSize: Appearance.font.pixelSize.larger
color: Appearance.colors.colOnSecondaryContainer
fill: buttonWithIconRoot.materialIconFill ? 1 : 0
}
}
Loader {
id: nerdIconLoader
anchors.centerIn: parent
active: nerdIcon
sourceComponent: StyledText {
text: buttonWithIconRoot.nerdIcon
font.pixelSize: Appearance.font.pixelSize.larger
font.family: Appearance.font.family.iconNerd
color: Appearance.colors.colOnSecondaryContainer
}
}
}
Loader {
sourceComponent: buttonWithIconRoot.mainContentComponent
Layout.alignment: Qt.AlignVCenter
}
}
}
@@ -0,0 +1,24 @@
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import Quickshell.Hyprland
import "root:/services/"
import "root:/modules/common/"
import "root:/modules/common/widgets/"
GroupButton {
id: root
horizontalPadding: 12
verticalPadding: 8
bounce: false
property bool leftmost: false
property bool rightmost: false
leftRadius: (toggled || leftmost) ? (height / 2) : Appearance.rounding.unsharpenmore
rightRadius: (toggled || rightmost) ? (height / 2) : Appearance.rounding.unsharpenmore
colBackground: Appearance.colors.colSecondaryContainer
contentItem: StyledText {
color: parent.toggled ? Appearance.colors.colOnPrimary : Appearance.colors.colOnSecondaryContainer
text: root.buttonText
}
}
@@ -41,8 +41,7 @@ ProgressBar {
}
contentItem: Item {
implicitWidth: parent.width
implicitHeight: parent.height
anchors.fill: parent
Canvas {
id: wavyFill
@@ -6,7 +6,8 @@ RectangularShadow {
required property var target
anchors.fill: target
radius: target.radius
blur: 1.2 * Appearance.sizes.elevationMargin
blur: 0.9 * Appearance.sizes.elevationMargin
offset: Qt.vector2d(0.0, 1.0)
spread: 1
color: Appearance.colors.colShadow
cached: true
@@ -36,13 +36,13 @@ Switch {
// Custom thumb styling
indicator: Rectangle {
width: root.pressed ? (28 * root.scale) : root.checked ? (24 * root.scale) : (16 * root.scale)
height: root.pressed ? (28 * root.scale) : root.checked ? (24 * root.scale) : (16 * root.scale)
width: (root.pressed || root.down) ? (28 * root.scale) : root.checked ? (24 * root.scale) : (16 * root.scale)
height: (root.pressed || root.down) ? (28 * root.scale) : root.checked ? (24 * root.scale) : (16 * root.scale)
radius: Appearance.rounding.full
color: root.checked ? Appearance.m3colors.m3onPrimary : Appearance.m3colors.m3outline
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: root.checked ? (root.pressed ? (22 * root.scale) : 24 * root.scale) : (root.pressed ? (2 * root.scale) : 8 * root.scale)
anchors.leftMargin: root.checked ? ((root.pressed || root.down) ? (22 * root.scale) : 24 * root.scale) : ((root.pressed || root.down) ? (2 * root.scale) : 8 * root.scale)
Behavior on anchors.leftMargin {
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
@@ -2,6 +2,9 @@ import "root:/modules/common"
import QtQuick
import QtQuick.Controls
/**
* Does not include visual layout, but includes the easily neglected colors.
*/
TextArea {
renderType: Text.NativeRendering
selectedTextColor: Appearance.m3colors.m3onSecondaryContainer
@@ -54,7 +54,9 @@ Scope {
for (let j = i + 1; j < players.length; ++j) {
let p2 = players[j];
if (p1.trackTitle && p2.trackTitle &&
(p1.trackTitle.includes(p2.trackTitle) || p2.trackTitle.includes(p1.trackTitle))) {
(p1.trackTitle.includes(p2.trackTitle)
|| p2.trackTitle.includes(p1.trackTitle))
|| (p1.position - p2.position <= 2 && p1.length - p2.length <= 2)) {
group.push(j);
}
}
@@ -111,7 +111,6 @@ Item {
acceptedButtons: Qt.LeftButton
onClicked: {
if (root.draggingTargetWorkspace === -1) {
// Hyprland.dispatch(`exec qs ipc call overview close`)
GlobalStates.overviewOpen = false
Hyprland.dispatch(`workspace ${workspaceValue}`)
}
@@ -303,7 +303,9 @@ Item { // Wrapper
clickActionName: "",
type: `#${entry.match(/^\s*(\S+)/)?.[1] || ""}`,
execute: () => {
Hyprland.dispatch(`exec echo '${StringUtils.shellSingleQuoteEscape(entry)}' | cliphist decode | wl-copy`);
Quickshell.execDetached(
["bash", "-c", `echo '${StringUtils.shellSingleQuoteEscape(entry)}' | cliphist decode | wl-copy`]
);
}
};
}).filter(Boolean);
@@ -318,7 +320,7 @@ Item { // Wrapper
clickActionName: "",
type: "Emoji",
execute: () => {
Hyprland.dispatch(`exec wl-copy '${StringUtils.shellSingleQuoteEscape(entry.match(/^\s*(\S+)/)?.[1])}'`);
Quickshell.clipboardText = entry.match(/^\s*(\S+)/)?.[1]
}
};
}).filter(Boolean);
@@ -334,7 +336,7 @@ Item { // Wrapper
fontType: "monospace",
materialSymbol: 'calculate',
execute: () => {
Hyprland.dispatch(`exec wl-copy '${StringUtils.shellSingleQuoteEscape(root.mathResult)}'`)
Quickshell.clipboardText = root.mathResult;
}
}
const commandResultObject = {
@@ -122,7 +122,7 @@ Scope {
id: sessionTaskManager
buttonIcon: "browse_activity"
buttonText: Translation.tr("Task Manager")
onClicked: { Hyprland.dispatch(`exec ${ConfigOptions.apps.taskManager}`); sessionRoot.hide() }
onClicked: { Quickshell.execDetached(["bash", "-c", `${ConfigOptions.apps.taskManager}`]); sessionRoot.hide() }
onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText }
KeyNavigation.left: sessionLogout
KeyNavigation.down: sessionFirmwareReboot
@@ -132,7 +132,7 @@ Scope {
id: sessionHibernate
buttonIcon: "downloading"
buttonText: Translation.tr("Hibernate")
onClicked: { Hyprland.dispatch("exec systemctl hibernate || loginctl hibernate"); sessionRoot.hide() }
onClicked: { Quickshell.execDetached(["bash", "-c", `systemctl hibernate || loginctl hibernate`]); sessionRoot.hide() }
onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText }
KeyNavigation.up: sessionLock
KeyNavigation.right: sessionShutdown
@@ -141,7 +141,7 @@ Scope {
id: sessionShutdown
buttonIcon: "power_settings_new"
buttonText: Translation.tr("Shutdown")
onClicked: { Hyprland.dispatch("exec systemctl poweroff || loginctl poweroff"); sessionRoot.hide() }
onClicked: { Quickshell.execDetached(["bash", "-c", `systemctl poweroff || loginctl poweroff`]); sessionRoot.hide() }
onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText }
KeyNavigation.left: sessionHibernate
KeyNavigation.right: sessionReboot
@@ -151,7 +151,7 @@ Scope {
id: sessionReboot
buttonIcon: "restart_alt"
buttonText: Translation.tr("Reboot")
onClicked: { Hyprland.dispatch("exec reboot || loginctl reboot"); sessionRoot.hide() }
onClicked: { Quickshell.execDetached(["bash", "-c", `reboot || loginctl reboot`]); sessionRoot.hide() }
onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText }
KeyNavigation.left: sessionShutdown
KeyNavigation.right: sessionFirmwareReboot
@@ -161,7 +161,7 @@ Scope {
id: sessionFirmwareReboot
buttonIcon: "settings_applications"
buttonText: Translation.tr("Reboot to firmware settings")
onClicked: { Hyprland.dispatch("exec systemctl reboot --firmware-setup || loginctl reboot --firmware-setup"); sessionRoot.hide() }
onClicked: { Quickshell.execDetached(["bash", "-c", `systemctl reboot --firmware-setup || loginctl reboot --firmware-setup`]); sessionRoot.hide() }
onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText }
KeyNavigation.up: sessionTaskManager
KeyNavigation.left: sessionReboot
@@ -0,0 +1,151 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import Quickshell.Widgets
import "root:/services/"
import "root:/modules/common/"
import "root:/modules/common/widgets/"
ContentPage {
forceWidth: true
ContentSection {
title: "Distro"
RowLayout {
Layout.alignment: Qt.AlignHCenter
spacing: 20
Layout.topMargin: 10
Layout.bottomMargin: 10
IconImage {
implicitSize: 100
source: Quickshell.iconPath(SystemInfo.logo)
}
ColumnLayout {
Layout.alignment: Qt.AlignVCenter
// spacing: 10
StyledText {
text: SystemInfo.distroName
font.pixelSize: Appearance.font.pixelSize.title
}
StyledText {
font.pixelSize: Appearance.font.pixelSize.normal
text: SystemInfo.homeUrl
textFormat: Text.MarkdownText
onLinkActivated: (link) => {
Qt.openUrlExternally(link)
}
PointingHandLinkHover {}
}
}
}
Flow {
Layout.fillWidth: true
spacing: 5
RippleButtonWithIcon {
materialIcon: "auto_stories"
mainText: "Documentation"
onClicked: {
Qt.openUrlExternally(SystemInfo.documentationUrl)
}
}
RippleButtonWithIcon {
materialIcon: "support"
mainText: "Help & Support"
onClicked: {
Qt.openUrlExternally(SystemInfo.supportUrl)
}
}
RippleButtonWithIcon {
materialIcon: "bug_report"
mainText: "Report a Bug"
onClicked: {
Qt.openUrlExternally(SystemInfo.bugReportUrl)
}
}
RippleButtonWithIcon {
materialIcon: "policy"
materialIconFill: false
mainText: "Privacy Policy"
onClicked: {
Qt.openUrlExternally(SystemInfo.privacyPolicyUrl)
}
}
}
}
ContentSection {
title: "Dotfiles"
RowLayout {
Layout.alignment: Qt.AlignHCenter
spacing: 20
Layout.topMargin: 10
Layout.bottomMargin: 10
MaterialSymbol {
iconSize: 70
text: "files"
color: Appearance.colors.colOnSecondaryContainer
}
ColumnLayout {
Layout.alignment: Qt.AlignVCenter
// spacing: 10
StyledText {
text: "illogical-impulse"
font.pixelSize: Appearance.font.pixelSize.title
}
StyledText {
text: "https://github.com/end-4/dots-hyprland"
font.pixelSize: Appearance.font.pixelSize.normal
textFormat: Text.MarkdownText
onLinkActivated: (link) => {
Qt.openUrlExternally(link)
}
PointingHandLinkHover {}
}
}
}
Flow {
Layout.fillWidth: true
spacing: 5
RippleButtonWithIcon {
materialIcon: "auto_stories"
mainText: "Documentation"
onClicked: {
Qt.openUrlExternally("https://end-4.github.io/dots-hyprland-wiki/en/ii-qs/02usage/")
}
}
RippleButtonWithIcon {
materialIcon: "adjust"
materialIconFill: false
mainText: "Issues"
onClicked: {
Qt.openUrlExternally("https://github.com/end-4/dots-hyprland/issues")
}
}
RippleButtonWithIcon {
materialIcon: "forum"
mainText: "Discussions"
onClicked: {
Qt.openUrlExternally("https://github.com/end-4/dots-hyprland/discussions")
}
}
RippleButtonWithIcon {
materialIcon: "favorite"
mainText: "Donate"
onClicked: {
Qt.openUrlExternally("https://github.com/sponsors/end-4")
}
}
}
}
}
@@ -0,0 +1,54 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import "root:/services/"
import "root:/modules/common/"
import "root:/modules/common/widgets/"
ContentPage {
ContentSection {
title: "Policies"
ConfigRow {
ColumnLayout { // Weeb policy
StyledText {
text: "Weeb"
color: Appearance.colors.colSubtext
}
ConfigSelectionArray {
currentValue: ConfigOptions.policies.weeb
configOptionName: "policies.weeb"
onSelected: (newValue) => {
ConfigLoader.setConfigValueAndSave("policies.weeb", newValue);
}
options: [
{ displayName: "No", value: 0 },
{ displayName: "Yes", value: 1 },
{ displayName: "Closet", value: 2 }
]
}
}
ColumnLayout { // AI policy
StyledText {
text: "AI"
color: Appearance.colors.colSubtext
}
ConfigSelectionArray {
currentValue: ConfigOptions.policies.ai
configOptionName: "policies.ai"
onSelected: (newValue) => {
ConfigLoader.setConfigValueAndSave("policies.ai", newValue);
}
options: [
{ displayName: "No", value: 0 },
{ displayName: "Yes", value: 1 },
{ displayName: "Local only", value: 2 }
]
}
}
}
}
}
@@ -0,0 +1,214 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import Quickshell.Hyprland
import "root:/services/"
import "root:/modules/common/"
import "root:/modules/common/widgets/"
import "root:/modules/common/functions/color_utils.js" as ColorUtils
import "root:/modules/common/functions/file_utils.js" as FileUtils
ContentPage {
baseWidth: lightDarkButtonGroup.implicitWidth
forceWidth: true
Process {
id: konachanWallProc
property string status: ""
command: ["bash", "-c", FileUtils.trimFileProtocol(`${Directories.config}/quickshell/scripts/colors/random_konachan_wall.sh`)]
stdout: SplitParser {
onRead: data => {
console.log(`Konachan wall proc output: ${data}`);
konachanWallProc.status = data.trim();
}
}
}
ContentSection {
title: "Colors & Wallpaper"
// Light/Dark mode preference
ButtonGroup {
id: lightDarkButtonGroup
Layout.fillWidth: true
LightDarkPreferenceButton {
dark: false
}
LightDarkPreferenceButton {
dark: true
}
}
// Material palette selection
StyledText {
text: "Material palette"
color: Appearance.colors.colSubtext
}
ConfigSelectionArray {
currentValue: ConfigOptions.appearance.palette.type
configOptionName: "appearance.palette.type"
onSelected: (newValue) => {
ConfigLoader.setConfigValueAndSave("appearance.palette.type", newValue);
}
options: [
{"value": "auto", "displayName": "Auto"},
{"value": "scheme-content", "displayName": "Content"},
{"value": "scheme-expressive", "displayName": "Expressive"},
{"value": "scheme-fidelity", "displayName": "Fidelity"},
{"value": "scheme-fruit-salad", "displayName": "Fruit Salad"},
{"value": "scheme-monochrome", "displayName": "Monochrome"},
{"value": "scheme-neutral", "displayName": "Neutral"},
{"value": "scheme-rainbow", "displayName": "Rainbow"},
{"value": "scheme-tonal-spot", "displayName": "Tonal Spot"}
]
}
// Wallpaper selection
StyledText {
text: "Wallpaper"
color: Appearance.colors.colSubtext
}
RowLayout {
Layout.alignment: Qt.AlignHCenter
RippleButtonWithIcon {
id: rndWallBtn
Layout.alignment: Qt.AlignHCenter
buttonRadius: Appearance.rounding.small
materialIcon: "wallpaper"
mainText: konachanWallProc.running ? "Be patient..." : "Random: Konachan"
onClicked: {
console.log(konachanWallProc.command.join(" "))
konachanWallProc.running = true;
}
StyledToolTip {
content: "Random SFW Anime wallpaper from Konachan\nImage is saved to ~/Pictures/Wallpapers"
}
}
RippleButtonWithIcon {
materialIcon: "wallpaper"
StyledToolTip {
content: "Pick wallpaper image on your system"
}
onClicked: {
Quickshell.execDetached(`${Directories.wallpaperSwitchScriptPath}`)
}
mainContentComponent: Component {
RowLayout {
spacing: 10
StyledText {
font.pixelSize: Appearance.font.pixelSize.small
text: "Choose file"
color: Appearance.colors.colOnSecondaryContainer
}
RowLayout {
spacing: 3
KeyboardKey {
key: "Ctrl"
}
KeyboardKey {
key: "󰖳"
}
StyledText {
Layout.alignment: Qt.AlignVCenter
text: "+"
}
KeyboardKey {
key: "T"
}
}
}
}
}
}
StyledText {
Layout.alignment: Qt.AlignHCenter
text: "Change any time later with /dark, /light, /img in the launcher"
font.pixelSize: Appearance.font.pixelSize.smaller
color: Appearance.colors.colSubtext
}
}
ContentSection {
title: "Shell style"
ColumnLayout { // Fake screen rounding
StyledText {
text: "Fake screen rounding"
color: Appearance.colors.colSubtext
}
ButtonGroup {
id: fakeScreenRoundingButtonGroup
property int selectedPolicy: ConfigOptions.appearance.fakeScreenRounding
spacing: 2
SelectionGroupButton {
property int value: 0
leftmost: true
buttonText: "No"
toggled: (fakeScreenRoundingButtonGroup.selectedPolicy === value)
onClicked: {
ConfigLoader.setConfigValueAndSave("appearance.fakeScreenRounding", value);
}
}
SelectionGroupButton {
property int value: 1
buttonText: "Yes"
toggled: (fakeScreenRoundingButtonGroup.selectedPolicy === value)
onClicked: {
ConfigLoader.setConfigValueAndSave("appearance.fakeScreenRounding", value);
}
}
SelectionGroupButton {
property int value: 2
rightmost: true
buttonText: "When not fullscreen"
toggled: (fakeScreenRoundingButtonGroup.selectedPolicy === value)
onClicked: {
ConfigLoader.setConfigValueAndSave("appearance.fakeScreenRounding", value);
}
}
}
}
ConfigSwitch {
text: "Transparency"
checked: ConfigOptions.appearance.transparency
onClicked: checked = !checked;
onCheckedChanged: {
ConfigLoader.setConfigValueAndSave("appearance.transparency", checked);
}
StyledToolTip {
content: "Might look ass. Unsupported."
}
}
}
ContentSection {
title: "Shell windows"
spacing: 4
ConfigRow {
uniform: true
ConfigSwitch {
text: "Title bar"
checked: ConfigOptions.windows.showTitlebar
onClicked: checked = !checked;
onCheckedChanged: {
ConfigLoader.setConfigValueAndSave("windows.showTitlebar", checked);
}
}
ConfigSwitch {
text: "Center title"
checked: ConfigOptions.windows.centerTitle
onClicked: checked = !checked;
onCheckedChanged: {
ConfigLoader.setConfigValueAndSave("windows.centerTitle", checked);
}
}
}
}
}
@@ -185,7 +185,7 @@ Rectangle {
buttonIcon: activated ? "inventory" : "content_copy"
onClicked: {
Hyprland.dispatch(`exec wl-copy '${StringUtils.shellSingleQuoteEscape(root.messageData?.content)}'`)
Quickshell.clipboardText = root.messageData?.content
copyButton.activated = true
copyIconTimer.restart()
}
@@ -73,7 +73,7 @@ ColumnLayout {
buttonIcon: activated ? "inventory" : "content_copy"
onClicked: {
Hyprland.dispatch(`exec wl-copy '${StringUtils.shellSingleQuoteEscape(segmentContent)}'`)
Quickshell.clipboardText = segmentContent
copyCodeButton.activated = true
copyIconTimer.restart()
}
@@ -96,8 +96,10 @@ ColumnLayout {
onClicked: {
const downloadPath = FileUtils.trimFileProtocol(Directories.downloads)
Hyprland.dispatch(`exec echo '${StringUtils.shellSingleQuoteEscape(segmentContent)}' > '${downloadPath}/code.${segmentLang || "txt"}'`)
Hyprland.dispatch(`exec notify-send 'Code saved to file' '${downloadPath}/code.${segmentLang || "txt"}' -a Shell`)
Quickshell.execDetached(["bash", "-c",
`echo '${StringUtils.shellSingleQuoteEscape(segmentContent)}' > '${downloadPath}/code.${segmentLang || "txt"}'`
])
Quickshell.execDetached(["bash", "-c", `notify-send 'Code saved to file' '${downloadPath}/code.${segmentLang || "txt"}' -a Shell`])
saveCodeButton.activated = true
saveIconTimer.restart()
}
@@ -180,7 +180,9 @@ Button {
buttonText: Translation.tr("Download")
onClicked: {
root.showActions = false
Hyprland.dispatch(`exec curl '${root.imageData.file_url}' -o '${root.imageData.is_nsfw ? root.nsfwPath : root.downloadPath}/${root.fileName}' && notify-send '${Translation.tr("Download complete")}' '${root.downloadPath}/${root.fileName}' -a 'Shell'`)
Quickshell.execDetached(["bash", "-c",
`curl '${root.imageData.file_url}' -o '${root.imageData.is_nsfw ? root.nsfwPath : root.downloadPath}/${root.fileName}' && notify-send '${qsTr("Download complete")}' '${root.downloadPath}/${root.fileName}' -a 'Shell'`
])
}
}
}
@@ -163,12 +163,7 @@ Rectangle {
Qt.openUrlExternally(link)
Hyprland.dispatch("global quickshell:sidebarLeftClose")
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.NoButton // Only for hover
hoverEnabled: true
cursorShape: parent.hoveredLink !== "" ? Qt.PointingHandCursor : Qt.ArrowCursor
}
PointingHandLinkHover {}
}
Repeater {
@@ -130,15 +130,17 @@ Rectangle {
Layout.topMargin: 10
width: tabBar.width
// Navigation rail buttons
ColumnLayout {
NavigationRailTabArray {
id: tabBar
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 5
id: tabBar
spacing: 15
currentIndex: root.selectedTab
expanded: false
Repeater {
model: root.tabs
NavRailButton {
NavigationRailButton {
showToggledHighlight: false
toggled: root.selectedTab == index
buttonText: modelData.name
buttonIcon: modelData.icon
@@ -3,6 +3,7 @@ import "root:/services"
import "root:/modules/common"
import "root:/modules/common/widgets"
import "root:/modules/common/functions/string_utils.js" as StringUtils
import "root:/modules/common/functions/file_utils.js" as FileUtils
import "./quickToggles/"
import "root:/services/"
import QtQuick
@@ -17,8 +18,10 @@ import Quickshell.Wayland
import Quickshell.Hyprland
Scope {
id: root
property int sidebarWidth: Appearance.sizes.sidebarWidth
property int sidebarPadding: 15
property string settingsQmlPath: FileUtils.trimFileProtocol(`${Directories.config}/quickshell/settings.qml`)
PanelWindow {
id: sidebarRoot
@@ -144,11 +147,11 @@ Scope {
toggled: false
buttonIcon: "settings"
onClicked: {
Hyprland.dispatch(`exec ${ConfigOptions.apps.settings}`)
Hyprland.dispatch(`global quickshell:sidebarRightClose`)
Hyprland.dispatch("global quickshell:sidebarRightClose")
Quickshell.execDetached(["qs", "-p", root.settingsQmlPath])
}
StyledToolTip {
content: Translation.tr("Plasma Settings")
content: Translation.tr("Settings")
}
}
QuickToggleButton {
@@ -15,8 +15,8 @@ QuickToggleButton {
toggleBluetooth.running = true
}
altAction: () => {
Hyprland.dispatch(`exec ${ConfigOptions.apps.bluetooth}`)
Hyprland.dispatch("global quickshell:sidebarRightClose")
Quickshell.execDetached(["bash", "-c", `${ConfigOptions.apps.bluetooth}`])
Hyprland.dispatch("global quickshell:sidebarRightClose")
}
Process {
id: toggleBluetooth
@@ -7,20 +7,27 @@ import Quickshell.Io
import Quickshell.Hyprland
QuickToggleButton {
property bool enabled: false
id: root
buttonIcon: "gamepad"
toggled: enabled
toggled: toggled
onClicked: {
enabled = !enabled
if (enabled) {
// gameModeOn.running = true
Hyprland.dispatch(`exec hyprctl --batch "keyword animations:enabled 0; keyword decoration:shadow:enabled 0; keyword decoration:blur:enabled 0; keyword general:gaps_in 0; keyword general:gaps_out 0; keyword general:border_size 1; keyword decoration:rounding 0; keyword general:allow_tearing 1"`)
root.toggled = !root.toggled
if (root.toggled) {
Quickshell.execDetached(["bash", "-c", `hyprctl --batch "keyword animations:enabled 0; keyword decoration:shadow:enabled 0; keyword decoration:blur:enabled 0; keyword general:gaps_in 0; keyword general:gaps_out 0; keyword general:border_size 1; keyword decoration:rounding 0; keyword general:allow_tearing 1"`])
} else {
Hyprland.dispatch("exec hyprctl reload")
Quickshell.execDetached(["hyprctl", "reload"])
}
}
Process {
id: fetchActiveState
running: true
command: ["bash", "-c", `test "$(hyprctl getoption animations:enabled -j | jq ".int")" -ne 0`]
onExited: (exitCode, exitStatus) => {
console.log("Game mode toggle exited with code:", exitCode, "and status:", exitStatus)
root.toggled = exitCode !== 0 // Inverted because enabled = nonzero exit
}
}
StyledToolTip {
content: Translation.tr("Game mode")
}
@@ -16,7 +16,7 @@ QuickToggleButton {
toggleNetwork.running = true
}
altAction: () => {
Hyprland.dispatch(`exec ${Network.ethernet ? ConfigOptions.apps.networkEthernet : ConfigOptions.apps.network}`)
Quickshell.execDetached(["bash", "-c", `${Network.ethernet ? ConfigOptions.apps.networkEthernet : ConfigOptions.apps.network}`])
Hyprland.dispatch("global quickshell:sidebarRightClose")
}
Process {
@@ -154,31 +154,18 @@ Item {
// + FAB
StyledRectangularShadow {
target: fabButton
radius: Appearance.rounding.normal
radius: fabButton.buttonRadius
blur: 0.6 * Appearance.sizes.elevationMargin
}
Button {
FloatingActionButton {
id: fabButton
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.rightMargin: root.fabMargins
anchors.bottomMargin: root.fabMargins
width: root.fabSize
height: root.fabSize
PointingHandInteraction {}
onClicked: root.showAddDialog = true
background: Rectangle {
id: fabBackground
anchors.fill: parent
radius: Appearance.rounding.normal
color: (fabButton.down) ? Appearance.colors.colPrimaryContainerActive : (fabButton.hovered ? Appearance.colors.colPrimaryContainerHover : Appearance.colors.colPrimaryContainer)
Behavior on color {
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
}
}
contentItem: MaterialSymbol {
text: "add"
horizontalAlignment: Text.AlignHCenter
@@ -219,7 +219,7 @@ switch() {
# Set wallpaper with swww
swww img "$imgpath" --transition-step 100 --transition-fps 120 \
--transition-type grow --transition-angle 30 --transition-duration 1 \
--transition-pos "$cursorposx, $cursorposy_inverted"
--transition-pos "$cursorposx, $cursorposy_inverted" &
remove_restore
fi
fi
+4 -2
View File
@@ -21,10 +21,12 @@ Singleton {
property bool isCriticalAndNotCharging: isCritical && !isCharging
onIsLowAndNotChargingChanged: {
if (available && isLowAndNotCharging) Hyprland.dispatch(`exec notify-send "Low battery" "Consider plugging in your device" -u critical -a "Shell"`)
if (available && isLowAndNotCharging)
Quickshell.execDetached(["bash", "-c", `notify-send "Low battery" "Consider plugging in your device" -u critical -a "Shell"`]);
}
onIsCriticalAndNotChargingChanged: {
if (available && isCriticalAndNotCharging) Hyprland.dispatch(`exec notify-send "Critically low battery" "🙏 I beg for pleas charg\nAutomatic suspend triggers at ${ConfigOptions.battery.suspend}%" -u critical -a "Shell"`)
if (available && isCriticalAndNotCharging)
Quickshell.execDetached(["bash", "-c", `notify-send "Critically low battery" "🙏 I beg for pleas charg\nAutomatic suspend triggers at ${ConfigOptions.battery.suspend}%" -u critical -a "Shell"`]);
}
}
+5 -6
View File
@@ -44,9 +44,8 @@ Singleton {
} catch (e) {
console.error("[ConfigLoader] Error reading file:", e);
console.log("[ConfigLoader] File content was:", fileContent);
Hyprland.dispatch(`exec notify-send "${Translation.tr("Shell configuration failed to load")}" "${root.filePath}"`)
Quickshell.execDetached(["bash", "-c", `notify-send '${Translation.tr("Shell configuration failed to load")}' '${root.filePath}'`])
return;
}
}
@@ -82,7 +81,7 @@ Singleton {
function saveConfig() {
const plainConfig = ObjectUtils.toPlainObject(ConfigOptions)
Hyprland.dispatch(`exec echo '${StringUtils.shellSingleQuoteEscape(JSON.stringify(plainConfig, null, 2))}' > '${root.filePath}'`)
Quickshell.execDetached(["bash", "-c", `echo '${StringUtils.shellSingleQuoteEscape(JSON.stringify(plainConfig, null, 2))}' > '${FileUtils.trimFileProtocol(root.filePath)}'`])
}
function setConfigValueAndSave(nestedKey, value, preventNextNotification = true) {
@@ -105,7 +104,7 @@ Singleton {
} else {
root.applyConfig(configFileView.text())
if (!root.preventNextNotification) {
// Hyprland.dispatch(`exec notify-send "${Translation.tr("Shell configuration reloaded")}" "${root.filePath}"`)
// Quickshell.execDetached(["bash", "-c", `notify-send '${qsTr("Shell configuration reloaded")}' '${root.filePath}'`])
} else {
root.preventNextNotification = false;
}
@@ -129,9 +128,9 @@ Singleton {
if(error == FileViewError.FileNotFound) {
console.log("[ConfigLoader] File not found, creating new file.")
root.saveConfig()
Hyprland.dispatch(`exec notify-send "${Translation.tr("Shell configuration created")}" "${root.filePath}"`)
Quickshell.execDetached(["bash", "-c", `notify-send '${Translation.tr("Shell configuration created")}' '${root.filePath}'`])
} else {
Hyprland.dispatch(`exec notify-send "${Translation.tr("Shell configuration failed to load")}" "${root.filePath}"`)
Quickshell.execDetached(["bash", "-c", `notify-send '${Translation.tr("Shell configuration failed to load")}' '${root.filePath}'`])
}
}
}
+3 -3
View File
@@ -9,9 +9,9 @@ pragma ComponentBehavior: Bound
* A nice wrapper for date and time strings.
*/
Singleton {
property string time: Qt.formatDateTime(clock.date, ConfigOptions?.time.format ?? "hh:mm")
property string date: Qt.formatDateTime(clock.date, ConfigOptions?.time.dateFormat ?? "dddd, dd/MM")
property string collapsedCalendarFormat: Qt.formatDateTime(clock.date, "dd MMMM yyyy")
property string time: Qt.locale().toString(clock.date, ConfigOptions?.time.format ?? "hh:mm")
property string date: Qt.locale().toString(clock.date, ConfigOptions?.time.dateFormat ?? "dddd, dd/MM")
property string collapsedCalendarFormat: Qt.locale().toString(clock.date, "dd MMMM yyyy")
property string uptime: "0h, 0m"
SystemClock {
@@ -20,15 +20,15 @@ Singleton {
}
function enableNextTime() {
Hyprland.dispatch(`exec rm -f '${root.firstRunFilePath}'`)
Quickshell.execDetached(["rm", "-f", root.firstRunFilePath])
}
function disableNextTime() {
Hyprland.dispatch(`exec echo '${root.firstRunFileContent}' > '${root.firstRunFilePath}'`)
Quickshell.execDetached(["bash", "-c", `echo '${root.firstRunFileContent}' > '${root.firstRunFilePath}'`])
}
function handleFirstRun() {
Hyprland.dispatch(`exec swww query | grep 'image' || '${Directories.wallpaperSwitchScriptPath}' '${root.defaultWallpaperPath}'`)
Hyprland.dispatch(`exec qs -p '${root.welcomeQmlPath}'`)
Quickshell.execDetached(["bash", "-c", `swww query | grep 'image' || '${Directories.wallpaperSwitchScriptPath}' '${root.defaultWallpaperPath}'`])
Quickshell.execDetached(["bash", "-c", `qs -p '${root.welcomeQmlPath}'`])
}
FileView {
@@ -243,7 +243,7 @@ Singleton {
root.list = JSON.parse(fileContents).map((notif) => {
return notifComponent.createObject(root, {
"id": notif.id,
"actions": notif.actions,
"actions": [], // Notification actions are meaningless if they're not tracked by the server or the sender is dead
"appIcon": notif.appIcon,
"appName": notif.appName,
"body": notif.body,
+22 -1
View File
@@ -9,10 +9,17 @@ import Quickshell.Io
* Provides some system info: distro, username.
*/
Singleton {
id: root
property string distroName: "Unknown"
property string distroId: "unknown"
property string distroIcon: "linux-symbolic"
property string username: "user"
property string homeUrl: ""
property string documentationUrl: ""
property string supportUrl: ""
property string bugReportUrl: ""
property string privacyPolicyUrl: ""
property string logo: ""
Timer {
triggeredOnStart: true
@@ -33,6 +40,20 @@ Singleton {
const logoMatch = textOsRelease.match(/^LOGO=(.+)$/m)
distroId = logoMatch ? logoMatch[1].replace(/"/g, "") : "unknown"
// Extract additional URLs and logo
const homeUrlMatch = textOsRelease.match(/^HOME_URL="(.+?)"/m)
homeUrl = homeUrlMatch ? homeUrlMatch[1] : ""
const documentationUrlMatch = textOsRelease.match(/^DOCUMENTATION_URL="(.+?)"/m)
documentationUrl = documentationUrlMatch ? documentationUrlMatch[1] : ""
const supportUrlMatch = textOsRelease.match(/^SUPPORT_URL="(.+?)"/m)
supportUrl = supportUrlMatch ? supportUrlMatch[1] : ""
const bugReportUrlMatch = textOsRelease.match(/^BUG_REPORT_URL="(.+?)"/m)
bugReportUrl = bugReportUrlMatch ? bugReportUrlMatch[1] : ""
const privacyPolicyUrlMatch = textOsRelease.match(/^PRIVACY_POLICY_URL="(.+?)"/m)
privacyPolicyUrl = privacyPolicyUrlMatch ? privacyPolicyUrlMatch[1] : ""
const logoFieldMatch = textOsRelease.match(/^LOGO="?(.+?)"?$/m)
logo = logoFieldMatch ? logoFieldMatch[1] : ""
// Update the distroIcon property based on distroId
switch (distroId) {
case "arch": distroIcon = "arch-symbolic"; break;
@@ -57,7 +78,7 @@ Singleton {
command: ["whoami"]
stdout: SplitParser {
onRead: data => {
username = data.trim()
root.username = data.trim()
}
}
}
+20 -7
View File
@@ -20,23 +20,36 @@ Singleton {
function releaseAllKeys() {
const keycodes = Array.from(Array(249).keys());
const releaseCommand = `ydotool key --key-delay 0 ${keycodes.map(keycode => `${keycode}:0`).join(" ")}`
Hyprland.dispatch(`exec ${releaseCommand}`)
Quickshell.execDetached([
"ydotool",
"key", "--key-delay", "0",
...keycodes.map(keycode => `${keycode}:0`)
])
root.shiftMode = 0; // Reset shift mode
}
function releaseShiftKeys() {
const releaseCommand = `ydotool key --key-delay 0 ${root.shiftKeys.map(keycode => `${keycode}:0`).join(" ")}`
Hyprland.dispatch(`exec ${releaseCommand}`)
Quickshell.execDetached([
"ydotool",
"key", "--key-delay", "0",
...root.shiftKeys.map(keycode => `${keycode}:0`)
])
root.shiftMode = 0; // Reset shift mode
}
function press(keycode) {
Hyprland.dispatch(`exec ydotool key --key-delay 0 ${keycode}:1`);
Quickshell.execDetached([
"ydotool",
"key", "--key-delay", "0",
`${keycode}:1`
]);
}
function release(keycode) {
Hyprland.dispatch(`exec ydotool key --key-delay 0 ${keycode}:0`);
Quickshell.execDetached([
"ydotool",
"key", "--key-delay", "0",
`${keycode}:0`
]);
}
}
+219
View File
@@ -0,0 +1,219 @@
//@ pragma UseQApplication
//@ pragma Env QS_NO_RELOAD_POPUP=1
//@ pragma Env QT_QUICK_CONTROLS_STYLE=Basic
// Adjust this to make the app smaller or larger
//@ pragma Env QT_SCALE_FACTOR=1
import Qt5Compat.GraphicalEffects
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQuick.Window
import Quickshell
import Quickshell.Io
import Quickshell.Hyprland
import "root:/services/"
import "root:/modules/common/"
import "root:/modules/common/widgets/"
import "root:/modules/common/functions/color_utils.js" as ColorUtils
import "root:/modules/common/functions/file_utils.js" as FileUtils
import "root:/modules/common/functions/string_utils.js" as StringUtils
ApplicationWindow {
id: root
property string firstRunFilePath: FileUtils.trimFileProtocol(`${Directories.state}/user/first_run.txt`)
property string firstRunFileContent: "This file is just here to confirm you've been greeted :>"
property real contentPadding: 8
property bool showNextTime: false
property var pages: [
{
name: "Style",
icon: "palette",
component: "modules/settings/Style.qml"
},
{
name: "Behavior",
icon: "settings",
component: "modules/settings/BehaviorConfig.qml"
},
{
name: "About",
icon: "info",
component: "modules/settings/About.qml"
}
]
property int currentPage: 0
visible: true
onClosing: Qt.quit()
title: "illogical-impulse Settings"
Component.onCompleted: {
MaterialThemeLoader.reapplyTheme()
ConfigLoader.loadConfig()
}
minimumWidth: 600
minimumHeight: 400
width: 900
height: 650
color: Appearance.m3colors.m3background
ColumnLayout {
anchors {
fill: parent
margins: contentPadding
}
Item { // Titlebar
visible: ConfigOptions?.windows.showTitlebar
Layout.fillWidth: true
Layout.fillHeight: false
implicitHeight: Math.max(titleText.implicitHeight, windowControlsRow.implicitHeight)
StyledText {
id: titleText
anchors {
left: ConfigOptions.windows.centerTitle ? undefined : parent.left
horizontalCenter: ConfigOptions.windows.centerTitle ? parent.horizontalCenter : undefined
verticalCenter: parent.verticalCenter
leftMargin: 12
}
color: Appearance.colors.colOnLayer0
text: "Settings"
font.pixelSize: Appearance.font.pixelSize.title
font.family: Appearance.font.family.title
}
RowLayout { // Window controls row
id: windowControlsRow
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
RippleButton {
buttonRadius: Appearance.rounding.full
implicitWidth: 35
implicitHeight: 35
onClicked: root.close()
contentItem: MaterialSymbol {
anchors.centerIn: parent
horizontalAlignment: Text.AlignHCenter
text: "close"
iconSize: 20
}
}
}
}
RowLayout { // Window content with navigation rail and content pane
Layout.fillWidth: true
Layout.fillHeight: true
spacing: contentPadding
Item {
id: navRailWrapper
Layout.fillHeight: true
Layout.margins: 5
implicitWidth: navRail.expanded ? 150 : fab.baseSize
Behavior on implicitWidth {
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
}
NavigationRail { // Window content with navigation rail and content pane
id: navRail
anchors {
left: parent.left
top: parent.top
bottom: parent.bottom
}
spacing: 10
expanded: root.width > 900
NavigationRailExpandButton {}
FloatingActionButton {
id: fab
iconText: "edit"
buttonText: "Edit config"
expanded: navRail.expanded
onClicked: {
Qt.openUrlExternally(`${Directories.config}/illogical-impulse/config.json`);
}
StyledToolTip {
extraVisibleCondition: !navRail.expanded
content: "Edit shell config file"
}
}
NavigationRailTabArray {
currentIndex: root.currentPage
expanded: navRail.expanded
Repeater {
model: root.pages
NavigationRailButton {
required property var index
required property var modelData
toggled: root.currentPage === index
onClicked: root.currentPage = index;
expanded: navRail.expanded
buttonIcon: modelData.icon
buttonText: modelData.name
showToggledHighlight: false
}
}
}
Item {
Layout.fillHeight: true
}
}
}
Rectangle { // Content container
Layout.fillWidth: true
Layout.fillHeight: true
color: Appearance.m3colors.m3surfaceContainerLow
radius: Appearance.rounding.windowRounding - root.contentPadding
Loader {
id: pageLoader
anchors.fill: parent
opacity: 1.0
source: root.pages[0].component
Connections {
target: root
function onCurrentPageChanged() {
if (pageLoader.sourceComponent !== root.pages[root.currentPage].component) {
switchAnim.restart();
}
}
}
SequentialAnimation {
id: switchAnim
NumberAnimation {
target: pageLoader
properties: "opacity"
from: 1
to: 0
duration: 150
easing.type: Appearance.animation.elementMoveExit.type
easing.bezierCurve: Appearance.animationCurves.emphasizedFirstHalf
}
PropertyAction {
target: pageLoader
property: "source"
value: root.pages[root.currentPage].component
}
NumberAnimation {
target: pageLoader
properties: "opacity"
from: 0
to: 1
duration: 250
easing.type: Appearance.animation.elementMoveEnter.type
easing.bezierCurve: Appearance.animationCurves.emphasizedLastHalf
}
}
}
}
}
}
}
+1 -1
View File
@@ -32,7 +32,7 @@ ShellRoot {
property bool enableBar: true
property bool enableBackgroundWidgets: true
property bool enableCheatsheet: true
property bool enableDock: true
property bool enableDock: false
property bool enableMediaControls: true
property bool enableNotificationPopup: true
property bool enableOnScreenDisplayBrightness: true
+2 -5
View File
@@ -44,7 +44,6 @@
"Disable NSFW content": "Disable NSFW content",
"Done": "Done",
"Download": "Download",
"Download complete": "Download complete",
"Edit": "Edit",
"Enter text to translate...": "Enter text to translate...",
"Finished tasks will go here": "Finished tasks will go here",
@@ -90,7 +89,6 @@
"Opens session screen on press": "Opens session screen on press",
"Output": "Output",
"Page {0}": "Page {0}",
"Plasma Settings": "Plasma Settings",
"Reboot": "Reboot",
"Reboot to firmware settings": "Reboot to firmware settings",
"Reload Hyprland & Quickshell": "Reload Hyprland & Quickshell",
@@ -110,7 +108,6 @@
"Set the current API provider": "Set the current API provider",
"Shell configuration created": "Shell configuration created",
"Shell configuration failed to load": "Shell configuration failed to load",
"Shell configuration reloaded": "Shell configuration reloaded",
"Shutdown": "Shutdown",
"Silent": "Silent",
"Sleep": "Sleep",
@@ -151,7 +148,6 @@
"Waifus only | Excellent quality, limited quantity": "Waifus only | Excellent quality, limited quantity",
"Waiting for response...": "Waiting for response...",
"Workspace": "Workspace",
"{0} (copied)": "{0} (copied)",
"{0} Safe Storage": "{0} Safe Storage",
"{0} does not require an API key": "{0} does not require an API key",
"{0} queries pending": "{0} queries pending",
@@ -172,5 +168,6 @@
"Switched to search mode. Continue with the user's request.": "Switched to search mode. Continue with the user's request.",
"Experimental | Online | Google's model\nCan do a little more but doesn't search quickly": "Experimental | Online | Google's model\nCan do a little more but doesn't search quickly",
"Message the model... \"{0}\" for commands": "Message the model... \"{0}\" for commands",
"To set an API key, pass it with the command\n\nTo view the key, pass \"get\" with the command<br/>\n\n### For {0}:\n\n**Link**: {1}\n\n{2}": "To set an API key, pass it with the command\n\nTo view the key, pass \"get\" with the command<br/>\n\n### For {0}:\n\n**Link**: {1}\n\n{2}"
"To set an API key, pass it with the command\n\nTo view the key, pass \"get\" with the command<br/>\n\n### For {0}:\n\n**Link**: {1}\n\n{2}": "To set an API key, pass it with the command\n\nTo view the key, pass \"get\" with the command<br/>\n\n### For {0}:\n\n**Link**: {1}\n\n{2}",
"Settings": "Settings"
}
+2 -5
View File
@@ -44,7 +44,6 @@
"Disable NSFW content": "禁用 NSFW 内容",
"Done": "完成",
"Download": "下载",
"Download complete": "下载完成",
"Edit": "编辑",
"Enter text to translate...": "输入要翻译的文本...",
"Finished tasks will go here": "已完成的任务将显示在这里",
@@ -90,7 +89,6 @@
"Opens session screen on press": "按下时打开会话屏幕",
"Output": "输出",
"Page {0}": "第 {0} 页",
"Plasma Settings": "Plasma 设置",
"Reboot": "重启",
"Reboot to firmware settings": "重启到固件设置",
"Reload Hyprland & Quickshell": "重新加载 Hyprland 和 Quickshell",
@@ -110,7 +108,6 @@
"Set the current API provider": "设置当前 API 提供商",
"Shell configuration created": "Shell 配置已创建",
"Shell configuration failed to load": "Shell 配置加载失败",
"Shell configuration reloaded": "Shell 配置已重新加载",
"Shutdown": "关机",
"Silent": "静音",
"Sleep": "睡眠",
@@ -151,7 +148,6 @@
"Waifus only | Excellent quality, limited quantity": "仅限角色 | 优秀质量,数量有限",
"Waiting for response...": "等待响应...",
"Workspace": "工作区",
"{0} (copied)": "{0}(已复制)",
"{0} Safe Storage": "{0} 安全存储",
"{0} does not require an API key": "{0} 不需要 API 密钥",
"{0} queries pending": "{0} 个查询等待中",
@@ -172,5 +168,6 @@
"Enter tags, or \"{0}\" for commands": "输入标签,或 \"{0}\" 查看命令",
"Online via {0} | {1}'s model": "通过 {0} 在线 | {1} 的模型",
"That didn't work. Tips:\n- Check your tags and NSFW settings\n- If you don't have a tag in mind, type a page number": "没有找到结果。提示:\n- 检查您的标签和 NSFW 设置\n- 如果没有想到标签,请输入页码",
"Online | Google's model\nGives up-to-date information with search.": "在线 | Google 模型\n通过搜索提供最新信息。"
"Online | Google's model\nGives up-to-date information with search.": "在线 | Google 模型\n通过搜索提供最新信息。",
"Settings": "设置"
}
+212 -451
View File
@@ -24,7 +24,7 @@ ApplicationWindow {
id: root
property string firstRunFilePath: FileUtils.trimFileProtocol(`${Directories.state}/user/first_run.txt`)
property string firstRunFileContent: "This file is just here to confirm you've been greeted :>"
property real contentPadding: 5
property real contentPadding: 8
property bool showNextTime: false
visible: true
onClosing: Qt.quit()
@@ -53,220 +53,27 @@ ApplicationWindow {
}
}
component SelectionConnectedButton: GroupButton {
id: selectionConnectedButtonRoot
horizontalPadding: 12
verticalPadding: 8
bounce: false
property bool leftmost: false
property bool rightmost: false
leftRadius: (toggled || leftmost) ? (height / 2) : Appearance.rounding.unsharpenmore
rightRadius: (toggled || rightmost) ? (height / 2) : Appearance.rounding.unsharpenmore
colBackground: Appearance.colors.colSecondaryContainer
contentItem: StyledText {
color: parent.toggled ? Appearance.colors.colOnPrimary : Appearance.colors.colOnSecondaryContainer
text: selectionConnectedButtonRoot.buttonText
}
}
component Section: ColumnLayout {
id: sectionRoot
property string title
default property alias data: sectionContent.data
Layout.fillWidth: true
spacing: 8
StyledText {
text: sectionRoot.title
font.pixelSize: Appearance.font.pixelSize.larger
}
ColumnLayout {
id: sectionContent
spacing: 5
}
}
component ButtonWithIcon: RippleButton {
id: buttonWithIconRoot
property string nerdIcon
property string iconText
property string mainText: "Button text"
property Component mainContentComponent: Component {
StyledText {
text: buttonWithIconRoot.mainText
font.pixelSize: Appearance.font.pixelSize.small
color: Appearance.colors.colOnSecondaryContainer
}
}
implicitHeight: 35
horizontalPadding: 15
buttonRadius: Appearance.rounding.small
colBackground: Appearance.colors.colLayer2
contentItem: RowLayout {
Item {
implicitWidth: Math.max(materialIconLoader.implicitWidth, nerdIconLoader.implicitWidth)
Loader {
id: materialIconLoader
anchors.centerIn: parent
active: !nerdIcon
sourceComponent: MaterialSymbol {
text: buttonWithIconRoot.iconText
iconSize: Appearance.font.pixelSize.larger
color: Appearance.colors.colOnSecondaryContainer
fill: 1
}
}
Loader {
id: nerdIconLoader
anchors.centerIn: parent
active: nerdIcon
sourceComponent: StyledText {
text: buttonWithIconRoot.nerdIcon
font.pixelSize: Appearance.font.pixelSize.larger
font.family: Appearance.font.family.iconNerd
color: Appearance.colors.colOnSecondaryContainer
}
}
}
Loader {
sourceComponent: buttonWithIconRoot.mainContentComponent
Layout.alignment: Qt.AlignVCenter
}
}
}
component LightDarkPrefButton: GroupButton {
id: lightDarkButtonRoot
required property bool dark
property color previewBg: dark ? ColorUtils.colorWithHueOf("#3f3838", Appearance.m3colors.m3primary) :
ColorUtils.colorWithHueOf("#F7F9FF", Appearance.m3colors.m3primary)
property color previewFg: dark ? Qt.lighter(previewBg, 2.2) : ColorUtils.mix(previewBg, "#292929", 0.85)
padding: 5
Layout.fillWidth: true
colBackground: Appearance.colors.colLayer2
toggled: Appearance.m3colors.darkmode === dark
onClicked: {
Hyprland.dispatch(`exec ${Directories.wallpaperSwitchScriptPath} --mode ${dark ? "dark" : "light"} --noswitch`)
}
contentItem: Item {
anchors.centerIn: parent
implicitWidth: buttonContentLayout.implicitWidth
implicitHeight: buttonContentLayout.implicitHeight
ColumnLayout {
id: buttonContentLayout
anchors.centerIn: parent
Rectangle {
Layout.alignment: Qt.AlignHCenter
implicitWidth: 250
implicitHeight: skeletonColumnLayout.implicitHeight + 10 * 2
radius: lightDarkButtonRoot.buttonRadius - lightDarkButtonRoot.padding
color: lightDarkButtonRoot.previewBg
border {
width: 1
color: Appearance.m3colors.m3outlineVariant
}
// Some skeleton items
ColumnLayout {
id: skeletonColumnLayout
anchors.fill: parent
anchors.margins: 10
spacing: 10
RowLayout {
Rectangle {
radius: Appearance.rounding.full
color: lightDarkButtonRoot.previewFg
implicitWidth: 50
implicitHeight: 50
}
ColumnLayout {
spacing: 4
Rectangle {
radius: Appearance.rounding.unsharpenmore
color: lightDarkButtonRoot.previewFg
Layout.fillWidth: true
implicitHeight: 22
}
Rectangle {
radius: Appearance.rounding.unsharpenmore
color: lightDarkButtonRoot.previewFg
Layout.fillWidth: true
Layout.rightMargin: 45
implicitHeight: 18
}
}
}
StyledProgressBar {
Layout.topMargin: 5
Layout.bottomMargin: 5
Layout.fillWidth: true
value: 0.7
sperm: true
animateSperm: lightDarkButtonRoot.toggled
highlightColor: lightDarkButtonRoot.toggled ? Appearance.m3colors.m3primary : lightDarkButtonRoot.previewFg
trackColor: ColorUtils.mix(lightDarkButtonRoot.previewBg, lightDarkButtonRoot.previewFg, 0.5)
}
RowLayout {
spacing: 2
Rectangle {
radius: Appearance.rounding.full
color: lightDarkButtonRoot.toggled ? Appearance.m3colors.m3primary : lightDarkButtonRoot.previewFg
Layout.fillWidth: true
implicitHeight: 30
MaterialSymbol {
visible: lightDarkButtonRoot.toggled
anchors.centerIn: parent
horizontalAlignment: Text.AlignHCenter
text: "check"
iconSize: 20
color: lightDarkButtonRoot.toggled ? Appearance.m3colors.m3onPrimary : lightDarkButtonRoot.previewBg
}
}
Rectangle {
radius: Appearance.rounding.unsharpenmore
color: lightDarkButtonRoot.toggled ? Appearance.m3colors.m3secondaryContainer : lightDarkButtonRoot.previewFg
Layout.fillWidth: true
implicitHeight: 30
}
Rectangle {
topLeftRadius: Appearance.rounding.unsharpenmore
bottomLeftRadius: Appearance.rounding.unsharpenmore
topRightRadius: Appearance.rounding.full
bottomRightRadius: Appearance.rounding.full
color: lightDarkButtonRoot.toggled ? Appearance.m3colors.m3secondaryContainer : lightDarkButtonRoot.previewFg
Layout.fillWidth: true
implicitHeight: 30
}
}
}
}
StyledText {
Layout.fillWidth: true
text: dark ? "Dark" : "Light"
color: lightDarkButtonRoot.toggled ? Appearance.m3colors.m3onPrimary : Appearance.colors.colOnLayer2
horizontalAlignment: Text.AlignHCenter
}
}
}
}
ColumnLayout {
anchors {
fill: parent
margins: contentPadding
}
Item {
Item { // Titlebar
visible: ConfigOptions?.windows.showTitlebar
Layout.fillWidth: true
implicitHeight: Math.max(welcomeText.implicitHeight, windowControlsRow.implicitHeight)
StyledText {
id: welcomeText
anchors.centerIn: parent
anchors {
left: ConfigOptions.windows.centerTitle ? undefined : parent.left
horizontalCenter: ConfigOptions.windows.centerTitle ? parent.horizontalCenter : undefined
verticalCenter: parent.verticalCenter
leftMargin: 12
}
color: Appearance.colors.colOnLayer0
text: "Yooooo hi there"
font.pixelSize: Appearance.font.pixelSize.hugeass
font.pixelSize: Appearance.font.pixelSize.title
font.family: Appearance.font.family.title
}
RowLayout { // Window controls row
@@ -284,10 +91,9 @@ ApplicationWindow {
Layout.alignment: Qt.AlignVCenter
onCheckedChanged: {
if (checked) {
Hyprland.dispatch(`exec rm '${StringUtils.shellSingleQuoteEscape(root.firstRunFilePath)}'`)
Quickshell.execDetached(["rm", root.firstRunFilePath])
} else {
console.log(`exec echo '${StringUtils.shellSingleQuoteEscape(root.firstRunFileContent)}' > '${StringUtils.shellSingleQuoteEscape(root.firstRunFilePath)}'`)
Hyprland.dispatch(`exec echo '${StringUtils.shellSingleQuoteEscape(root.firstRunFileContent)}' > '${StringUtils.shellSingleQuoteEscape(root.firstRunFilePath)}'`)
Quickshell.execDetached(["bash", "-c", `echo '${StringUtils.shellSingleQuoteEscape(root.firstRunFileContent)}' > '${StringUtils.shellSingleQuoteEscape(root.firstRunFilePath)}'`])
}
}
}
@@ -305,262 +111,217 @@ ApplicationWindow {
}
}
}
Rectangle {
Rectangle { // Content container
color: Appearance.m3colors.m3surfaceContainerLow
radius: Appearance.rounding.windowRounding - root.contentPadding
implicitHeight: contentColumn.implicitHeight
implicitWidth: contentColumn.implicitWidth
Layout.fillWidth: true
Layout.fillHeight: true
radius: Appearance.rounding.windowRounding - root.contentPadding
Flickable {
clip: true
ContentPage {
id: contentColumn
anchors.fill: parent
contentHeight: contentColumn.implicitHeight
implicitWidth: contentColumn.implicitWidth
ColumnLayout {
id: contentColumn
anchors {
top: parent.top
bottom: parent.bottom
horizontalCenter: parent.horizontalCenter
margins: 10
}
spacing: 20
ContentSection {
title: "Style & wallpaper"
Section {
title: "Style & wallpaper"
ButtonGroup {
Layout.fillWidth: true
LightDarkPrefButton {
dark: false
}
LightDarkPrefButton {
dark: true
}
}
RowLayout {
Layout.alignment: Qt.AlignHCenter
ButtonWithIcon {
id: rndWallBtn
Layout.alignment: Qt.AlignHCenter
buttonRadius: Appearance.rounding.small
iconText: "wallpaper"
mainText: konachanWallProc.running ? "Be patient..." : "Random: Konachan"
onClicked: {
console.log(konachanWallProc.command.join(" "))
konachanWallProc.running = true;
}
}
ButtonWithIcon {
iconText: "wallpaper"
onClicked: {
Hyprland.dispatch(`exec ${Directories.wallpaperSwitchScriptPath}`)
}
mainContentComponent: Component {
RowLayout {
spacing: 10
StyledText {
font.pixelSize: Appearance.font.pixelSize.small
text: "Choose file"
color: Appearance.colors.colOnSecondaryContainer
}
RowLayout {
spacing: 3
KeyboardKey {
key: "Ctrl"
}
KeyboardKey {
key: "󰖳"
}
StyledText {
Layout.alignment: Qt.AlignVCenter
text: "+"
}
KeyboardKey {
key: "T"
}
}
}
}
}
}
StyledText {
Layout.alignment: Qt.AlignHCenter
text: "Change any time later with /dark, /light, /img in the launcher"
font.pixelSize: Appearance.font.pixelSize.smaller
color: Appearance.colors.colSubtext
}
}
Section {
title: "Policies"
RowLayout {
Layout.alignment: Qt.AlignHCenter
spacing: 15
ColumnLayout { // Weeb policy
StyledText {
text: "Weeb"
color: Appearance.colors.colSubtext
}
ButtonGroup {
id: weebPolicyBtnGroup
property int selectedPolicy: ConfigOptions.policies.weeb
spacing: 2
SelectionConnectedButton {
property int value: 0
leftmost: true
buttonText: "No"
toggled: (weebPolicyBtnGroup.selectedPolicy === value)
onClicked: {
ConfigLoader.setConfigValueAndSave("policies.weeb", value);
}
}
SelectionConnectedButton {
property int value: 1
buttonText: "Yes"
toggled: (weebPolicyBtnGroup.selectedPolicy === value)
onClicked: {
ConfigLoader.setConfigValueAndSave("policies.weeb", value);
}
}
SelectionConnectedButton {
property int value: 2
rightmost: true
buttonText: "Closet"
toggled: (weebPolicyBtnGroup.selectedPolicy === value)
onClicked: {
ConfigLoader.setConfigValueAndSave("policies.weeb", value);
}
}
}
}
ColumnLayout { // AI policy
StyledText {
text: "AI"
color: Appearance.colors.colSubtext
}
ButtonGroup {
id: aiPolicyBtnGroup
property int selectedPolicy: ConfigOptions.policies.ai
spacing: 2
SelectionConnectedButton {
property int value: 0
leftmost: true
buttonText: "No"
toggled: (aiPolicyBtnGroup.selectedPolicy === value)
onClicked: {
ConfigLoader.setConfigValueAndSave("policies.ai", value);
}
}
SelectionConnectedButton {
property int value: 1
buttonText: "Yes"
toggled: (aiPolicyBtnGroup.selectedPolicy === value)
onClicked: {
ConfigLoader.setConfigValueAndSave("policies.ai", value);
}
}
SelectionConnectedButton {
property int value: 2
rightmost: true
buttonText: "Local only"
toggled: (aiPolicyBtnGroup.selectedPolicy === value)
onClicked: {
ConfigLoader.setConfigValueAndSave("policies.ai", value);
}
}
}
}
}
}
Section {
title: "Info"
Flow {
Layout.fillWidth: true
spacing: 10
ButtonWithIcon {
iconText: "keyboard_alt"
onClicked: {
Hyprland.dispatch("global quickshell:cheatsheetOpen")
}
mainContentComponent: Component {
RowLayout {
spacing: 10
StyledText {
font.pixelSize: Appearance.font.pixelSize.small
text: "Keybinds"
color: Appearance.colors.colOnSecondaryContainer
}
RowLayout {
spacing: 3
KeyboardKey {
key: "󰖳"
}
StyledText {
Layout.alignment: Qt.AlignVCenter
text: "+"
}
KeyboardKey {
key: "/"
}
}
}
}
}
ButtonWithIcon {
iconText: "help"
mainText: "Usage"
onClicked: {
Qt.openUrlExternally("https://end-4.github.io/dots-hyprland-wiki/en/ii-qs/02usage/")
}
}
ButtonWithIcon {
iconText: "construction"
mainText: "Configuration"
onClicked: {
Qt.openUrlExternally("https://end-4.github.io/dots-hyprland-wiki/en/ii-qs/03config/")
}
}
}
}
Section {
title: "Useless buttons"
Flow {
Layout.fillWidth: true
spacing: 10
ButtonWithIcon {
nerdIcon: "󰊤"
mainText: "GitHub"
onClicked: {
Qt.openUrlExternally("https://github.com/end-4/dots-hyprland")
}
}
ButtonWithIcon {
iconText: "favorite"
mainText: "Funny number"
onClicked: {
Qt.openUrlExternally("https://github.com/sponsors/end-4")
}
}
}
}
Item {
ButtonGroup {
Layout.fillWidth: true
Layout.fillHeight: true
LightDarkPreferenceButton {
dark: false
}
LightDarkPreferenceButton {
dark: true
}
}
RowLayout {
Layout.alignment: Qt.AlignHCenter
RippleButtonWithIcon {
id: rndWallBtn
Layout.alignment: Qt.AlignHCenter
buttonRadius: Appearance.rounding.small
materialIcon: "wallpaper"
mainText: konachanWallProc.running ? "Be patient..." : "Random: Konachan"
onClicked: {
console.log(konachanWallProc.command.join(" "))
konachanWallProc.running = true;
}
StyledToolTip {
content: "Random SFW Anime wallpaper from Konachan\nImage is saved to ~/Pictures/Wallpapers"
}
}
RippleButtonWithIcon {
materialIcon: "wallpaper"
StyledToolTip {
content: "Pick wallpaper image on your system"
}
onClicked: {
Quickshell.execDetached([`${Directories.wallpaperSwitchScriptPath}`])
}
mainContentComponent: Component {
RowLayout {
spacing: 10
StyledText {
font.pixelSize: Appearance.font.pixelSize.small
text: "Choose file"
color: Appearance.colors.colOnSecondaryContainer
}
RowLayout {
spacing: 3
KeyboardKey {
key: "Ctrl"
}
KeyboardKey {
key: "󰖳"
}
StyledText {
Layout.alignment: Qt.AlignVCenter
text: "+"
}
KeyboardKey {
key: "T"
}
}
}
}
}
}
StyledText {
Layout.alignment: Qt.AlignHCenter
text: "Change any time later with /dark, /light, /img in the launcher"
font.pixelSize: Appearance.font.pixelSize.smaller
color: Appearance.colors.colSubtext
}
}
ContentSection {
title: "Policies"
ConfigRow {
ColumnLayout { // Weeb policy
StyledText {
text: "Weeb"
color: Appearance.colors.colSubtext
}
ConfigSelectionArray {
currentValue: ConfigOptions.policies.weeb
configOptionName: "policies.weeb"
onSelected: (newValue) => {
ConfigLoader.setConfigValueAndSave("policies.weeb", newValue);
}
options: [
{ displayName: "No", value: 0 },
{ displayName: "Yes", value: 1 },
{ displayName: "Closet", value: 2 }
]
}
}
ColumnLayout { // AI policy
StyledText {
text: "AI"
color: Appearance.colors.colSubtext
}
ConfigSelectionArray {
currentValue: ConfigOptions.policies.ai
configOptionName: "policies.ai"
onSelected: (newValue) => {
ConfigLoader.setConfigValueAndSave("policies.ai", newValue);
}
options: [
{ displayName: "No", value: 0 },
{ displayName: "Yes", value: 1 },
{ displayName: "Local only", value: 2 }
]
}
}
}
}
ContentSection {
title: "Info"
Flow {
Layout.fillWidth: true
spacing: 5
RippleButtonWithIcon {
materialIcon: "keyboard_alt"
onClicked: {
Hyprland.dispatch("global quickshell:cheatsheetOpen")
}
mainContentComponent: Component {
RowLayout {
spacing: 10
StyledText {
font.pixelSize: Appearance.font.pixelSize.small
text: "Keybinds"
color: Appearance.colors.colOnSecondaryContainer
}
RowLayout {
spacing: 3
KeyboardKey {
key: "󰖳"
}
StyledText {
Layout.alignment: Qt.AlignVCenter
text: "+"
}
KeyboardKey {
key: "/"
}
}
}
}
}
RippleButtonWithIcon {
materialIcon: "help"
mainText: "Usage"
onClicked: {
Qt.openUrlExternally("https://end-4.github.io/dots-hyprland-wiki/en/ii-qs/02usage/")
}
}
RippleButtonWithIcon {
materialIcon: "construction"
mainText: "Configuration"
onClicked: {
Qt.openUrlExternally("https://end-4.github.io/dots-hyprland-wiki/en/ii-qs/03config/")
}
}
}
}
ContentSection {
title: "Useless buttons"
Flow {
Layout.fillWidth: true
spacing: 5
RippleButtonWithIcon {
nerdIcon: "󰊤"
mainText: "GitHub"
onClicked: {
Qt.openUrlExternally("https://github.com/end-4/dots-hyprland")
}
}
RippleButtonWithIcon {
materialIcon: "favorite"
mainText: "Funny number"
onClicked: {
Qt.openUrlExternally("https://github.com/sponsors/end-4")
}
}
}
}
Item {
Layout.fillWidth: true
Layout.fillHeight: true
}
}
}