forked from Shinonome/dots-hyprland
Merge branch 'ii-qs' into ii-qs-patch-1
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
import "root:/"
|
||||
import "root:/modules/common"
|
||||
import "root:/modules/common/widgets"
|
||||
import "root:/services"
|
||||
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.Wayland
|
||||
import Quickshell.Hyprland
|
||||
import Quickshell.Services.UPower
|
||||
|
||||
Scope {
|
||||
id: root
|
||||
property string filePath: `${Directories.state}/user/generated/wallpaper/least_busy_region.json`
|
||||
property real centerX: 0
|
||||
property real centerY: 0
|
||||
property color dominantColor: Appearance.colors.colPrimary
|
||||
property bool dominantColorIsDark: dominantColor.hslLightness < 0.5
|
||||
property color colBackground: ColorUtils.transparentize(ColorUtils.mix(Appearance.colors.colPrimary, Appearance.colors.colSecondaryContainer), 1)
|
||||
property color colText: ColorUtils.colorWithLightness(Appearance.colors.colPrimary, (root.dominantColorIsDark ? 0.8 : 0.12))
|
||||
|
||||
function updateWidgetPosition(fileContent) {
|
||||
// console.log("[BackgroundWidgets] Updating widget position with content:", fileContent)
|
||||
const parsedContent = JSON.parse(fileContent)
|
||||
root.centerX = parsedContent.center_x
|
||||
root.centerY = parsedContent.center_y
|
||||
root.dominantColor = parsedContent.dominant_color || Appearance.colors.colPrimary
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: delayedFileRead
|
||||
interval: ConfigOptions.hacks.arbitraryRaceConditionDelay
|
||||
running: false
|
||||
onTriggered: {
|
||||
root.updateWidgetPosition(leastBusyRegionFileView.text())
|
||||
}
|
||||
}
|
||||
|
||||
FileView {
|
||||
id: leastBusyRegionFileView
|
||||
path: Qt.resolvedUrl(root.filePath)
|
||||
watchChanges: true
|
||||
onFileChanged: {
|
||||
this.reload()
|
||||
delayedFileRead.start()
|
||||
}
|
||||
onLoadedChanged: {
|
||||
const fileContent = leastBusyRegionFileView.text()
|
||||
root.updateWidgetPosition(fileContent)
|
||||
}
|
||||
}
|
||||
|
||||
Variants { // For each monitor
|
||||
model: Quickshell.screens
|
||||
|
||||
Loader {
|
||||
required property var modelData
|
||||
readonly property HyprlandMonitor monitor: Hyprland.monitorFor(modelData)
|
||||
active: !ToplevelManager.activeToplevel?.activated
|
||||
sourceComponent: PanelWindow { // Window
|
||||
id: windowRoot
|
||||
screen: modelData
|
||||
property var textHorizontalAlignment: root.centerX / monitor.scale < windowRoot.width / 3 ? Text.AlignLeft :
|
||||
(root.centerX / monitor.scale > windowRoot.width * 2 / 3 ? Text.AlignRight : Text.AlignHCenter)
|
||||
|
||||
WlrLayershell.layer: WlrLayer.Bottom
|
||||
WlrLayershell.namespace: "quickshell:backgroundWidgets"
|
||||
|
||||
anchors {
|
||||
top: true
|
||||
bottom:true
|
||||
left: true
|
||||
right: true
|
||||
}
|
||||
color: "transparent"
|
||||
HyprlandWindow.visibleMask: Region {
|
||||
item: widgetBackground
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: widgetBackground
|
||||
property real verticalPadding: 20
|
||||
property real horizontalPadding: 30
|
||||
radius: 40
|
||||
color: root.colBackground
|
||||
implicitHeight: columnLayout.implicitHeight + verticalPadding * 2
|
||||
implicitWidth: columnLayout.implicitWidth + horizontalPadding * 2
|
||||
anchors {
|
||||
left: parent.left
|
||||
top: parent.top
|
||||
leftMargin: (root.centerX / monitor.scale - implicitWidth / 2)
|
||||
topMargin: (root.centerY / monitor.scale - implicitHeight / 2)
|
||||
Behavior on leftMargin {
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
}
|
||||
Behavior on topMargin {
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: columnLayout
|
||||
anchors.centerIn: parent
|
||||
spacing: -5
|
||||
|
||||
StyledText {
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: windowRoot.textHorizontalAlignment
|
||||
font.pixelSize: 95
|
||||
color: root.colText
|
||||
style: Text.Raised
|
||||
styleColor: Appearance.colors.colShadow
|
||||
text: DateTime.time
|
||||
}
|
||||
StyledText {
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: windowRoot.textHorizontalAlignment
|
||||
font.pixelSize: 25
|
||||
color: root.colText
|
||||
style: Text.Raised
|
||||
styleColor: Appearance.colors.colShadow
|
||||
text: DateTime.date
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -12,7 +12,6 @@ Item {
|
||||
|
||||
height: parent.height
|
||||
width: colLayout.width
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
id: colLayout
|
||||
|
||||
@@ -19,6 +19,13 @@ Scope {
|
||||
readonly property int osdHideMouseMoveThreshold: 20
|
||||
property bool showBarBackground: ConfigOptions.bar.showBackground
|
||||
|
||||
component VerticalBarSeparator: Rectangle {
|
||||
Layout.topMargin: barHeight / 3
|
||||
Layout.bottomMargin: barHeight / 3
|
||||
Layout.fillHeight: true
|
||||
implicitWidth: 1
|
||||
color: Appearance.colors.colOutlineVariant
|
||||
|
||||
|
||||
// Check screensList from config, If no screens are specified, show on all screens
|
||||
property var filteredScreens: {
|
||||
@@ -150,7 +157,7 @@ Scope {
|
||||
colBackground: barLeftSideMouseArea.hovered ? Appearance.colors.colLayer1Hover : ColorUtils.transparentize(Appearance.colors.colLayer1Hover, 1)
|
||||
colBackgroundHover: Appearance.colors.colLayer1Hover
|
||||
colRipple: Appearance.colors.colLayer1Active
|
||||
colBackgroundToggled: Appearance.m3colors.m3secondaryContainer
|
||||
colBackgroundToggled: Appearance.colors.colSecondaryContainer
|
||||
colBackgroundToggledHover: Appearance.colors.colSecondaryContainerHover
|
||||
colRippleToggled: Appearance.colors.colSecondaryContainerActive
|
||||
toggled: GlobalStates.sidebarLeftOpen
|
||||
@@ -177,7 +184,7 @@ Scope {
|
||||
}
|
||||
|
||||
ActiveWindow {
|
||||
visible: barRoot.useShortenedForm === 0
|
||||
visible: barRoot.useShortenedForm === 0 && width > 0 && height > 0
|
||||
Layout.rightMargin: Appearance.rounding.screenRounding
|
||||
Layout.fillWidth: true
|
||||
bar: barRoot
|
||||
@@ -189,7 +196,7 @@ Scope {
|
||||
RowLayout { // Middle section
|
||||
id: middleSection
|
||||
anchors.centerIn: parent
|
||||
spacing: 8
|
||||
spacing: ConfigOptions?.bar.borderless ? 4 : 8
|
||||
|
||||
RowLayout {
|
||||
id: leftCenterGroup
|
||||
@@ -210,9 +217,10 @@ Scope {
|
||||
|
||||
}
|
||||
|
||||
VerticalBarSeparator {visible: ConfigOptions?.bar.borderless}
|
||||
|
||||
RowLayout {
|
||||
id: middleCenterGroup
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
Workspaces {
|
||||
@@ -231,6 +239,8 @@ Scope {
|
||||
|
||||
}
|
||||
|
||||
VerticalBarSeparator {visible: ConfigOptions?.bar.borderless}
|
||||
|
||||
RowLayout {
|
||||
id: rightCenterGroup
|
||||
Layout.preferredWidth: leftCenterGroup.width
|
||||
@@ -344,7 +354,7 @@ Scope {
|
||||
colBackground: barRightSideMouseArea.hovered ? Appearance.colors.colLayer1Hover : ColorUtils.transparentize(Appearance.colors.colLayer1Hover, 1)
|
||||
colBackgroundHover: Appearance.colors.colLayer1Hover
|
||||
colRipple: Appearance.colors.colLayer1Active
|
||||
colBackgroundToggled: Appearance.m3colors.m3secondaryContainer
|
||||
colBackgroundToggled: Appearance.colors.colSecondaryContainer
|
||||
colBackgroundToggledHover: Appearance.colors.colSecondaryContainerHover
|
||||
colRippleToggled: Appearance.colors.colSecondaryContainerActive
|
||||
toggled: GlobalStates.sidebarRightOpen
|
||||
|
||||
@@ -48,7 +48,7 @@ Rectangle {
|
||||
lineWidth: 2
|
||||
value: percentage
|
||||
size: 26
|
||||
secondaryColor: (isLow && !isCharging) ? batteryLowBackground : Appearance.m3colors.m3secondaryContainer
|
||||
secondaryColor: (isLow && !isCharging) ? batteryLowBackground : Appearance.colors.colSecondaryContainer
|
||||
primaryColor: (isLow && !isCharging) ? batteryLowOnBackground : Appearance.m3colors.m3onSecondaryContainer
|
||||
fill: (isLow && !isCharging)
|
||||
|
||||
|
||||
@@ -62,13 +62,13 @@ Item {
|
||||
lineWidth: 2
|
||||
value: activePlayer?.position / activePlayer?.length
|
||||
size: 26
|
||||
secondaryColor: Appearance.m3colors.m3secondaryContainer
|
||||
secondaryColor: Appearance.colors.colSecondaryContainer
|
||||
primaryColor: Appearance.m3colors.m3onSecondaryContainer
|
||||
|
||||
MaterialSymbol {
|
||||
anchors.centerIn: parent
|
||||
fill: 1
|
||||
text: activePlayer?.isPlaying ? "pause" : "play_arrow"
|
||||
text: activePlayer?.isPlaying ? "pause" : "music_note"
|
||||
iconSize: Appearance.font.pixelSize.normal
|
||||
color: Appearance.m3colors.m3onSecondaryContainer
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ Item {
|
||||
lineWidth: 2
|
||||
value: percentage
|
||||
size: 26
|
||||
secondaryColor: Appearance.m3colors.m3secondaryContainer
|
||||
secondaryColor: Appearance.colors.colSecondaryContainer
|
||||
primaryColor: Appearance.m3colors.m3onSecondaryContainer
|
||||
|
||||
MaterialSymbol {
|
||||
|
||||
@@ -2,6 +2,7 @@ import "root:/"
|
||||
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
|
||||
import QtQuick.Layouts
|
||||
@@ -98,15 +99,17 @@ Item {
|
||||
implicitWidth: workspaceButtonWidth
|
||||
implicitHeight: workspaceButtonWidth
|
||||
radius: Appearance.rounding.full
|
||||
property var radiusLeft: (workspaceOccupied[index-1] && !(!activeWindow?.activated && monitor.activeWorkspace?.id === index)) ? 0 : Appearance.rounding.full
|
||||
property var radiusRight: (workspaceOccupied[index+1] && !(!activeWindow?.activated && monitor.activeWorkspace?.id === index+2)) ? 0 : Appearance.rounding.full
|
||||
property var leftOccupied: (workspaceOccupied[index-1] && !(!activeWindow?.activated && monitor.activeWorkspace?.id === index))
|
||||
property var rightOccupied: (workspaceOccupied[index+1] && !(!activeWindow?.activated && monitor.activeWorkspace?.id === index+2))
|
||||
property var radiusLeft: leftOccupied ? 0 : Appearance.rounding.full
|
||||
property var radiusRight: rightOccupied ? 0 : Appearance.rounding.full
|
||||
|
||||
topLeftRadius: radiusLeft
|
||||
bottomLeftRadius: radiusLeft
|
||||
topRightRadius: radiusRight
|
||||
bottomRightRadius: radiusRight
|
||||
|
||||
color: Appearance.colors.colLayer2
|
||||
color: ColorUtils.transparentize(Appearance.m3colors.m3secondaryContainer, 0.4)
|
||||
opacity: (workspaceOccupied[index] && !(!activeWindow?.activated && monitor.activeWorkspace?.id === index+1)) ? 1 : 0
|
||||
|
||||
Behavior on opacity {
|
||||
@@ -144,13 +147,13 @@ Item {
|
||||
Behavior on activeWorkspaceMargin {
|
||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
}
|
||||
Behavior on idx1 {
|
||||
Behavior on idx1 { // Leading anim
|
||||
NumberAnimation {
|
||||
duration: 100
|
||||
easing.type: Easing.OutSine
|
||||
}
|
||||
}
|
||||
Behavior on idx2 {
|
||||
Behavior on idx2 { // Following anim
|
||||
NumberAnimation {
|
||||
duration: 300
|
||||
easing.type: Easing.OutSine
|
||||
@@ -203,7 +206,7 @@ Item {
|
||||
elide: Text.ElideRight
|
||||
color: (monitor.activeWorkspace?.id == button.workspaceValue) ?
|
||||
Appearance.m3colors.m3onPrimary :
|
||||
(workspaceOccupied[index] ? Appearance.colors.colOnLayer1 :
|
||||
(workspaceOccupied[index] ? Appearance.m3colors.m3onSecondaryContainer :
|
||||
Appearance.colors.colOnLayer1Inactive)
|
||||
|
||||
Behavior on opacity {
|
||||
|
||||
@@ -15,11 +15,11 @@ Singleton {
|
||||
property QtObject sizes
|
||||
property string syntaxHighlightingTheme
|
||||
|
||||
// [!] Enabling transparency can affect readability when using light theme.
|
||||
// Extremely conservative transparency values for consistency and readability
|
||||
property real transparency: 0
|
||||
property real contentTransparency: 0
|
||||
// property real transparency: 0.15
|
||||
// property real contentTransparency: 0.5
|
||||
// property real transparency: m3colors.darkmode ? 0.05 : 0
|
||||
// property real contentTransparency: m3colors.darkmode ? 0.18 : 0
|
||||
|
||||
m3colors: QtObject {
|
||||
property bool darkmode: false
|
||||
@@ -106,10 +106,10 @@ Singleton {
|
||||
property color colOnLayer0: m3colors.m3onBackground
|
||||
property color colLayer0Hover: ColorUtils.transparentize(ColorUtils.mix(colLayer0, colOnLayer0, 0.9, root.contentTransparency))
|
||||
property color colLayer0Active: ColorUtils.transparentize(ColorUtils.mix(colLayer0, colOnLayer0, 0.8, root.contentTransparency))
|
||||
property color colLayer1: ColorUtils.transparentize(ColorUtils.mix(m3colors.m3surfaceContainerLow, m3colors.m3background, 0.7), root.contentTransparency);
|
||||
property color colLayer1: ColorUtils.transparentize(ColorUtils.mix(m3colors.m3surfaceContainerLow, m3colors.m3background, 0.8), root.contentTransparency);
|
||||
property color colOnLayer1: m3colors.m3onSurfaceVariant;
|
||||
property color colOnLayer1Inactive: ColorUtils.mix(colOnLayer1, colLayer1, 0.45);
|
||||
property color colLayer2: ColorUtils.transparentize(ColorUtils.mix(m3colors.m3surfaceContainer, m3colors.m3surfaceContainerHigh, 0.55), root.contentTransparency)
|
||||
property color colLayer2: ColorUtils.transparentize(ColorUtils.mix(m3colors.m3surfaceContainer, m3colors.m3surfaceContainerHigh, 0.7), root.contentTransparency)
|
||||
property color colOnLayer2: m3colors.m3onSurface;
|
||||
property color colOnLayer2Disabled: ColorUtils.mix(colOnLayer2, m3colors.m3background, 0.4);
|
||||
property color colLayer3: ColorUtils.transparentize(ColorUtils.mix(m3colors.m3surfaceContainerHigh, m3colors.m3onSurface, 0.96), root.contentTransparency)
|
||||
@@ -122,21 +122,30 @@ Singleton {
|
||||
property color colLayer3Hover: ColorUtils.transparentize(ColorUtils.mix(colLayer3, colOnLayer3, 0.90), root.contentTransparency)
|
||||
property color colLayer3Active: ColorUtils.transparentize(ColorUtils.mix(colLayer3, colOnLayer3, 0.80), root.contentTransparency);
|
||||
property color colPrimary: m3colors.m3primary
|
||||
property color colOnPrimary: m3colors.m3onPrimary
|
||||
property color colPrimaryHover: ColorUtils.mix(colors.colPrimary, colLayer1Hover, 0.87)
|
||||
property color colPrimaryActive: ColorUtils.mix(colors.colPrimary, colLayer1Active, 0.7)
|
||||
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 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 colSecondaryContainerHover: ColorUtils.mix(m3colors.m3secondaryContainer, colLayer1Hover, 0.6)
|
||||
property color colSecondaryContainerActive: ColorUtils.mix(m3colors.m3secondaryContainer, colLayer1Active, 0.54)
|
||||
property color colOnSecondaryContainer: m3colors.m3onSecondaryContainer
|
||||
property color colSurfaceContainerLow: ColorUtils.transparentize(m3colors.m3surfaceContainerLow, root.contentTransparency)
|
||||
property color colSurfaceContainer: ColorUtils.transparentize(m3colors.m3surfaceContainer, root.contentTransparency)
|
||||
property color colSurfaceContainerHigh: ColorUtils.transparentize(m3colors.m3surfaceContainerHigh, root.contentTransparency)
|
||||
property color colSurfaceContainerHighest: ColorUtils.transparentize(m3colors.m3surfaceContainerHighest, root.contentTransparency)
|
||||
property color colSurfaceContainerHighestHover: ColorUtils.mix(m3colors.m3surfaceContainerHighest, m3colors.m3onSurface, 0.95)
|
||||
property color colSurfaceContainerHighestActive: ColorUtils.mix(m3colors.m3surfaceContainerHighest, m3colors.m3onSurface, 0.85)
|
||||
property color colTooltip: "#3C4043" // m3colors.m3inverseSurface in the specs, but the m3 website actually uses this color
|
||||
property color colTooltip: m3colors.darkmode ? ColorUtils.mix(m3colors.m3background, "#3C4043", 0.5) : "#3C4043" // m3colors.m3inverseSurface in the specs, but the m3 website actually uses #3C4043
|
||||
property color colOnTooltip: "#F8F9FA" // m3colors.m3inverseOnSurface in the specs, but the m3 website actually uses this color
|
||||
property color colScrim: ColorUtils.transparentize(m3colors.m3scrim, 0.5)
|
||||
property color colShadow: ColorUtils.transparentize(m3colors.m3shadow, 0.85)
|
||||
property color colShadow: ColorUtils.transparentize(m3colors.m3shadow, 0.7)
|
||||
property color colOutlineVariant: m3colors.m3outlineVariant
|
||||
}
|
||||
|
||||
rounding: QtObject {
|
||||
@@ -176,6 +185,7 @@ Singleton {
|
||||
animationCurves: QtObject {
|
||||
readonly property list<real> expressiveFastSpatial: [0.42, 1.67, 0.21, 0.90, 1, 1] // Default, 350ms
|
||||
readonly property list<real> expressiveDefaultSpatial: [0.38, 1.21, 0.22, 1.00, 1, 1] // Default, 500ms
|
||||
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> emphasizedAccel: [0.3, 0, 0.8, 0.15, 1, 1]
|
||||
@@ -287,7 +297,7 @@ Singleton {
|
||||
property real searchWidthCollapsed: 260
|
||||
property real searchWidth: 450
|
||||
property real hyprlandGapsOut: 5
|
||||
property real elevationMargin: 8
|
||||
property real elevationMargin: 10
|
||||
property real fabShadowRadius: 5
|
||||
property real fabHoveredShadowRadius: 7
|
||||
}
|
||||
|
||||
@@ -9,14 +9,23 @@ Singleton {
|
||||
}
|
||||
|
||||
property QtObject appearance: QtObject {
|
||||
property int fakeScreenRounding: 1 // 0: None | 1: Always | 2: When not fullscreen
|
||||
property int fakeScreenRounding: 2 // 0: None | 1: Always | 2: When not fullscreen
|
||||
}
|
||||
|
||||
property QtObject audio: QtObject { // Values in %
|
||||
property QtObject protection: QtObject { // Prevent sudden bangs
|
||||
property bool enable: true
|
||||
property real maxAllowedIncrease: 10
|
||||
property real maxAllowed: 90 // Realistically should already provide some protection when it's 99...
|
||||
}
|
||||
}
|
||||
|
||||
property QtObject apps: QtObject {
|
||||
property string bluetooth: "better-control --bluetooth"
|
||||
property string bluetooth: "kcmshell6 kcm_bluetooth"
|
||||
property string imageViewer: "loupe"
|
||||
property string network: "XDG_CURRENT_DESKTOP=\"gnome\" gnome-control-center wifi"
|
||||
property string settings: "XDG_CURRENT_DESKTOP=\"gnome\" gnome-control-center"
|
||||
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
|
||||
}
|
||||
@@ -29,7 +38,7 @@ Singleton {
|
||||
|
||||
property QtObject bar: QtObject {
|
||||
property bool bottom: false // Instead of top
|
||||
property bool borderless: true
|
||||
property bool borderless: false // true for no grouping of items
|
||||
property string topLeftIcon: "spark" // Options: distro, spark
|
||||
property bool showBackground: true
|
||||
property QtObject resources: QtObject {
|
||||
@@ -48,15 +57,24 @@ Singleton {
|
||||
}
|
||||
|
||||
property QtObject dock: QtObject {
|
||||
property bool enable: false
|
||||
property real height: 60
|
||||
property real hoverRegionHeight: 3
|
||||
property bool pinnedOnStartup: false
|
||||
property bool hoverToReveal: false // When false, only reveals on empty workspace
|
||||
property list<string> pinnedApps: [ // IDs of pinned entries
|
||||
"org.kde.dolphin",
|
||||
"kitty",
|
||||
]
|
||||
}
|
||||
|
||||
property QtObject language: QtObject {
|
||||
property QtObject translator: QtObject {
|
||||
property string engine: "auto" // Run `trans -list-engines` for available engines. auto should use google
|
||||
property string targetLanguage: "auto" // Run `trans -list-all` for available languages
|
||||
property string sourceLanguage: "auto"
|
||||
}
|
||||
}
|
||||
|
||||
property QtObject networking: QtObject {
|
||||
property string userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36"
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ Singleton {
|
||||
property string booruPreviews: FileUtils.trimFileProtocol(`${Directories.cache}/media/boorus`)
|
||||
property string booruDownloads: FileUtils.trimFileProtocol(Directories.pictures + "/homework")
|
||||
property string booruDownloadsNsfw: FileUtils.trimFileProtocol(Directories.pictures + "/homework/🌶️")
|
||||
property string latexOutput: FileUtils.trimFileProtocol(`${Directories.cache}/latex`)
|
||||
property string latexOutput: FileUtils.trimFileProtocol(`${Directories.cache}/media/latex`)
|
||||
property string shellConfig: FileUtils.trimFileProtocol(`${Directories.config}/illogical-impulse`)
|
||||
property string shellConfigName: "config.json"
|
||||
property string shellConfigPath: `${Directories.shellConfig}/${Directories.shellConfigName}`
|
||||
@@ -32,6 +32,7 @@ Singleton {
|
||||
property string wallpaperSwitchScriptPath: FileUtils.trimFileProtocol(`${Directories.config}/quickshell/scripts/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}'`)
|
||||
|
||||
@@ -39,6 +39,30 @@ function colorWithSaturationOf(color1, color2) {
|
||||
return Qt.hsva(hue, sat, val, alpha);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a color with the given lightness and the hue, saturation, and alpha of the input color (using HSL).
|
||||
*
|
||||
* @param {string} color - The base color (any Qt.color-compatible string).
|
||||
* @param {number} lightness - The lightness value to use (0-1).
|
||||
* @returns {Qt.rgba} The resulting color.
|
||||
*/
|
||||
function colorWithLightness(color, lightness) {
|
||||
var c = Qt.color(color);
|
||||
return Qt.hsla(c.hslHue, c.hslSaturation, lightness, c.a);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a color with the lightness of color2 and the hue, saturation, and alpha of color1 (using HSL).
|
||||
*
|
||||
* @param {string} color1 - The base color (any Qt.color-compatible string).
|
||||
* @param {string} color2 - The color to take lightness from.
|
||||
* @returns {Qt.rgba} The resulting color.
|
||||
*/
|
||||
function colorWithLightnessOf(color1, color2) {
|
||||
var c2 = Qt.color(color2);
|
||||
return colorWithLightness(color1, c2.hslLightness);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapts color1 to the accent (hue and saturation) of color2 using HSL, keeping lightness and alpha from color1.
|
||||
*
|
||||
@@ -66,7 +90,7 @@ function adaptToAccent(color1, color2) {
|
||||
* @param {number} percentage - The mix ratio (0-1). 1 = all color1, 0 = all color2.
|
||||
* @returns {Qt.rgba} The resulting mixed color.
|
||||
*/
|
||||
function mix(color1, color2, percentage) {
|
||||
function mix(color1, color2, percentage = 0.5) {
|
||||
var c1 = Qt.color(color1);
|
||||
var c2 = Qt.color(color2);
|
||||
return Qt.rgba(percentage * c1.r + (1 - percentage) * c2.r, percentage * c1.g + (1 - percentage) * c2.g, percentage * c1.b + (1 - percentage) * c2.b, percentage * c1.a + (1 - percentage) * c2.a);
|
||||
|
||||
@@ -11,7 +11,7 @@ import QtQuick.Layouts
|
||||
*/
|
||||
Rectangle {
|
||||
id: root
|
||||
default property alias content: rowLayout.data
|
||||
default property alias data: rowLayout.data
|
||||
property real spacing: 5
|
||||
property real padding: 0
|
||||
property int clickIndex: rowLayout.clickIndex
|
||||
|
||||
@@ -14,8 +14,8 @@ Item {
|
||||
property int lineWidth: 2
|
||||
property real value: 0
|
||||
property color primaryColor: Appearance.m3colors.m3onSecondaryContainer
|
||||
property color secondaryColor: Appearance.m3colors.m3secondaryContainer
|
||||
property real gapAngle: Math.PI / 10
|
||||
property color secondaryColor: Appearance.colors.colSecondaryContainer
|
||||
property real gapAngle: Math.PI / 9
|
||||
property bool fill: false
|
||||
property int fillOverflow: 2
|
||||
property int animationDuration: 1000
|
||||
|
||||
@@ -15,7 +15,7 @@ Rectangle {
|
||||
property real extraBottomBorderWidth: 2
|
||||
property color borderColor: Appearance.colors.colOnLayer0
|
||||
property real borderRadius: 5
|
||||
property color keyColor: Appearance.m3colors.m3surfaceContainerLow
|
||||
property color keyColor: Appearance.colors.colSurfaceContainerLow
|
||||
implicitWidth: keyFace.implicitWidth + borderWidth * 2
|
||||
implicitHeight: keyFace.implicitHeight + borderWidth * 2 + extraBottomBorderWidth
|
||||
radius: borderRadius
|
||||
|
||||
@@ -6,23 +6,26 @@ Text {
|
||||
id: root
|
||||
property real iconSize: Appearance?.font.pixelSize.small ?? 16
|
||||
property real fill: 0
|
||||
renderType: Text.NativeRendering
|
||||
font.hintingPreference: Font.PreferFullHinting
|
||||
property real truncatedFill: Math.round(fill * 100) / 100 // Reduce memory consumption spikes from constant font remapping
|
||||
renderType: Text.CurveRendering
|
||||
font {
|
||||
hintingPreference: Font.PreferFullHinting
|
||||
family: Appearance?.font.family.iconMaterial ?? "Material Symbols Rounded"
|
||||
pixelSize: iconSize
|
||||
}
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
font.family: Appearance?.font.family.iconMaterial ?? "Material Symbols Rounded"
|
||||
font.pixelSize: iconSize
|
||||
color: Appearance.m3colors.m3onBackground
|
||||
|
||||
Behavior on fill {
|
||||
NumberAnimation {
|
||||
duration: Appearance?.animation.elementMoveFast.duration ?? 200
|
||||
easing.type: Appearance?.animation.elementMoveFast.type ?? Easing.BezierSpline
|
||||
easing.bezierCurve: Appearance?.animation.elementMoveFast.bezierCurve ?? [0.34, 0.80, 0.34, 1.00, 1, 1]
|
||||
}
|
||||
}
|
||||
// Behavior on fill {
|
||||
// NumberAnimation {
|
||||
// duration: Appearance?.animation.elementMoveFast.duration ?? 200
|
||||
// easing.type: Appearance?.animation.elementMoveFast.type ?? Easing.BezierSpline
|
||||
// easing.bezierCurve: Appearance?.animation.elementMoveFast.bezierCurve ?? [0.34, 0.80, 0.34, 1.00, 1, 1]
|
||||
// }
|
||||
// }
|
||||
|
||||
font.variableAxes: {
|
||||
"FILL": fill,
|
||||
"FILL": truncatedFill,
|
||||
// "wght": font.weight,
|
||||
// "GRAD": 0,
|
||||
"opsz": iconSize,
|
||||
|
||||
@@ -30,7 +30,7 @@ Button {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
radius: Appearance.rounding.full
|
||||
color: toggled ?
|
||||
(button.down ? Appearance.colors.colSecondaryContainerActive : button.hovered ? Appearance.colors.colSecondaryContainerHover : Appearance.m3colors.m3secondaryContainer) :
|
||||
(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 {
|
||||
|
||||
@@ -15,7 +15,7 @@ RippleButton {
|
||||
leftPadding: 15
|
||||
rightPadding: 15
|
||||
buttonRadius: Appearance.rounding.small
|
||||
colBackground: (urgency == NotificationUrgency.Critical) ? Appearance.m3colors.m3secondaryContainer : Appearance.m3colors.m3surfaceContainerHighest
|
||||
colBackground: (urgency == NotificationUrgency.Critical) ? Appearance.colors.colSecondaryContainer : Appearance.colors.colSurfaceContainerHighest
|
||||
colBackgroundHover: (urgency == NotificationUrgency.Critical) ? Appearance.colors.colSecondaryContainerHover : Appearance.colors.colSurfaceContainerHighestHover
|
||||
colRipple: (urgency == NotificationUrgency.Critical) ? Appearance.colors.colSecondaryContainerActive : Appearance.colors.colSurfaceContainerHighestActive
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ Rectangle { // App icon
|
||||
implicitWidth: size
|
||||
implicitHeight: size
|
||||
radius: Appearance.rounding.full
|
||||
color: Appearance.m3colors.m3secondaryContainer
|
||||
color: Appearance.colors.colSecondaryContainer
|
||||
Loader {
|
||||
id: materialSymbolLoader
|
||||
active: root.appIcon == ""
|
||||
|
||||
@@ -113,7 +113,7 @@ Item { // Notification group area
|
||||
id: background
|
||||
anchors.left: parent.left
|
||||
width: parent.width
|
||||
color: Appearance.m3colors.m3surfaceContainer
|
||||
color: Appearance.colors.colSurfaceContainer
|
||||
radius: Appearance.rounding.normal
|
||||
anchors.leftMargin: root.xOffset
|
||||
|
||||
@@ -154,42 +154,56 @@ Item { // Notification group area
|
||||
|
||||
ColumnLayout { // Content
|
||||
Layout.fillWidth: true
|
||||
spacing: expanded ?
|
||||
((root.multipleNotifications &&
|
||||
notificationGroup?.notifications[root.notificationCount - 1].image != "") ? 35 :
|
||||
5) : 0
|
||||
spacing: expanded ? (root.multipleNotifications ?
|
||||
(notificationGroup?.notifications[root.notificationCount - 1].image != "") ? 35 :
|
||||
5 : 0) : 0
|
||||
// spacing: 00
|
||||
Behavior on spacing {
|
||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
RowLayout { // App name (or summary when there's only 1 notif) and time
|
||||
Item { // App name (or summary when there's only 1 notif) and time
|
||||
id: topRow
|
||||
spacing: 0
|
||||
// spacing: 0
|
||||
Layout.fillWidth: true
|
||||
property real fontSize: Appearance.font.pixelSize.smaller
|
||||
property bool showAppName: root.multipleNotifications
|
||||
implicitHeight: Math.max(topTextRow.implicitHeight, expandButton.implicitHeight)
|
||||
|
||||
StyledText {
|
||||
id: appName
|
||||
text: (topRow.showAppName ?
|
||||
notificationGroup?.appName :
|
||||
notificationGroup?.notifications[0]?.summary) || ""
|
||||
font.pixelSize: topRow.showAppName ?
|
||||
topRow.fontSize :
|
||||
Appearance.font.pixelSize.small
|
||||
color: topRow.showAppName ?
|
||||
Appearance.colors.colSubtext :
|
||||
Appearance.colors.colOnLayer2
|
||||
RowLayout {
|
||||
id: topTextRow
|
||||
anchors.left: parent.left
|
||||
anchors.right: expandButton.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 5
|
||||
StyledText {
|
||||
id: appName
|
||||
elide: Text.ElideRight
|
||||
Layout.fillWidth: true
|
||||
text: (topRow.showAppName ?
|
||||
notificationGroup?.appName :
|
||||
notificationGroup?.notifications[0]?.summary) || ""
|
||||
font.pixelSize: topRow.showAppName ?
|
||||
topRow.fontSize :
|
||||
Appearance.font.pixelSize.small
|
||||
color: topRow.showAppName ?
|
||||
Appearance.colors.colSubtext :
|
||||
Appearance.colors.colOnLayer2
|
||||
}
|
||||
StyledText {
|
||||
id: timeText
|
||||
// Layout.fillWidth: true
|
||||
Layout.rightMargin: 10
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
text: NotificationUtils.getFriendlyNotifTimeString(notificationGroup?.time)
|
||||
font.pixelSize: topRow.fontSize
|
||||
color: Appearance.colors.colSubtext
|
||||
}
|
||||
}
|
||||
StyledText {
|
||||
id: timeText
|
||||
text: " • " + NotificationUtils.getFriendlyNotifTimeString(notificationGroup?.time)
|
||||
font.pixelSize: topRow.fontSize
|
||||
color: Appearance.colors.colSubtext
|
||||
Layout.alignment: Qt.AlignRight
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
Item { Layout.fillWidth: true }
|
||||
NotificationGroupExpandButton {
|
||||
id: expandButton
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
count: root.notificationCount
|
||||
expanded: root.expanded
|
||||
fontSize: topRow.fontSize
|
||||
|
||||
@@ -129,9 +129,9 @@ Item { // Notification item area
|
||||
|
||||
color: (expanded && !onlyNotification) ?
|
||||
(notificationObject.urgency == NotificationUrgency.Critical) ?
|
||||
ColorUtils.mix(Appearance.m3colors.m3secondaryContainer, Appearance.colors.colLayer2, 0.35) :
|
||||
(Appearance.m3colors.m3surfaceContainerHigh) :
|
||||
ColorUtils.transparentize(Appearance.m3colors.m3surfaceContainerHighest)
|
||||
ColorUtils.mix(Appearance.colors.colSecondaryContainer, Appearance.colors.colLayer2, 0.35) :
|
||||
(Appearance.colors.colSurfaceContainerHigh) :
|
||||
ColorUtils.transparentize(Appearance.colors.colSurfaceContainerHighest)
|
||||
|
||||
implicitHeight: expanded ? (contentColumn.implicitHeight + padding * 2) : summaryRow.implicitHeight
|
||||
Behavior on implicitHeight {
|
||||
|
||||
@@ -110,13 +110,29 @@ TabButton {
|
||||
animation: Appearance?.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Item {
|
||||
id: ripple
|
||||
|
||||
radius: Appearance?.rounding.full ?? 9999
|
||||
color: button.colRipple
|
||||
width: ripple.implicitWidth
|
||||
height: ripple.implicitHeight
|
||||
opacity: 0
|
||||
|
||||
property real implicitWidth: 0
|
||||
property real implicitHeight: 0
|
||||
visible: width > 0 && height > 0
|
||||
|
||||
Behavior on opacity {
|
||||
animation: Appearance?.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
|
||||
RadialGradient {
|
||||
anchors.fill: parent
|
||||
gradient: Gradient {
|
||||
GradientStop { position: 0.0; color: button.colRipple }
|
||||
GradientStop { position: 0.3; color: button.colRipple }
|
||||
GradientStop { position: 0.5 ; color: Qt.rgba(button.colRipple.r, button.colRipple.g, button.colRipple.b, 0) }
|
||||
}
|
||||
}
|
||||
|
||||
transform: Translate {
|
||||
x: -ripple.width / 2
|
||||
y: -ripple.height / 2
|
||||
|
||||
@@ -13,6 +13,7 @@ Item {
|
||||
|
||||
implicitWidth: (reveal || vertical) ? childrenRect.width : 0
|
||||
implicitHeight: (reveal || !vertical) ? childrenRect.height : 0
|
||||
visible: width > 0 && height > 0
|
||||
|
||||
Behavior on implicitWidth {
|
||||
enabled: !vertical
|
||||
|
||||
@@ -150,16 +150,29 @@ Button {
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Item {
|
||||
id: ripple
|
||||
|
||||
radius: Appearance?.rounding.full ?? 9999
|
||||
width: ripple.implicitWidth
|
||||
height: ripple.implicitHeight
|
||||
opacity: 0
|
||||
color: root.rippleColor
|
||||
Behavior on color {
|
||||
visible: width > 0 && height > 0
|
||||
|
||||
property real implicitWidth: 0
|
||||
property real implicitHeight: 0
|
||||
|
||||
Behavior on opacity {
|
||||
animation: Appearance?.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
|
||||
RadialGradient {
|
||||
anchors.fill: parent
|
||||
gradient: Gradient {
|
||||
GradientStop { position: 0.0; color: root.rippleColor }
|
||||
GradientStop { position: 0.3; color: root.rippleColor }
|
||||
GradientStop { position: 0.5; color: Qt.rgba(root.rippleColor.r, root.rippleColor.g, root.rippleColor.b, 0) }
|
||||
}
|
||||
}
|
||||
|
||||
transform: Translate {
|
||||
x: -ripple.width / 2
|
||||
y: -ripple.height / 2
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
import "root:/modules/common"
|
||||
import "root:/modules/common/widgets"
|
||||
import "root:/services"
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property real dialogPadding: 15
|
||||
property real dialogMargin: 30
|
||||
property string titleText: "Selection Dialog"
|
||||
property alias items: choiceModel.values
|
||||
property int selectedId: choiceListView.currentIndex
|
||||
property var defaultChoice
|
||||
|
||||
signal canceled();
|
||||
signal selected(var result);
|
||||
|
||||
Rectangle { // Scrim
|
||||
id: scrimOverlay
|
||||
anchors.fill: parent
|
||||
radius: Appearance.rounding.small
|
||||
color: Appearance.colors.colScrim
|
||||
MouseArea {
|
||||
hoverEnabled: true
|
||||
anchors.fill: parent
|
||||
preventStealing: true
|
||||
propagateComposedEvents: false
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle { // The dialog
|
||||
id: dialog
|
||||
color: Appearance.colors.colSurfaceContainerHigh
|
||||
radius: Appearance.rounding.normal
|
||||
anchors.fill: parent
|
||||
anchors.margins: dialogMargin
|
||||
implicitHeight: dialogColumnLayout.implicitHeight
|
||||
|
||||
ColumnLayout {
|
||||
id: dialogColumnLayout
|
||||
anchors.fill: parent
|
||||
spacing: 16
|
||||
|
||||
StyledText {
|
||||
id: dialogTitle
|
||||
Layout.topMargin: dialogPadding
|
||||
Layout.leftMargin: dialogPadding
|
||||
Layout.rightMargin: dialogPadding
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
color: Appearance.m3colors.m3onSurface
|
||||
font.pixelSize: Appearance.font.pixelSize.larger
|
||||
text: root.titleText
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
color: Appearance.m3colors.m3outline
|
||||
implicitHeight: 1
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: dialogPadding
|
||||
Layout.rightMargin: dialogPadding
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: choiceListView
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
clip: true
|
||||
currentIndex: root.defaultChoice !== undefined ? root.items.indexOf(root.defaultChoice) : -1
|
||||
|
||||
model: ScriptModel {
|
||||
id: choiceModel
|
||||
}
|
||||
|
||||
delegate: StyledRadioButton {
|
||||
id: radioButton
|
||||
required property var modelData
|
||||
required property int index
|
||||
anchors {
|
||||
left: parent?.left
|
||||
right: parent?.right
|
||||
leftMargin: root.dialogPadding
|
||||
rightMargin: root.dialogPadding
|
||||
}
|
||||
|
||||
description: modelData.toString()
|
||||
checked: index === choiceListView.currentIndex
|
||||
|
||||
onCheckedChanged: {
|
||||
if (checked) {
|
||||
choiceListView.currentIndex = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
color: Appearance.m3colors.m3outline
|
||||
implicitHeight: 1
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: dialogPadding
|
||||
Layout.rightMargin: dialogPadding
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: dialogButtonsRowLayout
|
||||
Layout.bottomMargin: dialogPadding
|
||||
Layout.leftMargin: dialogPadding
|
||||
Layout.rightMargin: dialogPadding
|
||||
Layout.alignment: Qt.AlignRight
|
||||
|
||||
DialogButton {
|
||||
buttonText: qsTr("Cancel")
|
||||
onClicked: root.canceled()
|
||||
}
|
||||
DialogButton {
|
||||
buttonText: qsTr("OK")
|
||||
onClicked: root.selected(
|
||||
root.selectedId === -1 ? null :
|
||||
root.items[root.selectedId]
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import "root:/modules/common"
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
Label {
|
||||
renderType: Text.NativeRendering
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
font {
|
||||
hintingPreference: Font.PreferFullHinting
|
||||
family: Appearance?.font.family.main ?? "sans-serif"
|
||||
pixelSize: Appearance?.font.pixelSize.small ?? 15
|
||||
}
|
||||
color: Appearance?.m3colors.m3onBackground ?? "black"
|
||||
linkColor: Appearance?.m3colors.m3primary
|
||||
}
|
||||
@@ -18,6 +18,7 @@ ListView {
|
||||
property real removeOvershoot: 20 // Account for gaps and bouncy animations
|
||||
property int dragIndex: -1
|
||||
property real dragDistance: 0
|
||||
property bool popin: true
|
||||
|
||||
function resetDrag() {
|
||||
root.dragIndex = -1
|
||||
@@ -27,7 +28,7 @@ ListView {
|
||||
add: Transition {
|
||||
animations: [
|
||||
Appearance?.animation.elementMove.numberAnimation.createObject(this, {
|
||||
properties: "opacity,scale",
|
||||
properties: popin ? "opacity,scale" : "opacity",
|
||||
from: 0,
|
||||
to: 1,
|
||||
}),
|
||||
@@ -40,46 +41,46 @@ ListView {
|
||||
property: "y",
|
||||
}),
|
||||
Appearance?.animation.elementMove.numberAnimation.createObject(this, {
|
||||
properties: "opacity,scale",
|
||||
properties: popin ? "opacity,scale" : "opacity",
|
||||
to: 1,
|
||||
}),
|
||||
]
|
||||
}
|
||||
|
||||
displaced: Transition {
|
||||
animations: [
|
||||
Appearance?.animation.elementMove.numberAnimation.createObject(this, {
|
||||
property: "y",
|
||||
}),
|
||||
Appearance?.animation.elementMove.numberAnimation.createObject(this, {
|
||||
properties: "opacity,scale",
|
||||
to: 1,
|
||||
}),
|
||||
]
|
||||
}
|
||||
// displaced: Transition {
|
||||
// animations: [
|
||||
// Appearance?.animation.elementMove.numberAnimation.createObject(this, {
|
||||
// property: "y",
|
||||
// }),
|
||||
// Appearance?.animation.elementMove.numberAnimation.createObject(this, {
|
||||
// properties: "opacity,scale",
|
||||
// to: 1,
|
||||
// }),
|
||||
// ]
|
||||
// }
|
||||
|
||||
move: Transition {
|
||||
animations: [
|
||||
Appearance?.animation.elementMove.numberAnimation.createObject(this, {
|
||||
property: "y",
|
||||
}),
|
||||
Appearance?.animation.elementMove.numberAnimation.createObject(this, {
|
||||
properties: "opacity,scale",
|
||||
to: 1,
|
||||
}),
|
||||
]
|
||||
}
|
||||
moveDisplaced: Transition {
|
||||
animations: [
|
||||
Appearance?.animation.elementMove.numberAnimation.createObject(this, {
|
||||
property: "y",
|
||||
}),
|
||||
Appearance?.animation.elementMove.numberAnimation.createObject(this, {
|
||||
properties: "opacity,scale",
|
||||
to: 1,
|
||||
}),
|
||||
]
|
||||
}
|
||||
// move: Transition {
|
||||
// animations: [
|
||||
// Appearance?.animation.elementMove.numberAnimation.createObject(this, {
|
||||
// property: "y",
|
||||
// }),
|
||||
// Appearance?.animation.elementMove.numberAnimation.createObject(this, {
|
||||
// properties: "opacity,scale",
|
||||
// to: 1,
|
||||
// }),
|
||||
// ]
|
||||
// }
|
||||
// moveDisplaced: Transition {
|
||||
// animations: [
|
||||
// Appearance?.animation.elementMove.numberAnimation.createObject(this, {
|
||||
// property: "y",
|
||||
// }),
|
||||
// Appearance?.animation.elementMove.numberAnimation.createObject(this, {
|
||||
// properties: "opacity,scale",
|
||||
// to: 1,
|
||||
// }),
|
||||
// ]
|
||||
// }
|
||||
|
||||
remove: Transition {
|
||||
animations: [
|
||||
|
||||
@@ -19,7 +19,7 @@ Slider {
|
||||
property real handleLimit: root.backgroundDotMargins
|
||||
property real trackHeight: 30 * scale
|
||||
property color highlightColor: Appearance.colors.colPrimary
|
||||
property color trackColor: Appearance.m3colors.m3secondaryContainer
|
||||
property color trackColor: Appearance.colors.colSecondaryContainer
|
||||
property color handleColor: Appearance.m3colors.m3onSecondaryContainer
|
||||
property real trackRadius: Appearance.rounding.verysmall * scale
|
||||
property real unsharpenRadius: Appearance.rounding.unsharpen
|
||||
|
||||
@@ -13,7 +13,7 @@ Switch {
|
||||
implicitHeight: 32 * root.scale
|
||||
implicitWidth: 52 * root.scale
|
||||
property color activeColor: Appearance?.colors.colPrimary ?? "#685496"
|
||||
property color inactiveColor: Appearance?.m3colors.m3surfaceContainerHighest ?? "#45464F"
|
||||
property color inactiveColor: Appearance?.colors.colSurfaceContainerHighest ?? "#45464F"
|
||||
|
||||
PointingHandInteraction {}
|
||||
|
||||
|
||||
@@ -11,4 +11,5 @@ Text {
|
||||
pixelSize: Appearance?.font.pixelSize.small ?? 15
|
||||
}
|
||||
color: Appearance?.m3colors.m3onBackground ?? "black"
|
||||
linkColor: Appearance?.m3colors.m3primary
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import QtQuick.Controls
|
||||
TextArea {
|
||||
renderType: Text.NativeRendering
|
||||
selectedTextColor: Appearance.m3colors.m3onSecondaryContainer
|
||||
selectionColor: Appearance.m3colors.m3secondaryContainer
|
||||
selectionColor: Appearance.colors.colSecondaryContainer
|
||||
placeholderTextColor: Appearance.m3colors.m3outline
|
||||
font {
|
||||
family: Appearance?.font.family.main ?? "sans-serif"
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
import "root:/modules/common"
|
||||
import "root:/modules/common/widgets"
|
||||
import "root:/services"
|
||||
import "root:/modules/common/functions/color_utils.js" as ColorUtils
|
||||
import QtQuick
|
||||
import QtQuick.Effects
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
|
||||
Canvas { // Visualizer
|
||||
id: root
|
||||
property list<var> points
|
||||
property list<var> smoothPoints
|
||||
property real maxVisualizerValue: 1000
|
||||
property int smoothing: 2
|
||||
property bool live: true
|
||||
property color color: Appearance.m3colors.m3primary
|
||||
|
||||
onPointsChanged: () => {
|
||||
root.requestPaint()
|
||||
}
|
||||
|
||||
anchors.fill: parent
|
||||
onPaint: {
|
||||
var ctx = getContext("2d");
|
||||
ctx.clearRect(0, 0, width, height);
|
||||
|
||||
var points = root.points;
|
||||
var maxVal = root.maxVisualizerValue || 1;
|
||||
var h = height;
|
||||
var w = width;
|
||||
var n = points.length;
|
||||
if (n < 2) return;
|
||||
|
||||
// Smoothing: simple moving average (optional)
|
||||
var smoothWindow = root.smoothing; // adjust for more/less smoothing
|
||||
root.smoothPoints = [];
|
||||
for (var i = 0; i < n; ++i) {
|
||||
var sum = 0, count = 0;
|
||||
for (var j = -smoothWindow; j <= smoothWindow; ++j) {
|
||||
var idx = Math.max(0, Math.min(n - 1, i + j));
|
||||
sum += points[idx];
|
||||
count++;
|
||||
}
|
||||
root.smoothPoints.push(sum / count);
|
||||
}
|
||||
if (!root.live) smoothedPoints.fill(0); // If not playing, show no points
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(0, h);
|
||||
for (var i = 0; i < n; ++i) {
|
||||
var x = i * w / (n - 1);
|
||||
var y = h - (root.smoothPoints[i] / maxVal) * h;
|
||||
ctx.lineTo(x, y);
|
||||
}
|
||||
ctx.lineTo(w, h);
|
||||
ctx.closePath();
|
||||
|
||||
ctx.fillStyle = Qt.rgba(
|
||||
root.color.r,
|
||||
root.color.g,
|
||||
root.color.b,
|
||||
0.15
|
||||
);
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
layer.enabled: true
|
||||
layer.effect: MultiEffect { // Blur a bit to obscure away the points
|
||||
source: root
|
||||
saturation: 0.2
|
||||
blurEnabled: true
|
||||
blurMax: 7
|
||||
blur: 1
|
||||
}
|
||||
}
|
||||
@@ -18,115 +18,130 @@ Scope { // Scope
|
||||
|
||||
Variants { // For each monitor
|
||||
model: Quickshell.screens
|
||||
PanelWindow { // Window
|
||||
|
||||
Loader {
|
||||
id: dockLoader
|
||||
required property var modelData
|
||||
id: dockRoot
|
||||
screen: modelData
|
||||
|
||||
property bool reveal: root.pinned || dockMouseArea.containsMouse || dockApps.requestDockShow
|
||||
active: ConfigOptions?.dock.hoverToReveal || (!ToplevelManager.activeToplevel?.activated)
|
||||
|
||||
anchors {
|
||||
bottom: true
|
||||
left: true
|
||||
right: true
|
||||
}
|
||||
sourceComponent: PanelWindow { // Window
|
||||
id: dockRoot
|
||||
screen: dockLoader.modelData
|
||||
|
||||
property bool reveal: root.pinned
|
||||
|| (ConfigOptions?.dock.hoverToReveal && dockMouseArea.containsMouse)
|
||||
|| dockApps.requestDockShow
|
||||
|| (!ToplevelManager.activeToplevel?.activated)
|
||||
|
||||
function hide() {
|
||||
cheatsheetLoader.active = false
|
||||
}
|
||||
exclusiveZone: root.pinned ? implicitHeight - Appearance.sizes.hyprlandGapsOut : 0
|
||||
|
||||
implicitWidth: dockBackground.implicitWidth
|
||||
WlrLayershell.namespace: "quickshell:dock"
|
||||
color: "transparent"
|
||||
|
||||
implicitHeight: (ConfigOptions?.dock.height ?? 70) + Appearance.sizes.elevationMargin + Appearance.sizes.hyprlandGapsOut
|
||||
|
||||
mask: Region {
|
||||
item: dockMouseArea
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: dockMouseArea
|
||||
anchors.top: parent.top
|
||||
height: parent.height
|
||||
anchors.topMargin: dockRoot.reveal ? 0 : dockRoot.implicitHeight - ConfigOptions.dock.hoverRegionHeight
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
hoverEnabled: true
|
||||
|
||||
Behavior on anchors.topMargin {
|
||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
anchors {
|
||||
bottom: true
|
||||
left: true
|
||||
right: true
|
||||
}
|
||||
|
||||
Item {
|
||||
id: dockHoverRegion
|
||||
anchors.fill: parent
|
||||
exclusiveZone: root.pinned ? implicitHeight
|
||||
- (Appearance.sizes.hyprlandGapsOut)
|
||||
- (Appearance.sizes.elevationMargin - Appearance.sizes.hyprlandGapsOut) : 0
|
||||
|
||||
implicitWidth: dockBackground.implicitWidth
|
||||
WlrLayershell.namespace: "quickshell:dock"
|
||||
color: "transparent"
|
||||
|
||||
implicitHeight: (ConfigOptions?.dock.height ?? 70) + Appearance.sizes.elevationMargin + Appearance.sizes.hyprlandGapsOut
|
||||
|
||||
mask: Region {
|
||||
item: dockMouseArea
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: dockMouseArea
|
||||
anchors.top: parent.top
|
||||
height: parent.height
|
||||
anchors.topMargin: dockRoot.reveal ? 0 :
|
||||
ConfigOptions?.dock.hoverToReveal ? (dockRoot.implicitHeight - ConfigOptions.dock.hoverRegionHeight) :
|
||||
(dockRoot.implicitHeight + 1)
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
hoverEnabled: true
|
||||
|
||||
Behavior on anchors.topMargin {
|
||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
Item {
|
||||
id: dockBackground
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
id: dockHoverRegion
|
||||
anchors.fill: parent
|
||||
|
||||
implicitWidth: dockRow.implicitWidth + 5 * 2
|
||||
height: parent.height - Appearance.sizes.elevationMargin - Appearance.sizes.hyprlandGapsOut
|
||||
Item { // Wrapper for the dock background
|
||||
id: dockBackground
|
||||
anchors {
|
||||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
StyledRectangularShadow {
|
||||
target: dockVisualBackground
|
||||
}
|
||||
Rectangle {
|
||||
id: dockVisualBackground
|
||||
property real margin: Appearance.sizes.elevationMargin
|
||||
anchors.fill: parent
|
||||
anchors.topMargin: margin
|
||||
anchors.bottomMargin: margin
|
||||
color: Appearance.colors.colLayer0
|
||||
radius: Appearance.rounding.large
|
||||
}
|
||||
implicitWidth: dockRow.implicitWidth + 5 * 2
|
||||
height: parent.height - Appearance.sizes.elevationMargin - Appearance.sizes.hyprlandGapsOut
|
||||
|
||||
RowLayout {
|
||||
id: dockRow
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
spacing: 3
|
||||
property real padding: 5
|
||||
StyledRectangularShadow {
|
||||
target: dockVisualBackground
|
||||
}
|
||||
Rectangle { // The real rectangle that is visible
|
||||
id: dockVisualBackground
|
||||
property real margin: Appearance.sizes.elevationMargin
|
||||
anchors.fill: parent
|
||||
anchors.topMargin: Appearance.sizes.elevationMargin
|
||||
anchors.bottomMargin: Appearance.sizes.hyprlandGapsOut
|
||||
color: Appearance.colors.colLayer0
|
||||
radius: Appearance.rounding.large
|
||||
}
|
||||
|
||||
VerticalButtonGroup {
|
||||
GroupButton { // Pin button
|
||||
baseWidth: 35
|
||||
baseHeight: 35
|
||||
clickedWidth: baseWidth
|
||||
clickedHeight: baseHeight + 20
|
||||
buttonRadius: Appearance.rounding.normal
|
||||
toggled: root.pinned
|
||||
onClicked: root.pinned = !root.pinned
|
||||
RowLayout {
|
||||
id: dockRow
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
spacing: 3
|
||||
property real padding: 5
|
||||
|
||||
VerticalButtonGroup {
|
||||
Layout.topMargin: Appearance.sizes.hyprlandGapsOut // why does this work
|
||||
GroupButton { // Pin button
|
||||
baseWidth: 35
|
||||
baseHeight: 35
|
||||
clickedWidth: baseWidth
|
||||
clickedHeight: baseHeight + 20
|
||||
buttonRadius: Appearance.rounding.normal
|
||||
toggled: root.pinned
|
||||
onClicked: root.pinned = !root.pinned
|
||||
contentItem: MaterialSymbol {
|
||||
text: "keep"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
iconSize: Appearance.font.pixelSize.larger
|
||||
color: root.pinned ? Appearance.m3colors.m3onPrimary : Appearance.colors.colOnLayer0
|
||||
}
|
||||
}
|
||||
}
|
||||
DockSeparator {}
|
||||
DockApps { id: dockApps; }
|
||||
DockSeparator {}
|
||||
DockButton {
|
||||
Layout.fillHeight: true
|
||||
onClicked: Hyprland.dispatch("global quickshell:overviewToggle")
|
||||
contentItem: MaterialSymbol {
|
||||
text: "keep"
|
||||
anchors.fill: parent
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
iconSize: Appearance.font.pixelSize.larger
|
||||
color: root.pinned ? Appearance.m3colors.m3onPrimary : Appearance.colors.colOnLayer0
|
||||
font.pixelSize: parent.width / 2
|
||||
text: "apps"
|
||||
color: Appearance.colors.colOnLayer0
|
||||
}
|
||||
}
|
||||
}
|
||||
DockSeparator {}
|
||||
DockApps { id: dockApps }
|
||||
DockSeparator {}
|
||||
DockButton {
|
||||
onClicked: Hyprland.dispatch("global quickshell:overviewToggle")
|
||||
contentItem: MaterialSymbol {
|
||||
anchors.centerIn: parent
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font.pixelSize: parent.width / 2
|
||||
text: "apps"
|
||||
color: Appearance.colors.colOnLayer0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import "root:/"
|
||||
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
|
||||
import QtQuick.Effects
|
||||
@@ -13,32 +14,102 @@ import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
|
||||
DockButton {
|
||||
id: appButton
|
||||
required property var appToplevel
|
||||
id: root
|
||||
property var appToplevel
|
||||
property var appListRoot
|
||||
property int lastFocused: -1
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
acceptedButtons: Qt.NoButton
|
||||
onEntered: {
|
||||
appListRoot.lastHoveredButton = appButton
|
||||
appListRoot.buttonHovered = true
|
||||
lastFocused = appToplevel.toplevels.length - 1
|
||||
property real iconSize: 35
|
||||
property real countDotWidth: 10
|
||||
property real countDotHeight: 4
|
||||
property bool appIsActive: appToplevel.toplevels.find(t => (t.activated == true)) !== undefined
|
||||
|
||||
property bool isSeparator: appToplevel.appId === "SEPARATOR"
|
||||
property var desktopEntry: DesktopEntries.byId(appToplevel.appId)
|
||||
enabled: !isSeparator
|
||||
implicitWidth: isSeparator ? 1 : implicitHeight - topInset - bottomInset
|
||||
|
||||
Loader {
|
||||
active: isSeparator
|
||||
anchors {
|
||||
fill: parent
|
||||
topMargin: dockVisualBackground.margin + dockRow.padding + Appearance.rounding.normal
|
||||
bottomMargin: dockVisualBackground.margin + dockRow.padding + Appearance.rounding.normal
|
||||
}
|
||||
onExited: {
|
||||
if (appListRoot.lastHoveredButton === appButton) {
|
||||
appListRoot.buttonHovered = false
|
||||
sourceComponent: DockSeparator {}
|
||||
}
|
||||
|
||||
Loader {
|
||||
anchors.fill: parent
|
||||
active: appToplevel.toplevels.length > 0
|
||||
sourceComponent: MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
acceptedButtons: Qt.NoButton
|
||||
onEntered: {
|
||||
appListRoot.lastHoveredButton = root
|
||||
appListRoot.buttonHovered = true
|
||||
lastFocused = appToplevel.toplevels.length - 1
|
||||
}
|
||||
onExited: {
|
||||
if (appListRoot.lastHoveredButton === root) {
|
||||
appListRoot.buttonHovered = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
if (appToplevel.toplevels.length === 0) {
|
||||
root.desktopEntry?.execute();
|
||||
return;
|
||||
}
|
||||
lastFocused = (lastFocused + 1) % appToplevel.toplevels.length
|
||||
appToplevel.toplevels[lastFocused].activate()
|
||||
}
|
||||
contentItem: IconImage {
|
||||
id: iconImage
|
||||
source: Quickshell.iconPath(AppSearch.guessIcon(appToplevel.appId), "image-missing")
|
||||
|
||||
middleClickAction: () => {
|
||||
root.desktopEntry?.execute();
|
||||
}
|
||||
|
||||
contentItem: Loader {
|
||||
active: !isSeparator
|
||||
sourceComponent: Item {
|
||||
anchors.centerIn: parent
|
||||
|
||||
Loader {
|
||||
id: iconImageLoader
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
active: !root.isSeparator
|
||||
sourceComponent: IconImage {
|
||||
source: Quickshell.iconPath(AppSearch.guessIcon(appToplevel.appId), "image-missing")
|
||||
implicitSize: root.iconSize
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: 3
|
||||
anchors {
|
||||
top: iconImageLoader.bottom
|
||||
topMargin: 2
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
Repeater {
|
||||
model: Math.min(appToplevel.toplevels.length, 3)
|
||||
delegate: Rectangle {
|
||||
required property int index
|
||||
radius: Appearance.rounding.full
|
||||
implicitWidth: (appToplevel.toplevels.length <= 3) ?
|
||||
root.countDotWidth : root.countDotHeight // Circles when too many
|
||||
implicitHeight: root.countDotHeight
|
||||
color: appIsActive ? Appearance.colors.colPrimary : ColorUtils.transparentize(Appearance.colors.colOnLayer0, 0.4)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,40 +23,66 @@ Item {
|
||||
property Item lastHoveredButton
|
||||
property bool buttonHovered: false
|
||||
property bool requestDockShow: previewPopup.show
|
||||
property real popupX: parentWindow.mapFromItem(root.lastHoveredButton, root.lastHoveredButton.width / 2, root.lastHoveredButton.height / 2).x - implicitWidth / 2
|
||||
|
||||
implicitWidth: rowLayout.implicitWidth
|
||||
implicitHeight: rowLayout.implicitHeight
|
||||
|
||||
RowLayout {
|
||||
id: rowLayout
|
||||
Layout.fillHeight: true
|
||||
Layout.topMargin: Appearance.sizes.hyprlandGapsOut // why does this work
|
||||
implicitWidth: listView.implicitWidth
|
||||
|
||||
StyledListView {
|
||||
id: listView
|
||||
spacing: 2
|
||||
orientation: ListView.Horizontal
|
||||
anchors {
|
||||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
}
|
||||
implicitWidth: contentWidth
|
||||
|
||||
Repeater {
|
||||
model: ScriptModel {
|
||||
objectProp: "appId"
|
||||
values: {
|
||||
var map = new Map();
|
||||
Behavior on implicitWidth {
|
||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
for (const toplevel of ToplevelManager.toplevels.values) {
|
||||
if (!map.has(toplevel.appId.toLowerCase())) map.set(toplevel.appId.toLowerCase(), []);
|
||||
map.get(toplevel.appId.toLowerCase()).push(toplevel);
|
||||
}
|
||||
model: ScriptModel {
|
||||
objectProp: "appId"
|
||||
values: {
|
||||
var map = new Map();
|
||||
|
||||
var values = [];
|
||||
|
||||
for (const [key, value] of map) {
|
||||
values.push({ appId: key, toplevels: value });
|
||||
}
|
||||
|
||||
return values;
|
||||
// Pinned apps
|
||||
const pinnedApps = ConfigOptions?.dock.pinnedApps ?? [];
|
||||
for (const appId of pinnedApps) {
|
||||
if (!map.has(appId.toLowerCase())) map.set(appId.toLowerCase(), ({
|
||||
pinned: true,
|
||||
toplevels: []
|
||||
}));
|
||||
}
|
||||
|
||||
// Separator
|
||||
if (pinnedApps.length > 0) {
|
||||
map.set("SEPARATOR", { pinned: false, toplevels: [] });
|
||||
}
|
||||
|
||||
// Open windows
|
||||
for (const toplevel of ToplevelManager.toplevels.values) {
|
||||
if (!map.has(toplevel.appId.toLowerCase())) map.set(toplevel.appId.toLowerCase(), ({
|
||||
pinned: false,
|
||||
toplevels: []
|
||||
}));
|
||||
map.get(toplevel.appId.toLowerCase()).toplevels.push(toplevel);
|
||||
}
|
||||
|
||||
var values = [];
|
||||
|
||||
for (const [key, value] of map) {
|
||||
values.push({ appId: key, toplevels: value.toplevels, pinned: value.pinned });
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
delegate: DockAppButton {
|
||||
required property var modelData
|
||||
appToplevel: modelData
|
||||
appListRoot: root
|
||||
}
|
||||
}
|
||||
delegate: DockAppButton {
|
||||
required property var modelData
|
||||
appToplevel: modelData
|
||||
appListRoot: root
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,14 +144,9 @@ Item {
|
||||
anchors.bottom: parent.bottom
|
||||
implicitWidth: popupBackground.implicitWidth + Appearance.sizes.elevationMargin * 2
|
||||
implicitHeight: root.maxWindowPreviewHeight + root.windowControlsHeight + Appearance.sizes.elevationMargin * 2
|
||||
// anchors.horizontalCenter: parent.horizontalCenter
|
||||
hoverEnabled: true
|
||||
// x: previewPopup.width / 2 + root.popupX
|
||||
// Behavior on x {
|
||||
// animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
// }
|
||||
x: {
|
||||
const itemCenter = root.QsWindow.mapFromItem(root.lastHoveredButton, root.lastHoveredButton.width / 2, 0);
|
||||
const itemCenter = root.QsWindow?.mapFromItem(root.lastHoveredButton, root.lastHoveredButton?.width / 2, 0);
|
||||
return itemCenter.x - width / 2
|
||||
}
|
||||
StyledRectangularShadow {
|
||||
@@ -145,7 +166,7 @@ Item {
|
||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
}
|
||||
clip: true
|
||||
color: Appearance.m3colors.m3surfaceContainer
|
||||
color: Appearance.colors.colSurfaceContainer
|
||||
radius: Appearance.rounding.normal
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: Appearance.sizes.elevationMargin
|
||||
@@ -163,7 +184,9 @@ Item {
|
||||
id: previewRowLayout
|
||||
anchors.centerIn: parent
|
||||
Repeater {
|
||||
model: previewPopup.appTopLevel?.toplevels ?? []
|
||||
model: ScriptModel {
|
||||
values: previewPopup.appTopLevel?.toplevels ?? []
|
||||
}
|
||||
RippleButton {
|
||||
id: windowButton
|
||||
required property var modelData
|
||||
@@ -182,7 +205,7 @@ Item {
|
||||
contentWidth: parent.width - anchors.margins * 2
|
||||
WrapperRectangle {
|
||||
Layout.fillWidth: true
|
||||
color: ColorUtils.transparentize(Appearance.m3colors.m3surfaceContainer)
|
||||
color: ColorUtils.transparentize(Appearance.colors.colSurfaceContainer)
|
||||
radius: Appearance.rounding.small
|
||||
margin: 5
|
||||
StyledText {
|
||||
@@ -195,7 +218,7 @@ Item {
|
||||
}
|
||||
GroupButton {
|
||||
id: closeButton
|
||||
colBackground: ColorUtils.transparentize(Appearance.m3colors.m3surfaceContainer)
|
||||
colBackground: ColorUtils.transparentize(Appearance.colors.colSurfaceContainer)
|
||||
baseWidth: windowControlsHeight
|
||||
baseHeight: windowControlsHeight
|
||||
buttonRadius: Appearance.rounding.full
|
||||
|
||||
@@ -7,9 +7,10 @@ import QtQuick.Layouts
|
||||
|
||||
RippleButton {
|
||||
Layout.fillHeight: true
|
||||
Layout.topMargin: Appearance.sizes.elevationMargin - Appearance.sizes.hyprlandGapsOut
|
||||
implicitWidth: implicitHeight - topInset - bottomInset
|
||||
buttonRadius: Appearance.rounding.normal
|
||||
|
||||
topInset: dockVisualBackground.margin + dockRow.padding
|
||||
bottomInset: dockVisualBackground.margin + dockRow.padding
|
||||
topInset: Appearance.sizes.hyprlandGapsOut + dockRow.padding
|
||||
bottomInset: Appearance.sizes.hyprlandGapsOut + dockRow.padding
|
||||
}
|
||||
|
||||
@@ -5,9 +5,9 @@ import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
Rectangle {
|
||||
Layout.topMargin: dockVisualBackground.margin + dockRow.padding + Appearance.rounding.normal
|
||||
Layout.bottomMargin: dockVisualBackground.margin + dockRow.padding + Appearance.rounding.normal
|
||||
Layout.topMargin: Appearance.sizes.elevationMargin + dockRow.padding + Appearance.rounding.normal
|
||||
Layout.bottomMargin: Appearance.sizes.hyprlandGapsOut + dockRow.padding + Appearance.rounding.normal
|
||||
Layout.fillHeight: true
|
||||
implicitWidth: 1
|
||||
color: Appearance.m3colors.m3outlineVariant
|
||||
color: Appearance.colors.colOutlineVariant
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ Scope {
|
||||
property real contentPadding: 13
|
||||
property real popupRounding: Appearance.rounding.screenRounding - Appearance.sizes.elevationMargin + 1
|
||||
property real artRounding: Appearance.rounding.verysmall
|
||||
property list<real> visualizerPoints: []
|
||||
|
||||
property bool hasPlasmaIntegration: false
|
||||
function isRealPlayer(player) {
|
||||
@@ -53,7 +54,7 @@ Scope {
|
||||
for (let j = i + 1; j < players.length; ++j) {
|
||||
let p2 = players[j];
|
||||
if (p1.trackTitle && p2.trackTitle &&
|
||||
(p1.trackTitle.startsWith(p2.trackTitle) || p2.trackTitle.startsWith(p1.trackTitle))) {
|
||||
(p1.trackTitle.includes(p2.trackTitle) || p2.trackTitle.includes(p1.trackTitle))) {
|
||||
group.push(j);
|
||||
}
|
||||
}
|
||||
@@ -68,13 +69,31 @@ Scope {
|
||||
return filtered;
|
||||
}
|
||||
|
||||
Process {
|
||||
id: cavaProc
|
||||
running: mediaControlsLoader.active
|
||||
onRunningChanged: {
|
||||
if (!cavaProc.running) {
|
||||
root.visualizerPoints = [];
|
||||
}
|
||||
}
|
||||
command: ["cava", "-p", `${FileUtils.trimFileProtocol(Directories.config)}/quickshell/scripts/cava/raw_output_config.txt`]
|
||||
stdout: SplitParser {
|
||||
onRead: data => {
|
||||
// Parse `;`-separated values into the visualizerPoints array
|
||||
let points = data.split(";").map(p => parseFloat(p.trim())).filter(p => !isNaN(p));
|
||||
root.visualizerPoints = points;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: mediaControlsLoader
|
||||
active: false
|
||||
|
||||
sourceComponent: PanelWindow {
|
||||
id: mediaControlsRoot
|
||||
visible: mediaControlsLoader.active
|
||||
visible: true
|
||||
|
||||
exclusiveZone: 0
|
||||
implicitWidth: (
|
||||
@@ -112,6 +131,7 @@ Scope {
|
||||
delegate: PlayerControl {
|
||||
required property MprisPlayer modelData
|
||||
player: modelData
|
||||
visualizerPoints: root.visualizerPoints
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,9 @@ Item { // Player instance
|
||||
property string artFilePath: `${artDownloadLocation}/${artFileName}`
|
||||
property color artDominantColor: colorQuantizer?.colors[0] || Appearance.m3colors.m3secondaryContainer
|
||||
property bool downloaded: false
|
||||
property list<real> visualizerPoints: []
|
||||
property real maxVisualizerValue: 1000 // Max value in the data points
|
||||
property int visualizerSmoothing: 2 // Number of points to average for smoothing
|
||||
|
||||
implicitWidth: widgetWidth
|
||||
implicitHeight: widgetHeight
|
||||
@@ -150,6 +153,16 @@ Item { // Player instance
|
||||
}
|
||||
}
|
||||
|
||||
WaveVisualizer {
|
||||
id: visualizerCanvas
|
||||
anchors.fill: parent
|
||||
live: playerController.player?.isPlaying
|
||||
points: playerController.visualizerPoints
|
||||
maxVisualizerValue: playerController.maxVisualizerValue
|
||||
smoothing: playerController.visualizerSmoothing
|
||||
color: blendedColors.colPrimary
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: root.contentPadding
|
||||
@@ -160,7 +173,7 @@ Item { // Player instance
|
||||
Layout.fillHeight: true
|
||||
implicitWidth: height
|
||||
radius: root.artRounding
|
||||
color: blendedColors.colLayer1
|
||||
color: ColorUtils.transparentize(blendedColors.colLayer1, 0.5)
|
||||
|
||||
layer.enabled: true
|
||||
layer.effect: OpacityMask {
|
||||
@@ -235,12 +248,18 @@ Item { // Player instance
|
||||
iconName: "skip_previous"
|
||||
onClicked: playerController.player?.previous()
|
||||
}
|
||||
StyledProgressBar {
|
||||
id: slider
|
||||
Item {
|
||||
id: progressBarContainer
|
||||
Layout.fillWidth: true
|
||||
highlightColor: blendedColors.colPrimary
|
||||
trackColor: blendedColors.colSecondaryContainer
|
||||
value: playerController.player?.position / playerController.player?.length
|
||||
implicitHeight: progressBar.implicitHeight
|
||||
|
||||
StyledProgressBar {
|
||||
id: progressBar
|
||||
anchors.fill: parent
|
||||
highlightColor: blendedColors.colPrimary
|
||||
trackColor: blendedColors.colSecondaryContainer
|
||||
value: playerController.player?.position / playerController.player?.length
|
||||
}
|
||||
}
|
||||
TrackChangeButton {
|
||||
iconName: "skip_next"
|
||||
|
||||
@@ -15,7 +15,7 @@ Scope {
|
||||
PanelWindow {
|
||||
id: root
|
||||
visible: (Notifications.popupList.length > 0)
|
||||
screen: Quickshell.screens.find(s => s.name === Hyprland.focusedMonitor?.name)
|
||||
screen: Quickshell.screens.find(s => s.name === Hyprland.focusedMonitor?.name) ?? null
|
||||
|
||||
WlrLayershell.namespace: "quickshell:notificationPopup"
|
||||
WlrLayershell.layer: WlrLayer.Overlay
|
||||
|
||||
@@ -73,7 +73,7 @@ Scope {
|
||||
item: osdValuesWrapper
|
||||
}
|
||||
|
||||
implicitWidth: Appearance.sizes.osdWidth
|
||||
implicitWidth: columnLayout.implicitWidth
|
||||
implicitHeight: columnLayout.implicitHeight
|
||||
visible: osdLoader.active
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import Quickshell.Hyprland
|
||||
Scope {
|
||||
id: root
|
||||
property bool showOsdValues: false
|
||||
property string protectionMessage: ""
|
||||
property var focusedScreen: Quickshell.screens.find(s => s.name === Hyprland.focusedMonitor?.name)
|
||||
|
||||
function triggerOsd() {
|
||||
@@ -25,7 +26,8 @@ Scope {
|
||||
repeat: false
|
||||
running: false
|
||||
onTriggered: {
|
||||
showOsdValues = false
|
||||
root.showOsdValues = false
|
||||
root.protectionMessage = ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +38,7 @@ Scope {
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
Connections { // Listen to volume changes
|
||||
target: Audio.sink?.audio ?? null
|
||||
function onVolumeChanged() {
|
||||
if (!Audio.ready) return
|
||||
@@ -48,6 +50,14 @@ Scope {
|
||||
}
|
||||
}
|
||||
|
||||
Connections { // Listen to protection triggers
|
||||
target: Audio
|
||||
function onSinkProtectionTriggered(reason) {
|
||||
root.protectionMessage = reason;
|
||||
root.triggerOsd()
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: osdLoader
|
||||
active: showOsdValues
|
||||
@@ -75,7 +85,7 @@ Scope {
|
||||
item: osdValuesWrapper
|
||||
}
|
||||
|
||||
implicitWidth: Appearance.sizes.osdWidth
|
||||
implicitWidth: columnLayout.implicitWidth
|
||||
implicitHeight: columnLayout.implicitHeight
|
||||
visible: osdLoader.active
|
||||
|
||||
@@ -85,8 +95,8 @@ Scope {
|
||||
Item {
|
||||
id: osdValuesWrapper
|
||||
// Extra space for shadow
|
||||
implicitHeight: osdValues.implicitHeight + Appearance.sizes.elevationMargin * 2
|
||||
implicitWidth: osdValues.implicitWidth
|
||||
implicitHeight: contentColumnLayout.implicitHeight + Appearance.sizes.elevationMargin * 2
|
||||
implicitWidth: contentColumnLayout.implicitWidth
|
||||
clip: true
|
||||
|
||||
MouseArea {
|
||||
@@ -95,20 +105,63 @@ Scope {
|
||||
onEntered: root.showOsdValues = false
|
||||
}
|
||||
|
||||
Behavior on implicitHeight {
|
||||
NumberAnimation {
|
||||
duration: Appearance.animation.menuDecel.duration
|
||||
easing.type: Appearance.animation.menuDecel.type
|
||||
ColumnLayout {
|
||||
id: contentColumnLayout
|
||||
anchors {
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
leftMargin: Appearance.sizes.elevationMargin
|
||||
rightMargin: Appearance.sizes.elevationMargin
|
||||
}
|
||||
}
|
||||
spacing: 0
|
||||
|
||||
OsdValueIndicator {
|
||||
id: osdValues
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.sizes.elevationMargin
|
||||
value: Audio.sink?.audio.volume ?? 0
|
||||
icon: Audio.sink?.audio.muted ? "volume_off" : "volume_up"
|
||||
name: qsTr("Volume")
|
||||
OsdValueIndicator {
|
||||
id: osdValues
|
||||
Layout.fillWidth: true
|
||||
value: Audio.sink?.audio.volume ?? 0
|
||||
icon: Audio.sink?.audio.muted ? "volume_off" : "volume_up"
|
||||
name: qsTr("Volume")
|
||||
}
|
||||
|
||||
Item {
|
||||
id: protectionMessageWrapper
|
||||
implicitHeight: protectionMessageBackground.implicitHeight
|
||||
implicitWidth: protectionMessageBackground.implicitWidth
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
opacity: root.protectionMessage !== "" ? 1 : 0
|
||||
|
||||
StyledRectangularShadow {
|
||||
target: protectionMessageBackground
|
||||
}
|
||||
Rectangle {
|
||||
id: protectionMessageBackground
|
||||
anchors.centerIn: parent
|
||||
color: Appearance.m3colors.m3error
|
||||
property real padding: 10
|
||||
implicitHeight: protectionMessageRowLayout.implicitHeight + padding * 2
|
||||
implicitWidth: protectionMessageRowLayout.implicitWidth + padding * 2
|
||||
radius: Appearance.rounding.normal
|
||||
|
||||
RowLayout {
|
||||
id: protectionMessageRowLayout
|
||||
anchors.centerIn: parent
|
||||
MaterialSymbol {
|
||||
id: protectionMessageIcon
|
||||
text: "dangerous"
|
||||
iconSize: Appearance.font.pixelSize.hugeass
|
||||
color: Appearance.m3colors.m3onError
|
||||
}
|
||||
StyledText {
|
||||
id: protectionMessageTextWidget
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
color: Appearance.m3colors.m3onError
|
||||
wrapMode: Text.Wrap
|
||||
text: root.protectionMessage
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ Scope { // Scope
|
||||
Layout.bottomMargin: 20
|
||||
Layout.fillHeight: true
|
||||
implicitWidth: 1
|
||||
color: Appearance.m3colors.m3outlineVariant
|
||||
color: Appearance.colors.colOutlineVariant
|
||||
}
|
||||
OskContent {
|
||||
id: oskContent
|
||||
|
||||
@@ -92,8 +92,8 @@ Scope {
|
||||
visible: GlobalStates.overviewOpen
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
top: !ConfigOptions.bar.bottom ? parent.top : null
|
||||
bottom: ConfigOptions.bar.bottom ? parent.bottom : null
|
||||
top: !ConfigOptions.bar.bottom ? parent.top : undefined
|
||||
bottom: ConfigOptions.bar.bottom ? parent.bottom : undefined
|
||||
}
|
||||
|
||||
Keys.onPressed: (event) => {
|
||||
|
||||
@@ -25,7 +25,7 @@ Item {
|
||||
property var windowAddresses: HyprlandData.addresses
|
||||
property var monitorData: HyprlandData.monitors.find(m => m.id === root.monitor.id)
|
||||
property real scale: ConfigOptions.overview.scale
|
||||
property color activeBorderColor: Appearance.m3colors.m3secondary
|
||||
property color activeBorderColor: Appearance.colors.colSecondary
|
||||
|
||||
property real workspaceImplicitWidth: (monitorData?.transform % 2 === 1) ?
|
||||
((monitor.height - monitorData?.reserved[0] - monitorData?.reserved[2]) * root.scale / monitor.scale) :
|
||||
@@ -144,25 +144,38 @@ Item {
|
||||
implicitHeight: workspaceColumnLayout.implicitHeight
|
||||
|
||||
Repeater { // Window repeater
|
||||
model: windowAddresses.filter((address) => {
|
||||
var win = windowByAddress[address]
|
||||
return (root.workspaceGroup * root.workspacesShown < win?.workspace?.id && win?.workspace?.id <= (root.workspaceGroup + 1) * root.workspacesShown)
|
||||
})
|
||||
model: ScriptModel {
|
||||
values: {
|
||||
// console.log(JSON.stringify(ToplevelManager.toplevels.values.map(t => t), null, 2))
|
||||
return ToplevelManager.toplevels.values.filter((toplevel) => {
|
||||
const address = `0x${toplevel.HyprlandToplevel.address}`
|
||||
// console.log(`Checking window with address: ${address}`)
|
||||
var win = windowByAddress[address]
|
||||
return (root.workspaceGroup * root.workspacesShown < win?.workspace?.id && win?.workspace?.id <= (root.workspaceGroup + 1) * root.workspacesShown)
|
||||
})
|
||||
}
|
||||
}
|
||||
delegate: OverviewWindow {
|
||||
id: window
|
||||
windowData: windowByAddress[modelData]
|
||||
required property var modelData
|
||||
property var address: `0x${modelData.HyprlandToplevel.address}`
|
||||
windowData: windowByAddress[address]
|
||||
toplevel: modelData
|
||||
monitorData: root.monitorData
|
||||
scale: root.scale
|
||||
availableWorkspaceWidth: root.workspaceImplicitWidth
|
||||
availableWorkspaceHeight: root.workspaceImplicitHeight
|
||||
|
||||
property int monitorId: windowData?.monitor
|
||||
property var monitor: HyprlandData.monitors[monitorId]
|
||||
|
||||
property bool atInitPosition: (initX == x && initY == y)
|
||||
restrictToWorkspace: Drag.active || atInitPosition
|
||||
|
||||
property int workspaceColIndex: (windowData?.workspace.id - 1) % ConfigOptions.overview.numOfCols
|
||||
property int workspaceRowIndex: Math.floor((windowData?.workspace.id - 1) % root.workspacesShown / ConfigOptions.overview.numOfCols)
|
||||
xOffset: (root.workspaceImplicitWidth + workspaceSpacing) * workspaceColIndex
|
||||
yOffset: (root.workspaceImplicitHeight + workspaceSpacing) * workspaceRowIndex
|
||||
xOffset: (root.workspaceImplicitWidth + workspaceSpacing) * workspaceColIndex - (monitor?.x * root.scale)
|
||||
yOffset: (root.workspaceImplicitHeight + workspaceSpacing) * workspaceRowIndex - (monitor?.y * root.scale)
|
||||
|
||||
Timer {
|
||||
id: updateWindowPosition
|
||||
@@ -170,8 +183,9 @@ Item {
|
||||
repeat: false
|
||||
running: false
|
||||
onTriggered: {
|
||||
window.x = Math.max((windowData?.at[0] - monitorData?.reserved[0]) * root.scale, 0) + xOffset
|
||||
window.y = Math.max((windowData?.at[1] - monitorData?.reserved[1]) * root.scale, 0) + yOffset
|
||||
window.x = Math.round(Math.max((windowData?.at[0] - monitorData?.reserved[0]) * root.scale, 0) + xOffset)
|
||||
window.y = Math.round(Math.max((windowData?.at[1] - monitorData?.reserved[1]) * root.scale, 0) + yOffset)
|
||||
console.log(`[OverviewWindow] Updated position for window ${windowData?.address} to (${window.x}, ${window.y})`)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,6 +205,7 @@ Item {
|
||||
window.pressed = true
|
||||
window.Drag.active = true
|
||||
window.Drag.source = window
|
||||
console.log(`[OverviewWindow] Dragging window ${windowData?.address} from position (${window.x}, ${window.y})`)
|
||||
}
|
||||
onReleased: {
|
||||
const targetWorkspace = root.draggingTargetWorkspace
|
||||
|
||||
@@ -1,17 +1,21 @@
|
||||
import "root:/"
|
||||
import "root:/services/"
|
||||
import "root:/modules/common"
|
||||
import "root:/modules/common/widgets"
|
||||
import "root:/modules/common/functions/color_utils.js" as ColorUtils
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Widgets
|
||||
import Quickshell.Io
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
|
||||
Rectangle { // Window
|
||||
Item { // Window
|
||||
id: root
|
||||
property var toplevel
|
||||
property var windowData
|
||||
property var monitorData
|
||||
property var scale
|
||||
@@ -38,14 +42,17 @@ Rectangle { // Window
|
||||
|
||||
x: initX
|
||||
y: initY
|
||||
width: Math.min(windowData?.size[0] * root.scale, (restrictToWorkspace ? windowData?.size[0] : availableWorkspaceWidth - x + xOffset))
|
||||
height: Math.min(windowData?.size[1] * root.scale, (restrictToWorkspace ? windowData?.size[1] : availableWorkspaceHeight - y + yOffset))
|
||||
width: Math.round(Math.min(windowData?.size[0] * root.scale, (restrictToWorkspace ? windowData?.size[0] : availableWorkspaceWidth - x + xOffset)))
|
||||
height: Math.round(Math.min(windowData?.size[1] * root.scale, (restrictToWorkspace ? windowData?.size[1] : availableWorkspaceHeight - y + yOffset)))
|
||||
|
||||
radius: Appearance.rounding.windowRounding * root.scale
|
||||
color: pressed ? Appearance.colors.colLayer2Active : hovered ? Appearance.colors.colLayer2Hover : Appearance.colors.colLayer2
|
||||
border.color : ColorUtils.transparentize(Appearance.m3colors.m3outline, 0.9)
|
||||
border.pixelAligned : false
|
||||
border.width : 1
|
||||
layer.enabled: true
|
||||
layer.effect: OpacityMask {
|
||||
maskSource: Rectangle {
|
||||
width: root.width
|
||||
height: root.height
|
||||
radius: Appearance.rounding.windowRounding * root.scale
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on x {
|
||||
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||
@@ -60,34 +67,45 @@ Rectangle { // Window
|
||||
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
spacing: Appearance.font.pixelSize.smaller * 0.5
|
||||
ScreencopyView {
|
||||
id: windowPreview
|
||||
anchors.fill: parent
|
||||
captureSource: GlobalStates.overviewOpen ? root.toplevel : null
|
||||
live: true
|
||||
|
||||
IconImage {
|
||||
id: windowIcon
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
source: root.iconPath
|
||||
implicitSize: Math.min(targetWindowWidth, targetWindowHeight) * (root.compactMode ? root.iconToWindowRatioCompact : root.iconToWindowRatio)
|
||||
|
||||
Behavior on implicitSize {
|
||||
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||
}
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: Appearance.rounding.windowRounding * root.scale
|
||||
color: pressed ? ColorUtils.transparentize(Appearance.colors.colLayer2Active, 0.5) :
|
||||
hovered ? ColorUtils.transparentize(Appearance.colors.colLayer2Hover, 0.7) :
|
||||
ColorUtils.transparentize(Appearance.colors.colLayer2)
|
||||
border.color : ColorUtils.transparentize(Appearance.m3colors.m3outline, 0.7)
|
||||
border.width : 1
|
||||
}
|
||||
|
||||
StyledText {
|
||||
Layout.leftMargin: 10
|
||||
Layout.rightMargin: 10
|
||||
visible: !compactMode
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font.pixelSize: Appearance.font.pixelSize.smaller
|
||||
font.italic: indicateXWayland ? true : false
|
||||
elide: Text.ElideRight
|
||||
text: windowData?.title ?? ""
|
||||
ColumnLayout {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
spacing: Appearance.font.pixelSize.smaller * 0.5
|
||||
|
||||
Image {
|
||||
id: windowIcon
|
||||
property var iconSize: Math.min(targetWindowWidth, targetWindowHeight) * (root.compactMode ? root.iconToWindowRatioCompact : root.iconToWindowRatio)
|
||||
// mipmap: true
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
source: root.iconPath
|
||||
width: iconSize
|
||||
height: iconSize
|
||||
sourceSize: Qt.size(iconSize, iconSize)
|
||||
|
||||
Behavior on width {
|
||||
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||
}
|
||||
Behavior on height {
|
||||
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -80,7 +80,7 @@ RippleButton {
|
||||
buttonRadius: Appearance.rounding.normal
|
||||
colBackground: (root.down || root.keyboardDown) ? Appearance.colors.colLayer1Active :
|
||||
((root.hovered || root.focus) ? Appearance.colors.colLayer1Hover :
|
||||
ColorUtils.transparentize(Appearance.m3colors.m3surfaceContainerHigh, 1))
|
||||
ColorUtils.transparentize(Appearance.colors.colSurfaceContainerHigh, 1))
|
||||
colBackgroundHover: Appearance.colors.colLayer1Hover
|
||||
colRipple: Appearance.colors.colLayer1Active
|
||||
|
||||
|
||||
@@ -219,7 +219,7 @@ Item { // Wrapper
|
||||
}
|
||||
color: activeFocus ? Appearance.m3colors.m3onSurface : Appearance.m3colors.m3onSurfaceVariant
|
||||
selectedTextColor: Appearance.m3colors.m3onSecondaryContainer
|
||||
selectionColor: Appearance.m3colors.m3secondaryContainer
|
||||
selectionColor: Appearance.colors.colSecondaryContainer
|
||||
placeholderText: qsTr("Search, calculate or run")
|
||||
placeholderTextColor: Appearance.m3colors.m3outline
|
||||
implicitWidth: root.searchingText == "" ? Appearance.sizes.searchWidthCollapsed : Appearance.sizes.searchWidth
|
||||
@@ -260,7 +260,7 @@ Item { // Wrapper
|
||||
visible: root.showResults
|
||||
Layout.fillWidth: true
|
||||
height: 1
|
||||
color: Appearance.m3colors.m3outlineVariant
|
||||
color: Appearance.colors.colOutlineVariant
|
||||
}
|
||||
|
||||
ListView { // App results
|
||||
|
||||
@@ -5,6 +5,7 @@ import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
|
||||
Scope {
|
||||
id: screenCorners
|
||||
@@ -14,7 +15,9 @@ Scope {
|
||||
model: Quickshell.screens
|
||||
|
||||
PanelWindow {
|
||||
visible: (ConfigOptions.appearance.fakeScreenRounding === 1 || (ConfigOptions.appearance.fakeScreenRounding === 2 && !activeWindow?.fullscreen))
|
||||
visible: (ConfigOptions.appearance.fakeScreenRounding === 1
|
||||
|| (ConfigOptions.appearance.fakeScreenRounding === 2
|
||||
&& !activeWindow?.fullscreen))
|
||||
|
||||
property var modelData
|
||||
|
||||
@@ -23,6 +26,20 @@ Scope {
|
||||
mask: Region {
|
||||
item: null
|
||||
}
|
||||
HyprlandWindow.visibleMask: Region {
|
||||
Region {
|
||||
item: topLeftCorner
|
||||
}
|
||||
Region {
|
||||
item: topRightCorner
|
||||
}
|
||||
Region {
|
||||
item: bottomLeftCorner
|
||||
}
|
||||
Region {
|
||||
item: bottomRightCorner
|
||||
}
|
||||
}
|
||||
WlrLayershell.namespace: "quickshell:screenCorners"
|
||||
WlrLayershell.layer: WlrLayer.Overlay
|
||||
color: "transparent"
|
||||
@@ -35,24 +52,28 @@ Scope {
|
||||
}
|
||||
|
||||
RoundCorner {
|
||||
id: topLeftCorner
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
size: Appearance.rounding.screenRounding
|
||||
corner: cornerEnum.topLeft
|
||||
}
|
||||
RoundCorner {
|
||||
id: topRightCorner
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
size: Appearance.rounding.screenRounding
|
||||
corner: cornerEnum.topRight
|
||||
}
|
||||
RoundCorner {
|
||||
id: bottomLeftCorner
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
size: Appearance.rounding.screenRounding
|
||||
corner: cornerEnum.bottomLeft
|
||||
}
|
||||
RoundCorner {
|
||||
id: bottomRightCorner
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
size: Appearance.rounding.screenRounding
|
||||
|
||||
@@ -18,7 +18,7 @@ RippleButton {
|
||||
buttonRadius: (button.focus || button.down) ? size / 2 : Appearance.rounding.verylarge
|
||||
colBackground: button.keyboardDown ? Appearance.colors.colSecondaryContainerActive :
|
||||
button.focus ? Appearance.colors.colPrimary :
|
||||
Appearance.m3colors.m3secondaryContainer
|
||||
Appearance.colors.colSecondaryContainer
|
||||
colBackgroundHover: Appearance.colors.colPrimary
|
||||
colRipple: Appearance.colors.colPrimaryActive
|
||||
property color colText: (button.down || button.keyboardDown || button.focus || button.hovered) ?
|
||||
|
||||
@@ -125,9 +125,14 @@ int main(int argc, char* argv[]) {
|
||||
|
||||
### LaTeX
|
||||
|
||||
- Simple inline: $\\frac{1}{2} = \\frac{2}{4}$
|
||||
- Complex inline: $$\\int_0^\\infty e^{-x^2} dx = \\frac{\\sqrt{\\pi}}{2}$$
|
||||
- Another complex inline: \\\\[\\int_0^\\infty \\frac{1}{x^2} dx = \\infty\\\\]
|
||||
|
||||
Inline w/ dollar signs: $\\frac{1}{2} = \\frac{2}{4}$
|
||||
|
||||
Inline w/ double dollar signs: $$\\int_0^\\infty e^{-x^2} dx = \\frac{\\sqrt{\\pi}}{2}$$
|
||||
|
||||
Inline w/ backslash and square brackets \\[\\int_0^\\infty \\frac{1}{x^2} dx = \\infty\\]
|
||||
|
||||
Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\)
|
||||
`,
|
||||
Ai.interfaceRole);
|
||||
}
|
||||
@@ -162,6 +167,7 @@ int main(int argc, char* argv[]) {
|
||||
id: messageListView
|
||||
anchors.fill: parent
|
||||
spacing: 10
|
||||
popin: false
|
||||
|
||||
property int lastResponseLength: 0
|
||||
|
||||
@@ -175,6 +181,8 @@ int main(int argc, char* argv[]) {
|
||||
}
|
||||
}
|
||||
|
||||
add: null // Prevent function calls from being janky
|
||||
|
||||
Behavior on contentY {
|
||||
NumberAnimation {
|
||||
id: scrollAnim
|
||||
@@ -337,7 +345,7 @@ int main(int argc, char* argv[]) {
|
||||
implicitHeight: Math.max(inputFieldRowLayout.implicitHeight + inputFieldRowLayout.anchors.topMargin
|
||||
+ commandButtonsRow.implicitHeight + commandButtonsRow.anchors.bottomMargin + columnSpacing, 45)
|
||||
clip: true
|
||||
border.color: Appearance.m3colors.m3outlineVariant
|
||||
border.color: Appearance.colors.colOutlineVariant
|
||||
border.width: 1
|
||||
|
||||
Behavior on implicitHeight {
|
||||
|
||||
@@ -359,7 +359,7 @@ Item {
|
||||
implicitHeight: Math.max(inputFieldRowLayout.implicitHeight + inputFieldRowLayout.anchors.topMargin
|
||||
+ commandButtonsRow.implicitHeight + commandButtonsRow.anchors.bottomMargin + columnSpacing, 45)
|
||||
clip: true
|
||||
border.color: Appearance.m3colors.m3outlineVariant
|
||||
border.color: Appearance.colors.colOutlineVariant
|
||||
border.width: 1
|
||||
|
||||
Behavior on implicitHeight {
|
||||
@@ -562,8 +562,10 @@ Item {
|
||||
text: "•"
|
||||
}
|
||||
|
||||
Rectangle { // NSFW toggle
|
||||
Item { // NSFW toggle
|
||||
visible: width > 0
|
||||
implicitWidth: switchesRow.implicitWidth
|
||||
Layout.fillHeight: true
|
||||
|
||||
RowLayout {
|
||||
id: switchesRow
|
||||
|
||||
@@ -97,7 +97,7 @@ Scope { // Scope
|
||||
anchors.left: parent.left
|
||||
anchors.topMargin: Appearance.sizes.hyprlandGapsOut
|
||||
anchors.leftMargin: Appearance.sizes.hyprlandGapsOut
|
||||
width: sidebarRoot.sidebarWidth - Appearance.sizes.hyprlandGapsOut * 2
|
||||
width: sidebarRoot.sidebarWidth - Appearance.sizes.hyprlandGapsOut - Appearance.sizes.elevationMargin
|
||||
height: parent.height - Appearance.sizes.hyprlandGapsOut * 2
|
||||
color: Appearance.colors.colLayer0
|
||||
radius: Appearance.rounding.screenRounding - Appearance.sizes.hyprlandGapsOut + 1
|
||||
|
||||
@@ -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 "./translator/"
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
@@ -15,11 +16,24 @@ import Quickshell.Hyprland
|
||||
*/
|
||||
Item {
|
||||
id: root
|
||||
property var inputField: inputTextArea
|
||||
property var outputField: outputTextArea
|
||||
|
||||
// Widgets
|
||||
property var inputField: inputCanvas.inputTextArea
|
||||
// Widget variables
|
||||
property bool translationFor: false // Indicates if the translation is for an autocorrected text
|
||||
property string translatedText: ""
|
||||
property list<string> languages: []
|
||||
// Options
|
||||
property string targetLanguage: ConfigOptions.language.translator.targetLanguage
|
||||
property string sourceLanguage: ConfigOptions.language.translator.sourceLanguage
|
||||
property string hostLanguage: targetLanguage
|
||||
|
||||
property bool showLanguageSelector: false
|
||||
property bool languageSelectorTarget: false // true for target language, false for source language
|
||||
|
||||
function showLanguageSelectorDialog(isTargetLang: bool) {
|
||||
root.languageSelectorTarget = isTargetLang;
|
||||
root.showLanguageSelector = true
|
||||
}
|
||||
|
||||
onFocusChanged: (focus) => {
|
||||
if (focus) {
|
||||
@@ -32,19 +46,23 @@ Item {
|
||||
interval: ConfigOptions.sidebar.translator.delay
|
||||
repeat: false
|
||||
onTriggered: () => {
|
||||
if (inputTextArea.text.trim().length > 0) {
|
||||
if (root.inputField.text.trim().length > 0) {
|
||||
// console.log("Translating with command:", translateProc.command);
|
||||
translateProc.running = false;
|
||||
translateProc.buffer = ""; // Clear the buffer
|
||||
translateProc.running = true; // Restart the process
|
||||
} else {
|
||||
outputTextArea.text = "";
|
||||
root.translatedText = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: translateProc
|
||||
command: ["bash", "-c", `trans -no-theme -no-ansi '${StringUtils.shellSingleQuoteEscape(inputTextArea.text.trim())}'`]
|
||||
command: ["bash", "-c", `trans -no-theme`
|
||||
+ ` -source '${StringUtils.shellSingleQuoteEscape(root.sourceLanguage)}'`
|
||||
+ ` -target '${StringUtils.shellSingleQuoteEscape(root.targetLanguage)}'`
|
||||
+ ` -no-ansi '${StringUtils.shellSingleQuoteEscape(root.inputField.text.trim())}'`]
|
||||
property string buffer: ""
|
||||
stdout: SplitParser {
|
||||
onRead: data => {
|
||||
@@ -59,171 +77,172 @@ Item {
|
||||
|
||||
// 2. Extract relevant data
|
||||
root.translatedText = sections.length > 1 ? sections[1].trim() : "";
|
||||
root.outputField.text = root.translatedText;
|
||||
}
|
||||
}
|
||||
|
||||
Flickable {
|
||||
|
||||
Process {
|
||||
id: getLanguagesProc
|
||||
command: ["trans", "-list-languages"]
|
||||
property list<string> bufferList: ["auto"]
|
||||
running: true
|
||||
stdout: SplitParser {
|
||||
onRead: data => {
|
||||
getLanguagesProc.bufferList.push(data.trim());
|
||||
}
|
||||
}
|
||||
onExited: (exitCode, exitStatus) => {
|
||||
// Ensure "auto" is always the first language
|
||||
let langs = getLanguagesProc.bufferList
|
||||
.filter(lang => lang.trim().length > 0 && lang !== "auto")
|
||||
.sort((a, b) => a.localeCompare(b));
|
||||
langs.unshift("auto");
|
||||
root.languages = langs;
|
||||
getLanguagesProc.bufferList = []; // Clear the buffer
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
contentHeight: contentColumn.implicitHeight
|
||||
Flickable {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
contentHeight: contentColumn.implicitHeight
|
||||
|
||||
ColumnLayout {
|
||||
id: contentColumn
|
||||
anchors.fill: parent
|
||||
ColumnLayout {
|
||||
id: contentColumn
|
||||
anchors.fill: parent
|
||||
|
||||
Rectangle { // INPUT
|
||||
id: inputCanvas
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: Math.max(150, inputColumn.implicitHeight)
|
||||
color: Appearance.colors.colLayer1
|
||||
radius: Appearance.rounding.normal
|
||||
border.color: Appearance.m3colors.m3outlineVariant
|
||||
border.width: 1
|
||||
LanguageSelectorButton { // Target language button
|
||||
id: targetLanguageButton
|
||||
displayText: root.targetLanguage
|
||||
onClicked: {
|
||||
root.showLanguageSelectorDialog(true);
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: inputColumn
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
StyledTextArea { // Input area
|
||||
id: inputTextArea
|
||||
Layout.fillWidth: true
|
||||
placeholderText: qsTr("Enter text to translate...")
|
||||
wrapMode: TextEdit.Wrap
|
||||
textFormat: TextEdit.PlainText
|
||||
font.pixelSize: Appearance.font.pixelSize.small
|
||||
color: Appearance.colors.colOnLayer1
|
||||
padding: 15
|
||||
background: null
|
||||
onTextChanged: {
|
||||
if (inputTextArea.text.trim().length > 0) {
|
||||
translateTimer.restart();
|
||||
} else {
|
||||
outputTextArea.text = "";
|
||||
}
|
||||
TextCanvas { // Content translation
|
||||
id: outputCanvas
|
||||
isInput: false
|
||||
placeholderText: qsTr("Translation goes here...")
|
||||
property bool hasTranslation: (root.translatedText.trim().length > 0)
|
||||
text: hasTranslation ? root.translatedText : ""
|
||||
GroupButton {
|
||||
id: copyButton
|
||||
baseWidth: height
|
||||
buttonRadius: Appearance.rounding.small
|
||||
enabled: outputCanvas.displayedText.trim().length > 0
|
||||
contentItem: MaterialSymbol {
|
||||
anchors.centerIn: parent
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
iconSize: Appearance.font.pixelSize.larger
|
||||
text: "content_copy"
|
||||
color: copyButton.enabled ? Appearance.colors.colOnLayer1 : Appearance.colors.colSubtext
|
||||
}
|
||||
onClicked: {
|
||||
Quickshell.clipboardText = outputCanvas.displayedText
|
||||
}
|
||||
}
|
||||
|
||||
Item { Layout.fillHeight: true }
|
||||
|
||||
RowLayout { // Status row
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: 10
|
||||
spacing: 10
|
||||
|
||||
Text {
|
||||
Layout.leftMargin: 10
|
||||
text: qsTr("%1 characters").arg(inputTextArea.text.length)
|
||||
color: Appearance.colors.colOnLayer1
|
||||
font.pixelSize: Appearance.font.pixelSize.smaller
|
||||
GroupButton {
|
||||
id: searchButton
|
||||
baseWidth: height
|
||||
buttonRadius: Appearance.rounding.small
|
||||
enabled: outputCanvas.displayedText.trim().length > 0
|
||||
contentItem: MaterialSymbol {
|
||||
anchors.centerIn: parent
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
iconSize: Appearance.font.pixelSize.larger
|
||||
text: "travel_explore"
|
||||
color: searchButton.enabled ? Appearance.colors.colOnLayer1 : Appearance.colors.colSubtext
|
||||
}
|
||||
Item { Layout.fillWidth: true }
|
||||
ButtonGroup {
|
||||
GroupButton {
|
||||
id: pasteButton
|
||||
baseWidth: height
|
||||
buttonRadius: Appearance.rounding.small
|
||||
contentItem: MaterialSymbol {
|
||||
anchors.centerIn: parent
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
iconSize: Appearance.font.pixelSize.larger
|
||||
text: "content_paste"
|
||||
color: deleteButton.enabled ? Appearance.colors.colOnLayer1 : Appearance.colors.colSubtext
|
||||
}
|
||||
onClicked: {
|
||||
root.inputField.text = Quickshell.clipboardText
|
||||
}
|
||||
}
|
||||
GroupButton {
|
||||
id: deleteButton
|
||||
baseWidth: height
|
||||
buttonRadius: Appearance.rounding.small
|
||||
enabled: inputTextArea.text.length > 0
|
||||
contentItem: MaterialSymbol {
|
||||
anchors.centerIn: parent
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
iconSize: Appearance.font.pixelSize.larger
|
||||
text: "close"
|
||||
color: deleteButton.enabled ? Appearance.colors.colOnLayer1 : Appearance.colors.colSubtext
|
||||
}
|
||||
onClicked: {
|
||||
root.inputField.text = ""
|
||||
}
|
||||
onClicked: {
|
||||
let url = ConfigOptions.search.engineBaseUrl + outputCanvas.displayedText;
|
||||
for (let site of ConfigOptions.search.excludedSites) {
|
||||
url += ` -site:${site}`;
|
||||
}
|
||||
Qt.openUrlExternally(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
LanguageSelectorButton { // Source language button
|
||||
id: sourceLanguageButton
|
||||
displayText: root.sourceLanguage
|
||||
onClicked: {
|
||||
root.showLanguageSelectorDialog(false);
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle { // OUTPUT
|
||||
id: outputCanvas
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: Math.max(150, outputColumn.implicitHeight)
|
||||
color: Appearance.m3colors.m3surfaceContainer
|
||||
radius: Appearance.rounding.normal
|
||||
|
||||
ColumnLayout { // Output column
|
||||
id: outputColumn
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
StyledText { // Output area
|
||||
id: outputTextArea
|
||||
Layout.fillWidth: true
|
||||
property bool hasTranslation: (root.translatedText.trim().length > 0)
|
||||
wrapMode: TextEdit.Wrap
|
||||
font.pixelSize: Appearance.font.pixelSize.small
|
||||
color: hasTranslation ? Appearance.colors.colOnLayer1 : Appearance.colors.colSubtext
|
||||
padding: 15
|
||||
text: hasTranslation ? root.translatedText : ""
|
||||
}
|
||||
Item { Layout.fillHeight: true }
|
||||
RowLayout { // Status row
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: 10
|
||||
spacing: 10
|
||||
Item { Layout.fillWidth: true }
|
||||
ButtonGroup {
|
||||
GroupButton {
|
||||
id: copyButton
|
||||
baseWidth: height
|
||||
buttonRadius: Appearance.rounding.small
|
||||
enabled: root.outputField.text.trim().length > 0
|
||||
contentItem: MaterialSymbol {
|
||||
anchors.centerIn: parent
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
iconSize: Appearance.font.pixelSize.larger
|
||||
text: "content_copy"
|
||||
color: deleteButton.enabled ? Appearance.colors.colOnLayer1 : Appearance.colors.colSubtext
|
||||
}
|
||||
onClicked: {
|
||||
Quickshell.clipboardText = root.outputField.text
|
||||
}
|
||||
}
|
||||
GroupButton {
|
||||
id: searchButton
|
||||
baseWidth: height
|
||||
buttonRadius: Appearance.rounding.small
|
||||
enabled: root.outputField.text.trim().length > 0
|
||||
contentItem: MaterialSymbol {
|
||||
anchors.centerIn: parent
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
iconSize: Appearance.font.pixelSize.larger
|
||||
text: "travel_explore"
|
||||
color: deleteButton.enabled ? Appearance.colors.colOnLayer1 : Appearance.colors.colSubtext
|
||||
}
|
||||
onClicked: {
|
||||
let url = ConfigOptions.search.engineBaseUrl + root.outputField.text;
|
||||
for (let site of ConfigOptions.search.excludedSites) {
|
||||
url += ` -site:${site}`;
|
||||
}
|
||||
Qt.openUrlExternally(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
TextCanvas { // Content input
|
||||
id: inputCanvas
|
||||
isInput: true
|
||||
placeholderText: qsTr("Enter text to translate...")
|
||||
onInputTextChanged: {
|
||||
translateTimer.restart();
|
||||
}
|
||||
GroupButton {
|
||||
id: pasteButton
|
||||
baseWidth: height
|
||||
buttonRadius: Appearance.rounding.small
|
||||
contentItem: MaterialSymbol {
|
||||
anchors.centerIn: parent
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
iconSize: Appearance.font.pixelSize.larger
|
||||
text: "content_paste"
|
||||
color: deleteButton.enabled ? Appearance.colors.colOnLayer1 : Appearance.colors.colSubtext
|
||||
}
|
||||
onClicked: {
|
||||
root.inputField.text = Quickshell.clipboardText
|
||||
}
|
||||
}
|
||||
}
|
||||
GroupButton {
|
||||
id: deleteButton
|
||||
baseWidth: height
|
||||
buttonRadius: Appearance.rounding.small
|
||||
enabled: inputCanvas.inputTextArea.text.length > 0
|
||||
contentItem: MaterialSymbol {
|
||||
anchors.centerIn: parent
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
iconSize: Appearance.font.pixelSize.larger
|
||||
text: "close"
|
||||
color: deleteButton.enabled ? Appearance.colors.colOnLayer1 : Appearance.colors.colSubtext
|
||||
}
|
||||
onClicked: {
|
||||
root.inputField.text = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
anchors.fill: parent
|
||||
active: root.showLanguageSelector
|
||||
visible: root.showLanguageSelector
|
||||
z: 9999
|
||||
sourceComponent: SelectionDialog {
|
||||
id: languageSelectorDialog
|
||||
titleText: qsTr("Select Language")
|
||||
items: root.languages
|
||||
defaultChoice: root.languageSelectorTarget ? root.targetLanguage : root.sourceLanguage
|
||||
onCanceled: () => {
|
||||
root.showLanguageSelector = false;
|
||||
}
|
||||
onSelected: (result) => {
|
||||
root.showLanguageSelector = false;
|
||||
if (!result || result.length === 0) return; // No selection made
|
||||
|
||||
if (root.languageSelectorTarget) {
|
||||
root.targetLanguage = result;
|
||||
ConfigLoader.setConfigValueAndSave("language.translator.targetLanguage", result); // Save to config
|
||||
} else {
|
||||
root.sourceLanguage = result;
|
||||
ConfigLoader.setConfigValueAndSave("language.translator.sourceLanguage", result); // Save to config
|
||||
}
|
||||
|
||||
translateTimer.restart(); // Restart translation after language change
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,8 @@ Rectangle {
|
||||
property bool renderMarkdown: true
|
||||
property bool editing: false
|
||||
|
||||
property list<var> messageBlocks: StringUtils.splitMarkdownBlocks(root.messageData?.content)
|
||||
|
||||
anchors.left: parent?.left
|
||||
anchors.right: parent?.right
|
||||
implicitHeight: columnLayout.implicitHeight + root.messagePadding * 2
|
||||
@@ -89,7 +91,7 @@ Rectangle {
|
||||
|
||||
Rectangle { // Name
|
||||
id: nameWrapper
|
||||
color: Appearance.m3colors.m3secondaryContainer
|
||||
color: Appearance.colors.colSecondaryContainer
|
||||
// color: "transparent"
|
||||
radius: Appearance.rounding.small
|
||||
implicitHeight: Math.max(nameRowLayout.implicitHeight + 5 * 2, 30)
|
||||
@@ -246,23 +248,25 @@ Rectangle {
|
||||
spacing: 0
|
||||
Repeater {
|
||||
model: ScriptModel {
|
||||
values: StringUtils.splitMarkdownBlocks(root.messageData?.content)
|
||||
values: root.messageBlocks.map((block, index) => index)
|
||||
}
|
||||
delegate: Loader {
|
||||
required property int index
|
||||
property var thisBlock: root.messageBlocks[index]
|
||||
Layout.fillWidth: true
|
||||
// property var segment: modelData
|
||||
property var segmentContent: modelData.content
|
||||
property var segmentLang: modelData.lang
|
||||
// property var segment: thisBlock
|
||||
property var segmentContent: thisBlock.content
|
||||
property var segmentLang: thisBlock.lang
|
||||
property var messageData: root.messageData
|
||||
property var editing: root.editing
|
||||
property var renderMarkdown: root.renderMarkdown
|
||||
property var enableMouseSelection: root.enableMouseSelection
|
||||
property bool thinking: root.messageData?.thinking ?? true
|
||||
property bool done: root.messageData?.done ?? false
|
||||
property bool completed: modelData.completed ?? false
|
||||
property bool completed: thisBlock.completed ?? false
|
||||
|
||||
source: modelData.type === "code" ? "MessageCodeBlock.qml" :
|
||||
modelData.type === "think" ? "MessageThinkBlock.qml" :
|
||||
source: thisBlock.type === "code" ? "MessageCodeBlock.qml" :
|
||||
thisBlock.type === "think" ? "MessageThinkBlock.qml" :
|
||||
"MessageTextBlock.qml"
|
||||
|
||||
}
|
||||
@@ -277,7 +281,9 @@ Rectangle {
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
|
||||
Repeater {
|
||||
model: root.messageData?.annotationSources
|
||||
model: ScriptModel {
|
||||
values: root.messageData?.annotationSources || []
|
||||
}
|
||||
delegate: AnnotationSourceButton {
|
||||
id: annotationButton
|
||||
displayText: modelData.text
|
||||
|
||||
@@ -21,7 +21,7 @@ RippleButton {
|
||||
leftPadding: (implicitHeight - faviconSize) / 2
|
||||
rightPadding: 10
|
||||
buttonRadius: Appearance.rounding.full
|
||||
colBackground: Appearance.m3colors.m3surfaceContainerHighest
|
||||
colBackground: Appearance.colors.colSurfaceContainerHighest
|
||||
colBackgroundHover: Appearance.colors.colSurfaceContainerHighestHover
|
||||
colRipple: Appearance.colors.colSurfaceContainerHighestActive
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ ColumnLayout {
|
||||
topRightRadius: codeBlockBackgroundRounding
|
||||
bottomLeftRadius: Appearance.rounding.unsharpen
|
||||
bottomRightRadius: Appearance.rounding.unsharpen
|
||||
color: Appearance.m3colors.m3surfaceContainerHighest
|
||||
color: Appearance.colors.colSurfaceContainerHighest
|
||||
implicitHeight: codeBlockTitleBarRowLayout.implicitHeight + codeBlockHeaderPadding * 2
|
||||
|
||||
RowLayout { // Language and buttons
|
||||
@@ -97,7 +97,7 @@ 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"}'`)
|
||||
Hyprland.dispatch(`exec notify-send 'Code saved to file' '${downloadPath}/code.${segmentLang || "txt"}' -a Shell`)
|
||||
saveCodeButton.activated = true
|
||||
saveIconTimer.restart()
|
||||
}
|
||||
@@ -209,7 +209,7 @@ ColumnLayout {
|
||||
font.hintingPreference: Font.PreferNoHinting // Prevent weird bold text
|
||||
font.pixelSize: Appearance.font.pixelSize.small
|
||||
selectedTextColor: Appearance.m3colors.m3onSecondaryContainer
|
||||
selectionColor: Appearance.m3colors.m3secondaryContainer
|
||||
selectionColor: Appearance.colors.colSecondaryContainer
|
||||
// wrapMode: TextEdit.Wrap
|
||||
color: messageData.thinking ? Appearance.colors.colSubtext : Appearance.colors.colOnLayer1
|
||||
|
||||
|
||||
@@ -30,20 +30,32 @@ ColumnLayout {
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
Timer {
|
||||
id: renderTimer
|
||||
interval: 1000
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
renderLatex()
|
||||
for (const hash of renderedLatexHashes) {
|
||||
handleRenderedLatex(hash, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function renderLatex() {
|
||||
// Regex for $...$, $$...$$, \[...\]
|
||||
// Note: This is a simple approach and may need refinement for edge cases
|
||||
let regex = /(\$\$([\s\S]+?)\$\$)|(\$([^\$]+?)\$)|(\\\[((?:.|\n)+?)\\\])/g;
|
||||
let regex = /(\$\$([\s\S]+?)\$\$)|(\$([^\$]+?)\$)|(\\\[((?:.|\n)+?)\\\])|(\\\(([\s\S]+?)\\\))/g;
|
||||
let match;
|
||||
while ((match = regex.exec(segmentContent)) !== null) {
|
||||
let expression = match[1] || match[2] || match[3];
|
||||
let expression = match[1] || match[2] || match[3] || match[4] || match[5] || match[6] || match[7] || match[8];
|
||||
if (expression) {
|
||||
// Qt.callLater(() => {
|
||||
// });
|
||||
Qt.callLater(() => {
|
||||
const [renderHash, isNew] = LatexRenderer.requestRender(expression.trim());
|
||||
if (!renderedLatexHashes.includes(renderHash)) {
|
||||
renderedLatexHashes.push(renderHash);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,16 +65,13 @@ ColumnLayout {
|
||||
const imagePath = LatexRenderer.renderedImagePaths[hash];
|
||||
const markdownImage = ``;
|
||||
|
||||
const expression = StringUtils.escapeBackslashes(LatexRenderer.processedExpressions[hash]);
|
||||
const expression = LatexRenderer.processedExpressions[hash];
|
||||
renderedSegmentContent = renderedSegmentContent.replace(expression, markdownImage);
|
||||
}
|
||||
}
|
||||
|
||||
onDoneChanged: {
|
||||
renderLatex()
|
||||
for (const hash of renderedLatexHashes) {
|
||||
handleRenderedLatex(hash, true);
|
||||
}
|
||||
renderTimer.restart();
|
||||
}
|
||||
onEditingChanged: {
|
||||
if (!editing) {
|
||||
@@ -111,7 +120,7 @@ ColumnLayout {
|
||||
font.hintingPreference: Font.PreferNoHinting // Prevent weird bold text
|
||||
font.pixelSize: Appearance.font.pixelSize.small
|
||||
selectedTextColor: Appearance.m3colors.m3onSecondaryContainer
|
||||
selectionColor: Appearance.m3colors.m3secondaryContainer
|
||||
selectionColor: Appearance.colors.colSecondaryContainer
|
||||
wrapMode: TextEdit.Wrap
|
||||
color: messageData.thinking ? Appearance.colors.colSubtext : Appearance.colors.colOnLayer1
|
||||
textFormat: renderMarkdown ? TextEdit.MarkdownText : TextEdit.PlainText
|
||||
|
||||
@@ -64,7 +64,7 @@ Item {
|
||||
|
||||
Rectangle { // Header background
|
||||
id: header
|
||||
color: Appearance.m3colors.m3surfaceContainerHighest
|
||||
color: Appearance.colors.colSurfaceContainerHighest
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: thinkBlockTitleBarRowLayout.implicitHeight + thinkBlockHeaderPaddingVertical * 2
|
||||
|
||||
|
||||
@@ -133,7 +133,7 @@ Button {
|
||||
opacity: root.showActions ? 1 : 0
|
||||
visible: opacity > 0
|
||||
radius: Appearance.rounding.small
|
||||
color: Appearance.m3colors.m3surfaceContainer
|
||||
color: Appearance.colors.colSurfaceContainer
|
||||
implicitHeight: contextMenuColumnLayout.implicitHeight + radius * 2
|
||||
implicitWidth: contextMenuColumnLayout.implicitWidth
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ Rectangle {
|
||||
RowLayout { // Header
|
||||
Rectangle { // Provider name
|
||||
id: providerNameWrapper
|
||||
color: Appearance.m3colors.m3secondaryContainer
|
||||
color: Appearance.colors.colSecondaryContainer
|
||||
radius: Appearance.rounding.small
|
||||
implicitWidth: providerName.implicitWidth + 10 * 2
|
||||
implicitHeight: Math.max(providerName.implicitHeight + 5 * 2, 30)
|
||||
@@ -269,7 +269,7 @@ Rectangle {
|
||||
}
|
||||
|
||||
buttonRadius: Appearance.rounding.small
|
||||
colBackground: Appearance.m3colors.m3surfaceContainerHighest
|
||||
colBackground: Appearance.colors.colSurfaceContainerHighest
|
||||
colBackgroundHover: Appearance.colors.colSurfaceContainerHighestHover
|
||||
colRipple: Appearance.colors.colSurfaceContainerHighestActive
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import "root:/"
|
||||
import "root:/services"
|
||||
import "root:/modules/common"
|
||||
import "root:/modules/common/widgets"
|
||||
import "root:/modules/common/functions/string_utils.js" as StringUtils
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Hyprland
|
||||
|
||||
RippleButton {
|
||||
id: root
|
||||
property string displayText: ""
|
||||
colBackground: Appearance.colors.colLayer2
|
||||
|
||||
implicitWidth: contentItem.implicitWidth + horizontalPadding * 2
|
||||
implicitHeight: contentItem.implicitHeight + verticalPadding * 2
|
||||
|
||||
contentItem: Item {
|
||||
anchors.centerIn: parent
|
||||
implicitWidth: languageRow.implicitWidth
|
||||
implicitHeight: languageText.implicitHeight
|
||||
RowLayout {
|
||||
id: languageRow
|
||||
anchors.centerIn: parent
|
||||
spacing: 0
|
||||
StyledText {
|
||||
id: languageText
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.leftMargin: 5
|
||||
text: root.displayText
|
||||
color: Appearance.colors.colOnLayer2
|
||||
font.pixelSize: Appearance.font.pixelSize.small
|
||||
}
|
||||
MaterialSymbol {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
iconSize: Appearance.font.pixelSize.hugeass
|
||||
text: "arrow_drop_down"
|
||||
color: Appearance.colors.colOnLayer2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
import "root:/"
|
||||
import "root:/services"
|
||||
import "root:/modules/common"
|
||||
import "root:/modules/common/widgets"
|
||||
import "root:/modules/common/functions/string_utils.js" as StringUtils
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Hyprland
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
property bool isInput: true // true for input, false for output
|
||||
property string placeholderText
|
||||
property string text: ""
|
||||
property var inputTextArea: isInput ? inputLoader.item : undefined
|
||||
readonly property string displayedText: isInput ? inputLoader.item.text :
|
||||
root.text.length > 0 ? outputLoader.item.text : ""
|
||||
default property alias actionButtons: actions.data
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: Math.max(150, inputColumn.implicitHeight)
|
||||
color: isInput ? Appearance.colors.colLayer1 : Appearance.colors.colSurfaceContainer
|
||||
radius: Appearance.rounding.normal
|
||||
border.color: isInput ? Appearance.colors.colOutlineVariant : "transparent"
|
||||
border.width: isInput ? 1 : 0
|
||||
|
||||
signal inputTextChanged(); // Signal emitted when text changes
|
||||
|
||||
ColumnLayout {
|
||||
id: inputColumn
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
Loader {
|
||||
id: inputLoader
|
||||
active: root.isInput
|
||||
visible: root.isInput
|
||||
Layout.fillWidth: true
|
||||
sourceComponent: StyledTextArea { // Input area
|
||||
id: inputTextArea
|
||||
placeholderText: root.placeholderText
|
||||
wrapMode: TextEdit.Wrap
|
||||
textFormat: TextEdit.PlainText
|
||||
font.pixelSize: Appearance.font.pixelSize.small
|
||||
color: Appearance.colors.colOnLayer1
|
||||
padding: 15
|
||||
background: null
|
||||
onTextChanged: root.inputTextChanged()
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: outputLoader
|
||||
active: !root.isInput
|
||||
visible: !root.isInput
|
||||
Layout.fillWidth: true
|
||||
sourceComponent: StyledText { // Output area
|
||||
id: outputTextArea
|
||||
padding: 15
|
||||
wrapMode: Text.Wrap
|
||||
font.pixelSize: Appearance.font.pixelSize.small
|
||||
color: root.text.length > 0 ? Appearance.colors.colOnLayer1 : Appearance.colors.colSubtext
|
||||
text: root.text.length > 0 ? root.text : root.placeholderText
|
||||
}
|
||||
}
|
||||
|
||||
Item { Layout.fillHeight: true }
|
||||
|
||||
RowLayout { // Status row
|
||||
Layout.fillWidth: true
|
||||
Layout.margins: 10
|
||||
spacing: 10
|
||||
|
||||
Loader {
|
||||
active: root.isInput
|
||||
visible: root.isInput
|
||||
Layout.leftMargin: 10
|
||||
sourceComponent: Text {
|
||||
text: qsTr("%1 characters").arg(inputLoader.item.text.length)
|
||||
color: Appearance.colors.colOnLayer1
|
||||
font.pixelSize: Appearance.font.pixelSize.smaller
|
||||
}
|
||||
}
|
||||
Item { Layout.fillWidth: true }
|
||||
ButtonGroup {
|
||||
id: actions
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -52,8 +52,17 @@ Scope {
|
||||
Loader {
|
||||
id: sidebarContentLoader
|
||||
active: GlobalStates.sidebarRightOpen
|
||||
anchors.centerIn: parent
|
||||
width: sidebarWidth - Appearance.sizes.hyprlandGapsOut * 2
|
||||
anchors {
|
||||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
right: parent.right
|
||||
left: parent.left
|
||||
topMargin: Appearance.sizes.hyprlandGapsOut
|
||||
rightMargin: Appearance.sizes.hyprlandGapsOut
|
||||
bottomMargin: Appearance.sizes.hyprlandGapsOut
|
||||
leftMargin: Appearance.sizes.elevationMargin
|
||||
}
|
||||
width: sidebarWidth - Appearance.sizes.hyprlandGapsOut - Appearance.sizes.elevationMargin
|
||||
height: parent.height - Appearance.sizes.hyprlandGapsOut * 2
|
||||
|
||||
sourceComponent: Item {
|
||||
@@ -118,14 +127,38 @@ Scope {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
QuickToggleButton {
|
||||
toggled: false
|
||||
buttonIcon: "power_settings_new"
|
||||
onClicked: {
|
||||
Hyprland.dispatch("global quickshell:sessionOpen")
|
||||
ButtonGroup {
|
||||
QuickToggleButton {
|
||||
toggled: false
|
||||
buttonIcon: "restart_alt"
|
||||
onClicked: {
|
||||
Hyprland.dispatch("reload")
|
||||
Quickshell.reload(true)
|
||||
}
|
||||
StyledToolTip {
|
||||
content: qsTr("Reload Hyprland & Quickshell")
|
||||
}
|
||||
}
|
||||
StyledToolTip {
|
||||
content: qsTr("Session")
|
||||
QuickToggleButton {
|
||||
toggled: false
|
||||
buttonIcon: "settings"
|
||||
onClicked: {
|
||||
Hyprland.dispatch(`exec ${ConfigOptions.apps.settings}`)
|
||||
Hyprland.dispatch(`global quickshell:sidebarRightClose`)
|
||||
}
|
||||
StyledToolTip {
|
||||
content: qsTr("Plasma Settings")
|
||||
}
|
||||
}
|
||||
QuickToggleButton {
|
||||
toggled: false
|
||||
buttonIcon: "power_settings_new"
|
||||
onClicked: {
|
||||
Hyprland.dispatch("global quickshell:sessionOpen")
|
||||
}
|
||||
StyledToolTip {
|
||||
content: qsTr("Session")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ RippleButton {
|
||||
font.weight: bold ? Font.DemiBold : Font.Normal
|
||||
color: (isToday == 1) ? Appearance.m3colors.m3onPrimary :
|
||||
(isToday == 0) ? Appearance.colors.colOnLayer1 :
|
||||
Appearance.m3colors.m3outlineVariant
|
||||
Appearance.colors.colOutlineVariant
|
||||
|
||||
Behavior on color {
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
|
||||
@@ -3,16 +3,28 @@ import "root:/modules/common/widgets"
|
||||
import "../"
|
||||
import Quickshell.Io
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
|
||||
QuickToggleButton {
|
||||
toggled: idleInhibitor.running
|
||||
id: root
|
||||
toggled: false
|
||||
buttonIcon: "coffee"
|
||||
onClicked: {
|
||||
idleInhibitor.running = !idleInhibitor.running
|
||||
if (toggled) {
|
||||
root.toggled = false
|
||||
Hyprland.dispatch("exec pkill wayland-idle") // pkill doesn't accept too long names
|
||||
} else {
|
||||
root.toggled = true
|
||||
Hyprland.dispatch('exec ${XDG_CONFIG_HOME:-$HOME/.config}/quickshell/scripts/wayland-idle-inhibitor.py')
|
||||
}
|
||||
}
|
||||
Process {
|
||||
id: idleInhibitor
|
||||
command: ["bash", "-c", "${XDG_CONFIG_HOME:-$HOME/.config}/quickshell/scripts/wayland-idle-inhibitor.py"]
|
||||
id: fetchActiveState
|
||||
running: true
|
||||
command: ["bash", "-c", "pidof wayland-idle-inhibitor.py"]
|
||||
onExited: (exitCode, exitStatus) => {
|
||||
root.toggled = exitCode === 0
|
||||
}
|
||||
}
|
||||
StyledToolTip {
|
||||
content: qsTr("Keep system awake")
|
||||
|
||||
@@ -15,7 +15,7 @@ QuickToggleButton {
|
||||
toggleNetwork.running = true
|
||||
}
|
||||
altAction: () => {
|
||||
Hyprland.dispatch(`exec ${ConfigOptions.apps.network}`)
|
||||
Hyprland.dispatch(`exec ${Network.ethernet ? ConfigOptions.apps.networkEthernet : ConfigOptions.apps.network}`)
|
||||
Hyprland.dispatch("global quickshell:sidebarRightClose")
|
||||
}
|
||||
Process {
|
||||
|
||||
@@ -114,7 +114,7 @@ Item {
|
||||
id: tabBarBottomBorder
|
||||
Layout.fillWidth: true
|
||||
height: 1
|
||||
color: Appearance.m3colors.m3outlineVariant
|
||||
color: Appearance.colors.colOutlineVariant
|
||||
}
|
||||
|
||||
SwipeView {
|
||||
@@ -228,7 +228,7 @@ Item {
|
||||
anchors.margins: root.dialogMargins
|
||||
implicitHeight: dialogColumnLayout.implicitHeight
|
||||
|
||||
color: Appearance.m3colors.m3surfaceContainerHigh
|
||||
color: Appearance.colors.colSurfaceContainerHigh
|
||||
radius: Appearance.rounding.normal
|
||||
|
||||
function addTask() {
|
||||
@@ -264,7 +264,7 @@ Item {
|
||||
color: activeFocus ? Appearance.m3colors.m3onSurface : Appearance.m3colors.m3onSurfaceVariant
|
||||
renderType: Text.NativeRendering
|
||||
selectedTextColor: Appearance.m3colors.m3onSecondaryContainer
|
||||
selectionColor: Appearance.m3colors.m3secondaryContainer
|
||||
selectionColor: Appearance.colors.colSecondaryContainer
|
||||
placeholderText: qsTr("Task description")
|
||||
placeholderTextColor: Appearance.m3colors.m3outline
|
||||
focus: root.showAddDialog
|
||||
|
||||
@@ -5,6 +5,7 @@ import Qt5Compat.GraphicalEffects
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Widgets
|
||||
import Quickshell.Services.Pipewire
|
||||
|
||||
@@ -16,7 +17,7 @@ Item {
|
||||
property int dialogMargins: 16
|
||||
property PwNode selectedDevice
|
||||
|
||||
function showDeviceSelectorDialog(input) {
|
||||
function showDeviceSelectorDialog(input: bool) {
|
||||
root.selectedDevice = null
|
||||
root.showDeviceSelector = true
|
||||
root.deviceSelectorInput = input
|
||||
@@ -160,7 +161,7 @@ Item {
|
||||
|
||||
Rectangle { // The dialog
|
||||
id: dialog
|
||||
color: Appearance.m3colors.m3surfaceContainerHigh
|
||||
color: Appearance.colors.colSurfaceContainerHigh
|
||||
radius: Appearance.rounding.normal
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
@@ -207,9 +208,11 @@ Item {
|
||||
spacing: 0
|
||||
|
||||
Repeater {
|
||||
model: Pipewire.nodes.values.filter(node => {
|
||||
return !node.isStream && node.isSink !== root.deviceSelectorInput && node.audio
|
||||
})
|
||||
model: ScriptModel {
|
||||
values: Pipewire.nodes.values.filter(node => {
|
||||
return !node.isStream && node.isSink !== root.deviceSelectorInput && node.audio
|
||||
})
|
||||
}
|
||||
|
||||
// This could and should be refractored, but all data becomes null when passed wtf
|
||||
delegate: StyledRadioButton {
|
||||
|
||||
Reference in New Issue
Block a user