forked from Shinonome/dots-hyprland
merge upstream
This commit is contained in:
@@ -46,10 +46,10 @@
|
|||||||
"fakeScreenRounding": 2 // 0: None | 1: Always | 2: When not fullscreen
|
"fakeScreenRounding": 2 // 0: None | 1: Always | 2: When not fullscreen
|
||||||
},
|
},
|
||||||
"apps": {
|
"apps": {
|
||||||
"bluetooth": "blueberry",
|
"bluetooth": "better-control --bluetooth",
|
||||||
"imageViewer": "loupe",
|
"imageViewer": "loupe",
|
||||||
"network": "XDG_CURRENT_DESKTOP=\"gnome\" gnome-control-center wifi",
|
"network": "better-control --wifi",
|
||||||
"settings": "XDG_CURRENT_DESKTOP=\"gnome\" gnome-control-center",
|
"settings": "better-control",
|
||||||
"taskManager": "gnome-usage",
|
"taskManager": "gnome-usage",
|
||||||
"terminal": "foot" // This is only for shell actions
|
"terminal": "foot" // This is only for shell actions
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ var lastCoverPath = '';
|
|||||||
function isRealPlayer(player) {
|
function isRealPlayer(player) {
|
||||||
return (
|
return (
|
||||||
// Remove unecessary native buses from browsers if there's plasma integration
|
// Remove unecessary native buses from browsers if there's plasma integration
|
||||||
!(hasPlasmaIntegration && player.busName.startsWith('org.mpris.MediaPlayer2.firefox')) &&
|
// !(hasPlasmaIntegration && player.busName.startsWith('org.mpris.MediaPlayer2.firefox')) &&
|
||||||
!(hasPlasmaIntegration && player.busName.startsWith('org.mpris.MediaPlayer2.chromium')) &&
|
// !(hasPlasmaIntegration && player.busName.startsWith('org.mpris.MediaPlayer2.chromium')) &&
|
||||||
// playerctld just copies other buses and we don't need duplicates
|
// playerctld just copies other buses and we don't need duplicates
|
||||||
!player.busName.startsWith('org.mpris.MediaPlayer2.playerctld') &&
|
!player.busName.startsWith('org.mpris.MediaPlayer2.playerctld') &&
|
||||||
// Non-instance mpd bus
|
// Non-instance mpd bus
|
||||||
@@ -209,8 +209,15 @@ const CoverArt = ({ player, ...rest }) => {
|
|||||||
execAsync(['bash', '-c',
|
execAsync(['bash', '-c',
|
||||||
`${App.configDir}/scripts/color_generation/generate_colors_material.py --path '${coverPath}' --mode ${darkMode.value ? 'dark' : 'light'} > ${GLib.get_user_state_dir()}/ags/scss/_musicmaterial.scss`])
|
`${App.configDir}/scripts/color_generation/generate_colors_material.py --path '${coverPath}' --mode ${darkMode.value ? 'dark' : 'light'} > ${GLib.get_user_state_dir()}/ags/scss/_musicmaterial.scss`])
|
||||||
.then(() => {
|
.then(() => {
|
||||||
exec(`${App.configDir}/scripts/color_generation/pywal.sh -i "${player.coverPath}" -n -t -s -e -q ${darkMode.value ? '' : '-l'}`)
|
const dominantColor = `#${Utils.exec(`sh -c "magick '${coverPath}' -scale 1x1\\! -format '%[fx:int(255*r+.5)],%[fx:int(255*g+.5)],%[fx:int(255*b+.5)]' info: | sed 's/,/\\n/g' | xargs -L 1 printf '%02x' ; echo"`)}`
|
||||||
exec(`cp ${GLib.get_user_cache_dir()}/wal/colors.scss ${GLib.get_user_state_dir()}/ags/scss/_musicwal.scss`);
|
console.log(dominantColor);
|
||||||
|
// exec(`${App.configDir}/scripts/color_generation/pywal.sh -i "${player.coverPath}" -n -t -s -e -q ${darkMode.value ? '' : '-l'}`)
|
||||||
|
// exec(`cp ${GLib.get_user_cache_dir()}/wal/colors.scss ${GLib.get_user_state_dir()}/ags/scss/_musicwal.scss`);
|
||||||
|
exec(`cp '${App.configDir}/scripts/templates/wal/_musicwal.scss' '${GLib.get_user_state_dir()}/ags/scss/_musicwal.scss'`);
|
||||||
|
exec(`sed -i 's/{{dominantColor}}/${dominantColor}/g' '${GLib.get_user_state_dir()}/ags/scss/_musicwal.scss'`)
|
||||||
|
exec(`sed -i 's/{{backgroundColor}}/${darkMode.value ? "#0E1415" : "#EEF4F4"}/g' '${GLib.get_user_state_dir()}/ags/scss/_musicwal.scss'`)
|
||||||
|
exec(`sed -i 's/{{foregroundColor}}/${darkMode.value ? "#EEF4F4" : "#0E1415"}/g' '${GLib.get_user_state_dir()}/ags/scss/_musicwal.scss'`)
|
||||||
|
|
||||||
exec(`sass -I "${GLib.get_user_state_dir()}/ags/scss" -I "${App.configDir}/scss/fallback" "${App.configDir}/scss/_music.scss" "${stylePath}"`);
|
exec(`sass -I "${GLib.get_user_state_dir()}/ags/scss" -I "${App.configDir}/scss/fallback" "${App.configDir}/scss/_music.scss" "${stylePath}"`);
|
||||||
Utils.timeout(200, () => {
|
Utils.timeout(200, () => {
|
||||||
// self.attribute.showImage(self, coverPath)
|
// self.attribute.showImage(self, coverPath)
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
// Special
|
||||||
|
$background: {{backgroundColor}};
|
||||||
|
$foreground: {{foregroundColor}};
|
||||||
|
$cursor: {{foregroundColor}};
|
||||||
|
|
||||||
|
// Colors
|
||||||
|
$color0: {{dominantColor}};
|
||||||
|
$color1: {{dominantColor}};
|
||||||
|
$color2: {{dominantColor}};
|
||||||
|
$color3: {{dominantColor}};
|
||||||
|
$color4: {{dominantColor}};
|
||||||
|
$color5: {{dominantColor}};
|
||||||
|
$color6: {{dominantColor}};
|
||||||
|
$color7: {{dominantColor}};
|
||||||
|
$color8: {{dominantColor}};
|
||||||
|
$color9: {{dominantColor}};
|
||||||
|
$color10: {{dominantColor}};
|
||||||
|
$color11: {{dominantColor}};
|
||||||
|
$color12: {{dominantColor}};
|
||||||
|
$color13: {{dominantColor}};
|
||||||
|
$color14: {{dominantColor}};
|
||||||
|
$color15: {{dominantColor}};
|
||||||
@@ -20,8 +20,11 @@ $secondaryContainer: transparentize(mix(mix($background, $color2, 50%), $color6,
|
|||||||
$onSecondaryContainer: mix($color7, $color2, 90%);
|
$onSecondaryContainer: mix($color7, $color2, 90%);
|
||||||
@if $darkmode == False {
|
@if $darkmode == False {
|
||||||
$onSecondaryContainer: mix($onSecondaryContainer, black, 50%);
|
$onSecondaryContainer: mix($onSecondaryContainer, black, 50%);
|
||||||
|
} @else {
|
||||||
|
$onSecondaryContainer: mix($onSecondaryContainer, white, 50%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.osd-music {
|
.osd-music {
|
||||||
@include menu_decel;
|
@include menu_decel;
|
||||||
@include elevation2;
|
@include elevation2;
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
# See https://wiki.hyprland.org/Configuring/Binds/
|
# See https://wiki.hyprland.org/Configuring/Binds/
|
||||||
#!
|
#!
|
||||||
##! User keybinds
|
##! Extra keybinds
|
||||||
bind = Ctrl+Super+Alt, Slash, exec, xdg-open ~/.config/hypr/custom/keybinds.conf # Edit custom keybinds
|
bind = Ctrl+Super+Alt, Slash, exec, xdg-open ~/.config/hypr/custom/keybinds.conf # Edit extra keybinds
|
||||||
|
|
||||||
# Add stuff here
|
# Add stuff here
|
||||||
# Use #! to add an extra column on the cheatsheet
|
# Use #! to add an extra column on the cheatsheet
|
||||||
# Use ##! to add a section in that column
|
# Use ##! to add a section in that column
|
||||||
|
# Add a comment after a bind to add a description, like above
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# This file sources other files in `hyprland` and `custom` folders
|
# This file sources other files in `hyprland` and `custom` folders
|
||||||
# You wanna add your stuff in file in `custom`
|
# You wanna add your stuff in files in `custom`
|
||||||
|
|
||||||
exec = hyprctl dispatch submap global # DO NOT REMOVE THIS OR YOU WON'T BE ABLE TO USE ANY KEYBIND
|
exec = hyprctl dispatch submap global # DO NOT REMOVE THIS OR YOU WON'T BE ABLE TO USE ANY KEYBIND
|
||||||
submap = global # This is required for catchall to work
|
submap = global # This is required for catchall to work
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ bindd = Super, A, Toggle left sidebar, global, quickshell:sidebarLeftToggle # To
|
|||||||
bind = Super, O, global, quickshell:sidebarLeftToggle # [hidden]
|
bind = Super, O, global, quickshell:sidebarLeftToggle # [hidden]
|
||||||
bindd = Super, N, Toggle right sidebar, global, quickshell:sidebarRightToggle # Toggle right sidebar
|
bindd = Super, N, Toggle right sidebar, global, quickshell:sidebarRightToggle # Toggle right sidebar
|
||||||
bindd = Super, Slash, Toggle cheatsheet, global, quickshell:cheatsheetToggle # Toggle cheatsheet
|
bindd = Super, Slash, Toggle cheatsheet, global, quickshell:cheatsheetToggle # Toggle cheatsheet
|
||||||
|
bindd = Super, M, Toggle media controls, global, quickshell:mediaControlsToggle # Toggle media controls
|
||||||
bindd = Ctrl+Alt, Delete, Toggle session menu, global, quickshell:sessionToggle # Toggle session menu
|
bindd = Ctrl+Alt, Delete, Toggle session menu, global, quickshell:sessionToggle # Toggle session menu
|
||||||
bind = Ctrl+Alt, Delete, exec, qs ipc call TEST_ALIVE || pkill wlogout || wlogout -p layer-shell # [hidden]
|
bind = Ctrl+Alt, Delete, exec, qs ipc call TEST_ALIVE || pkill wlogout || wlogout -p layer-shell # [hidden]
|
||||||
|
|
||||||
@@ -46,8 +47,8 @@ bindd = Ctrl+Shift+Alt+Super, Delete, Shutdown, exec, systemctl poweroff || logi
|
|||||||
|
|
||||||
##! Utilities
|
##! Utilities
|
||||||
# Screenshot, Record, OCR, Color picker, Clipboard history
|
# Screenshot, Record, OCR, Color picker, Clipboard history
|
||||||
bindd = Super, V, Copy clipboard history entry, exec, pkill fuzzel || cliphist list | fuzzel --match-mode fzf --dmenu | cliphist decode | wl-copy # Clipboard history >> clipboard
|
bindd = Super, V, Copy clipboard history entry, exec, pkill fuzzel || cliphist list | fuzzel --match-mode fzf --dmenu | cliphist decode | wl-copy # Clipboard history >> clipboard
|
||||||
bindd = Super, Period, Copy an emoji, exec, pkill fuzzel || ~/.local/bin/fuzzel-emoji # Pick emoji >> clipboard
|
bindd = Super, Period, Copy an emoji, exec, pkill fuzzel || ~/.local/bin/fuzzel-emoji # Emoji
|
||||||
bindd = Super+Shift, S, Screen snip, exec, grimblast --freeze copy area # Screen snip >> clipboard
|
bindd = Super+Shift, S, Screen snip, exec, grimblast --freeze copy area # Screen snip >> clipboard
|
||||||
bindd = Super+Shift+Alt, S, Screen snip and annotate, exec, grim -g "$(slurp)" - | swappy -f - # Screen snip and annotate
|
bindd = Super+Shift+Alt, S, Screen snip and annotate, exec, grim -g "$(slurp)" - | swappy -f - # Screen snip and annotate
|
||||||
# OCR
|
# OCR
|
||||||
@@ -198,8 +199,8 @@ bind = Super+Alt, E, exec, thunar # [hidden]
|
|||||||
bind = Super, W, exec, zen-browser # [hidden]
|
bind = Super, W, exec, zen-browser # [hidden]
|
||||||
bind = Super+Shift, W, exec, wps # WPS Office
|
bind = Super+Shift, W, exec, wps # WPS Office
|
||||||
bind = Ctrl+Super, V, exec, pavucontrol # Pavucontrol (volume mixer)
|
bind = Ctrl+Super, V, exec, pavucontrol # Pavucontrol (volume mixer)
|
||||||
|
bind = Super, I, exec, better-control # Better Control (settings app)
|
||||||
bind = Super, X, exec, gnome-text-editor --new-window # GNOME Text Editor
|
bind = Super, X, exec, gnome-text-editor --new-window # GNOME Text Editor
|
||||||
bind = Super, I, exec, XDG_CURRENT_DESKTOP="gnome" gnome-control-center # GNOME Settings
|
|
||||||
bind = Ctrl+Shift, Escape, exec, gnome-system-monitor # GNOME System monitor
|
bind = Ctrl+Shift, Escape, exec, gnome-system-monitor # GNOME System monitor
|
||||||
|
|
||||||
# Cursed stuff
|
# Cursed stuff
|
||||||
|
|||||||
@@ -105,15 +105,14 @@ layerrule = ignorealpha 0.6, osk[0-9]*
|
|||||||
|
|
||||||
# Quickshell
|
# Quickshell
|
||||||
## My stuff
|
## My stuff
|
||||||
|
layerrule = animation slide, quickshell:bar
|
||||||
layerrule = animation fade, quickshell:screenCorners
|
layerrule = animation fade, quickshell:screenCorners
|
||||||
layerrule = animation slide right, quickshell:sidebarRight
|
layerrule = animation slide right, quickshell:sidebarRight
|
||||||
layerrule = animation slide left, quickshell:sidebarLeft
|
layerrule = animation slide left, quickshell:sidebarLeft
|
||||||
layerrule = animation slide top, quickshell:onScreenDisplay
|
|
||||||
layerrule = blur, quickshell:session
|
layerrule = blur, quickshell:session
|
||||||
layerrule = noanim, quickshell:session
|
layerrule = noanim, quickshell:session
|
||||||
# Launchers need to be FAST
|
# Launchers need to be FAST
|
||||||
layerrule = noanim, quickshell:overview
|
layerrule = noanim, quickshell:overview
|
||||||
layerrule = noanim, launcher
|
|
||||||
layerrule = noanim, gtk4-layer-shell
|
layerrule = noanim, gtk4-layer-shell
|
||||||
## outfoxxed's stuff
|
## outfoxxed's stuff
|
||||||
layerrule = blur, shell:bar
|
layerrule = blur, shell:bar
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ pragma ComponentBehavior: Bound
|
|||||||
|
|
||||||
Singleton {
|
Singleton {
|
||||||
id: root
|
id: root
|
||||||
property int sidebarLeftOpenCount: 0
|
property bool sidebarLeftOpen: false
|
||||||
property int sidebarRightOpenCount: 0
|
property bool sidebarRightOpen: false
|
||||||
property bool overviewOpen: false
|
property bool overviewOpen: false
|
||||||
property bool workspaceShowNumbers: false
|
property bool workspaceShowNumbers: false
|
||||||
property bool superReleaseMightTrigger: true
|
property bool superReleaseMightTrigger: true
|
||||||
@@ -31,7 +31,7 @@ Singleton {
|
|||||||
|
|
||||||
GlobalShortcut {
|
GlobalShortcut {
|
||||||
name: "workspaceNumber"
|
name: "workspaceNumber"
|
||||||
description: "Hold to show workspace numbers, release to show icons"
|
description: qsTr("Hold to show workspace numbers, release to show icons")
|
||||||
|
|
||||||
onPressed: {
|
onPressed: {
|
||||||
workspaceShowNumbersTimer.start()
|
workspaceShowNumbersTimer.start()
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ Scope {
|
|||||||
ScrollHint {
|
ScrollHint {
|
||||||
reveal: barLeftSideMouseArea.hovered
|
reveal: barLeftSideMouseArea.hovered
|
||||||
icon: "light_mode"
|
icon: "light_mode"
|
||||||
tooltipText: "Scroll to change brightness"
|
tooltipText: qsTr("Scroll to change brightness")
|
||||||
side: "left"
|
side: "left"
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
@@ -127,7 +127,7 @@ Scope {
|
|||||||
|
|
||||||
// Layout.fillHeight: true
|
// Layout.fillHeight: true
|
||||||
radius: Appearance.rounding.full
|
radius: Appearance.rounding.full
|
||||||
color: (barLeftSideMouseArea.pressed || GlobalStates.sidebarLeftOpenCount > 0) ? Appearance.colors.colLayer1Active : barLeftSideMouseArea.hovered ? Appearance.colors.colLayer1Hover : "transparent"
|
color: (barLeftSideMouseArea.pressed || GlobalStates.sidebarLeftOpen) ? Appearance.colors.colLayer1Active : barLeftSideMouseArea.hovered ? Appearance.colors.colLayer1Hover : "transparent"
|
||||||
implicitWidth: distroIcon.width + 5*2
|
implicitWidth: distroIcon.width + 5*2
|
||||||
implicitHeight: distroIcon.height + 5*2
|
implicitHeight: distroIcon.height + 5*2
|
||||||
|
|
||||||
@@ -283,7 +283,7 @@ Scope {
|
|||||||
ScrollHint {
|
ScrollHint {
|
||||||
reveal: barRightSideMouseArea.hovered
|
reveal: barRightSideMouseArea.hovered
|
||||||
icon: "volume_up"
|
icon: "volume_up"
|
||||||
tooltipText: "Scroll to change volume"
|
tooltipText: qsTr("Scroll to change volume")
|
||||||
side: "right"
|
side: "right"
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
@@ -301,7 +301,7 @@ Scope {
|
|||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
implicitWidth: indicatorsRowLayout.implicitWidth + 10*2
|
implicitWidth: indicatorsRowLayout.implicitWidth + 10*2
|
||||||
radius: Appearance.rounding.full
|
radius: Appearance.rounding.full
|
||||||
color: (barRightSideMouseArea.pressed || GlobalStates.sidebarRightOpenCount > 0) ? Appearance.colors.colLayer1Active : barRightSideMouseArea.hovered ? Appearance.colors.colLayer1Hover : "transparent"
|
color: (barRightSideMouseArea.pressed || GlobalStates.sidebarRightOpen) ? Appearance.colors.colLayer1Active : barRightSideMouseArea.hovered ? Appearance.colors.colLayer1Hover : "transparent"
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: indicatorsRowLayout
|
id: indicatorsRowLayout
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import Quickshell.Services.UPower
|
|||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: root
|
id: root
|
||||||
|
property bool borderless: ConfigOptions.bar.borderless
|
||||||
readonly property var chargeState: UPower.displayDevice.state
|
readonly property var chargeState: UPower.displayDevice.state
|
||||||
readonly property bool isCharging: chargeState == UPowerDeviceState.Charging
|
readonly property bool isCharging: chargeState == UPowerDeviceState.Charging
|
||||||
readonly property bool isPluggedIn: isCharging || chargeState == UPowerDeviceState.PendingCharge
|
readonly property bool isPluggedIn: isCharging || chargeState == UPowerDeviceState.PendingCharge
|
||||||
@@ -18,7 +19,7 @@ Rectangle {
|
|||||||
|
|
||||||
implicitWidth: rowLayout.implicitWidth + rowLayout.spacing * 2
|
implicitWidth: rowLayout.implicitWidth + rowLayout.spacing * 2
|
||||||
implicitHeight: 32
|
implicitHeight: 32
|
||||||
color: Appearance.colors.colLayer1
|
color: borderless ? "transparent" : Appearance.colors.colLayer1
|
||||||
radius: Appearance.rounding.small
|
radius: Appearance.rounding.small
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
@@ -31,18 +32,14 @@ Rectangle {
|
|||||||
implicitWidth: (isCharging ? (boltIconLoader?.item?.width ?? 0) : 0)
|
implicitWidth: (isCharging ? (boltIconLoader?.item?.width ?? 0) : 0)
|
||||||
|
|
||||||
Behavior on implicitWidth {
|
Behavior on implicitWidth {
|
||||||
NumberAnimation {
|
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMove.duration
|
|
||||||
easing.type: Appearance.animation.elementMove.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
color: Appearance.colors.colOnLayer1
|
color: Appearance.colors.colOnLayer1
|
||||||
text: `${Math.round(percentage * 100)}%`
|
text: `${Math.round(percentage * 100)}`
|
||||||
}
|
}
|
||||||
|
|
||||||
CircularProgress {
|
CircularProgress {
|
||||||
@@ -56,6 +53,7 @@ Rectangle {
|
|||||||
|
|
||||||
MaterialSymbol {
|
MaterialSymbol {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
|
fill: 1
|
||||||
text: "battery_full"
|
text: "battery_full"
|
||||||
iconSize: Appearance.font.pixelSize.normal
|
iconSize: Appearance.font.pixelSize.normal
|
||||||
color: (isLow && !isCharging) ? batteryLowOnBackground : Appearance.m3colors.m3onSecondaryContainer
|
color: (isLow && !isCharging) ? batteryLowOnBackground : Appearance.m3colors.m3onSecondaryContainer
|
||||||
@@ -91,12 +89,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
NumberAnimation {
|
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMove.duration
|
|
||||||
easing.type: Appearance.animation.elementMove.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import "root:/modules/common"
|
import "root:/modules/common"
|
||||||
import "root:/modules/common/widgets/"
|
import "root:/modules/common/widgets/"
|
||||||
|
import "root:/modules/common/functions/color_utils.js" as ColorUtils
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
@@ -21,7 +22,9 @@ Button {
|
|||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
radius: Appearance.rounding.full
|
radius: Appearance.rounding.full
|
||||||
color: (button.down || extraActiveCondition) ? Appearance.colors.colLayer2Active : (button.hovered ? Appearance.colors.colLayer2Hover : Appearance.colors.colLayer2)
|
color: (button.down || extraActiveCondition) ? Appearance.colors.colLayer1Active :
|
||||||
|
(button.hovered ? Appearance.colors.colLayer1Hover :
|
||||||
|
ColorUtils.transparentize(Appearance.colors.colLayer1, 1))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,9 +5,10 @@ import QtQuick
|
|||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
property bool borderless: ConfigOptions.bar.borderless
|
||||||
implicitWidth: rowLayout.implicitWidth + rowLayout.spacing * 6 // idk, text seems nicer w/ more padding
|
implicitWidth: rowLayout.implicitWidth + rowLayout.spacing * 6 // idk, text seems nicer w/ more padding
|
||||||
implicitHeight: 32
|
implicitHeight: 32
|
||||||
color: Appearance.colors.colLayer1
|
color: borderless ? "transparent" : Appearance.colors.colLayer1
|
||||||
radius: Appearance.rounding.small
|
radius: Appearance.rounding.small
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
|
|||||||
@@ -1,29 +1,24 @@
|
|||||||
import "root:/modules/common"
|
import "root:/modules/common"
|
||||||
import "root:/modules/common/widgets"
|
import "root:/modules/common/widgets"
|
||||||
import "root:/services"
|
import "root:/services"
|
||||||
|
import "root:/modules/common/functions/string_utils.js" as StringUtils
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
import Quickshell.Services.Mpris
|
import Quickshell.Services.Mpris
|
||||||
|
import Quickshell.Hyprland
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
|
id: root
|
||||||
|
property bool borderless: ConfigOptions.bar.borderless
|
||||||
readonly property MprisPlayer activePlayer: MprisController.activePlayer
|
readonly property MprisPlayer activePlayer: MprisController.activePlayer
|
||||||
readonly property string cleanedTitle: activePlayer?.trackTitle.replace(/【[^】]*】/, "") || qsTr("No media")
|
readonly property string cleanedTitle: StringUtils.cleanMusicTitle(activePlayer?.trackTitle) || qsTr("No media")
|
||||||
|
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
implicitWidth: rowLayout.implicitWidth + rowLayout.spacing * 2
|
implicitWidth: rowLayout.implicitWidth + rowLayout.spacing * 2
|
||||||
implicitHeight: 40
|
implicitHeight: 40
|
||||||
|
|
||||||
// Background
|
|
||||||
Rectangle {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
width: parent.width
|
|
||||||
implicitHeight: 32
|
|
||||||
color: Appearance.colors.colLayer1
|
|
||||||
radius: Appearance.rounding.small
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
running: activePlayer?.playbackState == MprisPlaybackState.Playing
|
running: activePlayer?.playbackState == MprisPlaybackState.Playing
|
||||||
interval: 1000
|
interval: 1000
|
||||||
@@ -33,7 +28,7 @@ Item {
|
|||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
acceptedButtons: Qt.MiddleButton | Qt.BackButton | Qt.ForwardButton | Qt.RightButton
|
acceptedButtons: Qt.MiddleButton | Qt.BackButton | Qt.ForwardButton | Qt.RightButton | Qt.LeftButton
|
||||||
onPressed: (event) => {
|
onPressed: (event) => {
|
||||||
if (event.button === Qt.MiddleButton) {
|
if (event.button === Qt.MiddleButton) {
|
||||||
activePlayer.togglePlaying();
|
activePlayer.togglePlaying();
|
||||||
@@ -41,11 +36,21 @@ Item {
|
|||||||
activePlayer.previous();
|
activePlayer.previous();
|
||||||
} else if (event.button === Qt.ForwardButton || event.button === Qt.RightButton) {
|
} else if (event.button === Qt.ForwardButton || event.button === Qt.RightButton) {
|
||||||
activePlayer.next();
|
activePlayer.next();
|
||||||
|
} else if (event.button === Qt.LeftButton) {
|
||||||
|
Hyprland.dispatch("global quickshell:mediaControlsToggle")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
Rectangle { // Background
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: parent.width
|
||||||
|
implicitHeight: 32
|
||||||
|
color: borderless ? "transparent" : Appearance.colors.colLayer1
|
||||||
|
radius: Appearance.rounding.small
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout { // Real content
|
||||||
id: rowLayout
|
id: rowLayout
|
||||||
|
|
||||||
spacing: 4
|
spacing: 4
|
||||||
@@ -62,6 +67,7 @@ Item {
|
|||||||
|
|
||||||
MaterialSymbol {
|
MaterialSymbol {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
|
fill: 1
|
||||||
text: activePlayer?.isPlaying ? "pause" : "play_arrow"
|
text: activePlayer?.isPlaying ? "pause" : "play_arrow"
|
||||||
iconSize: Appearance.font.pixelSize.normal
|
iconSize: Appearance.font.pixelSize.normal
|
||||||
color: Appearance.m3colors.m3onSecondaryContainer
|
color: Appearance.m3colors.m3onSecondaryContainer
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ Item {
|
|||||||
|
|
||||||
MaterialSymbol {
|
MaterialSymbol {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
|
fill: 1
|
||||||
text: iconName
|
text: iconName
|
||||||
iconSize: Appearance.font.pixelSize.normal
|
iconSize: Appearance.font.pixelSize.normal
|
||||||
color: Appearance.m3colors.m3onSecondaryContainer
|
color: Appearance.m3colors.m3onSecondaryContainer
|
||||||
@@ -38,15 +39,11 @@ Item {
|
|||||||
StyledText {
|
StyledText {
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
color: Appearance.colors.colOnLayer1
|
color: Appearance.colors.colOnLayer1
|
||||||
text: `${Math.round(percentage * 100)}%`
|
text: `${Math.round(percentage * 100)}`
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on x {
|
Behavior on x {
|
||||||
NumberAnimation {
|
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMove.duration
|
|
||||||
easing.type: Appearance.animation.elementMove.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,9 +8,10 @@ import Quickshell.Io
|
|||||||
import Quickshell.Services.Mpris
|
import Quickshell.Services.Mpris
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
property bool borderless: ConfigOptions.bar.borderless
|
||||||
implicitWidth: rowLayout.implicitWidth + rowLayout.anchors.leftMargin + rowLayout.anchors.rightMargin
|
implicitWidth: rowLayout.implicitWidth + rowLayout.anchors.leftMargin + rowLayout.anchors.rightMargin
|
||||||
implicitHeight: 32
|
implicitHeight: 32
|
||||||
color: Appearance.colors.colLayer1
|
color: borderless ? "transparent" : Appearance.colors.colLayer1
|
||||||
radius: Appearance.rounding.small
|
radius: Appearance.rounding.small
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import "root:/modules/common/"
|
import "root:/modules/common/"
|
||||||
|
import "root:/modules/common/functions/color_utils.js" as ColorUtils
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import Quickshell
|
import Quickshell
|
||||||
@@ -57,7 +58,7 @@ MouseArea {
|
|||||||
ColorOverlay {
|
ColorOverlay {
|
||||||
anchors.fill: desaturatedIcon
|
anchors.fill: desaturatedIcon
|
||||||
source: desaturatedIcon
|
source: desaturatedIcon
|
||||||
color: Appearance.transparentize(Appearance.colors.colOnLayer0, 0.6)
|
color: ColorUtils.transparentize(Appearance.colors.colOnLayer0, 0.6)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,10 +7,12 @@ import Quickshell.Io
|
|||||||
import Quickshell.Hyprland
|
import Quickshell.Hyprland
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
property bool borderless: ConfigOptions.bar.borderless
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
implicitWidth: rowLayout.implicitWidth + rowLayout.spacing * 2
|
implicitWidth: rowLayout.implicitWidth + rowLayout.spacing * 2
|
||||||
implicitHeight: 32
|
implicitHeight: 32
|
||||||
color: Appearance.colors.colLayer1
|
color: borderless ? "transparent" : Appearance.colors.colLayer1
|
||||||
radius: Appearance.rounding.small
|
radius: Appearance.rounding.small
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
@@ -24,7 +26,8 @@ Rectangle {
|
|||||||
onClicked: Hyprland.dispatch("exec grimblast copy area")
|
onClicked: Hyprland.dispatch("exec grimblast copy area")
|
||||||
|
|
||||||
MaterialSymbol {
|
MaterialSymbol {
|
||||||
anchors.centerIn: parent
|
horizontalAlignment: Qt.AlignHCenter
|
||||||
|
fill: 1
|
||||||
text: "screenshot_region"
|
text: "screenshot_region"
|
||||||
iconSize: Appearance.font.pixelSize.normal
|
iconSize: Appearance.font.pixelSize.normal
|
||||||
color: Appearance.colors.colOnLayer2
|
color: Appearance.colors.colOnLayer2
|
||||||
@@ -37,7 +40,8 @@ Rectangle {
|
|||||||
onClicked: Hyprland.dispatch("exec hyprpicker -a")
|
onClicked: Hyprland.dispatch("exec hyprpicker -a")
|
||||||
|
|
||||||
MaterialSymbol {
|
MaterialSymbol {
|
||||||
anchors.centerIn: parent
|
horizontalAlignment: Qt.AlignHCenter
|
||||||
|
fill: 1
|
||||||
text: "colorize"
|
text: "colorize"
|
||||||
iconSize: Appearance.font.pixelSize.normal
|
iconSize: Appearance.font.pixelSize.normal
|
||||||
color: Appearance.colors.colOnLayer2
|
color: Appearance.colors.colOnLayer2
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import Qt5Compat.GraphicalEffects
|
|||||||
|
|
||||||
Item {
|
Item {
|
||||||
required property var bar
|
required property var bar
|
||||||
|
property bool borderless: ConfigOptions.bar.borderless
|
||||||
readonly property HyprlandMonitor monitor: Hyprland.monitorFor(bar.screen)
|
readonly property HyprlandMonitor monitor: Hyprland.monitorFor(bar.screen)
|
||||||
readonly property Toplevel activeWindow: ToplevelManager.activeToplevel
|
readonly property Toplevel activeWindow: ToplevelManager.activeToplevel
|
||||||
|
|
||||||
@@ -66,7 +67,7 @@ Item {
|
|||||||
implicitHeight: 32
|
implicitHeight: 32
|
||||||
implicitWidth: rowLayout.implicitWidth + widgetPadding * 2
|
implicitWidth: rowLayout.implicitWidth + widgetPadding * 2
|
||||||
radius: Appearance.rounding.small
|
radius: Appearance.rounding.small
|
||||||
color: Appearance.colors.colLayer1
|
color: borderless ? "transparent" : Appearance.colors.colLayer1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scroll to switch workspaces
|
// Scroll to switch workspaces
|
||||||
@@ -119,26 +120,14 @@ Item {
|
|||||||
opacity: (workspaceOccupied[index] && !(!activeWindow?.activated && monitor.activeWorkspace?.id === index+1)) ? 1 : 0
|
opacity: (workspaceOccupied[index] && !(!activeWindow?.activated && monitor.activeWorkspace?.id === index+1)) ? 1 : 0
|
||||||
|
|
||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
NumberAnimation {
|
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMove.duration
|
|
||||||
easing.type: Appearance.animation.elementMove.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Behavior on radiusLeft {
|
Behavior on radiusLeft {
|
||||||
NumberAnimation {
|
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMove.duration
|
|
||||||
easing.type: Appearance.animation.elementMove.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on radiusRight {
|
Behavior on radiusRight {
|
||||||
NumberAnimation {
|
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMove.duration
|
|
||||||
easing.type: Appearance.animation.elementMove.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -206,15 +195,6 @@ Item {
|
|||||||
(workspaceOccupied[index] ? Appearance.colors.colOnLayer1 :
|
(workspaceOccupied[index] ? Appearance.colors.colOnLayer1 :
|
||||||
Appearance.colors.colOnLayer1Inactive)
|
Appearance.colors.colOnLayer1Inactive)
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
ColorAnimation {
|
|
||||||
duration: Appearance.animation.elementMove.duration
|
|
||||||
easing.type: Appearance.animation.elementMove.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: Appearance.animation.elementMoveFast.duration
|
duration: Appearance.animation.elementMoveFast.duration
|
||||||
|
|||||||
@@ -2,10 +2,11 @@ import "root:/"
|
|||||||
import "root:/services"
|
import "root:/services"
|
||||||
import "root:/modules/common"
|
import "root:/modules/common"
|
||||||
import "root:/modules/common/widgets"
|
import "root:/modules/common/widgets"
|
||||||
|
import "root:/modules/common/functions/color_utils.js" as ColorUtils
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Effects
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import Qt5Compat.GraphicalEffects
|
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Widgets
|
import Quickshell.Widgets
|
||||||
@@ -15,18 +16,24 @@ import Quickshell.Hyprland
|
|||||||
Scope { // Scope
|
Scope { // Scope
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
Variants { // Window repeater
|
Loader {
|
||||||
id: cheatsheetVariants
|
id: cheatsheetLoader
|
||||||
model: Quickshell.screens
|
active: false
|
||||||
|
|
||||||
PanelWindow { // Window
|
sourceComponent: PanelWindow { // Window
|
||||||
id: cheatsheetRoot
|
id: cheatsheetRoot
|
||||||
visible: false
|
visible: cheatsheetLoader.active
|
||||||
focusable: true
|
|
||||||
|
|
||||||
property var modelData
|
anchors {
|
||||||
|
top: true
|
||||||
|
bottom: true
|
||||||
|
left: true
|
||||||
|
right: true
|
||||||
|
}
|
||||||
|
|
||||||
screen: modelData
|
function hide() {
|
||||||
|
cheatsheetLoader.active = false
|
||||||
|
}
|
||||||
exclusiveZone: 0
|
exclusiveZone: 0
|
||||||
implicitWidth: cheatsheetBackground.width + Appearance.sizes.elevationMargin * 2
|
implicitWidth: cheatsheetBackground.width + Appearance.sizes.elevationMargin * 2
|
||||||
implicitHeight: cheatsheetBackground.height + Appearance.sizes.elevationMargin * 2
|
implicitHeight: cheatsheetBackground.height + Appearance.sizes.elevationMargin * 2
|
||||||
@@ -42,25 +49,9 @@ Scope { // Scope
|
|||||||
HyprlandFocusGrab { // Click outside to close
|
HyprlandFocusGrab { // Click outside to close
|
||||||
id: grab
|
id: grab
|
||||||
windows: [ cheatsheetRoot ]
|
windows: [ cheatsheetRoot ]
|
||||||
active: false
|
active: cheatsheetRoot.visible
|
||||||
onCleared: () => {
|
onCleared: () => {
|
||||||
if (!active) cheatsheetRoot.visible = false
|
if (!active) cheatsheetRoot.hide()
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: cheatsheetRoot
|
|
||||||
function onVisibleChanged() {
|
|
||||||
delayedGrabTimer.start()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: delayedGrabTimer
|
|
||||||
interval: ConfigOptions.hacks.arbitraryRaceConditionDelay
|
|
||||||
repeat: false
|
|
||||||
onTriggered: {
|
|
||||||
grab.active = cheatsheetRoot.visible
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,9 +65,19 @@ Scope { // Scope
|
|||||||
implicitWidth: cheatsheetColumnLayout.implicitWidth + padding * 2
|
implicitWidth: cheatsheetColumnLayout.implicitWidth + padding * 2
|
||||||
implicitHeight: cheatsheetColumnLayout.implicitHeight + padding * 2
|
implicitHeight: cheatsheetColumnLayout.implicitHeight + padding * 2
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: MultiEffect {
|
||||||
|
source: cheatsheetBackground
|
||||||
|
anchors.fill: cheatsheetBackground
|
||||||
|
shadowEnabled: true
|
||||||
|
shadowVerticalOffset: 1
|
||||||
|
shadowColor: Appearance.colors.colShadow
|
||||||
|
shadowBlur: 0.5
|
||||||
|
}
|
||||||
|
|
||||||
Keys.onPressed: (event) => { // Esc to close
|
Keys.onPressed: (event) => { // Esc to close
|
||||||
if (event.key === Qt.Key_Escape) {
|
if (event.key === Qt.Key_Escape) {
|
||||||
cheatsheetRoot.visible = false
|
cheatsheetRoot.hide()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,23 +95,19 @@ Scope { // Scope
|
|||||||
|
|
||||||
PointingHandInteraction {}
|
PointingHandInteraction {}
|
||||||
onClicked: {
|
onClicked: {
|
||||||
cheatsheetRoot.visible = false
|
cheatsheetRoot.hide()
|
||||||
}
|
}
|
||||||
|
|
||||||
background: Item {}
|
background: null
|
||||||
contentItem: Rectangle {
|
contentItem: Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
radius: Appearance.rounding.full
|
radius: Appearance.rounding.full
|
||||||
color: closeButton.pressed ? Appearance.colors.colLayer0Active :
|
color: closeButton.pressed ? Appearance.colors.colLayer0Active :
|
||||||
closeButton.hovered ? Appearance.colors.colLayer0Hover :
|
closeButton.hovered ? Appearance.colors.colLayer0Hover :
|
||||||
Appearance.transparentize(Appearance.colors.colLayer0, 1)
|
ColorUtils.transparentize(Appearance.colors.colLayer0, 1)
|
||||||
|
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
ColorAnimation {
|
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMoveFast.duration
|
|
||||||
easing.type: Appearance.animation.elementMoveFast.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MaterialSymbol {
|
MaterialSymbol {
|
||||||
@@ -137,95 +134,49 @@ Scope { // Scope
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shadow
|
|
||||||
DropShadow {
|
|
||||||
anchors.fill: cheatsheetBackground
|
|
||||||
horizontalOffset: 0
|
|
||||||
verticalOffset: 2
|
|
||||||
radius: Appearance.sizes.elevationMargin
|
|
||||||
samples: Appearance.sizes.elevationMargin * 2 + 1 // Ideally should be 2 * radius + 1, see qt docs
|
|
||||||
color: Appearance.colors.colShadow
|
|
||||||
source: cheatsheetBackground
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IpcHandler {
|
IpcHandler {
|
||||||
target: "cheatsheet"
|
target: "cheatsheet"
|
||||||
|
|
||||||
function toggle(): void {
|
function toggle(): void {
|
||||||
for (let i = 0; i < cheatsheetVariants.instances.length; i++) {
|
cheatsheetLoader.active = !cheatsheetLoader.active
|
||||||
let panelWindow = cheatsheetVariants.instances[i];
|
|
||||||
if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
|
|
||||||
panelWindow.visible = !panelWindow.visible;
|
|
||||||
if(panelWindow.visible) Notifications.timeoutAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function close(): void {
|
function close(): void {
|
||||||
for (let i = 0; i < cheatsheetVariants.instances.length; i++) {
|
cheatsheetLoader.active = false
|
||||||
let panelWindow = cheatsheetVariants.instances[i];
|
|
||||||
if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
|
|
||||||
panelWindow.visible = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function open(): void {
|
function open(): void {
|
||||||
for (let i = 0; i < cheatsheetVariants.instances.length; i++) {
|
cheatsheetLoader.active = true
|
||||||
let panelWindow = cheatsheetVariants.instances[i];
|
|
||||||
if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
|
|
||||||
panelWindow.visible = true;
|
|
||||||
if(panelWindow.visible) Notifications.timeoutAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GlobalShortcut {
|
GlobalShortcut {
|
||||||
name: "cheatsheetToggle"
|
name: "cheatsheetToggle"
|
||||||
description: "Toggles cheatsheet on press"
|
description: qsTr("Toggles cheatsheet on press")
|
||||||
|
|
||||||
onPressed: {
|
onPressed: {
|
||||||
for (let i = 0; i < cheatsheetVariants.instances.length; i++) {
|
cheatsheetLoader.active = !cheatsheetLoader.active;
|
||||||
let panelWindow = cheatsheetVariants.instances[i];
|
|
||||||
if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
|
|
||||||
panelWindow.visible = !panelWindow.visible;
|
|
||||||
if(panelWindow.visible) Notifications.timeoutAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GlobalShortcut {
|
GlobalShortcut {
|
||||||
name: "cheatsheetOpen"
|
name: "cheatsheetOpen"
|
||||||
description: "Opens cheatsheet on press"
|
description: qsTr("Opens cheatsheet on press")
|
||||||
|
|
||||||
onPressed: {
|
onPressed: {
|
||||||
for (let i = 0; i < cheatsheetVariants.instances.length; i++) {
|
cheatsheetLoader.active = true;
|
||||||
let panelWindow = cheatsheetVariants.instances[i];
|
|
||||||
if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
|
|
||||||
panelWindow.visible = true;
|
|
||||||
if(panelWindow.visible) Notifications.timeoutAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GlobalShortcut {
|
GlobalShortcut {
|
||||||
name: "cheatsheetClose"
|
name: "cheatsheetClose"
|
||||||
description: "Closes cheatsheet on press"
|
description: qsTr("Closes cheatsheet on press")
|
||||||
|
|
||||||
onPressed: {
|
onPressed: {
|
||||||
for (let i = 0; i < cheatsheetVariants.instances.length; i++) {
|
cheatsheetLoader.active = false;
|
||||||
let panelWindow = cheatsheetVariants.instances[i];
|
|
||||||
if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
|
|
||||||
panelWindow.visible = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
|
import "root:/modules/common/functions/color_utils.js" as ColorUtils
|
||||||
pragma Singleton
|
pragma Singleton
|
||||||
pragma ComponentBehavior: Bound
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
Singleton {
|
Singleton {
|
||||||
|
id: root
|
||||||
property QtObject m3colors
|
property QtObject m3colors
|
||||||
property QtObject animation
|
property QtObject animation
|
||||||
property QtObject animationCurves
|
property QtObject animationCurves
|
||||||
@@ -13,18 +15,6 @@ Singleton {
|
|||||||
property QtObject sizes
|
property QtObject sizes
|
||||||
property string syntaxHighlightingTheme
|
property string syntaxHighlightingTheme
|
||||||
|
|
||||||
function mix(color1, color2, percentage) {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transparentize
|
|
||||||
function transparentize(color, percentage) {
|
|
||||||
var c = Qt.color(color);
|
|
||||||
return Qt.rgba(c.r, c.g, c.b, c.a * (1 - percentage));
|
|
||||||
}
|
|
||||||
|
|
||||||
m3colors: QtObject {
|
m3colors: QtObject {
|
||||||
property bool darkmode: false
|
property bool darkmode: false
|
||||||
property bool transparent: false
|
property bool transparent: false
|
||||||
@@ -108,37 +98,37 @@ Singleton {
|
|||||||
property color colSubtext: m3colors.m3outline
|
property color colSubtext: m3colors.m3outline
|
||||||
property color colLayer0: m3colors.m3background
|
property color colLayer0: m3colors.m3background
|
||||||
property color colOnLayer0: m3colors.m3onBackground
|
property color colOnLayer0: m3colors.m3onBackground
|
||||||
property color colLayer0Hover: mix(colLayer0, colOnLayer0, 0.9)
|
property color colLayer0Hover: ColorUtils.mix(colLayer0, colOnLayer0, 0.9)
|
||||||
property color colLayer0Active: mix(colLayer0, colOnLayer0, 0.8)
|
property color colLayer0Active: ColorUtils.mix(colLayer0, colOnLayer0, 0.8)
|
||||||
property color colLayer1: m3colors.m3surfaceContainerLow;
|
property color colLayer1: m3colors.m3surfaceContainerLow;
|
||||||
property color colOnLayer1: m3colors.m3onSurfaceVariant;
|
property color colOnLayer1: m3colors.m3onSurfaceVariant;
|
||||||
property color colOnLayer1Inactive: mix(colOnLayer1, colLayer1, 0.45);
|
property color colOnLayer1Inactive: ColorUtils.mix(colOnLayer1, colLayer1, 0.45);
|
||||||
property color colLayer2: mix(m3colors.m3surfaceContainer, m3colors.m3surfaceContainerHigh, 0.55);
|
property color colLayer2: ColorUtils.mix(m3colors.m3surfaceContainer, m3colors.m3surfaceContainerHigh, 0.55);
|
||||||
property color colOnLayer2: m3colors.m3onSurface;
|
property color colOnLayer2: m3colors.m3onSurface;
|
||||||
property color colOnLayer2Disabled: mix(colOnLayer2, m3colors.m3background, 0.4);
|
property color colOnLayer2Disabled: ColorUtils.mix(colOnLayer2, m3colors.m3background, 0.4);
|
||||||
property color colLayer3: mix(m3colors.m3surfaceContainerHigh, m3colors.m3onSurface, 0.96);
|
property color colLayer3: ColorUtils.mix(m3colors.m3surfaceContainerHigh, m3colors.m3onSurface, 0.96);
|
||||||
property color colOnLayer3: m3colors.m3onSurface;
|
property color colOnLayer3: m3colors.m3onSurface;
|
||||||
property color colLayer1Hover: mix(colLayer1, colOnLayer1, 0.88);
|
property color colLayer1Hover: ColorUtils.mix(colLayer1, colOnLayer1, 0.92);
|
||||||
property color colLayer1Active: mix(colLayer1, colOnLayer1, 0.77);
|
property color colLayer1Active: ColorUtils.mix(colLayer1, colOnLayer1, 0.85);
|
||||||
property color colLayer2Hover: mix(colLayer2, colOnLayer2, 0.90);
|
property color colLayer2Hover: ColorUtils.mix(colLayer2, colOnLayer2, 0.90);
|
||||||
property color colLayer2Active: mix(colLayer2, colOnLayer2, 0.80);
|
property color colLayer2Active: ColorUtils.mix(colLayer2, colOnLayer2, 0.80);
|
||||||
property color colLayer2Disabled: mix(colLayer2, m3colors.m3background, 0.8);
|
property color colLayer2Disabled: ColorUtils.mix(colLayer2, m3colors.m3background, 0.8);
|
||||||
property color colLayer3Hover: mix(colLayer3, colOnLayer3, 0.90);
|
property color colLayer3Hover: ColorUtils.mix(colLayer3, colOnLayer3, 0.90);
|
||||||
property color colLayer3Active: mix(colLayer3, colOnLayer3, 0.80);
|
property color colLayer3Active: ColorUtils.mix(colLayer3, colOnLayer3, 0.80);
|
||||||
property color colPrimaryHover: mix(m3colors.m3primary, colLayer1Hover, 0.85)
|
property color colPrimaryHover: ColorUtils.mix(m3colors.m3primary, colLayer1Hover, 0.85)
|
||||||
property color colPrimaryActive: mix(m3colors.m3primary, colLayer1Active, 0.7)
|
property color colPrimaryActive: ColorUtils.mix(m3colors.m3primary, colLayer1Active, 0.7)
|
||||||
property color colPrimaryContainerHover: mix(m3colors.m3primaryContainer, colLayer1Hover, 0.7)
|
property color colPrimaryContainerHover: ColorUtils.mix(m3colors.m3primaryContainer, colLayer1Hover, 0.7)
|
||||||
property color colPrimaryContainerActive: mix(m3colors.m3primaryContainer, colLayer1Active, 0.6)
|
property color colPrimaryContainerActive: ColorUtils.mix(m3colors.m3primaryContainer, colLayer1Active, 0.6)
|
||||||
property color colSecondaryHover: mix(m3colors.m3secondary, colLayer1Hover, 0.85)
|
property color colSecondaryHover: ColorUtils.mix(m3colors.m3secondary, colLayer1Hover, 0.85)
|
||||||
property color colSecondaryActive: mix(m3colors.m3secondary, colLayer1Active, 0.4)
|
property color colSecondaryActive: ColorUtils.mix(m3colors.m3secondary, colLayer1Active, 0.4)
|
||||||
property color colSecondaryContainerHover: mix(m3colors.m3secondaryContainer, colLayer1Hover, 0.6)
|
property color colSecondaryContainerHover: ColorUtils.mix(m3colors.m3secondaryContainer, colLayer1Hover, 0.6)
|
||||||
property color colSecondaryContainerActive: mix(m3colors.m3secondaryContainer, colLayer1Active, 0.54)
|
property color colSecondaryContainerActive: ColorUtils.mix(m3colors.m3secondaryContainer, colLayer1Active, 0.54)
|
||||||
property color colSurfaceContainerHighestHover: mix(m3colors.m3surfaceContainerHighest, m3colors.m3onSurface, 0.95)
|
property color colSurfaceContainerHighestHover: ColorUtils.mix(m3colors.m3surfaceContainerHighest, m3colors.m3onSurface, 0.95)
|
||||||
property color colSurfaceContainerHighestActive: mix(m3colors.m3surfaceContainerHighest, m3colors.m3onSurface, 0.85)
|
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: "#3C4043" // m3colors.m3inverseSurface in the specs, but the m3 website actually uses this color
|
||||||
property color colOnTooltip: "#F8F9FA" // m3colors.m3inverseOnSurface in the specs, but the m3 website actually uses this color
|
property color colOnTooltip: "#F8F9FA" // m3colors.m3inverseOnSurface in the specs, but the m3 website actually uses this color
|
||||||
property color colScrim: transparentize(m3colors.m3scrim, 0.5)
|
property color colScrim: ColorUtils.transparentize(m3colors.m3scrim, 0.5)
|
||||||
property color colShadow: transparentize(m3colors.m3shadow, 0.75)
|
property color colShadow: ColorUtils.transparentize(m3colors.m3shadow, 0.75)
|
||||||
}
|
}
|
||||||
|
|
||||||
rounding: QtObject {
|
rounding: QtObject {
|
||||||
@@ -189,24 +179,57 @@ Singleton {
|
|||||||
property int type: Easing.BezierSpline
|
property int type: Easing.BezierSpline
|
||||||
property list<real> bezierCurve: animationCurves.emphasized
|
property list<real> bezierCurve: animationCurves.emphasized
|
||||||
property int velocity: 650
|
property int velocity: 650
|
||||||
|
property Component numberAnimation: Component {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: root.animation.elementMove.duration
|
||||||
|
easing.type: root.animation.elementMove.type
|
||||||
|
easing.bezierCurve: root.animation.elementMove.bezierCurve
|
||||||
|
}
|
||||||
|
}
|
||||||
|
property Component colorAnimation: Component {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: root.animation.elementMove.duration
|
||||||
|
easing.type: root.animation.elementMove.type
|
||||||
|
easing.bezierCurve: root.animation.elementMove.bezierCurve
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
property QtObject elementMoveEnter: QtObject {
|
property QtObject elementMoveEnter: QtObject {
|
||||||
property int duration: 400
|
property int duration: 400
|
||||||
property int type: Easing.BezierSpline
|
property int type: Easing.BezierSpline
|
||||||
property list<real> bezierCurve: animationCurves.emphasizedDecel
|
property list<real> bezierCurve: animationCurves.emphasizedDecel
|
||||||
property int velocity: 650
|
property int velocity: 650
|
||||||
|
property Component numberAnimation: Component {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: root.animation.elementMoveEnter.duration
|
||||||
|
easing.type: root.animation.elementMoveEnter.type
|
||||||
|
easing.bezierCurve: root.animation.elementMoveEnter.bezierCurve
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
property QtObject elementMoveExit: QtObject {
|
property QtObject elementMoveExit: QtObject {
|
||||||
property int duration: 200
|
property int duration: 200
|
||||||
property int type: Easing.BezierSpline
|
property int type: Easing.BezierSpline
|
||||||
property list<real> bezierCurve: animationCurves.emphasizedAccel
|
property list<real> bezierCurve: animationCurves.emphasizedAccel
|
||||||
property int velocity: 650
|
property int velocity: 650
|
||||||
|
property Component numberAnimation: Component {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: root.animation.elementMoveExit.duration
|
||||||
|
easing.type: root.animation.elementMoveExit.type
|
||||||
|
easing.bezierCurve: root.animation.elementMoveExit.bezierCurve
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
property QtObject elementMoveFast: QtObject {
|
property QtObject elementMoveFast: QtObject {
|
||||||
property int duration: 200
|
property int duration: 200
|
||||||
property int type: Easing.BezierSpline
|
property int type: Easing.BezierSpline
|
||||||
property list<real> bezierCurve: animationCurves.standardDecel
|
property list<real> bezierCurve: animationCurves.standardDecel
|
||||||
property int velocity: 850
|
property int velocity: 850
|
||||||
|
property Component colorAnimation: Component {ColorAnimation {
|
||||||
|
duration: root.animation.elementMoveFast.duration
|
||||||
|
easing.type: root.animation.elementMoveFast.type
|
||||||
|
easing.bezierCurve: root.animation.elementMoveFast.bezierCurve
|
||||||
|
}}
|
||||||
}
|
}
|
||||||
property QtObject scroll: QtObject {
|
property QtObject scroll: QtObject {
|
||||||
property int duration: 400
|
property int duration: 400
|
||||||
@@ -226,18 +249,21 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sizes: QtObject {
|
sizes: QtObject {
|
||||||
property int barHeight: 40
|
property real barHeight: 40
|
||||||
property int barCenterSideModuleWidth: 360
|
property real barCenterSideModuleWidth: 360
|
||||||
property int barPreferredSideSectionWidth: 400
|
property real barPreferredSideSectionWidth: 400
|
||||||
property int sidebarWidth: 450
|
property real sidebarWidth: 460
|
||||||
property int sidebarWidthExtended: 750
|
property real sidebarWidthExtended: 750
|
||||||
property int notificationPopupWidth: 410
|
property real osdWidth: 200
|
||||||
property int searchWidthCollapsed: 260
|
property real mediaControlsWidth: 440
|
||||||
property int searchWidth: 450
|
property real mediaControlsHeight: 160
|
||||||
property int hyprlandGapsOut: 5
|
property real notificationPopupWidth: 410
|
||||||
property int elevationMargin: 7
|
property real searchWidthCollapsed: 260
|
||||||
property int fabShadowRadius: 5
|
property real searchWidth: 450
|
||||||
property int fabHoveredShadowRadius: 7
|
property real hyprlandGapsOut: 5
|
||||||
|
property real elevationMargin: 8
|
||||||
|
property real fabShadowRadius: 5
|
||||||
|
property real fabHoveredShadowRadius: 7
|
||||||
}
|
}
|
||||||
|
|
||||||
syntaxHighlightingTheme: Appearance.m3colors.darkmode ? "Monokai" : "ayu Light"
|
syntaxHighlightingTheme: Appearance.m3colors.darkmode ? "Monokai" : "ayu Light"
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ pragma ComponentBehavior: Bound
|
|||||||
|
|
||||||
Singleton {
|
Singleton {
|
||||||
property QtObject ai: QtObject {
|
property QtObject ai: QtObject {
|
||||||
property string systemPrompt: "Use casual tone. No user knowledge is to be assumed except basic Linux literacy. Be brief and concise: When explaining concepts, use bullet points (prefer minus sign (-) over asterisk (*)) and highlight keywords in bold to pinpoint the main concepts instead of long paragraphs. You are also encouraged to split your response with h2 headers, each header title beginning with an emoji, like `## 🐧 Linux`."
|
property string systemPrompt: qsTr("Use casual tone. No user knowledge is to be assumed except basic Linux literacy. Be brief and concise: When explaining concepts, use bullet points (prefer minus sign (-) over asterisk (*)) and highlight keywords in bold to pinpoint the main concepts instead of long paragraphs. You are also encouraged to split your response with h2 headers, each header title beginning with an emoji, like `## 🐧 Linux`.")
|
||||||
}
|
}
|
||||||
|
|
||||||
property QtObject appearance: QtObject {
|
property QtObject appearance: QtObject {
|
||||||
@@ -13,10 +13,10 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
property QtObject apps: QtObject {
|
property QtObject apps: QtObject {
|
||||||
property string bluetooth: "blueberry"
|
property string bluetooth: "better-control --bluetooth"
|
||||||
property string imageViewer: "loupe"
|
property string imageViewer: "loupe"
|
||||||
property string network: "XDG_CURRENT_DESKTOP=\"gnome\" gnome-control-center wifi"
|
property string network: "better-control --wifi"
|
||||||
property string settings: "XDG_CURRENT_DESKTOP=\"gnome\" gnome-control-center"
|
property string settings: "better-control"
|
||||||
property string taskManager: "gnome-usage"
|
property string taskManager: "gnome-usage"
|
||||||
property string terminal: "foot" // This is only for shell actions
|
property string terminal: "foot" // This is only for shell actions
|
||||||
}
|
}
|
||||||
@@ -25,6 +25,7 @@ Singleton {
|
|||||||
property int batteryLowThreshold: 20
|
property int batteryLowThreshold: 20
|
||||||
property string topLeftIcon: "spark" // Options: distro, spark
|
property string topLeftIcon: "spark" // Options: distro, spark
|
||||||
property bool showBackground: true
|
property bool showBackground: true
|
||||||
|
property bool borderless: false
|
||||||
property QtObject resources: QtObject {
|
property QtObject resources: QtObject {
|
||||||
property bool alwaysShowSwap: true
|
property bool alwaysShowSwap: true
|
||||||
property bool alwaysShowCpu: false
|
property bool alwaysShowCpu: false
|
||||||
@@ -70,7 +71,7 @@ Singleton {
|
|||||||
property string defaultProvider: "yandere"
|
property string defaultProvider: "yandere"
|
||||||
property int limit: 20
|
property int limit: 20
|
||||||
property QtObject zerochan: QtObject {
|
property QtObject zerochan: QtObject {
|
||||||
property string username: ""
|
property string username: "[unset]"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,12 +9,6 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
property QtObject sidebar: QtObject {
|
property QtObject sidebar: QtObject {
|
||||||
property QtObject leftSide: QtObject {
|
|
||||||
property int selectedTab: 0
|
|
||||||
}
|
|
||||||
property QtObject centerGroup: QtObject {
|
|
||||||
property int selectedTab: 0
|
|
||||||
}
|
|
||||||
property QtObject bottomGroup: QtObject {
|
property QtObject bottomGroup: QtObject {
|
||||||
property bool collapsed: false
|
property bool collapsed: false
|
||||||
property int selectedTab: 0
|
property int selectedTab: 0
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
// This module provides high level utility functions for color manipulation.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a color with the hue of color2 and the saturation, value, and alpha of color1.
|
||||||
|
*
|
||||||
|
* @param {string} color1 - The base color (any Qt.color-compatible string).
|
||||||
|
* @param {string} color2 - The color to take hue from.
|
||||||
|
* @returns {Qt.rgba} The resulting color.
|
||||||
|
*/
|
||||||
|
function colorWithHueOf(color1, color2) {
|
||||||
|
var c1 = Qt.color(color1);
|
||||||
|
var c2 = Qt.color(color2);
|
||||||
|
|
||||||
|
// Qt.color hsvHue/hsvSaturation/hsvValue/alpha return 0-1
|
||||||
|
var hue = c2.hsvHue;
|
||||||
|
var sat = c1.hsvSaturation;
|
||||||
|
var val = c1.hsvValue;
|
||||||
|
var alpha = c1.a;
|
||||||
|
|
||||||
|
return Qt.hsva(hue, sat, val, alpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a color with the saturation of color2 and the hue/value/alpha of color1.
|
||||||
|
*
|
||||||
|
* @param {string} color1 - The base color (any Qt.color-compatible string).
|
||||||
|
* @param {string} color2 - The color to take saturation from.
|
||||||
|
* @returns {Qt.rgba} The resulting color.
|
||||||
|
*/
|
||||||
|
function colorWithSaturationOf(color1, color2) {
|
||||||
|
var c1 = Qt.color(color1);
|
||||||
|
var c2 = Qt.color(color2);
|
||||||
|
|
||||||
|
var hue = c1.hsvHue;
|
||||||
|
var sat = c2.hsvSaturation;
|
||||||
|
var val = c1.hsvValue;
|
||||||
|
var alpha = c1.a;
|
||||||
|
|
||||||
|
return Qt.hsva(hue, sat, val, alpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapts color1 to the accent (hue and saturation) of color2 using HSL, keeping lightness and alpha from color1.
|
||||||
|
*
|
||||||
|
* @param {string} color1 - The base color (any Qt.color-compatible string).
|
||||||
|
* @param {string} color2 - The accent color.
|
||||||
|
* @returns {Qt.rgba} The resulting color.
|
||||||
|
*/
|
||||||
|
function adaptToAccent(color1, color2) {
|
||||||
|
var c1 = Qt.color(color1);
|
||||||
|
var c2 = Qt.color(color2);
|
||||||
|
|
||||||
|
var hue = c2.hslHue;
|
||||||
|
var sat = c2.hslSaturation;
|
||||||
|
var light = c1.hslLightness;
|
||||||
|
var alpha = c1.a;
|
||||||
|
|
||||||
|
return Qt.hsla(hue, sat, light, alpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mixes two colors by a given percentage.
|
||||||
|
*
|
||||||
|
* @param {string} color1 - The first color (any Qt.color-compatible string).
|
||||||
|
* @param {string} color2 - The second color.
|
||||||
|
* @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) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transparentizes a color by a given percentage.
|
||||||
|
*
|
||||||
|
* @param {string} color - The color (any Qt.color-compatible string).
|
||||||
|
* @param {number} percentage - The amount to transparentize (0-1).
|
||||||
|
* @returns {Qt.rgba} The resulting color.
|
||||||
|
*/
|
||||||
|
function transparentize(color, percentage) {
|
||||||
|
var c = Qt.color(color);
|
||||||
|
return Qt.rgba(c.r, c.g, c.b, c.a * (1 - percentage));
|
||||||
|
}
|
||||||
@@ -148,3 +148,31 @@ function wordWrap(str, maxLen) {
|
|||||||
if (current.length > 0) lines.push(current);
|
if (current.length > 0) lines.push(current);
|
||||||
return lines.join("\n");
|
return lines.join("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function cleanMusicTitle(title) {
|
||||||
|
if (!title) return "";
|
||||||
|
// Brackets
|
||||||
|
title = title.replace(/^ *\([^)]*\) */g, " "); // Round brackets
|
||||||
|
title = title.replace(/^ *\[[^\]]*\] */g, " "); // Square brackets
|
||||||
|
title = title.replace(/^ *\{[^\}]*\} */g, " "); // Curly brackets
|
||||||
|
// Japenis brackets
|
||||||
|
title = title.replace(/^ *【[^】]*】/, "") // Touhou
|
||||||
|
title = title.replace(/^ *《[^》]*》/, "") // ??
|
||||||
|
title = title.replace(/^ *「[^」]*」/, "") // OP/ED
|
||||||
|
title = title.replace(/^ *『[^』]*』/, "") // OP/ED
|
||||||
|
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
function friendlyTimeForSeconds(seconds) {
|
||||||
|
if (isNaN(seconds) || seconds < 0) return "0:00";
|
||||||
|
seconds = Math.floor(seconds);
|
||||||
|
const h = Math.floor(seconds / 3600);
|
||||||
|
const m = Math.floor((seconds % 3600) / 60);
|
||||||
|
const s = seconds % 60;
|
||||||
|
if (h > 0) {
|
||||||
|
return `${h}:${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`;
|
||||||
|
} else {
|
||||||
|
return `${m}:${s.toString().padStart(2, '0')}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import "root:/modules/common"
|
import "root:/modules/common"
|
||||||
|
import "root:/modules/common/functions/color_utils.js" as ColorUtils
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
@@ -17,14 +18,12 @@ Button {
|
|||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
radius: Appearance.rounding.full
|
radius: Appearance.rounding.full
|
||||||
color: (button.down && button.enabled) ? Appearance.colors.colLayer1Active : ((button.hovered && button.enabled) ? Appearance.colors.colLayer1Hover : Appearance.transparentize(Appearance.m3colors.m3surfaceContainerHigh, 1))
|
color: (button.down && button.enabled) ? Appearance.colors.colLayer1Active :
|
||||||
|
((button.hovered && button.enabled) ? Appearance.colors.colLayer1Hover :
|
||||||
|
ColorUtils.transparentize(Appearance.m3colors.m3surfaceContainerHigh, 1))
|
||||||
|
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
ColorAnimation {
|
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMove.duration
|
|
||||||
easing.type: Appearance.animation.elementMove.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,11 +40,7 @@ Button {
|
|||||||
color: button.enabled ? Appearance.m3colors.m3primary : Appearance.m3colors.m3outline
|
color: button.enabled ? Appearance.m3colors.m3primary : Appearance.m3colors.m3outline
|
||||||
|
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
ColorAnimation {
|
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMove.duration
|
|
||||||
easing.type: Appearance.animation.elementMove.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import "root:/modules/common"
|
import "root:/modules/common"
|
||||||
|
import "root:/modules/common/functions/color_utils.js" as ColorUtils
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
@@ -16,17 +17,12 @@ Button {
|
|||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: (button.down && button.enabled) ? Appearance.transparentize(Appearance.m3colors.m3onSurface, 0.84) :
|
color: (button.down && button.enabled) ? ColorUtils.transparentize(Appearance.m3colors.m3onSurface, 0.84) :
|
||||||
((button.hovered && button.enabled) ? Appearance.transparentize(Appearance.m3colors.m3onSurface, 0.92) :
|
((button.hovered && button.enabled) ? ColorUtils.transparentize(Appearance.m3colors.m3onSurface, 0.92) :
|
||||||
Appearance.transparentize(Appearance.m3colors.m3onSurface, 1))
|
ColorUtils.transparentize(Appearance.m3colors.m3onSurface, 1))
|
||||||
|
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
ColorAnimation {
|
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMove.duration
|
|
||||||
easing.type: Appearance.animation.elementMove.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -42,11 +38,7 @@ Button {
|
|||||||
color: button.enabled ? Appearance.m3colors.m3onSurface : Appearance.m3colors.m3outline
|
color: button.enabled ? Appearance.m3colors.m3onSurface : Appearance.m3colors.m3outline
|
||||||
|
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
ColorAnimation {
|
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMove.duration
|
|
||||||
easing.type: Appearance.animation.elementMove.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import "root:/modules/common"
|
import "root:/modules/common"
|
||||||
import "root:/modules/common/widgets"
|
import "root:/modules/common/widgets"
|
||||||
|
import "root:/modules/common/functions/color_utils.js" as ColorUtils
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
@@ -16,7 +17,7 @@ Button {
|
|||||||
implicitHeight: columnLayout.implicitHeight
|
implicitHeight: columnLayout.implicitHeight
|
||||||
implicitWidth: columnLayout.implicitWidth
|
implicitWidth: columnLayout.implicitWidth
|
||||||
|
|
||||||
background: Item {}
|
background: null
|
||||||
PointingHandInteraction {}
|
PointingHandInteraction {}
|
||||||
|
|
||||||
// Real stuff
|
// Real stuff
|
||||||
@@ -30,14 +31,10 @@ Button {
|
|||||||
radius: Appearance.rounding.full
|
radius: Appearance.rounding.full
|
||||||
color: toggled ?
|
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.m3colors.m3secondaryContainer) :
|
||||||
(button.down ? Appearance.colors.colLayer1Active : button.hovered ? Appearance.colors.colLayer1Hover : Appearance.transparentize(Appearance.colors.colLayer1Hover, 1))
|
(button.down ? Appearance.colors.colLayer1Active : button.hovered ? Appearance.colors.colLayer1Hover : ColorUtils.transparentize(Appearance.colors.colLayer1Hover, 1))
|
||||||
|
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
ColorAnimation {
|
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMove.duration
|
|
||||||
easing.type: Appearance.animation.elementMove.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
MaterialSymbol {
|
MaterialSymbol {
|
||||||
id: navRailButtonIcon
|
id: navRailButtonIcon
|
||||||
@@ -48,11 +45,7 @@ Button {
|
|||||||
color: toggled ? Appearance.m3colors.m3onSecondaryContainer : Appearance.colors.colOnLayer1
|
color: toggled ? Appearance.m3colors.m3onSecondaryContainer : Appearance.colors.colOnLayer1
|
||||||
|
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
ColorAnimation {
|
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMove.duration
|
|
||||||
easing.type: Appearance.animation.elementMove.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import "root:/modules/common"
|
import "root:/modules/common"
|
||||||
import "root:/services"
|
import "root:/services"
|
||||||
import "root:/modules/common/functions/string_utils.js" as StringUtils
|
import "root:/modules/common/functions/string_utils.js" as StringUtils
|
||||||
|
import "root:/modules/common/functions/color_utils.js" as ColorUtils
|
||||||
import Qt5Compat.GraphicalEffects
|
import Qt5Compat.GraphicalEffects
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Effects
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
@@ -126,7 +128,7 @@ Item {
|
|||||||
onPressAndHold: (mouse) => {
|
onPressAndHold: (mouse) => {
|
||||||
if (mouse.button === Qt.LeftButton) {
|
if (mouse.button === Qt.LeftButton) {
|
||||||
Hyprland.dispatch(`exec wl-copy '${StringUtils.shellSingleQuoteEscape(notificationObject.body)}'`)
|
Hyprland.dispatch(`exec wl-copy '${StringUtils.shellSingleQuoteEscape(notificationObject.body)}'`)
|
||||||
notificationSummaryText.text = `${notificationObject.summary} (copied)`
|
notificationSummaryText.text = String.format(qsTr("{0} (copied)"), notificationObject.summary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onDragStartedChanged: () => {
|
onDragStartedChanged: () => {
|
||||||
@@ -187,9 +189,19 @@ Item {
|
|||||||
height: notificationColumnLayout.implicitHeight
|
height: notificationColumnLayout.implicitHeight
|
||||||
|
|
||||||
color: (notificationObject.urgency == NotificationUrgency.Critical) ?
|
color: (notificationObject.urgency == NotificationUrgency.Critical) ?
|
||||||
Appearance.mix(Appearance.m3colors.m3secondaryContainer, Appearance.colors.colLayer2, 0.35) : Appearance.colors.colLayer2
|
ColorUtils.mix(Appearance.m3colors.m3secondaryContainer, Appearance.colors.colLayer2, 0.35) : Appearance.colors.colLayer2
|
||||||
radius: Appearance.rounding.normal
|
radius: Appearance.rounding.normal
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: MultiEffect {
|
||||||
|
source: notificationBackground
|
||||||
|
anchors.fill: notificationBackground
|
||||||
|
shadowEnabled: popup
|
||||||
|
shadowColor: Appearance.colors.colShadow
|
||||||
|
shadowVerticalOffset: 1
|
||||||
|
shadowBlur: 0.5
|
||||||
|
}
|
||||||
|
|
||||||
Behavior on x {
|
Behavior on x {
|
||||||
enabled: enableAnimation
|
enabled: enableAnimation
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
@@ -207,20 +219,6 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Loader {
|
|
||||||
active: popup
|
|
||||||
anchors.fill: notificationBackground
|
|
||||||
sourceComponent: DropShadow {
|
|
||||||
id: notificationShadow
|
|
||||||
source: notificationBackground
|
|
||||||
radius: 5
|
|
||||||
samples: radius * 2 + 1
|
|
||||||
color: Appearance.colors.colShadow
|
|
||||||
verticalOffset: 2
|
|
||||||
horizontalOffset: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -289,7 +287,7 @@ Item {
|
|||||||
}
|
}
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: (notificationObject.urgency == NotificationUrgency.Critical) ?
|
color: (notificationObject.urgency == NotificationUrgency.Critical) ?
|
||||||
Appearance.mix(Appearance.m3colors.m3onSecondary, Appearance.m3colors.m3onSecondaryContainer, 0.1) :
|
ColorUtils.mix(Appearance.m3colors.m3onSecondary, Appearance.m3colors.m3onSecondaryContainer, 0.1) :
|
||||||
Appearance.m3colors.m3onSecondaryContainer
|
Appearance.m3colors.m3onSecondaryContainer
|
||||||
iconSize: 27
|
iconSize: 27
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
@@ -422,17 +420,11 @@ Item {
|
|||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
radius: Appearance.rounding.full
|
radius: Appearance.rounding.full
|
||||||
color: (expandButton.down) ? Appearance.colors.colLayer2Active : (expandButton.hovered ? Appearance.colors.colLayer2Hover : Appearance.transparentize(Appearance.colors.colLayer2, 1))
|
color: (expandButton.down) ? Appearance.colors.colLayer2Active : (expandButton.hovered ? Appearance.colors.colLayer2Hover : ColorUtils.transparentize(Appearance.colors.colLayer2, 1))
|
||||||
|
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
ColorAnimation {
|
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMoveFast.duration
|
|
||||||
easing.type: Appearance.animation.elementMoveFast.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
contentItem: MaterialSymbol {
|
contentItem: MaterialSymbol {
|
||||||
|
|||||||
@@ -52,38 +52,31 @@ ColumnLayout {
|
|||||||
root.enableIndicatorAnimation = true
|
root.enableIndicatorAnimation = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
id: indicator
|
||||||
|
property int tabCount: root.tabButtonList.length
|
||||||
|
property real fullTabSize: root.width / tabCount;
|
||||||
|
property real targetWidth: tabBar.contentItem.children[0].children[tabBar.currentIndex].tabContentWidth
|
||||||
|
|
||||||
|
implicitWidth: targetWidth
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
bottom: parent.bottom
|
||||||
|
}
|
||||||
|
|
||||||
|
x: tabBar.currentIndex * fullTabSize + (fullTabSize - targetWidth) / 2
|
||||||
|
|
||||||
color: Appearance.m3colors.m3primary
|
color: Appearance.m3colors.m3primary
|
||||||
radius: Appearance.rounding.full
|
radius: Appearance.rounding.full
|
||||||
z: 2
|
|
||||||
|
|
||||||
anchors.fill: parent
|
Behavior on x {
|
||||||
// TODO: make the end point in the moving direction go first
|
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||||
anchors.leftMargin: {
|
|
||||||
const tabCount = root.tabButtonList.length
|
|
||||||
const targetWidth = tabBar.contentItem.children[0].children[tabBar.currentIndex].tabContentWidth
|
|
||||||
const fullTabSize = root.width / tabCount;
|
|
||||||
return fullTabSize * root.externalTrackedTab + (fullTabSize - targetWidth) / 2;
|
|
||||||
}
|
|
||||||
anchors.rightMargin: {
|
|
||||||
const tabCount = root.tabButtonList.length
|
|
||||||
const targetWidth = tabBar.contentItem.children[0].children[tabBar.currentIndex].tabContentWidth
|
|
||||||
const fullTabSize = root.width / tabCount;
|
|
||||||
return fullTabSize * (tabCount - root.externalTrackedTab - 1) + (fullTabSize - targetWidth) / 2;
|
|
||||||
}
|
|
||||||
Behavior on anchors.leftMargin {
|
|
||||||
enabled: root.enableIndicatorAnimation
|
|
||||||
SmoothedAnimation {
|
|
||||||
velocity: Appearance.animation.positionShift.velocity
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Behavior on anchors.rightMargin {
|
|
||||||
enabled: root.enableIndicatorAnimation
|
|
||||||
SmoothedAnimation {
|
|
||||||
velocity: Appearance.animation.positionShift.velocity
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Behavior on implicitWidth {
|
||||||
|
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import "root:/modules/common"
|
import "root:/modules/common"
|
||||||
import "root:/modules/common/widgets"
|
import "root:/modules/common/widgets"
|
||||||
|
import "root:/modules/common/functions/color_utils.js" as ColorUtils
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
@@ -12,24 +14,108 @@ TabButton {
|
|||||||
property string buttonIcon
|
property string buttonIcon
|
||||||
property bool selected: false
|
property bool selected: false
|
||||||
property int tabContentWidth: contentItem.children[0].implicitWidth
|
property int tabContentWidth: contentItem.children[0].implicitWidth
|
||||||
|
property int rippleDuration: 1200
|
||||||
height: buttonBackground.height
|
height: buttonBackground.height
|
||||||
|
|
||||||
PointingHandInteraction {}
|
component RippleAnim: NumberAnimation {
|
||||||
|
duration: rippleDuration
|
||||||
|
easing.type: Appearance.animation.elementMoveEnter.type
|
||||||
|
easing.bezierCurve: Appearance.animationCurves.standardDecel
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onPressed: (event) => {
|
||||||
|
const {x,y} = event
|
||||||
|
const stateY = buttonBackground.y;
|
||||||
|
rippleAnim.x = x;
|
||||||
|
rippleAnim.y = y - stateY;
|
||||||
|
|
||||||
|
const dist = (ox,oy) => ox*ox + oy*oy
|
||||||
|
const stateEndY = stateY + buttonBackground.height
|
||||||
|
rippleAnim.radius = Math.sqrt(Math.max(dist(0, stateY), dist(0, stateEndY), dist(width, stateY), dist(width, stateEndY)))
|
||||||
|
|
||||||
|
rippleFadeAnim.complete();
|
||||||
|
rippleAnim.restart();
|
||||||
|
}
|
||||||
|
onReleased: (event) => {
|
||||||
|
button.click() // Because the MouseArea already consumed the event
|
||||||
|
rippleFadeAnim.restart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RippleAnim {
|
||||||
|
id: rippleFadeAnim
|
||||||
|
target: ripple
|
||||||
|
property: "opacity"
|
||||||
|
to: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
SequentialAnimation {
|
||||||
|
id: rippleAnim
|
||||||
|
|
||||||
|
property real x
|
||||||
|
property real y
|
||||||
|
property real radius
|
||||||
|
|
||||||
|
PropertyAction {
|
||||||
|
target: ripple
|
||||||
|
property: "x"
|
||||||
|
value: rippleAnim.x
|
||||||
|
}
|
||||||
|
PropertyAction {
|
||||||
|
target: ripple
|
||||||
|
property: "y"
|
||||||
|
value: rippleAnim.y
|
||||||
|
}
|
||||||
|
PropertyAction {
|
||||||
|
target: ripple
|
||||||
|
property: "opacity"
|
||||||
|
value: 1
|
||||||
|
}
|
||||||
|
ParallelAnimation {
|
||||||
|
RippleAnim {
|
||||||
|
target: ripple
|
||||||
|
properties: "implicitWidth,implicitHeight"
|
||||||
|
from: 0
|
||||||
|
to: rippleAnim.radius * 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
id: buttonBackground
|
id: buttonBackground
|
||||||
radius: Appearance.rounding.small
|
radius: Appearance.rounding.small
|
||||||
implicitHeight: 50
|
implicitHeight: 50
|
||||||
color: (button.down ? Appearance.colors.colLayer1Active : button.hovered ? Appearance.colors.colLayer1Hover : Appearance.transparentize(Appearance.colors.colLayer1Hover, 1))
|
color: (button.hovered ? Appearance.colors.colLayer1Hover : ColorUtils.transparentize(Appearance.colors.colLayer1Hover, 1))
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: OpacityMask {
|
||||||
|
maskSource: Rectangle {
|
||||||
|
width: buttonBackground.width
|
||||||
|
height: buttonBackground.height
|
||||||
|
radius: buttonBackground.radius
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
ColorAnimation {
|
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMove.duration
|
}
|
||||||
easing.type: Appearance.animation.elementMove.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
Rectangle {
|
||||||
|
id: ripple
|
||||||
|
|
||||||
|
radius: Appearance.rounding.full
|
||||||
|
color: Appearance.colors.colLayer1Active
|
||||||
|
opacity: 0
|
||||||
|
|
||||||
|
transform: Translate {
|
||||||
|
x: -ripple.width / 2
|
||||||
|
y: -ripple.height / 2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
contentItem: Item {
|
contentItem: Item {
|
||||||
anchors.centerIn: buttonBackground
|
anchors.centerIn: buttonBackground
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
@@ -44,11 +130,7 @@ TabButton {
|
|||||||
fill: selected ? 1 : 0
|
fill: selected ? 1 : 0
|
||||||
color: selected ? Appearance.m3colors.m3primary : Appearance.colors.colOnLayer1
|
color: selected ? Appearance.m3colors.m3primary : Appearance.colors.colOnLayer1
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
ColorAnimation {
|
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMove.duration
|
|
||||||
easing.type: Appearance.animation.elementMove.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StyledText {
|
StyledText {
|
||||||
@@ -59,11 +141,7 @@ TabButton {
|
|||||||
color: selected ? Appearance.m3colors.m3primary : Appearance.colors.colOnLayer1
|
color: selected ? Appearance.m3colors.m3primary : Appearance.colors.colOnLayer1
|
||||||
text: buttonText
|
text: buttonText
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
ColorAnimation {
|
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMove.duration
|
|
||||||
easing.type: Appearance.animation.elementMove.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,18 +16,10 @@ Item {
|
|||||||
|
|
||||||
Behavior on implicitWidth {
|
Behavior on implicitWidth {
|
||||||
enabled: !vertical
|
enabled: !vertical
|
||||||
NumberAnimation {
|
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMoveEnter.duration
|
|
||||||
easing.type: Appearance.animation.elementMoveEnter.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Behavior on implicitHeight {
|
Behavior on implicitHeight {
|
||||||
enabled: vertical
|
enabled: vertical
|
||||||
NumberAnimation {
|
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMoveEnter.duration
|
|
||||||
easing.type: Appearance.animation.elementMoveEnter.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import "root:/modules/common"
|
import "root:/modules/common"
|
||||||
import "root:/modules/common/widgets"
|
import "root:/modules/common/widgets"
|
||||||
|
import "root:/modules/common/functions/color_utils.js" as ColorUtils
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
@@ -11,25 +13,111 @@ TabButton {
|
|||||||
property string buttonText
|
property string buttonText
|
||||||
property string buttonIcon
|
property string buttonIcon
|
||||||
property bool selected: false
|
property bool selected: false
|
||||||
|
property int rippleDuration: 1200
|
||||||
height: buttonBackground.height
|
height: buttonBackground.height
|
||||||
property int tabContentWidth: buttonBackground.width - buttonBackground.radius*2
|
property int tabContentWidth: buttonBackground.width - buttonBackground.radius*2
|
||||||
|
|
||||||
PointingHandInteraction {}
|
PointingHandInteraction {}
|
||||||
|
|
||||||
|
component RippleAnim: NumberAnimation {
|
||||||
|
duration: rippleDuration
|
||||||
|
easing.type: Appearance.animation.elementMoveEnter.type
|
||||||
|
easing.bezierCurve: Appearance.animationCurves.standardDecel
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onPressed: (event) => {
|
||||||
|
const {x,y} = event
|
||||||
|
const stateY = buttonBackground.y;
|
||||||
|
rippleAnim.x = x;
|
||||||
|
rippleAnim.y = y - stateY;
|
||||||
|
|
||||||
|
const dist = (ox,oy) => ox*ox + oy*oy
|
||||||
|
const stateEndY = stateY + buttonBackground.height
|
||||||
|
rippleAnim.radius = Math.sqrt(Math.max(dist(0, stateY), dist(0, stateEndY), dist(width, stateY), dist(width, stateEndY)))
|
||||||
|
|
||||||
|
rippleFadeAnim.complete();
|
||||||
|
rippleAnim.restart();
|
||||||
|
}
|
||||||
|
onReleased: (event) => {
|
||||||
|
button.click() // Because the MouseArea already consumed the event
|
||||||
|
rippleFadeAnim.restart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RippleAnim {
|
||||||
|
id: rippleFadeAnim
|
||||||
|
target: ripple
|
||||||
|
property: "opacity"
|
||||||
|
to: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
SequentialAnimation {
|
||||||
|
id: rippleAnim
|
||||||
|
|
||||||
|
property real x
|
||||||
|
property real y
|
||||||
|
property real radius
|
||||||
|
|
||||||
|
PropertyAction {
|
||||||
|
target: ripple
|
||||||
|
property: "x"
|
||||||
|
value: rippleAnim.x
|
||||||
|
}
|
||||||
|
PropertyAction {
|
||||||
|
target: ripple
|
||||||
|
property: "y"
|
||||||
|
value: rippleAnim.y
|
||||||
|
}
|
||||||
|
PropertyAction {
|
||||||
|
target: ripple
|
||||||
|
property: "opacity"
|
||||||
|
value: 1
|
||||||
|
}
|
||||||
|
ParallelAnimation {
|
||||||
|
RippleAnim {
|
||||||
|
target: ripple
|
||||||
|
properties: "implicitWidth,implicitHeight"
|
||||||
|
from: 0
|
||||||
|
to: rippleAnim.radius * 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
id: buttonBackground
|
id: buttonBackground
|
||||||
radius: Appearance.rounding.small
|
radius: Appearance.rounding.small
|
||||||
implicitHeight: 37
|
implicitHeight: 37
|
||||||
color: (button.down ? Appearance.colors.colLayer1Active : button.hovered ? Appearance.colors.colLayer1Hover : Appearance.transparentize(Appearance.colors.colLayer1Hover, 1))
|
color: (button.hovered ? Appearance.colors.colLayer1Hover : ColorUtils.transparentize(Appearance.colors.colLayer1Hover, 1))
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: OpacityMask {
|
||||||
|
maskSource: Rectangle {
|
||||||
|
width: buttonBackground.width
|
||||||
|
height: buttonBackground.height
|
||||||
|
radius: buttonBackground.radius
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
ColorAnimation {
|
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMove.duration
|
}
|
||||||
easing.type: Appearance.animation.elementMove.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
Rectangle {
|
||||||
|
id: ripple
|
||||||
|
|
||||||
|
radius: Appearance.rounding.full
|
||||||
|
color: Appearance.colors.colLayer1Active
|
||||||
|
opacity: 0
|
||||||
|
|
||||||
|
transform: Translate {
|
||||||
|
x: -ripple.width / 2
|
||||||
|
y: -ripple.height / 2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
contentItem: Item {
|
contentItem: Item {
|
||||||
anchors.centerIn: buttonBackground
|
anchors.centerIn: buttonBackground
|
||||||
RowLayout {
|
RowLayout {
|
||||||
@@ -52,11 +140,7 @@ TabButton {
|
|||||||
fill: selected ? 1 : 0
|
fill: selected ? 1 : 0
|
||||||
color: selected ? Appearance.m3colors.m3primary : Appearance.colors.colOnLayer1
|
color: selected ? Appearance.m3colors.m3primary : Appearance.colors.colOnLayer1
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
ColorAnimation {
|
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMove.duration
|
|
||||||
easing.type: Appearance.animation.elementMove.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -67,11 +151,7 @@ TabButton {
|
|||||||
color: selected ? Appearance.m3colors.m3primary : Appearance.colors.colOnLayer1
|
color: selected ? Appearance.m3colors.m3primary : Appearance.colors.colOnLayer1
|
||||||
text: buttonText
|
text: buttonText
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
ColorAnimation {
|
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMove.duration
|
|
||||||
easing.type: Appearance.animation.elementMove.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,13 +13,11 @@ ProgressBar {
|
|||||||
property real valueBarWidth: 120
|
property real valueBarWidth: 120
|
||||||
property real valueBarHeight: 4
|
property real valueBarHeight: 4
|
||||||
property real valueBarGap: 4
|
property real valueBarGap: 4
|
||||||
|
property color highlightColor: Appearance.m3colors.m3primary
|
||||||
|
property color trackColor: Appearance.m3colors.m3secondaryContainer
|
||||||
|
|
||||||
Behavior on value {
|
Behavior on value {
|
||||||
NumberAnimation {
|
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMoveEnter.duration
|
|
||||||
easing.type: Appearance.animation.elementMoveEnter.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
@@ -38,21 +36,21 @@ ProgressBar {
|
|||||||
width: root.visualPosition * parent.width
|
width: root.visualPosition * parent.width
|
||||||
height: parent.height
|
height: parent.height
|
||||||
radius: Appearance.rounding.full
|
radius: Appearance.rounding.full
|
||||||
color: Appearance.m3colors.m3primary
|
color: root.highlightColor
|
||||||
}
|
}
|
||||||
Rectangle { // Right remaining part fill
|
Rectangle { // Right remaining part fill
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
width: (1 - root.visualPosition) * parent.width - valueBarGap
|
width: (1 - root.visualPosition) * parent.width - valueBarGap
|
||||||
height: parent.height
|
height: parent.height
|
||||||
radius: Appearance.rounding.full
|
radius: Appearance.rounding.full
|
||||||
color: Appearance.m3colors.m3secondaryContainer
|
color: root.trackColor
|
||||||
}
|
}
|
||||||
Rectangle { // Stop point
|
Rectangle { // Stop point
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
width: valueBarGap
|
width: valueBarGap
|
||||||
height: valueBarGap
|
height: valueBarGap
|
||||||
radius: Appearance.rounding.full
|
radius: Appearance.rounding.full
|
||||||
color: Appearance.m3colors.m3primary
|
color: root.highlightColor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -16,8 +16,13 @@ Slider {
|
|||||||
property real handleWidth: (slider.pressed ? 3 : 5) * scale
|
property real handleWidth: (slider.pressed ? 3 : 5) * scale
|
||||||
property real handleHeight: 44 * scale
|
property real handleHeight: 44 * scale
|
||||||
property real handleLimit: slider.backgroundDotMargins * scale
|
property real handleLimit: slider.backgroundDotMargins * scale
|
||||||
|
property real trackHeight: 15 * scale
|
||||||
|
property color highlightColor: Appearance.m3colors.m3primary
|
||||||
|
property color trackColor: Appearance.m3colors.m3secondaryContainer
|
||||||
|
property color handleColor: Appearance.m3colors.m3onSecondaryContainer
|
||||||
|
|
||||||
property real limitedHandleRangeWidth: (slider.availableWidth - handleWidth - slider.handleLimit * 2)
|
property real limitedHandleRangeWidth: (slider.availableWidth - handleWidth - slider.handleLimit * 2)
|
||||||
|
property string tooltipContent: `${Math.round(value * 100)}%`
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
from: 0
|
from: 0
|
||||||
to: 1
|
to: 1
|
||||||
@@ -44,14 +49,15 @@ Slider {
|
|||||||
|
|
||||||
background: Item {
|
background: Item {
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
implicitHeight: 12 // Somehow binding this makes it fill height. Must be set with a constant like this
|
implicitHeight: trackHeight
|
||||||
|
|
||||||
// Fill left
|
// Fill left
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
width: slider.handleLimit + slider.visualPosition * slider.limitedHandleRangeWidth - (slider.handleMargins + slider.handleWidth / 2)
|
width: slider.handleLimit + slider.visualPosition * slider.limitedHandleRangeWidth - (slider.handleMargins + slider.handleWidth / 2)
|
||||||
height: parent.height
|
height: trackHeight
|
||||||
color: Appearance.m3colors.m3primary
|
color: slider.highlightColor
|
||||||
topLeftRadius: Appearance.rounding.full
|
topLeftRadius: Appearance.rounding.full
|
||||||
bottomLeftRadius: Appearance.rounding.full
|
bottomLeftRadius: Appearance.rounding.full
|
||||||
topRightRadius: Appearance.rounding.unsharpen
|
topRightRadius: Appearance.rounding.unsharpen
|
||||||
@@ -60,10 +66,11 @@ Slider {
|
|||||||
|
|
||||||
// Fill right
|
// Fill right
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
width: slider.handleLimit + (1 - slider.visualPosition) * slider.limitedHandleRangeWidth - (slider.handleMargins + slider.handleWidth / 2)
|
width: slider.handleLimit + (1 - slider.visualPosition) * slider.limitedHandleRangeWidth - (slider.handleMargins + slider.handleWidth / 2)
|
||||||
height: parent.height
|
height: trackHeight
|
||||||
color: Appearance.m3colors.m3secondaryContainer
|
color: slider.trackColor
|
||||||
topLeftRadius: Appearance.rounding.unsharpen
|
topLeftRadius: Appearance.rounding.unsharpen
|
||||||
bottomLeftRadius: Appearance.rounding.unsharpen
|
bottomLeftRadius: Appearance.rounding.unsharpen
|
||||||
topRightRadius: Appearance.rounding.full
|
topRightRadius: Appearance.rounding.full
|
||||||
@@ -78,7 +85,7 @@ Slider {
|
|||||||
width: slider.backgroundDotSize
|
width: slider.backgroundDotSize
|
||||||
height: slider.backgroundDotSize
|
height: slider.backgroundDotSize
|
||||||
radius: Appearance.rounding.full
|
radius: Appearance.rounding.full
|
||||||
color: Appearance.m3colors.m3onSecondaryContainer
|
color: slider.handleColor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,7 +96,7 @@ Slider {
|
|||||||
implicitWidth: slider.handleWidth
|
implicitWidth: slider.handleWidth
|
||||||
implicitHeight: slider.handleHeight
|
implicitHeight: slider.handleHeight
|
||||||
radius: Appearance.rounding.full
|
radius: Appearance.rounding.full
|
||||||
color: Appearance.m3colors.m3onSecondaryContainer
|
color: slider.handleColor
|
||||||
|
|
||||||
Behavior on implicitWidth {
|
Behavior on implicitWidth {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
@@ -101,7 +108,7 @@ Slider {
|
|||||||
|
|
||||||
StyledToolTip {
|
StyledToolTip {
|
||||||
extraVisibleCondition: slider.pressed
|
extraVisibleCondition: slider.pressed
|
||||||
content: `${Math.round(slider.value * 100)}%`
|
content: slider.tooltipContent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -22,18 +22,10 @@ Switch {
|
|||||||
border.color: root.checked ? Appearance.m3colors.m3primary : Appearance.m3colors.m3outline
|
border.color: root.checked ? Appearance.m3colors.m3primary : Appearance.m3colors.m3outline
|
||||||
|
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
ColorAnimation {
|
animation: Appearance.animation.elementMove.colorAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMove.duration
|
|
||||||
easing.type: Appearance.animation.elementMove.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Behavior on border.color {
|
Behavior on border.color {
|
||||||
ColorAnimation {
|
animation: Appearance.animation.elementMove.colorAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMove.duration
|
|
||||||
easing.type: Appearance.animation.elementMove.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,32 +40,16 @@ Switch {
|
|||||||
anchors.leftMargin: root.checked ? (root.pressed ? (22 * root.scale) : 24 * root.scale) : (root.pressed ? (2 * root.scale) : 8 * root.scale)
|
anchors.leftMargin: root.checked ? (root.pressed ? (22 * root.scale) : 24 * root.scale) : (root.pressed ? (2 * root.scale) : 8 * root.scale)
|
||||||
|
|
||||||
Behavior on anchors.leftMargin {
|
Behavior on anchors.leftMargin {
|
||||||
NumberAnimation {
|
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMove.duration
|
|
||||||
easing.type: Appearance.animation.elementMove.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Behavior on width {
|
Behavior on width {
|
||||||
NumberAnimation {
|
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMove.duration
|
|
||||||
easing.type: Appearance.animation.elementMove.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Behavior on height {
|
Behavior on height {
|
||||||
NumberAnimation {
|
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMove.duration
|
|
||||||
easing.type: Appearance.animation.elementMove.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
ColorAnimation {
|
animation: Appearance.animation.elementMove.colorAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMove.duration
|
|
||||||
easing.type: Appearance.animation.elementMove.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ ToolTip {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
background: Item {}
|
background: null
|
||||||
|
|
||||||
contentItem: Item {
|
contentItem: Item {
|
||||||
id: contentItemBackground
|
id: contentItemBackground
|
||||||
|
|||||||
@@ -0,0 +1,146 @@
|
|||||||
|
import "root:/modules/common"
|
||||||
|
import "root:/modules/common/widgets"
|
||||||
|
import "root:/services"
|
||||||
|
import "root:/modules/common/functions/string_utils.js" as StringUtils
|
||||||
|
import "root:/modules/common/functions/file_utils.js" as FileUtils
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import QtQuick.Controls
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Io
|
||||||
|
import Quickshell.Services.Mpris
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import Quickshell.Wayland
|
||||||
|
import Quickshell.Hyprland
|
||||||
|
|
||||||
|
Scope {
|
||||||
|
id: root
|
||||||
|
required property var bar
|
||||||
|
property bool visible: false
|
||||||
|
readonly property MprisPlayer activePlayer: MprisController.activePlayer
|
||||||
|
readonly property var realPlayers: Mpris.players.values.filter(player => isRealPlayer(player))
|
||||||
|
readonly property real osdWidth: Appearance.sizes.osdWidth
|
||||||
|
readonly property real widgetWidth: Appearance.sizes.mediaControlsWidth
|
||||||
|
readonly property real widgetHeight: Appearance.sizes.mediaControlsHeight
|
||||||
|
property real contentPadding: 13
|
||||||
|
property real popupRounding: Appearance.rounding.screenRounding - Appearance.sizes.elevationMargin + 1
|
||||||
|
property real artRounding: Appearance.rounding.verysmall
|
||||||
|
property string baseCoverArtDir: FileUtils.trimFileProtocol(`${XdgDirectories.cache}/media/coverart`)
|
||||||
|
|
||||||
|
property bool hasPlasmaIntegration: false
|
||||||
|
function isRealPlayer(player) {
|
||||||
|
// return true
|
||||||
|
return (
|
||||||
|
// Remove unecessary native buses from browsers if there's plasma integration
|
||||||
|
!(hasPlasmaIntegration && player.dbusName.startsWith('org.mpris.MediaPlayer2.firefox')) &&
|
||||||
|
!(hasPlasmaIntegration && player.dbusName.startsWith('org.mpris.MediaPlayer2.chromium')) &&
|
||||||
|
// playerctld just copies other buses and we don't need duplicates
|
||||||
|
!player.dbusName?.startsWith('org.mpris.MediaPlayer2.playerctld') &&
|
||||||
|
// Non-instance mpd bus
|
||||||
|
!(player.dbusName?.endsWith('.mpd') && !player.dbusName.endsWith('MediaPlayer2.mpd'))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
Hyprland.dispatch(`exec rm -rf ${baseCoverArtDir} && mkdir -p ${baseCoverArtDir}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: mediaControlsLoader
|
||||||
|
active: false
|
||||||
|
|
||||||
|
PanelWindow {
|
||||||
|
id: mediaControlsRoot
|
||||||
|
visible: mediaControlsLoader.active
|
||||||
|
|
||||||
|
exclusiveZone: 0
|
||||||
|
implicitWidth: (
|
||||||
|
(mediaControlsRoot.screen.width / 2) // Middle of screen
|
||||||
|
- (osdWidth / 2) // Dodge OSD
|
||||||
|
- (widgetWidth / 2) // Account for widget width
|
||||||
|
) * 2
|
||||||
|
implicitHeight: playerColumnLayout.implicitHeight
|
||||||
|
color: "transparent"
|
||||||
|
WlrLayershell.namespace: "quickshell:mediaControls"
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: true
|
||||||
|
left: true
|
||||||
|
}
|
||||||
|
mask: Region {
|
||||||
|
item: playerColumnLayout
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: playerColumnLayout
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
x: (mediaControlsRoot.screen.width / 2) // Middle of screen
|
||||||
|
- (osdWidth / 2) // Dodge OSD
|
||||||
|
- (widgetWidth) // Account for widget width
|
||||||
|
+ (Appearance.sizes.elevationMargin) // It's fine for shadows to overlap
|
||||||
|
spacing: -Appearance.sizes.elevationMargin // Shadow overlap okay
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: ScriptModel {
|
||||||
|
values: root.realPlayers
|
||||||
|
}
|
||||||
|
delegate: PlayerControl {
|
||||||
|
required property MprisPlayer modelData
|
||||||
|
player: modelData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IpcHandler {
|
||||||
|
target: "mediaControls"
|
||||||
|
|
||||||
|
function toggle(): void {
|
||||||
|
mediaControlsLoader.active = !mediaControlsLoader.active;
|
||||||
|
if(mediaControlsLoader.active) Notifications.timeoutAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
function close(): void {
|
||||||
|
mediaControlsLoader.active = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function open(): void {
|
||||||
|
mediaControlsLoader.active = true;
|
||||||
|
Notifications.timeoutAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GlobalShortcut {
|
||||||
|
name: "mediaControlsToggle"
|
||||||
|
description: qsTr("Toggles media controls on press")
|
||||||
|
|
||||||
|
onPressed: {
|
||||||
|
if (!mediaControlsLoader.active && Mpris.players.values.filter(player => isRealPlayer(player)).length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mediaControlsLoader.active = !mediaControlsLoader.active;
|
||||||
|
if(mediaControlsLoader.active) Notifications.timeoutAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GlobalShortcut {
|
||||||
|
name: "mediaControlsOpen"
|
||||||
|
description: qsTr("Opens media controls on press")
|
||||||
|
|
||||||
|
onPressed: {
|
||||||
|
mediaControlsLoader.active = true;
|
||||||
|
Notifications.timeoutAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GlobalShortcut {
|
||||||
|
name: "mediaControlsClose"
|
||||||
|
description: qsTr("Closes media controls on press")
|
||||||
|
|
||||||
|
onPressed: {
|
||||||
|
mediaControlsLoader.active = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,292 @@
|
|||||||
|
import "root:/modules/common"
|
||||||
|
import "root:/modules/common/widgets"
|
||||||
|
import "root:/services"
|
||||||
|
import "root:/modules/common/functions/string_utils.js" as StringUtils
|
||||||
|
import "root:/modules/common/functions/color_utils.js" as ColorUtils
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Effects
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import QtQuick.Controls
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Io
|
||||||
|
import Quickshell.Services.Mpris
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import Quickshell.Wayland
|
||||||
|
import Quickshell.Hyprland
|
||||||
|
|
||||||
|
Item { // Player instance
|
||||||
|
id: playerController
|
||||||
|
required property MprisPlayer player
|
||||||
|
// property var artUrl: player?.metadata["xesam:url"] || player?.metadata["mpris:artUrl"] || player?.trackArtUrl
|
||||||
|
property var artUrl: player?.trackArtUrl
|
||||||
|
property color artDominantColor: Appearance.m3colors.m3secondaryContainer
|
||||||
|
|
||||||
|
implicitWidth: widgetWidth
|
||||||
|
implicitHeight: widgetHeight
|
||||||
|
|
||||||
|
component TrackChangeButton: Button {
|
||||||
|
id: playPauseButton
|
||||||
|
implicitWidth: 24
|
||||||
|
implicitHeight: 24
|
||||||
|
|
||||||
|
property var iconName
|
||||||
|
PointingHandInteraction {}
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
color: playPauseButton.pressed ? blendedColors.colSecondaryContainerActive :
|
||||||
|
playPauseButton.hovered ? blendedColors.colSecondaryContainerHover :
|
||||||
|
ColorUtils.transparentize(blendedColors.colSecondaryContainer, 1)
|
||||||
|
radius: Appearance.rounding.full
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contentItem: MaterialSymbol {
|
||||||
|
iconSize: Appearance.font.pixelSize.huge
|
||||||
|
fill: 1
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
color: blendedColors.colOnSecondaryContainer
|
||||||
|
text: iconName
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer { // Force update for prevision
|
||||||
|
running: playerController.player?.playbackState == MprisPlaybackState.Playing
|
||||||
|
interval: 1000
|
||||||
|
repeat: true
|
||||||
|
onTriggered: {
|
||||||
|
playerController.player.positionChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onArtUrlChanged: {
|
||||||
|
if (playerController.artUrl.length == 0) {
|
||||||
|
playerController.artDominantColor = Appearance.m3colors.m3secondaryContainer
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
colorQuantizer.targetFile = playerController.artUrl // Yes this binding break is intentional
|
||||||
|
colorQuantizer.running = true
|
||||||
|
}
|
||||||
|
|
||||||
|
Process { // Average Color Runner
|
||||||
|
id: colorQuantizer
|
||||||
|
property string targetFile: playerController.artUrl
|
||||||
|
command: [ "sh", "-c", `magick '${targetFile}' -scale 1x1\\! -format '%[fx:int(255*r+.5)],%[fx:int(255*g+.5)],%[fx:int(255*b+.5)]' info: | sed 's/,/\\n/g' | xargs -L 1 printf '%02x' ; echo` ]
|
||||||
|
stdout: SplitParser {
|
||||||
|
onRead: data => {
|
||||||
|
// console.log("Color quantizer output:", data)
|
||||||
|
playerController.artDominantColor = "#" + data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
property QtObject blendedColors: QtObject {
|
||||||
|
property color colLayer0: ColorUtils.mix(Appearance.colors.colLayer0, artDominantColor, 0.5)
|
||||||
|
property color colLayer1: ColorUtils.mix(Appearance.colors.colLayer1, artDominantColor, 0.5)
|
||||||
|
property color colOnLayer0: ColorUtils.mix(Appearance.colors.colOnLayer0, artDominantColor, 0.5)
|
||||||
|
property color colOnLayer1: ColorUtils.mix(Appearance.colors.colOnLayer1, artDominantColor, 0.5)
|
||||||
|
property color colSubtext: ColorUtils.mix(Appearance.colors.colOnLayer1, artDominantColor, 0.5)
|
||||||
|
property color colPrimary: ColorUtils.mix(ColorUtils.adaptToAccent(Appearance.m3colors.m3primary, artDominantColor), artDominantColor, 0.5)
|
||||||
|
property color colPrimaryHover: ColorUtils.mix(ColorUtils.adaptToAccent(Appearance.colors.colPrimaryHover, artDominantColor), artDominantColor, 0.3)
|
||||||
|
property color colPrimaryActive: ColorUtils.mix(ColorUtils.adaptToAccent(Appearance.colors.colPrimaryActive, artDominantColor), artDominantColor, 0.3)
|
||||||
|
property color colSecondaryContainer: ColorUtils.mix(Appearance.m3colors.m3secondaryContainer, artDominantColor, 0.3)
|
||||||
|
property color colSecondaryContainerHover: ColorUtils.mix(Appearance.colors.colSecondaryContainerHover, artDominantColor, 0.3)
|
||||||
|
property color colSecondaryContainerActive: ColorUtils.mix(Appearance.colors.colSecondaryContainerActive, artDominantColor, 0.3)
|
||||||
|
property color colOnPrimary: ColorUtils.mix(ColorUtils.adaptToAccent(Appearance.m3colors.m3onPrimary, artDominantColor), artDominantColor, 0.5)
|
||||||
|
property color colOnSecondaryContainer: ColorUtils.mix(Appearance.m3colors.m3onSecondaryContainer, artDominantColor, 0.2)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle { // Background
|
||||||
|
id: background
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Appearance.sizes.elevationMargin
|
||||||
|
color: blendedColors.colLayer0
|
||||||
|
radius: root.popupRounding
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: OpacityMask {
|
||||||
|
maskSource: Rectangle {
|
||||||
|
width: background.width
|
||||||
|
height: background.height
|
||||||
|
radius: background.radius
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: blurredArt
|
||||||
|
anchors.fill: parent
|
||||||
|
visible: true
|
||||||
|
source: playerController.artUrl
|
||||||
|
sourceSize.width: background.width
|
||||||
|
sourceSize.height: background.height
|
||||||
|
fillMode: Image.PreserveAspectCrop
|
||||||
|
cache: false
|
||||||
|
antialiasing: true
|
||||||
|
asynchronous: true
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: MultiEffect {
|
||||||
|
source: blurredArt
|
||||||
|
anchors.fill: blurredArt
|
||||||
|
saturation: 0.2
|
||||||
|
blurEnabled: true
|
||||||
|
blurMax: 100
|
||||||
|
blur: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
color: ColorUtils.transparentize(blendedColors.colLayer0, 0.25)
|
||||||
|
radius: root.popupRounding
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: root.contentPadding
|
||||||
|
spacing: 15
|
||||||
|
|
||||||
|
Rectangle { // Art background
|
||||||
|
id: artBackground
|
||||||
|
Layout.fillHeight: true
|
||||||
|
implicitWidth: height
|
||||||
|
radius: root.artRounding
|
||||||
|
color: blendedColors.colLayer1
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: OpacityMask {
|
||||||
|
maskSource: Rectangle {
|
||||||
|
width: artBackground.width
|
||||||
|
height: artBackground.height
|
||||||
|
radius: artBackground.radius
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Image { // Art image
|
||||||
|
id: mediaArt
|
||||||
|
property int size: parent.height
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
source: playerController.artUrl
|
||||||
|
fillMode: Image.PreserveAspectCrop
|
||||||
|
cache: false
|
||||||
|
antialiasing: true
|
||||||
|
asynchronous: true
|
||||||
|
|
||||||
|
width: size
|
||||||
|
height: size
|
||||||
|
sourceSize.width: size
|
||||||
|
sourceSize.height: size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout { // Info & controls
|
||||||
|
Layout.fillHeight: true
|
||||||
|
spacing: 2
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: trackTitle
|
||||||
|
Layout.fillWidth: true
|
||||||
|
font.pixelSize: Appearance.font.pixelSize.large
|
||||||
|
color: blendedColors.colOnLayer0
|
||||||
|
elide: Text.ElideRight
|
||||||
|
text: StringUtils.cleanMusicTitle(playerController.player?.trackTitle) || "Untitled"
|
||||||
|
}
|
||||||
|
StyledText {
|
||||||
|
id: trackArtist
|
||||||
|
Layout.fillWidth: true
|
||||||
|
font.pixelSize: Appearance.font.pixelSize.smaller
|
||||||
|
color: blendedColors.colSubtext
|
||||||
|
elide: Text.ElideRight
|
||||||
|
text: playerController.player?.trackArtist
|
||||||
|
}
|
||||||
|
Item { Layout.fillHeight: true }
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
implicitHeight: trackTime.implicitHeight + sliderRow.implicitHeight
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: trackTime
|
||||||
|
anchors.bottom: sliderRow.top
|
||||||
|
anchors.bottomMargin: 5
|
||||||
|
anchors.left: parent.left
|
||||||
|
font.pixelSize: Appearance.font.pixelSize.small
|
||||||
|
color: blendedColors.colSubtext
|
||||||
|
elide: Text.ElideRight
|
||||||
|
text: `${StringUtils.friendlyTimeForSeconds(playerController.player?.position)} / ${StringUtils.friendlyTimeForSeconds(playerController.player?.length)}`
|
||||||
|
}
|
||||||
|
RowLayout {
|
||||||
|
id: sliderRow
|
||||||
|
anchors {
|
||||||
|
bottom: parent.bottom
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
}
|
||||||
|
TrackChangeButton {
|
||||||
|
iconName: "skip_previous"
|
||||||
|
onClicked: playerController.player?.previous()
|
||||||
|
}
|
||||||
|
StyledProgressBar {
|
||||||
|
id: slider
|
||||||
|
Layout.fillWidth: true
|
||||||
|
highlightColor: blendedColors.colPrimary
|
||||||
|
trackColor: blendedColors.colSecondaryContainer
|
||||||
|
value: playerController.player?.position / playerController.player?.length
|
||||||
|
}
|
||||||
|
TrackChangeButton {
|
||||||
|
iconName: "skip_next"
|
||||||
|
onClicked: playerController.player?.next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
id: playPauseButton
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: sliderRow.top
|
||||||
|
anchors.bottomMargin: 5
|
||||||
|
implicitWidth: 44
|
||||||
|
implicitHeight: 44
|
||||||
|
onClicked: playerController.player.togglePlaying();
|
||||||
|
|
||||||
|
PointingHandInteraction {}
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
color: playerController.player?.isPlaying ?
|
||||||
|
(playPauseButton.pressed ? blendedColors.colPrimaryActive :
|
||||||
|
playPauseButton.hovered ? blendedColors.colPrimaryHover :
|
||||||
|
blendedColors.colPrimary) :
|
||||||
|
(playPauseButton.pressed ? blendedColors.colSecondaryContainerActive :
|
||||||
|
playPauseButton.hovered ? blendedColors.colSecondaryContainerHover :
|
||||||
|
blendedColors.colSecondaryContainer)
|
||||||
|
radius: Appearance.rounding.full
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contentItem: MaterialSymbol {
|
||||||
|
iconSize: Appearance.font.pixelSize.huge
|
||||||
|
fill: 1
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
color: playerController.player?.isPlaying ? blendedColors.colOnPrimary : blendedColors.colOnSecondaryContainer
|
||||||
|
text: playerController.player?.isPlaying ? "pause" : "play_arrow"
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,111 +7,105 @@ import QtQuick.Controls
|
|||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Wayland
|
import Quickshell.Wayland
|
||||||
|
import Quickshell.Hyprland
|
||||||
|
|
||||||
Scope {
|
Scope {
|
||||||
id: screenCorners
|
id: notificationPopup
|
||||||
readonly property Toplevel activeWindow: ToplevelManager.activeToplevel
|
|
||||||
|
|
||||||
Variants {
|
LazyLoader {
|
||||||
model: Quickshell.screens
|
loading: true
|
||||||
|
PanelWindow {
|
||||||
|
id: root
|
||||||
|
visible: (columnLayout.children.length > 0 || notificationWidgetList.length > 0)
|
||||||
|
screen: Quickshell.screens.find(s => s.name === Hyprland.focusedMonitor?.name)
|
||||||
|
|
||||||
LazyLoader {
|
property Component notifComponent: NotificationWidget {}
|
||||||
property var modelData
|
property list<NotificationWidget> notificationWidgetList: []
|
||||||
loading: true
|
|
||||||
PanelWindow {
|
|
||||||
id: root
|
|
||||||
visible: (columnLayout.children.length > 0 || notificationWidgetList.length > 0)
|
|
||||||
|
|
||||||
property Component notifComponent: NotificationWidget {}
|
WlrLayershell.namespace: "quickshell:notificationPopup"
|
||||||
property list<NotificationWidget> notificationWidgetList: []
|
WlrLayershell.layer: WlrLayer.Overlay
|
||||||
|
exclusiveZone: 0
|
||||||
screen: modelData
|
|
||||||
WlrLayershell.namespace: "quickshell:notificationPopup"
|
|
||||||
WlrLayershell.layer: WlrLayer.Overlay
|
|
||||||
exclusiveZone: 0
|
|
||||||
|
|
||||||
anchors {
|
|
||||||
top: true
|
|
||||||
right: true
|
|
||||||
bottom: true
|
|
||||||
}
|
|
||||||
|
|
||||||
mask: Region {
|
|
||||||
item: columnLayout
|
|
||||||
}
|
|
||||||
|
|
||||||
color: "transparent"
|
|
||||||
implicitWidth: Appearance.sizes.notificationPopupWidth
|
|
||||||
|
|
||||||
// Signal handlers to add/remove notifications
|
|
||||||
Connections {
|
|
||||||
target: Notifications
|
|
||||||
function onNotify(notification) {
|
|
||||||
if (GlobalStates.sidebarRightOpenCount > 0) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// notificationRepeater.model = [notification, ...notificationRepeater.model]
|
|
||||||
const notif = root.notifComponent.createObject(columnLayout, {
|
|
||||||
notificationObject: notification,
|
|
||||||
popup: true
|
|
||||||
});
|
|
||||||
notificationWidgetList.unshift(notif)
|
|
||||||
|
|
||||||
// Remove stuff from t he column, add back
|
|
||||||
for (let i = 0; i < notificationWidgetList.length; i++) {
|
|
||||||
if (notificationWidgetList[i].parent === columnLayout) {
|
|
||||||
notificationWidgetList[i].parent = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add notification widgets to the column
|
|
||||||
for (let i = 0; i < notificationWidgetList.length; i++) {
|
|
||||||
if (notificationWidgetList[i].parent === null) {
|
|
||||||
notificationWidgetList[i].parent = columnLayout;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function onDiscard(id) {
|
|
||||||
for (let i = notificationWidgetList.length - 1; i >= 0; i--) {
|
|
||||||
const widget = notificationWidgetList[i];
|
|
||||||
if (widget && widget.notificationObject && widget.notificationObject.id === id) {
|
|
||||||
widget.destroyWithAnimation();
|
|
||||||
notificationWidgetList.splice(i, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function onTimeout(id) {
|
|
||||||
for (let i = notificationWidgetList.length - 1; i >= 0; i--) {
|
|
||||||
const widget = notificationWidgetList[i];
|
|
||||||
if (widget && widget.notificationObject && widget.notificationObject.id === id) {
|
|
||||||
widget.destroyWithAnimation();
|
|
||||||
notificationWidgetList.splice(i, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function onDiscardAll() {
|
|
||||||
for (let i = notificationWidgetList.length - 1; i >= 0; i--) {
|
|
||||||
const widget = notificationWidgetList[i];
|
|
||||||
if (widget && widget.notificationObject) {
|
|
||||||
widget.destroyWithAnimation();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
notificationWidgetList = [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout { // Scrollable window content
|
|
||||||
id: columnLayout
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
width: parent.width - Appearance.sizes.hyprlandGapsOut * 2
|
|
||||||
spacing: 0 // The widgets themselves have margins for spacing
|
|
||||||
|
|
||||||
// Notifications are added by the above signal handlers
|
|
||||||
}
|
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: true
|
||||||
|
right: true
|
||||||
|
bottom: true
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
mask: Region {
|
||||||
|
item: columnLayout
|
||||||
|
}
|
||||||
|
|
||||||
|
color: "transparent"
|
||||||
|
implicitWidth: Appearance.sizes.notificationPopupWidth
|
||||||
|
|
||||||
|
// Signal handlers to add/remove notifications
|
||||||
|
Connections {
|
||||||
|
target: Notifications
|
||||||
|
function onNotify(notification) {
|
||||||
|
if (GlobalStates.sidebarRightOpen) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// notificationRepeater.model = [notification, ...notificationRepeater.model]
|
||||||
|
const notif = root.notifComponent.createObject(columnLayout, {
|
||||||
|
notificationObject: notification,
|
||||||
|
popup: true
|
||||||
|
});
|
||||||
|
notificationWidgetList.unshift(notif)
|
||||||
|
|
||||||
|
// Remove stuff from t he column, add back
|
||||||
|
for (let i = 0; i < notificationWidgetList.length; i++) {
|
||||||
|
if (notificationWidgetList[i].parent === columnLayout) {
|
||||||
|
notificationWidgetList[i].parent = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add notification widgets to the column
|
||||||
|
for (let i = 0; i < notificationWidgetList.length; i++) {
|
||||||
|
if (notificationWidgetList[i].parent === null) {
|
||||||
|
notificationWidgetList[i].parent = columnLayout;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function onDiscard(id) {
|
||||||
|
for (let i = notificationWidgetList.length - 1; i >= 0; i--) {
|
||||||
|
const widget = notificationWidgetList[i];
|
||||||
|
if (widget && widget.notificationObject && widget.notificationObject.id === id) {
|
||||||
|
widget.destroyWithAnimation();
|
||||||
|
notificationWidgetList.splice(i, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function onTimeout(id) {
|
||||||
|
for (let i = notificationWidgetList.length - 1; i >= 0; i--) {
|
||||||
|
const widget = notificationWidgetList[i];
|
||||||
|
if (widget && widget.notificationObject && widget.notificationObject.id === id) {
|
||||||
|
widget.destroyWithAnimation();
|
||||||
|
notificationWidgetList.splice(i, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function onDiscardAll() {
|
||||||
|
for (let i = notificationWidgetList.length - 1; i >= 0; i--) {
|
||||||
|
const widget = notificationWidgetList[i];
|
||||||
|
if (widget && widget.notificationObject) {
|
||||||
|
widget.destroyWithAnimation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
notificationWidgetList = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout { // Scrollable window content
|
||||||
|
id: columnLayout
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
width: parent.width - Appearance.sizes.elevationMargin * 2
|
||||||
|
spacing: 0 // The widgets themselves have margins for spacing
|
||||||
|
|
||||||
|
// Notifications are added by the above signal handlers
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ import Quickshell.Wayland
|
|||||||
Scope {
|
Scope {
|
||||||
id: root
|
id: root
|
||||||
property bool showOsdValues: false
|
property bool showOsdValues: false
|
||||||
|
property var focusedScreen: Quickshell.screens.find(s => s.name === Hyprland.focusedMonitor?.name)
|
||||||
|
property var brightnessMonitor: Brightness.getMonitorForScreen(focusedScreen)
|
||||||
|
|
||||||
function triggerOsd() {
|
function triggerOsd() {
|
||||||
showOsdValues = true
|
showOsdValues = true
|
||||||
@@ -36,81 +38,79 @@ Scope {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Variants {
|
Connections {
|
||||||
model: Quickshell.screens
|
target: Brightness
|
||||||
|
function onBrightnessChanged() {
|
||||||
|
if (!root.brightnessMonitor.ready) return
|
||||||
|
root.triggerOsd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
id: osdLoader
|
id: osdLoader
|
||||||
property var modelData
|
active: showOsdValues
|
||||||
active: showOsdValues
|
|
||||||
property var brightnessMonitor: Brightness.getMonitorForScreen(modelData)
|
PanelWindow {
|
||||||
|
id: osdRoot
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: brightnessMonitor
|
target: root
|
||||||
function onBrightnessChanged() {
|
function onFocusedScreenChanged() {
|
||||||
if (!brightnessMonitor.ready) return
|
osdRoot.screen = root.focusedScreen
|
||||||
root.triggerOsd()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PanelWindow {
|
exclusionMode: ExclusionMode.Normal
|
||||||
screen: modelData
|
WlrLayershell.namespace: "quickshell:onScreenDisplay"
|
||||||
exclusionMode: ExclusionMode.Normal
|
WlrLayershell.layer: WlrLayer.Overlay
|
||||||
WlrLayershell.namespace: "quickshell:onScreenDisplay"
|
color: "transparent"
|
||||||
WlrLayershell.layer: WlrLayer.Overlay
|
|
||||||
color: "transparent"
|
|
||||||
|
|
||||||
anchors {
|
|
||||||
top: true
|
|
||||||
}
|
|
||||||
mask: Region {
|
|
||||||
item: osdValuesWrapper
|
|
||||||
}
|
|
||||||
|
|
||||||
implicitWidth: columnLayout.implicitWidth
|
|
||||||
implicitHeight: columnLayout.implicitHeight
|
|
||||||
visible: osdLoader.active
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: columnLayout
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
Item {
|
|
||||||
height: 1 // Prevent Wayland protocol error
|
|
||||||
}
|
|
||||||
Item {
|
|
||||||
id: osdValuesWrapper
|
|
||||||
// Extra space for shadow
|
|
||||||
implicitHeight: true ? (osdValues.implicitHeight + Appearance.sizes.elevationMargin * 2) : 0
|
|
||||||
implicitWidth: osdValues.implicitWidth + Appearance.sizes.elevationMargin * 2
|
|
||||||
clip: true
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
onEntered: root.showOsdValues = false
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on implicitHeight {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Appearance.animation.menuDecel.duration
|
|
||||||
easing.type: Appearance.animation.menuDecel.type
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
OsdValueIndicator {
|
|
||||||
id: osdValues
|
|
||||||
anchors.centerIn: parent
|
|
||||||
value: brightnessMonitor.brightness
|
|
||||||
icon: "light_mode"
|
|
||||||
rotateIcon: true
|
|
||||||
name: qsTr("Brightness")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
anchors.top: true
|
||||||
|
mask: Region {
|
||||||
|
item: osdValuesWrapper
|
||||||
}
|
}
|
||||||
|
|
||||||
|
implicitWidth: Appearance.sizes.osdWidth
|
||||||
|
implicitHeight: columnLayout.implicitHeight
|
||||||
|
visible: osdLoader.active
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: columnLayout
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
Item {
|
||||||
|
id: osdValuesWrapper
|
||||||
|
// Extra space for shadow
|
||||||
|
implicitHeight: osdValues.implicitHeight + Appearance.sizes.elevationMargin * 2
|
||||||
|
implicitWidth: osdValues.implicitWidth
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
onEntered: root.showOsdValues = false
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on implicitHeight {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Appearance.animation.menuDecel.duration
|
||||||
|
easing.type: Appearance.animation.menuDecel.type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OsdValueIndicator {
|
||||||
|
id: osdValues
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Appearance.sizes.elevationMargin
|
||||||
|
value: root.brightnessMonitor?.brightness ?? 50
|
||||||
|
icon: "light_mode"
|
||||||
|
rotateIcon: true
|
||||||
|
scaleIcon: true
|
||||||
|
name: qsTr("Brightness")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IpcHandler {
|
IpcHandler {
|
||||||
@@ -131,7 +131,7 @@ Scope {
|
|||||||
|
|
||||||
GlobalShortcut {
|
GlobalShortcut {
|
||||||
name: "osdBrightnessTrigger"
|
name: "osdBrightnessTrigger"
|
||||||
description: "Triggers brightness OSD on press"
|
description: qsTr("Triggers brightness OSD on press")
|
||||||
|
|
||||||
onPressed: {
|
onPressed: {
|
||||||
root.triggerOsd()
|
root.triggerOsd()
|
||||||
@@ -139,7 +139,7 @@ Scope {
|
|||||||
}
|
}
|
||||||
GlobalShortcut {
|
GlobalShortcut {
|
||||||
name: "osdBrightnessHide"
|
name: "osdBrightnessHide"
|
||||||
description: "Hides brightness OSD on press"
|
description: qsTr("Hides brightness OSD on press")
|
||||||
|
|
||||||
onPressed: {
|
onPressed: {
|
||||||
root.showOsdValues = false
|
root.showOsdValues = false
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import Quickshell.Hyprland
|
|||||||
Scope {
|
Scope {
|
||||||
id: root
|
id: root
|
||||||
property bool showOsdValues: false
|
property bool showOsdValues: false
|
||||||
|
property var focusedScreen: Quickshell.screens.find(s => s.name === Hyprland.focusedMonitor?.name)
|
||||||
|
|
||||||
function triggerOsd() {
|
function triggerOsd() {
|
||||||
showOsdValues = true
|
showOsdValues = true
|
||||||
@@ -47,70 +48,68 @@ Scope {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Variants {
|
Loader {
|
||||||
model: Quickshell.screens
|
id: osdLoader
|
||||||
|
active: showOsdValues
|
||||||
|
|
||||||
Loader {
|
PanelWindow {
|
||||||
id: osdLoader
|
id: osdRoot
|
||||||
property var modelData
|
|
||||||
active: showOsdValues
|
|
||||||
PanelWindow {
|
|
||||||
screen: modelData
|
|
||||||
exclusionMode: ExclusionMode.Normal
|
|
||||||
WlrLayershell.namespace: "quickshell:onScreenDisplay"
|
|
||||||
WlrLayershell.layer: WlrLayer.Overlay
|
|
||||||
color: "transparent"
|
|
||||||
|
|
||||||
anchors {
|
Connections {
|
||||||
top: true
|
target: root
|
||||||
}
|
function onFocusedScreenChanged() {
|
||||||
mask: Region {
|
osdRoot.screen = root.focusedScreen
|
||||||
item: osdValuesWrapper
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
implicitWidth: columnLayout.implicitWidth
|
exclusionMode: ExclusionMode.Normal
|
||||||
implicitHeight: columnLayout.implicitHeight
|
WlrLayershell.namespace: "quickshell:onScreenDisplay"
|
||||||
visible: osdLoader.active
|
WlrLayershell.layer: WlrLayer.Overlay
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
ColumnLayout {
|
anchors.top: true
|
||||||
id: columnLayout
|
mask: Region {
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
item: osdValuesWrapper
|
||||||
Item {
|
}
|
||||||
height: 1 // Prevent Wayland protocol error
|
|
||||||
|
implicitWidth: Appearance.sizes.osdWidth
|
||||||
|
implicitHeight: columnLayout.implicitHeight
|
||||||
|
visible: osdLoader.active
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: columnLayout
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
Item {
|
||||||
|
id: osdValuesWrapper
|
||||||
|
// Extra space for shadow
|
||||||
|
implicitHeight: osdValues.implicitHeight + Appearance.sizes.elevationMargin * 2
|
||||||
|
implicitWidth: osdValues.implicitWidth
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
onEntered: root.showOsdValues = false
|
||||||
}
|
}
|
||||||
Item {
|
|
||||||
id: osdValuesWrapper
|
|
||||||
// Extra space for shadow
|
|
||||||
implicitHeight: true ? (osdValues.implicitHeight + Appearance.sizes.elevationMargin * 2) : 0
|
|
||||||
implicitWidth: osdValues.implicitWidth + Appearance.sizes.elevationMargin * 2
|
|
||||||
clip: true
|
|
||||||
|
|
||||||
MouseArea {
|
Behavior on implicitHeight {
|
||||||
anchors.fill: parent
|
NumberAnimation {
|
||||||
hoverEnabled: true
|
duration: Appearance.animation.menuDecel.duration
|
||||||
onEntered: root.showOsdValues = false
|
easing.type: Appearance.animation.menuDecel.type
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on implicitHeight {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Appearance.animation.menuDecel.duration
|
|
||||||
easing.type: Appearance.animation.menuDecel.type
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
OsdValueIndicator {
|
|
||||||
id: osdValues
|
|
||||||
anchors.centerIn: parent
|
|
||||||
value: Audio.sink?.audio.volume ?? 0
|
|
||||||
icon: Audio.sink?.audio.muted ? "volume_off" : "volume_up"
|
|
||||||
name: qsTr("Volume")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IpcHandler {
|
IpcHandler {
|
||||||
@@ -130,7 +129,7 @@ Scope {
|
|||||||
}
|
}
|
||||||
GlobalShortcut {
|
GlobalShortcut {
|
||||||
name: "osdVolumeTrigger"
|
name: "osdVolumeTrigger"
|
||||||
description: "Triggers volume OSD on press"
|
description: qsTr("Triggers volume OSD on press")
|
||||||
|
|
||||||
onPressed: {
|
onPressed: {
|
||||||
root.triggerOsd()
|
root.triggerOsd()
|
||||||
@@ -138,7 +137,7 @@ Scope {
|
|||||||
}
|
}
|
||||||
GlobalShortcut {
|
GlobalShortcut {
|
||||||
name: "osdVolumeHide"
|
name: "osdVolumeHide"
|
||||||
description: "Hides volume OSD on press"
|
description: qsTr("Hides volume OSD on press")
|
||||||
|
|
||||||
onPressed: {
|
onPressed: {
|
||||||
root.showOsdValues = false
|
root.showOsdValues = false
|
||||||
|
|||||||
@@ -3,10 +3,11 @@ import "root:/modules/common"
|
|||||||
import "root:/modules/common/widgets"
|
import "root:/modules/common/widgets"
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Effects
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Widgets
|
import Quickshell.Widgets
|
||||||
import Qt5Compat.GraphicalEffects
|
// import Qt5Compat.GraphicalEffects
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
@@ -14,24 +15,37 @@ Item {
|
|||||||
required property string icon
|
required property string icon
|
||||||
required property string name
|
required property string name
|
||||||
property bool rotateIcon: false
|
property bool rotateIcon: false
|
||||||
|
property bool scaleIcon: false
|
||||||
|
|
||||||
property real valueIndicatorVerticalPadding: 5
|
property real valueIndicatorVerticalPadding: 9
|
||||||
property real valueIndicatorLeftPadding: 10
|
property real valueIndicatorLeftPadding: 10
|
||||||
property real valueIndicatorRightPadding: 20 // An icon is circle ish, a column isn't, hence the extra padding
|
property real valueIndicatorRightPadding: 20 // An icon is circle ish, a column isn't, hence the extra padding
|
||||||
|
|
||||||
Layout.margins: Appearance.sizes.elevationMargin
|
Layout.margins: Appearance.sizes.elevationMargin
|
||||||
implicitWidth: valueIndicator.implicitWidth
|
implicitWidth: Appearance.sizes.osdWidth
|
||||||
implicitHeight: valueIndicator.implicitHeight
|
implicitHeight: valueIndicator.implicitHeight
|
||||||
|
|
||||||
WrapperRectangle {
|
WrapperRectangle {
|
||||||
id: valueIndicator
|
id: valueIndicator
|
||||||
|
anchors.fill: parent
|
||||||
radius: Appearance.rounding.full
|
radius: Appearance.rounding.full
|
||||||
color: Appearance.colors.colLayer0
|
color: Appearance.colors.colLayer0
|
||||||
implicitWidth: valueRow.implicitWidth
|
implicitWidth: valueRow.implicitWidth
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: MultiEffect {
|
||||||
|
source: valueIndicator
|
||||||
|
anchors.fill: valueIndicator
|
||||||
|
shadowEnabled: true
|
||||||
|
shadowColor: Appearance.colors.colShadow
|
||||||
|
shadowVerticalOffset: 1
|
||||||
|
shadowBlur: 0.5
|
||||||
|
}
|
||||||
|
|
||||||
RowLayout { // Icon on the left, stuff on the right
|
RowLayout { // Icon on the left, stuff on the right
|
||||||
id: valueRow
|
id: valueRow
|
||||||
Layout.margins: 10
|
Layout.margins: 10
|
||||||
|
anchors.fill: parent
|
||||||
spacing: 10
|
spacing: 10
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
@@ -47,22 +61,14 @@ Item {
|
|||||||
renderType: Text.QtRendering
|
renderType: Text.QtRendering
|
||||||
|
|
||||||
text: root.icon
|
text: root.icon
|
||||||
iconSize: 20 + 10 * (root.rotateIcon ? value : 1)
|
iconSize: 20 + 10 * (root.scaleIcon ? value : 1)
|
||||||
rotation: 180 * (root.rotateIcon ? value : 0)
|
rotation: 180 * (root.rotateIcon ? value : 0)
|
||||||
|
|
||||||
Behavior on iconSize {
|
Behavior on iconSize {
|
||||||
NumberAnimation {
|
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMoveEnter.duration
|
|
||||||
easing.type: Appearance.animation.elementMoveEnter.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Behavior on rotation {
|
Behavior on rotation {
|
||||||
NumberAnimation {
|
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMoveEnter.duration
|
|
||||||
easing.type: Appearance.animation.elementMoveEnter.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -93,20 +99,10 @@ Item {
|
|||||||
|
|
||||||
StyledProgressBar {
|
StyledProgressBar {
|
||||||
id: valueProgressBar
|
id: valueProgressBar
|
||||||
|
Layout.fillWidth: true
|
||||||
value: root.value
|
value: root.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DropShadow {
|
|
||||||
id: valueShadow
|
|
||||||
anchors.fill: valueIndicator
|
|
||||||
source: valueIndicator
|
|
||||||
radius: Appearance.sizes.elevationMargin
|
|
||||||
samples: radius * 2 + 1
|
|
||||||
color: Appearance.colors.colShadow
|
|
||||||
verticalOffset: 2
|
|
||||||
horizontalOffset: 0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -128,7 +128,7 @@ Scope {
|
|||||||
|
|
||||||
GlobalShortcut {
|
GlobalShortcut {
|
||||||
name: "overviewToggle"
|
name: "overviewToggle"
|
||||||
description: "Toggles overview on press"
|
description: qsTr("Toggles overview on press")
|
||||||
|
|
||||||
onPressed: {
|
onPressed: {
|
||||||
GlobalStates.overviewOpen = !GlobalStates.overviewOpen
|
GlobalStates.overviewOpen = !GlobalStates.overviewOpen
|
||||||
@@ -136,7 +136,7 @@ Scope {
|
|||||||
}
|
}
|
||||||
GlobalShortcut {
|
GlobalShortcut {
|
||||||
name: "overviewClose"
|
name: "overviewClose"
|
||||||
description: "Closes overview"
|
description: qsTr("Closes overview")
|
||||||
|
|
||||||
onPressed: {
|
onPressed: {
|
||||||
GlobalStates.overviewOpen = false
|
GlobalStates.overviewOpen = false
|
||||||
@@ -144,7 +144,7 @@ Scope {
|
|||||||
}
|
}
|
||||||
GlobalShortcut {
|
GlobalShortcut {
|
||||||
name: "overviewToggleRelease"
|
name: "overviewToggleRelease"
|
||||||
description: "Toggles overview on release"
|
description: qsTr("Toggles overview on release")
|
||||||
|
|
||||||
onPressed: {
|
onPressed: {
|
||||||
GlobalStates.superReleaseMightTrigger = true
|
GlobalStates.superReleaseMightTrigger = true
|
||||||
@@ -160,9 +160,9 @@ Scope {
|
|||||||
}
|
}
|
||||||
GlobalShortcut {
|
GlobalShortcut {
|
||||||
name: "overviewToggleReleaseInterrupt"
|
name: "overviewToggleReleaseInterrupt"
|
||||||
description: "Interrupts possibility of overview being toggled on release. " +
|
description: qsTr("Interrupts possibility of overview being toggled on release. ") +
|
||||||
"This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key. " +
|
qsTr("This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key. ") +
|
||||||
"To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything."
|
qsTr("To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything.")
|
||||||
|
|
||||||
onPressed: {
|
onPressed: {
|
||||||
GlobalStates.superReleaseMightTrigger = false
|
GlobalStates.superReleaseMightTrigger = false
|
||||||
|
|||||||
@@ -2,8 +2,9 @@ import "root:/"
|
|||||||
import "root:/services/"
|
import "root:/services/"
|
||||||
import "root:/modules/common"
|
import "root:/modules/common"
|
||||||
import "root:/modules/common/widgets"
|
import "root:/modules/common/widgets"
|
||||||
import Qt5Compat.GraphicalEffects
|
import "root:/modules/common/functions/color_utils.js" as ColorUtils
|
||||||
import QtQuick
|
import QtQuick
|
||||||
|
import QtQuick.Effects
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
@@ -58,6 +59,16 @@ Item {
|
|||||||
color: Appearance.colors.colLayer0
|
color: Appearance.colors.colLayer0
|
||||||
radius: Appearance.rounding.screenRounding * root.scale + 5 * 2
|
radius: Appearance.rounding.screenRounding * root.scale + 5 * 2
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: MultiEffect {
|
||||||
|
source: overviewBackground
|
||||||
|
anchors.fill: overviewBackground
|
||||||
|
shadowEnabled: true
|
||||||
|
shadowColor: Appearance.colors.colShadow
|
||||||
|
shadowVerticalOffset: 1
|
||||||
|
shadowBlur: 0.5
|
||||||
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: workspaceColumnLayout
|
id: workspaceColumnLayout
|
||||||
|
|
||||||
@@ -78,7 +89,7 @@ Item {
|
|||||||
property int colIndex: index
|
property int colIndex: index
|
||||||
property int workspaceValue: root.workspaceGroup * workspacesShown + rowIndex * ConfigOptions.overview.numOfCols + colIndex + 1
|
property int workspaceValue: root.workspaceGroup * workspacesShown + rowIndex * ConfigOptions.overview.numOfCols + colIndex + 1
|
||||||
property color defaultWorkspaceColor: Appearance.colors.colLayer1 // TODO: reconsider this color for a cleaner look
|
property color defaultWorkspaceColor: Appearance.colors.colLayer1 // TODO: reconsider this color for a cleaner look
|
||||||
property color hoveredWorkspaceColor: Appearance.mix(defaultWorkspaceColor, Appearance.colors.colLayer1Hover, 0.1)
|
property color hoveredWorkspaceColor: ColorUtils.mix(defaultWorkspaceColor, Appearance.colors.colLayer1Hover, 0.1)
|
||||||
property color hoveredBorderColor: Appearance.colors.colLayer2Hover
|
property color hoveredBorderColor: Appearance.colors.colLayer2Hover
|
||||||
property color activeBorderColor: Appearance.m3colors.m3secondary
|
property color activeBorderColor: Appearance.m3colors.m3secondary
|
||||||
property bool hoveredWhileDragging: false
|
property bool hoveredWhileDragging: false
|
||||||
@@ -209,15 +220,4 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DropShadow {
|
|
||||||
z: -9999
|
|
||||||
anchors.fill: overviewBackground
|
|
||||||
horizontalOffset: 0
|
|
||||||
verticalOffset: 2
|
|
||||||
radius: Appearance.sizes.elevationMargin
|
|
||||||
samples: radius * 2 + 1 // Ideally should be 2 * radius + 1, see qt docs
|
|
||||||
color: Appearance.colors.colShadow
|
|
||||||
source: overviewBackground
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import "root:/services/"
|
|||||||
import "root:/modules/common"
|
import "root:/modules/common"
|
||||||
import "root:/modules/common/widgets"
|
import "root:/modules/common/widgets"
|
||||||
import "root:/modules/common/functions/icons.js" as Icons
|
import "root:/modules/common/functions/icons.js" as Icons
|
||||||
|
import "root:/modules/common/functions/color_utils.js" as ColorUtils
|
||||||
import Qt5Compat.GraphicalEffects
|
import Qt5Compat.GraphicalEffects
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
@@ -42,37 +43,21 @@ Rectangle { // Window
|
|||||||
|
|
||||||
radius: Appearance.rounding.windowRounding * root.scale
|
radius: Appearance.rounding.windowRounding * root.scale
|
||||||
color: pressed ? Appearance.colors.colLayer2Active : hovered ? Appearance.colors.colLayer2Hover : Appearance.colors.colLayer2
|
color: pressed ? Appearance.colors.colLayer2Active : hovered ? Appearance.colors.colLayer2Hover : Appearance.colors.colLayer2
|
||||||
border.color : Appearance.transparentize(Appearance.m3colors.m3outline, 0.9)
|
border.color : ColorUtils.transparentize(Appearance.m3colors.m3outline, 0.9)
|
||||||
border.pixelAligned : false
|
border.pixelAligned : false
|
||||||
border.width : 1
|
border.width : 1
|
||||||
|
|
||||||
Behavior on x {
|
Behavior on x {
|
||||||
NumberAnimation {
|
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMoveEnter.duration
|
|
||||||
easing.type: Appearance.animation.elementMoveEnter.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Behavior on y {
|
Behavior on y {
|
||||||
NumberAnimation {
|
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMoveEnter.duration
|
|
||||||
easing.type: Appearance.animation.elementMoveEnter.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Behavior on width {
|
Behavior on width {
|
||||||
NumberAnimation {
|
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMoveEnter.duration
|
|
||||||
easing.type: Appearance.animation.elementMoveEnter.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Behavior on height {
|
Behavior on height {
|
||||||
NumberAnimation {
|
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMoveEnter.duration
|
|
||||||
easing.type: Appearance.animation.elementMoveEnter.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
@@ -88,11 +73,7 @@ Rectangle { // Window
|
|||||||
implicitSize: Math.min(targetWindowWidth, targetWindowHeight) * (root.compactMode ? root.iconToWindowRatioCompact : root.iconToWindowRatio)
|
implicitSize: Math.min(targetWindowWidth, targetWindowHeight) * (root.compactMode ? root.iconToWindowRatioCompact : root.iconToWindowRatio)
|
||||||
|
|
||||||
Behavior on implicitSize {
|
Behavior on implicitSize {
|
||||||
NumberAnimation {
|
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMoveEnter.duration
|
|
||||||
easing.type: Appearance.animation.elementMoveEnter.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IconImage {
|
IconImage {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import "root:/"
|
import "root:/"
|
||||||
import "root:/modules/common"
|
import "root:/modules/common"
|
||||||
import "root:/modules/common/widgets"
|
import "root:/modules/common/widgets"
|
||||||
|
import "root:/modules/common/functions/color_utils.js" as ColorUtils
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
@@ -57,7 +58,9 @@ Button {
|
|||||||
anchors.leftMargin: root.horizontalMargin
|
anchors.leftMargin: root.horizontalMargin
|
||||||
anchors.rightMargin: root.horizontalMargin
|
anchors.rightMargin: root.horizontalMargin
|
||||||
radius: Appearance.rounding.normal
|
radius: Appearance.rounding.normal
|
||||||
color: (root.down || root.keyboardDown) ? Appearance.colors.colLayer1Active : ((root.hovered || root.focus) ? Appearance.colors.colLayer1Hover : Appearance.transparentize(Appearance.m3colors.m3surfaceContainerHigh, 1))
|
color: (root.down || root.keyboardDown) ? Appearance.colors.colLayer1Active :
|
||||||
|
((root.hovered || root.focus) ? Appearance.colors.colLayer1Hover :
|
||||||
|
ColorUtils.transparentize(Appearance.m3colors.m3surfaceContainerHigh, 1))
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
@@ -100,7 +103,7 @@ Button {
|
|||||||
StyledText {
|
StyledText {
|
||||||
font.pixelSize: Appearance.font.pixelSize.small
|
font.pixelSize: Appearance.font.pixelSize.small
|
||||||
color: Appearance.colors.colSubtext
|
color: Appearance.colors.colSubtext
|
||||||
visible: root.itemType && root.itemType != "App"
|
visible: root.itemType && root.itemType != qsTr("App")
|
||||||
text: root.itemType
|
text: root.itemType
|
||||||
}
|
}
|
||||||
StyledText {
|
StyledText {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import Qt5Compat.GraphicalEffects
|
|||||||
import Qt.labs.platform
|
import Qt.labs.platform
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Effects
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
@@ -160,6 +161,15 @@ Item { // Wrapper
|
|||||||
implicitHeight: columnLayout.implicitHeight
|
implicitHeight: columnLayout.implicitHeight
|
||||||
radius: Appearance.rounding.large
|
radius: Appearance.rounding.large
|
||||||
color: Appearance.colors.colLayer0
|
color: Appearance.colors.colLayer0
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: MultiEffect {
|
||||||
|
source: searchWidgetContent
|
||||||
|
anchors.fill: searchWidgetContent
|
||||||
|
shadowEnabled: true
|
||||||
|
shadowColor: Appearance.colors.colShadow
|
||||||
|
shadowVerticalOffset: 1
|
||||||
|
shadowBlur: 0.5
|
||||||
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: columnLayout
|
id: columnLayout
|
||||||
@@ -227,7 +237,7 @@ Item { // Wrapper
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
background: Item {}
|
background: null
|
||||||
|
|
||||||
cursorDelegate: Rectangle {
|
cursorDelegate: Rectangle {
|
||||||
width: 1
|
width: 1
|
||||||
@@ -276,7 +286,7 @@ Item { // Wrapper
|
|||||||
nonAppResultsTimer.restart();
|
nonAppResultsTimer.restart();
|
||||||
const mathResultObject = {
|
const mathResultObject = {
|
||||||
name: root.mathResult,
|
name: root.mathResult,
|
||||||
clickActionName: "Copy",
|
clickActionName: qsTr("Copy"),
|
||||||
type: qsTr("Math result"),
|
type: qsTr("Math result"),
|
||||||
fontType: "monospace",
|
fontType: "monospace",
|
||||||
materialSymbol: 'calculate',
|
materialSymbol: 'calculate',
|
||||||
@@ -286,7 +296,7 @@ Item { // Wrapper
|
|||||||
}
|
}
|
||||||
const commandResultObject = {
|
const commandResultObject = {
|
||||||
name: searchingText,
|
name: searchingText,
|
||||||
clickActionName: "Run",
|
clickActionName: qsTr("Run"),
|
||||||
type: qsTr("Run command"),
|
type: qsTr("Run command"),
|
||||||
fontType: "monospace",
|
fontType: "monospace",
|
||||||
materialSymbol: 'terminal',
|
materialSymbol: 'terminal',
|
||||||
@@ -300,8 +310,8 @@ Item { // Wrapper
|
|||||||
if (actionString.startsWith(root.searchingText) || root.searchingText.startsWith(actionString)) {
|
if (actionString.startsWith(root.searchingText) || root.searchingText.startsWith(actionString)) {
|
||||||
return {
|
return {
|
||||||
name: root.searchingText.startsWith(actionString) ? root.searchingText : actionString,
|
name: root.searchingText.startsWith(actionString) ? root.searchingText : actionString,
|
||||||
clickActionName: "Run",
|
clickActionName: qsTr("Run"),
|
||||||
type: "Action",
|
type: qsTr("Action"),
|
||||||
materialSymbol: 'settings_suggest',
|
materialSymbol: 'settings_suggest',
|
||||||
execute: () => {
|
execute: () => {
|
||||||
action.execute(root.searchingText.split(" ").slice(1).join(" "))
|
action.execute(root.searchingText.split(" ").slice(1).join(" "))
|
||||||
@@ -319,8 +329,8 @@ Item { // Wrapper
|
|||||||
result = result.concat(
|
result = result.concat(
|
||||||
AppSearch.fuzzyQuery(root.searchingText)
|
AppSearch.fuzzyQuery(root.searchingText)
|
||||||
.map((entry) => {
|
.map((entry) => {
|
||||||
entry.clickActionName = "Launch";
|
entry.clickActionName = qsTr("Launch");
|
||||||
entry.type = "App"
|
entry.type = qsTr("App");
|
||||||
return entry;
|
return entry;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -344,8 +354,8 @@ Item { // Wrapper
|
|||||||
// Web search
|
// Web search
|
||||||
result.push({
|
result.push({
|
||||||
name: root.searchingText,
|
name: root.searchingText,
|
||||||
clickActionName: "Search",
|
clickActionName: qsTr("Search"),
|
||||||
type: "Search the web",
|
type: qsTr("Search the web"),
|
||||||
materialSymbol: 'travel_explore',
|
materialSymbol: 'travel_explore',
|
||||||
execute: () => {
|
execute: () => {
|
||||||
let url = ConfigOptions.search.engineBaseUrl + root.searchingText
|
let url = ConfigOptions.search.engineBaseUrl + root.searchingText
|
||||||
@@ -361,22 +371,9 @@ Item { // Wrapper
|
|||||||
}
|
}
|
||||||
delegate: SearchItem {
|
delegate: SearchItem {
|
||||||
entry: modelData
|
entry: modelData
|
||||||
// itemName: modelData.name
|
|
||||||
// itemIcon: modelData.icon
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DropShadow {
|
|
||||||
id: searchWidgetShadow
|
|
||||||
anchors.fill: searchWidgetContent
|
|
||||||
source: searchWidgetContent
|
|
||||||
radius: Appearance.sizes.elevationMargin
|
|
||||||
samples: radius * 2 + 1
|
|
||||||
color: Appearance.colors.colShadow
|
|
||||||
verticalOffset: 2
|
|
||||||
horizontalOffset: 0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,201 +1,193 @@
|
|||||||
import "root:/modules/common"
|
import "root:/modules/common"
|
||||||
import "root:/modules/common/widgets"
|
import "root:/modules/common/widgets"
|
||||||
|
import "root:/modules/common/functions/color_utils.js" as ColorUtils
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Hyprland
|
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
import Quickshell.Wayland
|
|
||||||
import Quickshell.Widgets
|
import Quickshell.Widgets
|
||||||
|
import Quickshell.Wayland
|
||||||
|
import Quickshell.Hyprland
|
||||||
|
|
||||||
Scope {
|
Scope {
|
||||||
id: root
|
id: root
|
||||||
readonly property Toplevel activeWindow: ToplevelManager.activeToplevel
|
property var focusedScreen: Quickshell.screens.find(s => s.name === Hyprland.focusedMonitor?.name)
|
||||||
|
|
||||||
Variants {
|
Loader {
|
||||||
id: sessionVariants
|
id: sessionLoader
|
||||||
model: Quickshell.screens
|
active: false
|
||||||
|
|
||||||
Loader {
|
PanelWindow { // Session menu
|
||||||
id: sessionLoader
|
id: sessionRoot
|
||||||
property var modelData
|
visible: sessionLoader.active
|
||||||
active: false
|
property string subtitle
|
||||||
|
|
||||||
PanelWindow { // Session menu
|
|
||||||
id: sessionRoot
|
|
||||||
visible: sessionLoader.active
|
|
||||||
|
|
||||||
function hide() {
|
|
||||||
sessionLoader.active = false
|
|
||||||
}
|
|
||||||
|
|
||||||
property string subtitle
|
|
||||||
|
|
||||||
screen: modelData
|
|
||||||
exclusionMode: ExclusionMode.Ignore
|
|
||||||
WlrLayershell.namespace: "quickshell:session"
|
|
||||||
WlrLayershell.layer: WlrLayer.Overlay
|
|
||||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
|
|
||||||
color: Appearance.transparentize(Appearance.m3colors.m3background, 0.3)
|
|
||||||
|
|
||||||
anchors {
|
|
||||||
top: true
|
|
||||||
left: true
|
|
||||||
right: true
|
|
||||||
}
|
|
||||||
|
|
||||||
implicitWidth: modelData.width
|
|
||||||
implicitHeight: modelData.height
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: sessionMouseArea
|
|
||||||
anchors.fill: parent
|
|
||||||
onClicked: {
|
|
||||||
sessionRoot.hide()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout { // Content column
|
|
||||||
anchors.centerIn: parent
|
|
||||||
spacing: 15
|
|
||||||
|
|
||||||
Keys.onPressed: (event) => {
|
|
||||||
if (event.key === Qt.Key_Escape) {
|
|
||||||
sessionRoot.hide();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
spacing: 0
|
|
||||||
StyledText { // Title
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
font.family: Appearance.font.family.title
|
|
||||||
font.pixelSize: Appearance.font.pixelSize.title
|
|
||||||
font.weight: Font.DemiBold
|
|
||||||
text: qsTr("Session")
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText { // Small instruction
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
font.family: Appearance.font.family.title
|
|
||||||
font.pixelSize: Appearance.font.pixelSize.normal
|
|
||||||
text: qsTr("Arrow keys to navigate, Enter to select\nEsc or click anywhere to cancel")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout { // First row of buttons
|
|
||||||
spacing: 15
|
|
||||||
SessionActionButton {
|
|
||||||
id: sessionLock
|
|
||||||
focus: sessionRoot.visible
|
|
||||||
buttonIcon: "lock"
|
|
||||||
buttonText: qsTr("Lock")
|
|
||||||
onClicked: { Hyprland.dispatch("exec loginctl lock-session"); sessionRoot.hide() }
|
|
||||||
onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText }
|
|
||||||
KeyNavigation.right: sessionSleep
|
|
||||||
KeyNavigation.down: sessionHibernate
|
|
||||||
}
|
|
||||||
SessionActionButton {
|
|
||||||
id: sessionSleep
|
|
||||||
buttonIcon: "dark_mode"
|
|
||||||
buttonText: qsTr("Sleep")
|
|
||||||
onClicked: { Hyprland.dispatch("exec systemctl suspend || loginctl suspend"); sessionRoot.hide() }
|
|
||||||
onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText }
|
|
||||||
KeyNavigation.left: sessionLock
|
|
||||||
KeyNavigation.right: sessionLogout
|
|
||||||
KeyNavigation.down: sessionShutdown
|
|
||||||
}
|
|
||||||
SessionActionButton {
|
|
||||||
id: sessionLogout
|
|
||||||
buttonIcon: "logout"
|
|
||||||
buttonText: qsTr("Logout")
|
|
||||||
onClicked: { Hyprland.dispatch("exec pkill Hyprland"); sessionRoot.hide() }
|
|
||||||
onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText }
|
|
||||||
KeyNavigation.left: sessionSleep
|
|
||||||
KeyNavigation.right: sessionTaskManager
|
|
||||||
KeyNavigation.down: sessionReboot
|
|
||||||
}
|
|
||||||
SessionActionButton {
|
|
||||||
id: sessionTaskManager
|
|
||||||
buttonIcon: "browse_activity"
|
|
||||||
buttonText: qsTr("Task Manager")
|
|
||||||
onClicked: { Hyprland.dispatch("exec gnome-system-monitor & disown"); sessionRoot.hide() }
|
|
||||||
onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText }
|
|
||||||
KeyNavigation.left: sessionLogout
|
|
||||||
KeyNavigation.down: sessionFirmwareReboot
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout { // Second row of buttons
|
|
||||||
spacing: 15
|
|
||||||
SessionActionButton {
|
|
||||||
id: sessionHibernate
|
|
||||||
buttonIcon: "downloading"
|
|
||||||
buttonText: qsTr("Hibernate")
|
|
||||||
onClicked: { Hyprland.dispatch("exec systemctl hibernate || loginctl hibernate"); sessionRoot.hide() }
|
|
||||||
onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText }
|
|
||||||
KeyNavigation.up: sessionLock
|
|
||||||
KeyNavigation.right: sessionShutdown
|
|
||||||
}
|
|
||||||
SessionActionButton {
|
|
||||||
id: sessionShutdown
|
|
||||||
buttonIcon: "power_settings_new"
|
|
||||||
buttonText: qsTr("Shutdown")
|
|
||||||
onClicked: { Hyprland.dispatch("exec systemctl poweroff || loginctl poweroff"); sessionRoot.hide() }
|
|
||||||
onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText }
|
|
||||||
KeyNavigation.left: sessionHibernate
|
|
||||||
KeyNavigation.right: sessionReboot
|
|
||||||
KeyNavigation.up: sessionSleep
|
|
||||||
}
|
|
||||||
SessionActionButton {
|
|
||||||
id: sessionReboot
|
|
||||||
buttonIcon: "restart_alt"
|
|
||||||
buttonText: qsTr("Reboot")
|
|
||||||
onClicked: { Hyprland.dispatch("exec reboot || loginctl reboot"); sessionRoot.hide() }
|
|
||||||
onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText }
|
|
||||||
KeyNavigation.left: sessionShutdown
|
|
||||||
KeyNavigation.right: sessionFirmwareReboot
|
|
||||||
KeyNavigation.up: sessionLogout
|
|
||||||
}
|
|
||||||
SessionActionButton {
|
|
||||||
id: sessionFirmwareReboot
|
|
||||||
buttonIcon: "settings_applications"
|
|
||||||
buttonText: qsTr("Reboot to firmware settings")
|
|
||||||
onClicked: { Hyprland.dispatch("exec systemctl reboot --firmware-setup || loginctl reboot --firmware-setup"); sessionRoot.hide() }
|
|
||||||
onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText }
|
|
||||||
KeyNavigation.up: sessionTaskManager
|
|
||||||
KeyNavigation.left: sessionReboot
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
radius: Appearance.rounding.normal
|
|
||||||
implicitHeight: sessionSubtitle.implicitHeight + 10 * 2
|
|
||||||
implicitWidth: sessionSubtitle.implicitWidth + 15 * 2
|
|
||||||
color: Appearance.colors.colTooltip
|
|
||||||
clip: true
|
|
||||||
|
|
||||||
Behavior on implicitWidth {
|
|
||||||
SmoothedAnimation {
|
|
||||||
velocity: Appearance.animation.elementMoveFast.velocity
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
id: sessionSubtitle
|
|
||||||
anchors.centerIn: parent
|
|
||||||
color: Appearance.colors.colOnTooltip
|
|
||||||
text: sessionRoot.subtitle
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
function hide() {
|
||||||
|
sessionLoader.active = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
exclusionMode: ExclusionMode.Ignore
|
||||||
|
WlrLayershell.namespace: "quickshell:session"
|
||||||
|
WlrLayershell.layer: WlrLayer.Overlay
|
||||||
|
WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
|
||||||
|
color: ColorUtils.transparentize(Appearance.m3colors.m3background, 0.3)
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: true
|
||||||
|
left: true
|
||||||
|
right: true
|
||||||
|
}
|
||||||
|
|
||||||
|
implicitWidth: root.focusedScreen?.width ?? 0
|
||||||
|
implicitHeight: root.focusedScreen?.height ?? 0
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: sessionMouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: {
|
||||||
|
sessionRoot.hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout { // Content column
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: 15
|
||||||
|
|
||||||
|
Keys.onPressed: (event) => {
|
||||||
|
if (event.key === Qt.Key_Escape) {
|
||||||
|
sessionRoot.hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
spacing: 0
|
||||||
|
StyledText { // Title
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
font.family: Appearance.font.family.title
|
||||||
|
font.pixelSize: Appearance.font.pixelSize.title
|
||||||
|
font.weight: Font.DemiBold
|
||||||
|
text: qsTr("Session")
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText { // Small instruction
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
font.family: Appearance.font.family.title
|
||||||
|
font.pixelSize: Appearance.font.pixelSize.normal
|
||||||
|
text: qsTr("Arrow keys to navigate, Enter to select\nEsc or click anywhere to cancel")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout { // First row of buttons
|
||||||
|
spacing: 15
|
||||||
|
SessionActionButton {
|
||||||
|
id: sessionLock
|
||||||
|
focus: sessionRoot.visible
|
||||||
|
buttonIcon: "lock"
|
||||||
|
buttonText: qsTr("Lock")
|
||||||
|
onClicked: { Hyprland.dispatch("exec loginctl lock-session"); sessionRoot.hide() }
|
||||||
|
onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText }
|
||||||
|
KeyNavigation.right: sessionSleep
|
||||||
|
KeyNavigation.down: sessionHibernate
|
||||||
|
}
|
||||||
|
SessionActionButton {
|
||||||
|
id: sessionSleep
|
||||||
|
buttonIcon: "dark_mode"
|
||||||
|
buttonText: qsTr("Sleep")
|
||||||
|
onClicked: { Hyprland.dispatch("exec systemctl suspend || loginctl suspend"); sessionRoot.hide() }
|
||||||
|
onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText }
|
||||||
|
KeyNavigation.left: sessionLock
|
||||||
|
KeyNavigation.right: sessionLogout
|
||||||
|
KeyNavigation.down: sessionShutdown
|
||||||
|
}
|
||||||
|
SessionActionButton {
|
||||||
|
id: sessionLogout
|
||||||
|
buttonIcon: "logout"
|
||||||
|
buttonText: qsTr("Logout")
|
||||||
|
onClicked: { Hyprland.dispatch("exec pkill Hyprland"); sessionRoot.hide() }
|
||||||
|
onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText }
|
||||||
|
KeyNavigation.left: sessionSleep
|
||||||
|
KeyNavigation.right: sessionTaskManager
|
||||||
|
KeyNavigation.down: sessionReboot
|
||||||
|
}
|
||||||
|
SessionActionButton {
|
||||||
|
id: sessionTaskManager
|
||||||
|
buttonIcon: "browse_activity"
|
||||||
|
buttonText: qsTr("Task Manager")
|
||||||
|
onClicked: { Hyprland.dispatch("exec gnome-system-monitor & disown"); sessionRoot.hide() }
|
||||||
|
onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText }
|
||||||
|
KeyNavigation.left: sessionLogout
|
||||||
|
KeyNavigation.down: sessionFirmwareReboot
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout { // Second row of buttons
|
||||||
|
spacing: 15
|
||||||
|
SessionActionButton {
|
||||||
|
id: sessionHibernate
|
||||||
|
buttonIcon: "downloading"
|
||||||
|
buttonText: qsTr("Hibernate")
|
||||||
|
onClicked: { Hyprland.dispatch("exec systemctl hibernate || loginctl hibernate"); sessionRoot.hide() }
|
||||||
|
onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText }
|
||||||
|
KeyNavigation.up: sessionLock
|
||||||
|
KeyNavigation.right: sessionShutdown
|
||||||
|
}
|
||||||
|
SessionActionButton {
|
||||||
|
id: sessionShutdown
|
||||||
|
buttonIcon: "power_settings_new"
|
||||||
|
buttonText: qsTr("Shutdown")
|
||||||
|
onClicked: { Hyprland.dispatch("exec systemctl poweroff || loginctl poweroff"); sessionRoot.hide() }
|
||||||
|
onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText }
|
||||||
|
KeyNavigation.left: sessionHibernate
|
||||||
|
KeyNavigation.right: sessionReboot
|
||||||
|
KeyNavigation.up: sessionSleep
|
||||||
|
}
|
||||||
|
SessionActionButton {
|
||||||
|
id: sessionReboot
|
||||||
|
buttonIcon: "restart_alt"
|
||||||
|
buttonText: qsTr("Reboot")
|
||||||
|
onClicked: { Hyprland.dispatch("exec reboot || loginctl reboot"); sessionRoot.hide() }
|
||||||
|
onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText }
|
||||||
|
KeyNavigation.left: sessionShutdown
|
||||||
|
KeyNavigation.right: sessionFirmwareReboot
|
||||||
|
KeyNavigation.up: sessionLogout
|
||||||
|
}
|
||||||
|
SessionActionButton {
|
||||||
|
id: sessionFirmwareReboot
|
||||||
|
buttonIcon: "settings_applications"
|
||||||
|
buttonText: qsTr("Reboot to firmware settings")
|
||||||
|
onClicked: { Hyprland.dispatch("exec systemctl reboot --firmware-setup || loginctl reboot --firmware-setup"); sessionRoot.hide() }
|
||||||
|
onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText }
|
||||||
|
KeyNavigation.up: sessionTaskManager
|
||||||
|
KeyNavigation.left: sessionReboot
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
radius: Appearance.rounding.normal
|
||||||
|
implicitHeight: sessionSubtitle.implicitHeight + 10 * 2
|
||||||
|
implicitWidth: sessionSubtitle.implicitWidth + 15 * 2
|
||||||
|
color: Appearance.colors.colTooltip
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
Behavior on implicitWidth {
|
||||||
|
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: sessionSubtitle
|
||||||
|
anchors.centerIn: parent
|
||||||
|
color: Appearance.colors.colOnTooltip
|
||||||
|
text: sessionRoot.subtitle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,48 +195,33 @@ Scope {
|
|||||||
target: "session"
|
target: "session"
|
||||||
|
|
||||||
function toggle(): void {
|
function toggle(): void {
|
||||||
for (let i = 0; i < sessionVariants.instances.length; i++) {
|
sessionLoader.active = !sessionLoader.active;
|
||||||
let loader = sessionVariants.instances[i];
|
|
||||||
loader.active = !loader.active;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function close(): void {
|
function close(): void {
|
||||||
for (let i = 0; i < sessionVariants.instances.length; i++) {
|
sessionLoader.active = false;
|
||||||
let loader = sessionVariants.instances[i];
|
|
||||||
loader.active = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function open(): void {
|
function open(): void {
|
||||||
for (let i = 0; i < sessionVariants.instances.length; i++) {
|
sessionLoader.active = true;
|
||||||
let loader = sessionVariants.instances[i];
|
|
||||||
loader.active = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GlobalShortcut {
|
GlobalShortcut {
|
||||||
name: "sessionToggle"
|
name: "sessionToggle"
|
||||||
description: "Toggles session screen on press"
|
description: qsTr("Toggles session screen on press")
|
||||||
|
|
||||||
onPressed: {
|
onPressed: {
|
||||||
for (let i = 0; i < sessionVariants.instances.length; i++) {
|
sessionLoader.active = !sessionLoader.active;
|
||||||
let loader = sessionVariants.instances[i];
|
|
||||||
loader.active = !loader.active;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GlobalShortcut {
|
GlobalShortcut {
|
||||||
name: "sessionOpen"
|
name: "sessionOpen"
|
||||||
description: "Opens session screen on press"
|
description: qsTr("Opens session screen on press")
|
||||||
|
|
||||||
onPressed: {
|
onPressed: {
|
||||||
for (let i = 0; i < sessionVariants.instances.length; i++) {
|
sessionLoader.active = true;
|
||||||
let loader = sessionVariants.instances[i];
|
|
||||||
loader.active = !loader.active;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,12 +37,7 @@ Button {
|
|||||||
color: (button.down || button.keyboardDown) ? Appearance.colors.colLayer2Active : ((button.hovered || button.focus) ? Appearance.colors.colLayer2Hover : Appearance.colors.colLayer2)
|
color: (button.down || button.keyboardDown) ? Appearance.colors.colLayer2Active : ((button.hovered || button.focus) ? Appearance.colors.colLayer2Hover : Appearance.colors.colLayer2)
|
||||||
|
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
ColorAnimation {
|
animation: Appearance.animation.elementMove.colorAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMove.duration
|
|
||||||
easing.type: Appearance.animation.elementMove.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ Item {
|
|||||||
id: root
|
id: root
|
||||||
property var panelWindow
|
property var panelWindow
|
||||||
property var inputField: messageInputField
|
property var inputField: messageInputField
|
||||||
readonly property var messages: Ai.messages
|
|
||||||
property string commandPrefix: "/"
|
property string commandPrefix: "/"
|
||||||
property string faviconDownloadPath: FileUtils.trimFileProtocol(`${XdgDirectories.cache}/media/favicons`)
|
property string faviconDownloadPath: FileUtils.trimFileProtocol(`${XdgDirectories.cache}/media/favicons`)
|
||||||
|
|
||||||
@@ -31,7 +30,7 @@ Item {
|
|||||||
|
|
||||||
onFocusChanged: (focus) => {
|
onFocusChanged: (focus) => {
|
||||||
if (focus) {
|
if (focus) {
|
||||||
messageInputField.forceActiveFocus()
|
root.inputField.forceActiveFocus()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,46 +178,42 @@ int main(int argc, char* argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
add: Transition {
|
add: Transition {
|
||||||
NumberAnimation {
|
animations: [Appearance.animation.elementMoveEnter.numberAnimation.createObject(this, {
|
||||||
property: "opacity"
|
property: "opacity",
|
||||||
from: 0; to: 1
|
from: 0,
|
||||||
duration: Appearance.animation.elementMoveEnter.duration
|
to: 1
|
||||||
easing.type: Appearance.animation.elementMoveEnter.type
|
})]
|
||||||
easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
remove: Transition {
|
remove: Transition {
|
||||||
NumberAnimation {
|
animations: [Appearance.animation.elementMoveEnter.numberAnimation.createObject(this, {
|
||||||
property: "opacity"
|
property: "opacity",
|
||||||
from: 1; to: 0
|
from: 1,
|
||||||
duration: Appearance.animation.elementMoveEnter.duration
|
to: 0
|
||||||
easing.type: Appearance.animation.elementMoveEnter.type
|
})]
|
||||||
easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
model: ScriptModel {
|
model: ScriptModel {
|
||||||
values: root.messages
|
values: Ai.messageIDs
|
||||||
}
|
}
|
||||||
delegate: AiMessage {
|
delegate: AiMessage {
|
||||||
|
required property var modelData
|
||||||
|
required property int index
|
||||||
messageIndex: index
|
messageIndex: index
|
||||||
messageData: modelData
|
messageData: {
|
||||||
|
Ai.messageByID[modelData]
|
||||||
|
}
|
||||||
messageInputField: root.inputField
|
messageInputField: root.inputField
|
||||||
faviconDownloadPath: root.faviconDownloadPath
|
faviconDownloadPath: root.faviconDownloadPath
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item { // Placeholder when list is empty
|
Item { // Placeholder when list is empty
|
||||||
opacity: root.messages.length === 0 ? 1 : 0
|
opacity: Ai.messageIDs.length === 0 ? 1 : 0
|
||||||
visible: opacity > 0
|
visible: opacity > 0
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
NumberAnimation {
|
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMove.duration
|
|
||||||
easing.type: Appearance.animation.elementMove.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
@@ -356,11 +351,7 @@ int main(int argc, char* argv[]) {
|
|||||||
border.width: 1
|
border.width: 1
|
||||||
|
|
||||||
Behavior on implicitHeight {
|
Behavior on implicitHeight {
|
||||||
NumberAnimation {
|
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMove.duration
|
|
||||||
easing.type: Appearance.animation.elementMove.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout { // Input field and send button
|
RowLayout { // Input field and send button
|
||||||
@@ -383,7 +374,7 @@ int main(int argc, char* argv[]) {
|
|||||||
placeholderText: StringUtils.format(qsTr('Message the model... "{0}" for commands'), root.commandPrefix)
|
placeholderText: StringUtils.format(qsTr('Message the model... "{0}" for commands'), root.commandPrefix)
|
||||||
placeholderTextColor: Appearance.m3colors.m3outline
|
placeholderTextColor: Appearance.m3colors.m3outline
|
||||||
|
|
||||||
background: Item {}
|
background: null
|
||||||
|
|
||||||
onTextChanged: { // Handle suggestions
|
onTextChanged: { // Handle suggestions
|
||||||
if(messageInputField.text.length === 0) {
|
if(messageInputField.text.length === 0) {
|
||||||
@@ -474,11 +465,7 @@ int main(int argc, char* argv[]) {
|
|||||||
Appearance.m3colors.m3primary) : Appearance.colors.colLayer2Disabled
|
Appearance.m3colors.m3primary) : Appearance.colors.colLayer2Disabled
|
||||||
|
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
ColorAnimation {
|
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMove.duration
|
|
||||||
easing.type: Appearance.animation.elementMove.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -566,11 +553,7 @@ int main(int argc, char* argv[]) {
|
|||||||
Appearance.colors.colLayer2
|
Appearance.colors.colLayer2
|
||||||
|
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
ColorAnimation {
|
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMove.duration
|
|
||||||
easing.type: Appearance.animation.elementMove.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onClicked: {
|
onClicked: {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import "root:/modules/common"
|
|||||||
import "root:/modules/common/widgets"
|
import "root:/modules/common/widgets"
|
||||||
import "root:/modules/common/functions/fuzzysort.js" as Fuzzy
|
import "root:/modules/common/functions/fuzzysort.js" as Fuzzy
|
||||||
import "root:/modules/common/functions/string_utils.js" as StringUtils
|
import "root:/modules/common/functions/string_utils.js" as StringUtils
|
||||||
|
import "root:/modules/common/functions/file_utils.js" as FileUtils
|
||||||
import "./anime/"
|
import "./anime/"
|
||||||
import Qt.labs.platform
|
import Qt.labs.platform
|
||||||
import QtQuick
|
import QtQuick
|
||||||
@@ -19,9 +20,9 @@ Item {
|
|||||||
property var panelWindow
|
property var panelWindow
|
||||||
property var inputField: tagInputField
|
property var inputField: tagInputField
|
||||||
readonly property var responses: Booru.responses
|
readonly property var responses: Booru.responses
|
||||||
property string previewDownloadPath: `${XdgDirectories.cache}/media/waifus`.replace("file://", "")
|
property string previewDownloadPath: FileUtils.trimFileProtocol(`${XdgDirectories.cache}/media/waifus`)
|
||||||
property string downloadPath: (XdgDirectories.pictures + "/homework").replace("file://", "")
|
property string downloadPath: FileUtils.trimFileProtocol(XdgDirectories.pictures + "/homework")
|
||||||
property string nsfwPath: (XdgDirectories.pictures + "/homework/🌶️").replace("file://", "")
|
property string nsfwPath: FileUtils.trimFileProtocol(XdgDirectories.pictures + "/homework/🌶️")
|
||||||
property string commandPrefix: "/"
|
property string commandPrefix: "/"
|
||||||
property real scrollOnNewResponse: 100
|
property real scrollOnNewResponse: 100
|
||||||
property int tagSuggestionDelay: 210
|
property int tagSuggestionDelay: 210
|
||||||
@@ -37,7 +38,8 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
Hyprland.dispatch(`exec rm -rf ${previewDownloadPath} && mkdir -p ${previewDownloadPath}`)
|
Hyprland.dispatch(`exec rm -rf '${previewDownloadPath}' && mkdir -p '${previewDownloadPath}'`)
|
||||||
|
Hyprland.dispatch(`exec mkdir -p '${downloadPath}' && mkdir -p '${downloadPath}'`)
|
||||||
}
|
}
|
||||||
|
|
||||||
property var allCommands: [
|
property var allCommands: [
|
||||||
@@ -114,12 +116,6 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: panelWindow
|
|
||||||
function onVisibleChanged(visible) {
|
|
||||||
tagInputField.forceActiveFocus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onFocusChanged: (focus) => {
|
onFocusChanged: (focus) => {
|
||||||
if (focus) {
|
if (focus) {
|
||||||
tagInputField.forceActiveFocus()
|
tagInputField.forceActiveFocus()
|
||||||
@@ -174,13 +170,11 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
add: Transition {
|
add: Transition {
|
||||||
NumberAnimation {
|
animations: [Appearance.animation.elementMoveEnter.numberAnimation.createObject(this, {
|
||||||
property: "opacity"
|
property: "opacity",
|
||||||
from: 0; to: 1
|
from: 0,
|
||||||
duration: Appearance.animation.elementMoveEnter.duration
|
to: 1
|
||||||
easing.type: Appearance.animation.elementMoveEnter.type
|
})]
|
||||||
easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
model: ScriptModel {
|
model: ScriptModel {
|
||||||
@@ -208,11 +202,7 @@ Item {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
NumberAnimation {
|
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMove.duration
|
|
||||||
easing.type: Appearance.animation.elementMove.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
@@ -246,11 +236,7 @@ Item {
|
|||||||
visible: opacity > 0
|
visible: opacity > 0
|
||||||
|
|
||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
NumberAnimation {
|
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMove.duration
|
|
||||||
easing.type: Appearance.animation.elementMove.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -392,11 +378,7 @@ Item {
|
|||||||
border.width: 1
|
border.width: 1
|
||||||
|
|
||||||
Behavior on implicitHeight {
|
Behavior on implicitHeight {
|
||||||
NumberAnimation {
|
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMove.duration
|
|
||||||
easing.type: Appearance.animation.elementMove.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout { // Input field and send button
|
RowLayout { // Input field and send button
|
||||||
@@ -419,7 +401,7 @@ Item {
|
|||||||
placeholderText: StringUtils.format(qsTr('Enter tags, or "{0}" for commands'), root.commandPrefix)
|
placeholderText: StringUtils.format(qsTr('Enter tags, or "{0}" for commands'), root.commandPrefix)
|
||||||
placeholderTextColor: Appearance.m3colors.m3outline
|
placeholderTextColor: Appearance.m3colors.m3outline
|
||||||
|
|
||||||
background: Item {}
|
background: null
|
||||||
|
|
||||||
property Timer searchTimer: Timer { // Timer for tag suggestions
|
property Timer searchTimer: Timer { // Timer for tag suggestions
|
||||||
interval: root.tagSuggestionDelay
|
interval: root.tagSuggestionDelay
|
||||||
@@ -530,11 +512,7 @@ Item {
|
|||||||
Appearance.m3colors.m3primary) : Appearance.colors.colLayer2Disabled
|
Appearance.m3colors.m3primary) : Appearance.colors.colLayer2Disabled
|
||||||
|
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
ColorAnimation {
|
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMove.duration
|
|
||||||
easing.type: Appearance.animation.elementMove.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -666,11 +644,7 @@ Item {
|
|||||||
Appearance.colors.colLayer2
|
Appearance.colors.colLayer2
|
||||||
|
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
ColorAnimation {
|
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMove.duration
|
|
||||||
easing.type: Appearance.animation.elementMove.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onClicked: {
|
onClicked: {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import "root:/modules/common/widgets"
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
|
import QtQuick.Effects
|
||||||
import Qt5Compat.GraphicalEffects
|
import Qt5Compat.GraphicalEffects
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
import Quickshell
|
import Quickshell
|
||||||
@@ -17,30 +18,31 @@ Scope { // Scope
|
|||||||
property int sidebarPadding: 15
|
property int sidebarPadding: 15
|
||||||
property var tabButtonList: [{"icon": "neurology", "name": qsTr("Intelligence")}, {"icon": "bookmark_heart", "name": qsTr("Anime")}]
|
property var tabButtonList: [{"icon": "neurology", "name": qsTr("Intelligence")}, {"icon": "bookmark_heart", "name": qsTr("Anime")}]
|
||||||
|
|
||||||
Variants { // Window repeater
|
Loader {
|
||||||
id: sidebarVariants
|
id: sidebarLoader
|
||||||
model: Quickshell.screens
|
active: false
|
||||||
|
onActiveChanged: {
|
||||||
|
GlobalStates.sidebarLeftOpen = sidebarLoader.active
|
||||||
|
}
|
||||||
|
|
||||||
PanelWindow { // Window
|
PanelWindow { // Window
|
||||||
id: sidebarRoot
|
id: sidebarRoot
|
||||||
visible: false
|
visible: sidebarLoader.active
|
||||||
focusable: true
|
|
||||||
property int selectedTab: PersistentStates.sidebar.leftSide.selectedTab
|
property int selectedTab: 0
|
||||||
property bool extend: false
|
property bool extend: false
|
||||||
|
property bool pin: false
|
||||||
property real sidebarWidth: sidebarRoot.extend ? Appearance.sizes.sidebarWidthExtended : Appearance.sizes.sidebarWidth
|
property real sidebarWidth: sidebarRoot.extend ? Appearance.sizes.sidebarWidthExtended : Appearance.sizes.sidebarWidth
|
||||||
|
|
||||||
onVisibleChanged: {
|
function hide() {
|
||||||
GlobalStates.sidebarLeftOpenCount += visible ? 1 : -1
|
sidebarLoader.active = false
|
||||||
}
|
}
|
||||||
|
|
||||||
property var modelData
|
exclusiveZone: sidebarRoot.pin ? sidebarWidth : 0
|
||||||
|
|
||||||
screen: modelData
|
|
||||||
exclusiveZone: 0
|
|
||||||
implicitWidth: Appearance.sizes.sidebarWidthExtended
|
implicitWidth: Appearance.sizes.sidebarWidthExtended
|
||||||
WlrLayershell.namespace: "quickshell:sidebarLeft"
|
WlrLayershell.namespace: "quickshell:sidebarLeft"
|
||||||
// Hyprland 0.49: Focus is always exclusive and setting this breaks mouse focus grab
|
// Hyprland 0.49: OnDemand is Exclusive, Exclusive just breaks click-outside-to-close
|
||||||
// WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
|
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
@@ -56,26 +58,12 @@ Scope { // Scope
|
|||||||
HyprlandFocusGrab { // Click outside to close
|
HyprlandFocusGrab { // Click outside to close
|
||||||
id: grab
|
id: grab
|
||||||
windows: [ sidebarRoot ]
|
windows: [ sidebarRoot ]
|
||||||
active: false
|
active: sidebarRoot.visible && !sidebarRoot.pin
|
||||||
|
onActiveChanged: { // Focus the selected tab
|
||||||
|
if (active) swipeView.currentItem.forceActiveFocus()
|
||||||
|
}
|
||||||
onCleared: () => {
|
onCleared: () => {
|
||||||
if (!active) sidebarRoot.visible = false
|
if (!active) sidebarRoot.hide()
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: sidebarRoot
|
|
||||||
function onVisibleChanged() {
|
|
||||||
delayedGrabTimer.start()
|
|
||||||
swipeView.children[0].children[0].children[sidebarRoot.selectedTab].forceActiveFocus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: delayedGrabTimer
|
|
||||||
interval: ConfigOptions.hacks.arbitraryRaceConditionDelay
|
|
||||||
repeat: false
|
|
||||||
onTriggered: {
|
|
||||||
grab.active = sidebarRoot.visible
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,41 +75,50 @@ Scope { // Scope
|
|||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.topMargin: Appearance.sizes.hyprlandGapsOut
|
anchors.topMargin: Appearance.sizes.hyprlandGapsOut
|
||||||
anchors.leftMargin: Appearance.sizes.hyprlandGapsOut
|
anchors.leftMargin: Appearance.sizes.hyprlandGapsOut
|
||||||
width: sidebarWidth - Appearance.sizes.hyprlandGapsOut * 2
|
width: sidebarRoot.sidebarWidth - Appearance.sizes.hyprlandGapsOut * 2
|
||||||
height: parent.height - Appearance.sizes.hyprlandGapsOut * 2
|
height: parent.height - Appearance.sizes.hyprlandGapsOut * 2
|
||||||
color: Appearance.colors.colLayer0
|
color: Appearance.colors.colLayer0
|
||||||
radius: Appearance.rounding.screenRounding - Appearance.sizes.elevationMargin + 1
|
radius: Appearance.rounding.screenRounding - Appearance.sizes.elevationMargin + 1
|
||||||
focus: sidebarRoot.visible
|
focus: sidebarRoot.visible
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: MultiEffect {
|
||||||
|
source: sidebarLeftBackground
|
||||||
|
anchors.fill: sidebarLeftBackground
|
||||||
|
shadowEnabled: true
|
||||||
|
shadowColor: Appearance.colors.colShadow
|
||||||
|
shadowVerticalOffset: 1
|
||||||
|
shadowBlur: 0.5
|
||||||
|
}
|
||||||
|
|
||||||
Behavior on width {
|
Behavior on width {
|
||||||
NumberAnimation {
|
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMove.duration
|
|
||||||
easing.type: Appearance.animation.elementMove.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Keys.onPressed: (event) => {
|
Keys.onPressed: (event) => {
|
||||||
// console.log("Key pressed: " + event.key)
|
// console.log("Key pressed: " + event.key)
|
||||||
if (event.key === Qt.Key_Escape) {
|
if (event.key === Qt.Key_Escape) {
|
||||||
sidebarRoot.visible = false;
|
sidebarRoot.hide();
|
||||||
}
|
}
|
||||||
if (event.modifiers === Qt.ControlModifier) {
|
if (event.modifiers === Qt.ControlModifier) {
|
||||||
if (event.key === Qt.Key_PageDown) {
|
if (event.key === Qt.Key_PageDown) {
|
||||||
PersistentStateManager.setState("sidebar.leftSide.selectedTab", Math.min(sidebarRoot.selectedTab + 1, root.tabButtonList.length - 1))
|
sidebarRoot.selectedTab = Math.min(sidebarRoot.selectedTab + 1, root.tabButtonList.length - 1)
|
||||||
}
|
}
|
||||||
else if (event.key === Qt.Key_PageUp) {
|
else if (event.key === Qt.Key_PageUp) {
|
||||||
PersistentStateManager.setState("sidebar.leftSide.selectedTab", Math.max(sidebarRoot.selectedTab - 1, 0))
|
sidebarRoot.selectedTab = Math.max(sidebarRoot.selectedTab - 1, 0)
|
||||||
}
|
}
|
||||||
else if (event.key === Qt.Key_Tab) {
|
else if (event.key === Qt.Key_Tab) {
|
||||||
PersistentStateManager.setState("sidebar.leftSide.selectedTab", (sidebarRoot.selectedTab + 1) % root.tabButtonList.length);
|
sidebarRoot.selectedTab = (sidebarRoot.selectedTab + 1) % root.tabButtonList.length;
|
||||||
}
|
}
|
||||||
else if (event.key === Qt.Key_Backtab) {
|
else if (event.key === Qt.Key_Backtab) {
|
||||||
PersistentStateManager.setState("sidebar.leftSide.selectedTab", (sidebarRoot.selectedTab - 1 + root.tabButtonList.length) % root.tabButtonList.length);
|
sidebarRoot.selectedTab = (sidebarRoot.selectedTab - 1 + root.tabButtonList.length) % root.tabButtonList.length;
|
||||||
}
|
}
|
||||||
else if (event.key === Qt.Key_O) {
|
else if (event.key === Qt.Key_O) {
|
||||||
sidebarRoot.extend = !sidebarRoot.extend;
|
sidebarRoot.extend = !sidebarRoot.extend;
|
||||||
}
|
}
|
||||||
|
else if (event.key === Qt.Key_P) {
|
||||||
|
sidebarRoot.pin = !sidebarRoot.pin;
|
||||||
|
}
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -137,7 +134,7 @@ Scope { // Scope
|
|||||||
tabButtonList: root.tabButtonList
|
tabButtonList: root.tabButtonList
|
||||||
externalTrackedTab: sidebarRoot.selectedTab
|
externalTrackedTab: sidebarRoot.selectedTab
|
||||||
function onCurrentIndexChanged(currentIndex) {
|
function onCurrentIndexChanged(currentIndex) {
|
||||||
PersistentStateManager.setState("sidebar.leftSide.selectedTab", currentIndex)
|
sidebarRoot.selectedTab = currentIndex
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,10 +143,12 @@ Scope { // Scope
|
|||||||
Layout.topMargin: 5
|
Layout.topMargin: 5
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
currentIndex: sidebarRoot.selectedTab
|
spacing: 10
|
||||||
|
|
||||||
|
currentIndex: tabBar.externalTrackedTab
|
||||||
onCurrentIndexChanged: {
|
onCurrentIndexChanged: {
|
||||||
tabBar.enableIndicatorAnimation = true
|
tabBar.enableIndicatorAnimation = true
|
||||||
PersistentStateManager.setState("sidebar.leftSide.selectedTab", currentIndex)
|
sidebarRoot.selectedTab = currentIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
clip: true
|
clip: true
|
||||||
@@ -173,95 +172,53 @@ Scope { // Scope
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shadow
|
|
||||||
DropShadow {
|
|
||||||
anchors.fill: sidebarLeftBackground
|
|
||||||
horizontalOffset: 0
|
|
||||||
verticalOffset: 2
|
|
||||||
radius: Appearance.sizes.elevationMargin
|
|
||||||
samples: Appearance.sizes.elevationMargin * 2 + 1 // Ideally should be 2 * radius + 1, see qt docs
|
|
||||||
color: Appearance.colors.colShadow
|
|
||||||
source: sidebarLeftBackground
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IpcHandler {
|
IpcHandler {
|
||||||
target: "sidebarLeft"
|
target: "sidebarLeft"
|
||||||
|
|
||||||
function toggle(): void {
|
function toggle(): void {
|
||||||
for (let i = 0; i < sidebarVariants.instances.length; i++) {
|
sidebarLoader.active = !sidebarLoader.active
|
||||||
let panelWindow = sidebarVariants.instances[i];
|
if(sidebarLoader.active) Notifications.timeoutAll();
|
||||||
if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
|
|
||||||
panelWindow.visible = !panelWindow.visible;
|
|
||||||
if(panelWindow.visible) Notifications.timeoutAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function close(): void {
|
function close(): void {
|
||||||
for (let i = 0; i < sidebarVariants.instances.length; i++) {
|
sidebarLoader.active = false
|
||||||
let panelWindow = sidebarVariants.instances[i];
|
|
||||||
if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
|
|
||||||
panelWindow.visible = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function open(): void {
|
function open(): void {
|
||||||
for (let i = 0; i < sidebarVariants.instances.length; i++) {
|
sidebarLoader.active = true
|
||||||
let panelWindow = sidebarVariants.instances[i];
|
if(sidebarLoader.active) Notifications.timeoutAll();
|
||||||
if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
|
|
||||||
panelWindow.visible = true;
|
|
||||||
if(panelWindow.visible) Notifications.timeoutAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GlobalShortcut {
|
GlobalShortcut {
|
||||||
name: "sidebarLeftToggle"
|
name: "sidebarLeftToggle"
|
||||||
description: "Toggles left sidebar on press"
|
description: qsTr("Toggles left sidebar on press")
|
||||||
|
|
||||||
onPressed: {
|
onPressed: {
|
||||||
for (let i = 0; i < sidebarVariants.instances.length; i++) {
|
sidebarLoader.active = !sidebarLoader.active;
|
||||||
let panelWindow = sidebarVariants.instances[i];
|
if(sidebarLoader.active) Notifications.timeoutAll();
|
||||||
if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
|
|
||||||
panelWindow.visible = !panelWindow.visible;
|
|
||||||
if(panelWindow.visible) Notifications.timeoutAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GlobalShortcut {
|
GlobalShortcut {
|
||||||
name: "sidebarLeftOpen"
|
name: "sidebarLeftOpen"
|
||||||
description: "Opens left sidebar on press"
|
description: qsTr("Opens left sidebar on press")
|
||||||
|
|
||||||
onPressed: {
|
onPressed: {
|
||||||
for (let i = 0; i < sidebarVariants.instances.length; i++) {
|
sidebarLoader.active = true;
|
||||||
let panelWindow = sidebarVariants.instances[i];
|
if(sidebarLoader.active) Notifications.timeoutAll();
|
||||||
if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
|
|
||||||
panelWindow.visible = true;
|
|
||||||
if(panelWindow.visible) Notifications.timeoutAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GlobalShortcut {
|
GlobalShortcut {
|
||||||
name: "sidebarLeftClose"
|
name: "sidebarLeftClose"
|
||||||
description: "Closes left sidebar on press"
|
description: qsTr("Closes left sidebar on press")
|
||||||
|
|
||||||
onPressed: {
|
onPressed: {
|
||||||
for (let i = 0; i < sidebarVariants.instances.length; i++) {
|
sidebarLoader.active = false;
|
||||||
let panelWindow = sidebarVariants.instances[i];
|
|
||||||
if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
|
|
||||||
panelWindow.visible = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ Rectangle {
|
|||||||
color: Appearance.m3colors.m3onSecondaryContainer
|
color: Appearance.m3colors.m3onSecondaryContainer
|
||||||
text: messageData.role == 'assistant' ? Ai.models[messageData.model].name :
|
text: messageData.role == 'assistant' ? Ai.models[messageData.model].name :
|
||||||
(messageData.role == 'user' && SystemInfo.username) ? SystemInfo.username :
|
(messageData.role == 'user' && SystemInfo.username) ? SystemInfo.username :
|
||||||
(messageData.role == 'interface') ? qsTr("Interface") : qsTr("Unknown")
|
qsTr("Interface")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import "root:/modules/common"
|
import "root:/modules/common"
|
||||||
import "root:/modules/common/widgets"
|
import "root:/modules/common/widgets"
|
||||||
import "root:/services"
|
import "root:/services"
|
||||||
|
import "root:/modules/common/functions/color_utils.js" as ColorUtils
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
@@ -18,20 +19,16 @@ Button {
|
|||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
radius: Appearance.rounding.small
|
radius: Appearance.rounding.small
|
||||||
color: !button.enabled ? Appearance.transparentize(Appearance.m3colors.m3surfaceContainerHighest, 1) :
|
color: !button.enabled ? ColorUtils.transparentize(Appearance.m3colors.m3surfaceContainerHighest, 1) :
|
||||||
button.activated ? (button.down ? Appearance.colors.colPrimaryActive :
|
button.activated ? (button.down ? Appearance.colors.colPrimaryActive :
|
||||||
button.hovered ? Appearance.colors.colPrimaryHover :
|
button.hovered ? Appearance.colors.colPrimaryHover :
|
||||||
Appearance.m3colors.m3primary) :
|
Appearance.m3colors.m3primary) :
|
||||||
(button.down ? Appearance.colors.colSurfaceContainerHighestActive :
|
(button.down ? Appearance.colors.colSurfaceContainerHighestActive :
|
||||||
button.hovered ? Appearance.colors.colSurfaceContainerHighestHover :
|
button.hovered ? Appearance.colors.colSurfaceContainerHighestHover :
|
||||||
Appearance.transparentize(Appearance.m3colors.m3surfaceContainerHighest, 1))
|
ColorUtils.transparentize(Appearance.m3colors.m3surfaceContainerHighest, 1))
|
||||||
|
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
ColorAnimation {
|
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMoveFast.duration
|
|
||||||
easing.type: Appearance.animation.elementMoveFast.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,11 +41,7 @@ Button {
|
|||||||
Appearance.colors.colOnLayer1Inactive
|
Appearance.colors.colOnLayer1Inactive
|
||||||
|
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
ColorAnimation {
|
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMoveFast.duration
|
|
||||||
easing.type: Appearance.animation.elementMoveFast.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import "root:/modules/common/"
|
|||||||
import "root:/modules/common/widgets"
|
import "root:/modules/common/widgets"
|
||||||
import "../"
|
import "../"
|
||||||
import "root:/modules/common/functions/string_utils.js" as StringUtils
|
import "root:/modules/common/functions/string_utils.js" as StringUtils
|
||||||
|
import "root:/modules/common/functions/color_utils.js" as ColorUtils
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
@@ -98,7 +99,7 @@ Item {
|
|||||||
id: thinkBlockLanguage
|
id: thinkBlockLanguage
|
||||||
Layout.fillWidth: false
|
Layout.fillWidth: false
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
text: root.completed ? "Chain of Thought" : ("Thinking" + ".".repeat(Math.random() * 4))
|
text: root.completed ? qsTr("Chain of Thought") : (qsTr("Thinking") + ".".repeat(Math.random() * 4))
|
||||||
}
|
}
|
||||||
Item { Layout.fillWidth: true }
|
Item { Layout.fillWidth: true }
|
||||||
Button { // Expand button
|
Button { // Expand button
|
||||||
@@ -117,7 +118,7 @@ Item {
|
|||||||
radius: Appearance.rounding.full
|
radius: Appearance.rounding.full
|
||||||
color: (headerMouseArea.pressed) ? Appearance.colors.colLayer2Active
|
color: (headerMouseArea.pressed) ? Appearance.colors.colLayer2Active
|
||||||
: (headerMouseArea.containsMouse ? Appearance.colors.colLayer2Hover
|
: (headerMouseArea.containsMouse ? Appearance.colors.colLayer2Hover
|
||||||
: Appearance.transparentize(Appearance.colors.colLayer2, 1))
|
: ColorUtils.transparentize(Appearance.colors.colLayer2, 1))
|
||||||
|
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
ColorAnimation {
|
ColorAnimation {
|
||||||
|
|||||||
@@ -2,15 +2,17 @@ import "root:/"
|
|||||||
import "root:/modules/common"
|
import "root:/modules/common"
|
||||||
import "root:/modules/common/widgets"
|
import "root:/modules/common/widgets"
|
||||||
import "root:/modules/common/functions/string_utils.js" as StringUtils
|
import "root:/modules/common/functions/string_utils.js" as StringUtils
|
||||||
|
import "root:/modules/common/functions/color_utils.js" as ColorUtils
|
||||||
import QtQml
|
import QtQml
|
||||||
import Qt.labs.platform
|
import Qt.labs.platform
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
|
import QtQuick.Effects
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
import Quickshell.Hyprland
|
import Quickshell.Hyprland
|
||||||
import Qt5Compat.GraphicalEffects
|
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
id: root
|
id: root
|
||||||
@@ -78,11 +80,7 @@ Button {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
NumberAnimation {
|
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMoveEnter.duration
|
|
||||||
easing.type: Appearance.animation.elementMoveEnter.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,13 +95,13 @@ Button {
|
|||||||
PointingHandInteraction {}
|
PointingHandInteraction {}
|
||||||
|
|
||||||
StyledToolTip {
|
StyledToolTip {
|
||||||
content: `${StringUtils.wordWrap(root.imageData.tags, root.maxTagStringLineLength)}\nClick for options`
|
content: `${StringUtils.wordWrap(root.imageData.tags, root.maxTagStringLineLength)}\n${qsTr("Click for options")}`
|
||||||
}
|
}
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: menuButton.down ? Appearance.transparentize(Appearance.mix(Appearance.m3colors.m3surface, Appearance.m3colors.m3onSurface, 0.6), 0.1) :
|
color: menuButton.down ? ColorUtils.transparentize(ColorUtils.mix(Appearance.m3colors.m3surface, Appearance.m3colors.m3onSurface, 0.6), 0.1) :
|
||||||
menuButton.hovered ? Appearance.transparentize(Appearance.mix(Appearance.m3colors.m3surface, Appearance.m3colors.m3onSurface, 0.8), 0.2) :
|
menuButton.hovered ? ColorUtils.transparentize(ColorUtils.mix(Appearance.m3colors.m3surface, Appearance.m3colors.m3onSurface, 0.8), 0.2) :
|
||||||
Appearance.transparentize(Appearance.m3colors.m3surface, 0.3)
|
ColorUtils.transparentize(Appearance.m3colors.m3surface, 0.3)
|
||||||
radius: Appearance.rounding.full
|
radius: Appearance.rounding.full
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,6 +138,16 @@ Button {
|
|||||||
implicitHeight: contextMenuColumnLayout.implicitHeight + radius * 2
|
implicitHeight: contextMenuColumnLayout.implicitHeight + radius * 2
|
||||||
implicitWidth: contextMenuColumnLayout.implicitWidth
|
implicitWidth: contextMenuColumnLayout.implicitWidth
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: MultiEffect {
|
||||||
|
source: contextMenu
|
||||||
|
anchors.fill: contextMenu
|
||||||
|
shadowEnabled: true
|
||||||
|
shadowColor: Appearance.colors.colShadow
|
||||||
|
shadowVerticalOffset: 1
|
||||||
|
shadowBlur: 0.5
|
||||||
|
}
|
||||||
|
|
||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: Appearance.animation.elementMoveFast.duration
|
duration: Appearance.animation.elementMoveFast.duration
|
||||||
@@ -180,7 +188,7 @@ Button {
|
|||||||
MenuButton {
|
MenuButton {
|
||||||
id: downloadButton
|
id: downloadButton
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
buttonText: "Download"
|
buttonText: qsTr("Download")
|
||||||
onClicked: {
|
onClicked: {
|
||||||
root.showActions = false
|
root.showActions = false
|
||||||
Hyprland.dispatch(`exec curl '${root.imageData.file_url}' -o '${root.imageData.is_nsfw ? root.nsfwPath : root.downloadPath}/${root.fileName}' && notify-send '${qsTr("Download complete")}' '${root.downloadPath}/${root.fileName}'`)
|
Hyprland.dispatch(`exec curl '${root.imageData.file_url}' -o '${root.imageData.is_nsfw ? root.nsfwPath : root.downloadPath}/${root.fileName}' && notify-send '${qsTr("Download complete")}' '${root.downloadPath}/${root.fileName}'`)
|
||||||
@@ -188,26 +196,6 @@ Button {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DropShadow {
|
|
||||||
opacity: root.showActions ? 1 : 0
|
|
||||||
visible: opacity > 0
|
|
||||||
anchors.fill: contextMenu
|
|
||||||
source: contextMenu
|
|
||||||
radius: Appearance.sizes.elevationMargin
|
|
||||||
samples: radius * 2 + 1
|
|
||||||
color: Appearance.colors.colShadow
|
|
||||||
verticalOffset: 2
|
|
||||||
horizontalOffset: 0
|
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Appearance.animation.elementMoveFast.duration
|
|
||||||
easing.type: Appearance.animation.elementMoveFast.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import "root:/"
|
|||||||
import "root:/services"
|
import "root:/services"
|
||||||
import "root:/modules/common"
|
import "root:/modules/common"
|
||||||
import "root:/modules/common/widgets"
|
import "root:/modules/common/widgets"
|
||||||
|
import "root:/modules/common/functions/string_utils.js" as StringUtils
|
||||||
import "../"
|
import "../"
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
@@ -93,7 +94,8 @@ Rectangle {
|
|||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
font.pixelSize: Appearance.font.pixelSize.smaller
|
font.pixelSize: Appearance.font.pixelSize.smaller
|
||||||
color: Appearance.colors.colOnLayer2
|
color: Appearance.colors.colOnLayer2
|
||||||
text: `Page ${root.responseData.page}`
|
// text: `Page ${root.responseData.page}`
|
||||||
|
text: StringUtils.format(qsTr("Page {0}"), root.responseData.page)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -120,18 +122,10 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Behavior on height {
|
Behavior on height {
|
||||||
NumberAnimation {
|
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMove.duration
|
|
||||||
easing.type: Appearance.animation.elementMove.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Behavior on implicitHeight {
|
Behavior on implicitHeight {
|
||||||
NumberAnimation {
|
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMove.duration
|
|
||||||
easing.type: Appearance.animation.elementMove.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
|
|||||||
@@ -16,27 +16,23 @@ Rectangle {
|
|||||||
radius: Appearance.rounding.normal
|
radius: Appearance.rounding.normal
|
||||||
color: Appearance.colors.colLayer1
|
color: Appearance.colors.colLayer1
|
||||||
|
|
||||||
property int selectedTab: PersistentStates.sidebar.centerGroup.selectedTab
|
property int selectedTab: 0
|
||||||
property var tabButtonList: [{"icon": "notifications", "name": qsTr("Notifications")}, {"icon": "volume_up", "name": qsTr("Volume mixer")}]
|
property var tabButtonList: [{"icon": "notifications", "name": qsTr("Notifications")}, {"icon": "volume_up", "name": qsTr("Volume mixer")}]
|
||||||
|
|
||||||
onSelectedTabChanged: {
|
|
||||||
PersistentStateManager.setState("sidebar.centerGroup.selectedTab", selectedTab)
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys.onPressed: (event) => {
|
Keys.onPressed: (event) => {
|
||||||
if (event.key === Qt.Key_PageDown || event.key === Qt.Key_PageUp) {
|
if (event.key === Qt.Key_PageDown || event.key === Qt.Key_PageUp) {
|
||||||
if (event.key === Qt.Key_PageDown) {
|
if (event.key === Qt.Key_PageDown) {
|
||||||
PersistentStateManager.setState("sidebar.centerGroup.selectedTab", Math.min(root.selectedTab + 1, root.tabButtonList.length - 1))
|
root.selectedTab = Math.min(root.selectedTab + 1, root.tabButtonList.length - 1)
|
||||||
} else if (event.key === Qt.Key_PageUp) {
|
} else if (event.key === Qt.Key_PageUp) {
|
||||||
PersistentStateManager.setState("sidebar.centerGroup.selectedTab", Math.max(root.selectedTab - 1, 0))
|
root.selectedTab = Math.max(root.selectedTab - 1, 0)
|
||||||
}
|
}
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
}
|
}
|
||||||
if (event.modifiers === Qt.ControlModifier) {
|
if (event.modifiers === Qt.ControlModifier) {
|
||||||
if (event.key === Qt.Key_Tab) {
|
if (event.key === Qt.Key_Tab) {
|
||||||
PersistentStateManager.setState("sidebar.centerGroup.selectedTab", (root.selectedTab + 1) % root.tabButtonList.length);
|
root.selectedTab = (root.selectedTab + 1) % root.tabButtonList.length
|
||||||
} else if (event.key === Qt.Key_Backtab) {
|
} else if (event.key === Qt.Key_Backtab) {
|
||||||
PersistentStateManager.setState("sidebar.centerGroup.selectedTab", (root.selectedTab - 1 + root.tabButtonList.length) % root.tabButtonList.length);
|
root.selectedTab = (root.selectedTab - 1 + root.tabButtonList.length) % root.tabButtonList.length
|
||||||
}
|
}
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
}
|
}
|
||||||
@@ -53,7 +49,7 @@ Rectangle {
|
|||||||
externalTrackedTab: root.selectedTab
|
externalTrackedTab: root.selectedTab
|
||||||
|
|
||||||
function onCurrentIndexChanged(currentIndex) {
|
function onCurrentIndexChanged(currentIndex) {
|
||||||
PersistentStateManager.setState("sidebar.centerGroup.selectedTab", currentIndex)
|
root.selectedTab = currentIndex
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,10 +58,11 @@ Rectangle {
|
|||||||
Layout.topMargin: 5
|
Layout.topMargin: 5
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
|
spacing: 10
|
||||||
currentIndex: root.selectedTab
|
currentIndex: root.selectedTab
|
||||||
onCurrentIndexChanged: {
|
onCurrentIndexChanged: {
|
||||||
tabBar.enableIndicatorAnimation = true
|
tabBar.enableIndicatorAnimation = true
|
||||||
PersistentStateManager.setState("sidebar.centerGroup.selectedTab", currentIndex)
|
root.selectedTab = currentIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
clip: true
|
clip: true
|
||||||
|
|||||||
@@ -2,10 +2,12 @@ import "root:/"
|
|||||||
import "root:/services"
|
import "root:/services"
|
||||||
import "root:/modules/common"
|
import "root:/modules/common"
|
||||||
import "root:/modules/common/widgets"
|
import "root:/modules/common/widgets"
|
||||||
|
import "root:/modules/common/functions/string_utils.js" as StringUtils
|
||||||
import "./quickToggles/"
|
import "./quickToggles/"
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
|
import QtQuick.Effects
|
||||||
import Qt5Compat.GraphicalEffects
|
import Qt5Compat.GraphicalEffects
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
import Quickshell
|
import Quickshell
|
||||||
@@ -17,251 +19,207 @@ Scope {
|
|||||||
property int sidebarWidth: Appearance.sizes.sidebarWidth
|
property int sidebarWidth: Appearance.sizes.sidebarWidth
|
||||||
property int sidebarPadding: 15
|
property int sidebarPadding: 15
|
||||||
|
|
||||||
Variants {
|
Loader {
|
||||||
id: sidebarVariants
|
id: sidebarLoader
|
||||||
model: Quickshell.screens
|
active: false
|
||||||
|
onActiveChanged: {
|
||||||
Loader {
|
GlobalStates.sidebarRightOpen = sidebarLoader.active
|
||||||
id: sidebarLoader
|
|
||||||
active: false
|
|
||||||
property var modelData
|
|
||||||
onActiveChanged: {
|
|
||||||
GlobalStates.sidebarRightOpenCount += active ? 1 : -1
|
|
||||||
}
|
|
||||||
|
|
||||||
PanelWindow {
|
|
||||||
id: sidebarRoot
|
|
||||||
visible: sidebarLoader.active
|
|
||||||
focusable: true
|
|
||||||
|
|
||||||
onVisibleChanged: {
|
|
||||||
if (!visible) sidebarLoader.active = false
|
|
||||||
}
|
|
||||||
|
|
||||||
function hide() {
|
|
||||||
sidebarLoader.active = false
|
|
||||||
}
|
|
||||||
|
|
||||||
screen: modelData
|
|
||||||
exclusiveZone: 0
|
|
||||||
implicitWidth: sidebarWidth
|
|
||||||
WlrLayershell.namespace: "quickshell:sidebarRight"
|
|
||||||
// Hyprland 0.49: Focus is always exclusive and setting this breaks mouse focus grab
|
|
||||||
// WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
|
|
||||||
color: "transparent"
|
|
||||||
|
|
||||||
anchors {
|
|
||||||
top: true
|
|
||||||
right: true
|
|
||||||
bottom: true
|
|
||||||
}
|
|
||||||
|
|
||||||
HyprlandFocusGrab {
|
|
||||||
id: grab
|
|
||||||
windows: [ sidebarRoot ]
|
|
||||||
active: false
|
|
||||||
onCleared: () => {
|
|
||||||
if (!active) sidebarRoot.hide()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: sidebarRoot
|
|
||||||
function onVisibleChanged() {
|
|
||||||
delayedGrabTimer.start()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: delayedGrabTimer
|
|
||||||
interval: ConfigOptions.hacks.arbitraryRaceConditionDelay
|
|
||||||
repeat: false
|
|
||||||
onTriggered: {
|
|
||||||
grab.active = sidebarRoot.visible
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Background
|
|
||||||
Rectangle {
|
|
||||||
id: sidebarRightBackground
|
|
||||||
|
|
||||||
anchors.centerIn: parent
|
|
||||||
width: parent.width - Appearance.sizes.hyprlandGapsOut * 2
|
|
||||||
height: parent.height - Appearance.sizes.hyprlandGapsOut * 2
|
|
||||||
color: Appearance.colors.colLayer0
|
|
||||||
radius: Appearance.rounding.screenRounding - Appearance.sizes.elevationMargin + 1
|
|
||||||
|
|
||||||
Keys.onPressed: (event) => {
|
|
||||||
if (event.key === Qt.Key_Escape) {
|
|
||||||
sidebarRoot.hide();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: sidebarPadding
|
|
||||||
|
|
||||||
spacing: sidebarPadding
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
Layout.fillHeight: false
|
|
||||||
spacing: 10
|
|
||||||
Layout.margins: 10
|
|
||||||
Layout.topMargin: 5
|
|
||||||
Layout.bottomMargin: 0
|
|
||||||
|
|
||||||
Item {
|
|
||||||
implicitWidth: distroIcon.width
|
|
||||||
implicitHeight: distroIcon.height
|
|
||||||
CustomIcon {
|
|
||||||
id: distroIcon
|
|
||||||
width: 25
|
|
||||||
height: 25
|
|
||||||
source: SystemInfo.distroIcon
|
|
||||||
}
|
|
||||||
ColorOverlay {
|
|
||||||
anchors.fill: distroIcon
|
|
||||||
source: distroIcon
|
|
||||||
color: Appearance.colors.colOnLayer0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
font.pixelSize: Appearance.font.pixelSize.normal
|
|
||||||
color: Appearance.colors.colOnLayer0
|
|
||||||
text: `Uptime: ${DateTime.uptime}`
|
|
||||||
textFormat: Text.MarkdownText
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
|
||||||
|
|
||||||
QuickToggleButton {
|
|
||||||
toggled: false
|
|
||||||
buttonIcon: "power_settings_new"
|
|
||||||
onClicked: {
|
|
||||||
Hyprland.dispatch("global quickshell:sessionOpen")
|
|
||||||
}
|
|
||||||
StyledToolTip {
|
|
||||||
content: qsTr("Session")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
Layout.fillHeight: false
|
|
||||||
radius: Appearance.rounding.full
|
|
||||||
color: Appearance.colors.colLayer1
|
|
||||||
implicitWidth: sidebarQuickControlsRow.implicitWidth + 10
|
|
||||||
implicitHeight: sidebarQuickControlsRow.implicitHeight + 10
|
|
||||||
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
id: sidebarQuickControlsRow
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: 5
|
|
||||||
spacing: 5
|
|
||||||
|
|
||||||
NetworkToggle {}
|
|
||||||
BluetoothToggle {}
|
|
||||||
NightLight {}
|
|
||||||
GameMode {}
|
|
||||||
IdleInhibitor {}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Center widget group
|
|
||||||
CenterWidgetGroup {
|
|
||||||
focus: sidebarRoot.visible
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
Layout.fillHeight: true
|
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
|
||||||
|
|
||||||
BottomWidgetGroup {
|
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
Layout.fillHeight: false
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.preferredHeight: implicitHeight
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shadow
|
|
||||||
DropShadow {
|
|
||||||
anchors.fill: sidebarRightBackground
|
|
||||||
horizontalOffset: 0
|
|
||||||
verticalOffset: 2
|
|
||||||
radius: Appearance.sizes.elevationMargin
|
|
||||||
samples: Appearance.sizes.elevationMargin * 2 + 1 // Ideally should be 2 * radius + 1, see qt docs
|
|
||||||
color: Appearance.colors.colShadow
|
|
||||||
source: sidebarRightBackground
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PanelWindow {
|
||||||
|
id: sidebarRoot
|
||||||
|
visible: sidebarLoader.active
|
||||||
|
|
||||||
|
function hide() {
|
||||||
|
sidebarLoader.active = false
|
||||||
|
}
|
||||||
|
|
||||||
|
exclusiveZone: 0
|
||||||
|
implicitWidth: sidebarWidth
|
||||||
|
WlrLayershell.namespace: "quickshell:sidebarRight"
|
||||||
|
// Hyprland 0.49: Focus is always exclusive and setting this breaks mouse focus grab
|
||||||
|
// WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: true
|
||||||
|
right: true
|
||||||
|
bottom: true
|
||||||
|
}
|
||||||
|
|
||||||
|
HyprlandFocusGrab {
|
||||||
|
id: grab
|
||||||
|
windows: [ sidebarRoot ]
|
||||||
|
active: sidebarRoot.visible
|
||||||
|
onCleared: () => {
|
||||||
|
if (!active) sidebarRoot.hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Background
|
||||||
|
Rectangle {
|
||||||
|
id: sidebarRightBackground
|
||||||
|
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: parent.width - Appearance.sizes.hyprlandGapsOut * 2
|
||||||
|
height: parent.height - Appearance.sizes.hyprlandGapsOut * 2
|
||||||
|
color: Appearance.colors.colLayer0
|
||||||
|
radius: Appearance.rounding.screenRounding - Appearance.sizes.elevationMargin + 1
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: MultiEffect {
|
||||||
|
source: sidebarRightBackground
|
||||||
|
anchors.fill: sidebarRightBackground
|
||||||
|
shadowEnabled: true
|
||||||
|
shadowColor: Appearance.colors.colShadow
|
||||||
|
shadowVerticalOffset: 1
|
||||||
|
shadowBlur: 0.5
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onPressed: (event) => {
|
||||||
|
if (event.key === Qt.Key_Escape) {
|
||||||
|
sidebarRoot.hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: sidebarPadding
|
||||||
|
|
||||||
|
spacing: sidebarPadding
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillHeight: false
|
||||||
|
spacing: 10
|
||||||
|
Layout.margins: 10
|
||||||
|
Layout.topMargin: 5
|
||||||
|
Layout.bottomMargin: 0
|
||||||
|
|
||||||
|
Item {
|
||||||
|
implicitWidth: distroIcon.width
|
||||||
|
implicitHeight: distroIcon.height
|
||||||
|
CustomIcon {
|
||||||
|
id: distroIcon
|
||||||
|
width: 25
|
||||||
|
height: 25
|
||||||
|
source: SystemInfo.distroIcon
|
||||||
|
}
|
||||||
|
ColorOverlay {
|
||||||
|
anchors.fill: distroIcon
|
||||||
|
source: distroIcon
|
||||||
|
color: Appearance.colors.colOnLayer0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
font.pixelSize: Appearance.font.pixelSize.normal
|
||||||
|
color: Appearance.colors.colOnLayer0
|
||||||
|
text: StringUtils.format(qsTr("Uptime: {0}"), DateTime.uptime)
|
||||||
|
textFormat: Text.MarkdownText
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
QuickToggleButton {
|
||||||
|
toggled: false
|
||||||
|
buttonIcon: "power_settings_new"
|
||||||
|
onClicked: {
|
||||||
|
Hyprland.dispatch("global quickshell:sessionOpen")
|
||||||
|
}
|
||||||
|
StyledToolTip {
|
||||||
|
content: qsTr("Session")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.fillHeight: false
|
||||||
|
radius: Appearance.rounding.full
|
||||||
|
color: Appearance.colors.colLayer1
|
||||||
|
implicitWidth: sidebarQuickControlsRow.implicitWidth + 10
|
||||||
|
implicitHeight: sidebarQuickControlsRow.implicitHeight + 10
|
||||||
|
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: sidebarQuickControlsRow
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 5
|
||||||
|
spacing: 5
|
||||||
|
|
||||||
|
NetworkToggle {}
|
||||||
|
BluetoothToggle {}
|
||||||
|
NightLight {}
|
||||||
|
GameMode {}
|
||||||
|
IdleInhibitor {}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Center widget group
|
||||||
|
CenterWidgetGroup {
|
||||||
|
focus: sidebarRoot.visible
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
BottomWidgetGroup {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.fillHeight: false
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: implicitHeight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IpcHandler {
|
IpcHandler {
|
||||||
target: "sidebarRight"
|
target: "sidebarRight"
|
||||||
|
|
||||||
function toggle(): void {
|
function toggle(): void {
|
||||||
for (let i = 0; i < sidebarVariants.instances.length; i++) {
|
sidebarLoader.active = !sidebarLoader.active;
|
||||||
let loader = sidebarVariants.instances[i];
|
if(sidebarLoader.active) Notifications.timeoutAll();
|
||||||
loader.active = !loader.active;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function close(): void {
|
function close(): void {
|
||||||
for (let i = 0; i < sidebarVariants.instances.length; i++) {
|
sidebarLoader.active = false;
|
||||||
let loader = sidebarVariants.instances[i];
|
|
||||||
loader.active = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function open(): void {
|
function open(): void {
|
||||||
for (let i = 0; i < sidebarVariants.instances.length; i++) {
|
sidebarLoader.active = true;
|
||||||
let loader = sidebarVariants.instances[i];
|
Notifications.timeoutAll();
|
||||||
loader.active = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GlobalShortcut {
|
GlobalShortcut {
|
||||||
name: "sidebarRightToggle"
|
name: "sidebarRightToggle"
|
||||||
description: "Toggles right sidebar on press"
|
description: qsTr("Toggles right sidebar on press")
|
||||||
|
|
||||||
onPressed: {
|
onPressed: {
|
||||||
for (let i = 0; i < sidebarVariants.instances.length; i++) {
|
sidebarLoader.active = !sidebarLoader.active;
|
||||||
let loader = sidebarVariants.instances[i];
|
if(sidebarLoader.active) Notifications.timeoutAll();
|
||||||
loader.active = !loader.active;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GlobalShortcut {
|
GlobalShortcut {
|
||||||
name: "sidebarRightOpen"
|
name: "sidebarRightOpen"
|
||||||
description: "Opens right sidebar on press"
|
description: qsTr("Opens right sidebar on press")
|
||||||
|
|
||||||
onPressed: {
|
onPressed: {
|
||||||
for (let i = 0; i < sidebarVariants.instances.length; i++) {
|
sidebarLoader.active = true;
|
||||||
let loader = sidebarVariants.instances[i];
|
Notifications.timeoutAll();
|
||||||
loader.active = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GlobalShortcut {
|
GlobalShortcut {
|
||||||
name: "sidebarRightClose"
|
name: "sidebarRightClose"
|
||||||
description: "Closes right sidebar on press"
|
description: qsTr("Closes right sidebar on press")
|
||||||
|
|
||||||
onPressed: {
|
onPressed: {
|
||||||
for (let i = 0; i < sidebarVariants.instances.length; i++) {
|
sidebarLoader.active = false;
|
||||||
let loader = sidebarVariants.instances[i];
|
|
||||||
loader.active = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import "root:/modules/common"
|
import "root:/modules/common"
|
||||||
import "root:/modules/common/widgets"
|
import "root:/modules/common/widgets"
|
||||||
|
import "root:/modules/common/functions/color_utils.js" as ColorUtils
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
@@ -24,15 +25,10 @@ Button {
|
|||||||
Appearance.m3colors.m3primary) :
|
Appearance.m3colors.m3primary) :
|
||||||
(interactable && button.down) ? Appearance.colors.colLayer1Active :
|
(interactable && button.down) ? Appearance.colors.colLayer1Active :
|
||||||
(interactable && button.hovered) ? Appearance.colors.colLayer1Hover :
|
(interactable && button.hovered) ? Appearance.colors.colLayer1Hover :
|
||||||
Appearance.transparentize(Appearance.colors.colLayer1, 1)
|
ColorUtils.transparentize(Appearance.colors.colLayer1, 1)
|
||||||
|
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
ColorAnimation {
|
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMove.duration
|
|
||||||
easing.type: Appearance.animation.elementMove.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,12 +42,7 @@ Button {
|
|||||||
Appearance.m3colors.m3outlineVariant
|
Appearance.m3colors.m3outlineVariant
|
||||||
|
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
ColorAnimation {
|
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMove.duration
|
|
||||||
easing.type: Appearance.animation.elementMove.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,12 +26,7 @@ Button {
|
|||||||
color: (button.down) ? Appearance.colors.colLayer2Active : (button.hovered ? Appearance.colors.colLayer2Hover : Appearance.colors.colLayer2)
|
color: (button.down) ? Appearance.colors.colLayer2Active : (button.hovered ? Appearance.colors.colLayer2Hover : Appearance.colors.colLayer2)
|
||||||
|
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
ColorAnimation {
|
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMove.duration
|
|
||||||
easing.type: Appearance.animation.elementMove.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -142,11 +142,7 @@ Item {
|
|||||||
opacity: notificationWidgetList.length > 0 ? 1 : 0
|
opacity: notificationWidgetList.length > 0 ? 1 : 0
|
||||||
visible: opacity > 0
|
visible: opacity > 0
|
||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
NumberAnimation {
|
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMove.duration
|
|
||||||
easing.type: Appearance.animation.elementMove.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ Button {
|
|||||||
property string buttonText: ""
|
property string buttonText: ""
|
||||||
property string buttonIcon: ""
|
property string buttonIcon: ""
|
||||||
|
|
||||||
implicitHeight: 30
|
// implicitHeight: 30
|
||||||
implicitWidth: contentRowLayout.implicitWidth + 10 * 2
|
implicitWidth: contentRowLayout.implicitWidth + 10 * 2
|
||||||
Behavior on implicitWidth {
|
Behavior on implicitWidth {
|
||||||
SmoothedAnimation {
|
SmoothedAnimation {
|
||||||
@@ -25,19 +25,13 @@ Button {
|
|||||||
color: (button.down) ? Appearance.colors.colLayer2Active : (button.hovered ? Appearance.colors.colLayer2Hover : Appearance.colors.colLayer2)
|
color: (button.down) ? Appearance.colors.colLayer2Active : (button.hovered ? Appearance.colors.colLayer2Hover : Appearance.colors.colLayer2)
|
||||||
|
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
ColorAnimation {
|
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMove.duration
|
|
||||||
easing.type: Appearance.animation.elementMove.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
contentItem: RowLayout {
|
contentItem: RowLayout {
|
||||||
id: contentRowLayout
|
id: contentRowLayout
|
||||||
// anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
anchors.right: parent.right
|
|
||||||
spacing: 0
|
spacing: 0
|
||||||
MaterialSymbol {
|
MaterialSymbol {
|
||||||
text: buttonIcon
|
text: buttonIcon
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import "../"
|
|||||||
import "root:/services"
|
import "root:/services"
|
||||||
import "root:/modules/common"
|
import "root:/modules/common"
|
||||||
import "root:/modules/common/widgets"
|
import "root:/modules/common/widgets"
|
||||||
|
import "root:/modules/common/functions/string_utils.js" as StringUtils
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
@@ -37,8 +38,9 @@ QuickToggleButton {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
StyledToolTip {
|
StyledToolTip {
|
||||||
content: `${(Bluetooth.bluetoothEnabled && Bluetooth.bluetoothDeviceName.length > 0) ?
|
content: StringUtils.format(qsTr("{0} | Right-click to configure"),
|
||||||
Bluetooth.bluetoothDeviceName : "Bluetooth"} | ${qsTr("Right-click to configure")}`
|
(Bluetooth.bluetoothEnabled && Bluetooth.bluetoothDeviceName.length > 0) ?
|
||||||
|
Bluetooth.bluetoothDeviceName : qsTr("Bluetooth"))
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
|
import "root:/services"
|
||||||
import "root:/modules/common"
|
import "root:/modules/common"
|
||||||
import "root:/modules/common/widgets"
|
import "root:/modules/common/widgets"
|
||||||
import "root:/services"
|
import "root:/modules/common/functions/string_utils.js" as StringUtils
|
||||||
import "../"
|
import "../"
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
@@ -42,6 +43,6 @@ QuickToggleButton {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
StyledToolTip {
|
StyledToolTip {
|
||||||
content: `${Network.networkName} | Right-click to configure`
|
content: StringUtils.format(qsTr("{0} | Right-click to configure"), Network.networkName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import "root:/modules/common"
|
import "root:/modules/common"
|
||||||
import "root:/modules/common/widgets"
|
import "root:/modules/common/widgets"
|
||||||
|
import "root:/modules/common/functions/color_utils.js" as ColorUtils
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
@@ -20,14 +21,10 @@ Button {
|
|||||||
radius: Appearance.rounding.full
|
radius: Appearance.rounding.full
|
||||||
color: toggled ?
|
color: toggled ?
|
||||||
(button.down ? Appearance.colors.colPrimaryActive : button.hovered ? Appearance.colors.colPrimaryHover : Appearance.m3colors.m3primary) :
|
(button.down ? Appearance.colors.colPrimaryActive : button.hovered ? Appearance.colors.colPrimaryHover : Appearance.m3colors.m3primary) :
|
||||||
(button.down ? Appearance.colors.colLayer1Active : button.hovered ? Appearance.colors.colLayer1Hover : Appearance.transparentize(Appearance.colors.colLayer1Hover, 1))
|
(button.down ? Appearance.colors.colLayer1Active : button.hovered ? Appearance.colors.colLayer1Hover : ColorUtils.transparentize(Appearance.colors.colLayer1Hover, 1))
|
||||||
|
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
ColorAnimation {
|
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMove.duration
|
|
||||||
easing.type: Appearance.animation.elementMove.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,11 +36,7 @@ Button {
|
|||||||
color: toggled ? Appearance.m3colors.m3onPrimary : Appearance.colors.colOnLayer1
|
color: toggled ? Appearance.m3colors.m3onPrimary : Appearance.colors.colOnLayer1
|
||||||
|
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
ColorAnimation {
|
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMove.duration
|
|
||||||
easing.type: Appearance.animation.elementMove.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -155,11 +155,7 @@ Item {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
NumberAnimation {
|
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMove.duration
|
|
||||||
easing.type: Appearance.animation.elementMove.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import "root:/modules/common"
|
import "root:/modules/common"
|
||||||
import "root:/modules/common/widgets"
|
import "root:/modules/common/widgets"
|
||||||
|
import "root:/modules/common/functions/color_utils.js" as ColorUtils
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
@@ -23,15 +24,10 @@ Button {
|
|||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
radius: Appearance.rounding.full
|
radius: Appearance.rounding.full
|
||||||
color: (button.down) ? Appearance.colors.colLayer2Active : (button.hovered ? Appearance.colors.colLayer2Hover : Appearance.transparentize(Appearance.colors.colLayer2, 1))
|
color: (button.down) ? Appearance.colors.colLayer2Active : (button.hovered ? Appearance.colors.colLayer2Hover : ColorUtils.transparentize(Appearance.colors.colLayer2, 1))
|
||||||
|
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
ColorAnimation {
|
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMove.duration
|
|
||||||
easing.type: Appearance.animation.elementMove.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import "root:/modules/common"
|
import "root:/modules/common"
|
||||||
import "root:/modules/common/widgets"
|
import "root:/modules/common/widgets"
|
||||||
import "root:/services"
|
import "root:/services"
|
||||||
|
import "root:/modules/common/functions/color_utils.js" as ColorUtils
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Effects
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import Qt5Compat.GraphicalEffects
|
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
@@ -79,37 +80,33 @@ Item {
|
|||||||
tabIndicator.enableIndicatorAnimation = true
|
tabIndicator.enableIndicatorAnimation = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
id: indicator
|
||||||
|
property int tabCount: root.tabButtonList.length
|
||||||
|
property real fullTabSize: root.width / tabCount;
|
||||||
|
property real targetWidth: tabBar.contentItem.children[0].children[tabBar.currentIndex].tabContentWidth
|
||||||
|
|
||||||
|
implicitWidth: targetWidth
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
bottom: parent.bottom
|
||||||
|
}
|
||||||
|
|
||||||
|
x: tabBar.currentIndex * fullTabSize + (fullTabSize - targetWidth) / 2
|
||||||
|
|
||||||
color: Appearance.m3colors.m3primary
|
color: Appearance.m3colors.m3primary
|
||||||
radius: Appearance.rounding.full
|
radius: Appearance.rounding.full
|
||||||
z: 2
|
|
||||||
|
|
||||||
anchors.fill: parent
|
Behavior on x {
|
||||||
anchors.leftMargin: {
|
|
||||||
const tabCount = root.tabButtonList.length
|
|
||||||
const targetWidth = tabBar.contentItem.children[0].children[tabBar.currentIndex].tabContentWidth
|
|
||||||
const fullTabSize = tabBar.width / tabCount;
|
|
||||||
return fullTabSize * currentTab + (fullTabSize - targetWidth) / 2;
|
|
||||||
}
|
|
||||||
anchors.rightMargin: {
|
|
||||||
const tabCount = root.tabButtonList.length
|
|
||||||
const targetWidth = tabBar.contentItem.children[0].children[tabBar.currentIndex].tabContentWidth
|
|
||||||
const fullTabSize = tabBar.width / tabCount;
|
|
||||||
return fullTabSize * (tabCount - currentTab - 1) + (fullTabSize - targetWidth) / 2;
|
|
||||||
}
|
|
||||||
Behavior on anchors.leftMargin {
|
|
||||||
enabled: tabIndicator.enableIndicatorAnimation
|
enabled: tabIndicator.enableIndicatorAnimation
|
||||||
SmoothedAnimation {
|
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||||
velocity: Appearance.animation.positionShift.velocity
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Behavior on anchors.rightMargin {
|
|
||||||
enabled: tabIndicator.enableIndicatorAnimation
|
|
||||||
SmoothedAnimation {
|
|
||||||
velocity: Appearance.animation.positionShift.velocity
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Behavior on implicitWidth {
|
||||||
|
enabled: tabIndicator.enableIndicatorAnimation
|
||||||
|
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,6 +122,7 @@ Item {
|
|||||||
Layout.topMargin: 10
|
Layout.topMargin: 10
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
|
spacing: 10
|
||||||
clip: true
|
clip: true
|
||||||
currentIndex: currentTab
|
currentIndex: currentTab
|
||||||
onCurrentIndexChanged: {
|
onCurrentIndexChanged: {
|
||||||
@@ -173,31 +171,17 @@ Item {
|
|||||||
color: (fabButton.down) ? Appearance.colors.colPrimaryContainerActive : (fabButton.hovered ? Appearance.colors.colPrimaryContainerHover : Appearance.m3colors.m3primaryContainer)
|
color: (fabButton.down) ? Appearance.colors.colPrimaryContainerActive : (fabButton.hovered ? Appearance.colors.colPrimaryContainerHover : Appearance.m3colors.m3primaryContainer)
|
||||||
|
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
ColorAnimation {
|
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMove.duration
|
|
||||||
easing.type: Appearance.animation.elementMove.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
DropShadow {
|
layer.enabled: true
|
||||||
id: fabShadow
|
layer.effect: MultiEffect {
|
||||||
anchors.fill: fabBackground
|
source: fabBackground
|
||||||
source: fabBackground
|
anchors.fill: fabBackground
|
||||||
horizontalOffset: 0
|
shadowEnabled: true
|
||||||
verticalOffset: fabButton.hovered ? 4 : 2
|
shadowColor: Appearance.colors.colShadow
|
||||||
radius: fabButton.hovered ? Appearance.sizes.fabHoveredShadowRadius : Appearance.sizes.fabShadowRadius
|
shadowBlur: 0.6
|
||||||
samples: fabShadow.radius * 2 + 1
|
shadowVerticalOffset: fabButton.hovered ? 4 : 2
|
||||||
color: Appearance.transparentize(Appearance.m3colors.m3shadow, 0.55)
|
|
||||||
z: fabBackground.z - 1
|
|
||||||
|
|
||||||
Behavior on verticalOffset {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Appearance.animation.elementMoveFast.duration
|
|
||||||
easing.type: Appearance.animation.elementMoveFast.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,12 +18,7 @@ Button {
|
|||||||
color: (button.down) ? Appearance.colors.colLayer2Active : (button.hovered ? Appearance.colors.colLayer2Hover : Appearance.colors.colLayer2)
|
color: (button.down) ? Appearance.colors.colLayer2Active : (button.hovered ? Appearance.colors.colLayer2Hover : Appearance.colors.colLayer2)
|
||||||
|
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
ColorAnimation {
|
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||||
duration: Appearance.animation.elementMove.duration
|
|
||||||
easing.type: Appearance.animation.elementMove.type
|
|
||||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,82 @@ post_process() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
check_and_prompt_upscale() {
|
||||||
|
local img="$1"
|
||||||
|
min_width_desired="$(hyprctl monitors -j | jq '([.[].width] | max)' | xargs)" # max monitor width
|
||||||
|
min_height_desired="$(hyprctl monitors -j | jq '([.[].height] | max)' | xargs)" # max monitor height
|
||||||
|
|
||||||
|
if command -v identify &>/dev/null && [ -f "$img" ]; then
|
||||||
|
local img_width img_height
|
||||||
|
img_width=$(identify -format "%w" "$img" 2>/dev/null)
|
||||||
|
img_height=$(identify -format "%h" "$img" 2>/dev/null)
|
||||||
|
if [[ "$img_width" -lt "$min_width_desired" || "$img_height" -lt "$min_height_desired" ]]; then
|
||||||
|
action=$(notify-send "Upscale?" \
|
||||||
|
"Image resolution (${img_width}x${img_height}) is lower than screen resolution (${min_width_desired}x${min_height_desired})" \
|
||||||
|
-A "open_upscayl=Open Upscayl")
|
||||||
|
if [[ "$action" == "open_upscayl" ]]; then
|
||||||
|
if command -v upscayl &>/dev/null; then
|
||||||
|
nohup upscayl > /dev/null 2>&1 &
|
||||||
|
else
|
||||||
|
action2=$(notify-send \
|
||||||
|
-a "Wallpaper switcher" \
|
||||||
|
-c "im.error" \
|
||||||
|
-A "install_upscayl=Install Upscayl (Arch)" \
|
||||||
|
"Install Upscayl?" \
|
||||||
|
"yay -S upscayl-bin")
|
||||||
|
if [[ "$action2" == "install_upscayl" ]]; then
|
||||||
|
foot yay -S upscayl-bin
|
||||||
|
if command -v upscayl &>/dev/null; then
|
||||||
|
nohup upscayl > /dev/null 2>&1 &
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
THUMBNAIL_DIR="/tmp/mpvpaper_thumbnails"
|
||||||
|
CUSTOM_DIR="$XDG_CONFIG_HOME/hypr/custom"
|
||||||
|
RESTORE_SCRIPT_DIR="$CUSTOM_DIR/scripts"
|
||||||
|
RESTORE_SCRIPT="$RESTORE_SCRIPT_DIR/__restore_video_wallpaper.sh"
|
||||||
|
VIDEO_OPTS="no-audio loop hwdec=auto scale=bilinear interpolation=no video-sync=display-resample panscan=1.0 video-scale-x=1.0 video-scale-y=1.0 video-align-x=0.5 video-align-y=0.5"
|
||||||
|
|
||||||
|
is_video() {
|
||||||
|
local extension="${1##*.}"
|
||||||
|
[[ "$extension" == "mp4" || "$extension" == "mkv" || "$extension" == "webm" ]] && return 0 || return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
kill_existing_mpvpaper() {
|
||||||
|
pkill -f -9 mpvpaper || true
|
||||||
|
}
|
||||||
|
|
||||||
|
create_restore_script() {
|
||||||
|
local video_path=$1
|
||||||
|
cat > "$RESTORE_SCRIPT.tmp" << EOF
|
||||||
|
#!/bin/bash
|
||||||
|
# Generated by switchwall.sh - Don't modify it by yourself.
|
||||||
|
# Time: $(date)
|
||||||
|
|
||||||
|
pkill -f -9 mpvpaper
|
||||||
|
|
||||||
|
for monitor in \$(hyprctl monitors -j | jq -r '.[] | .name'); do
|
||||||
|
mpvpaper -o "$VIDEO_OPTS" "\$monitor" "$video_path" &
|
||||||
|
sleep 0.1
|
||||||
|
done
|
||||||
|
EOF
|
||||||
|
mv "$RESTORE_SCRIPT.tmp" "$RESTORE_SCRIPT"
|
||||||
|
chmod +x "$RESTORE_SCRIPT"
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_restore() {
|
||||||
|
cat > "$RESTORE_SCRIPT.tmp" << EOF
|
||||||
|
#!/bin/bash
|
||||||
|
# The content of this script will be generated by switchwall.sh - Don't modify it by yourself.
|
||||||
|
EOF
|
||||||
|
mv "$RESTORE_SCRIPT.tmp" "$RESTORE_SCRIPT"
|
||||||
|
}
|
||||||
|
|
||||||
switch() {
|
switch() {
|
||||||
imgpath="$1"
|
imgpath="$1"
|
||||||
mode_flag="$2"
|
mode_flag="$2"
|
||||||
@@ -48,8 +124,67 @@ switch() {
|
|||||||
echo 'Aborted'
|
echo 'Aborted'
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
matugen_args=(image "$imgpath")
|
|
||||||
generate_colors_material_args=(--path "$imgpath")
|
check_and_prompt_upscale "$imgpath" &
|
||||||
|
kill_existing_mpvpaper
|
||||||
|
|
||||||
|
if is_video "$imgpath"; then
|
||||||
|
mkdir -p "$THUMBNAIL_DIR"
|
||||||
|
|
||||||
|
missing_deps=()
|
||||||
|
if ! command -v mpvpaper &> /dev/null; then
|
||||||
|
missing_deps+=("mpvpaper")
|
||||||
|
fi
|
||||||
|
if ! command -v ffmpeg &> /dev/null; then
|
||||||
|
missing_deps+=("ffmpeg")
|
||||||
|
fi
|
||||||
|
if [ ${#missing_deps[@]} -gt 0 ]; then
|
||||||
|
echo "Missing deps: ${missing_deps[*]}"
|
||||||
|
echo "Arch: sudo pacman -S ${missing_deps[*]}"
|
||||||
|
action=$(notify-send \
|
||||||
|
-a "Wallpaper switcher" \
|
||||||
|
-c "im.error" \
|
||||||
|
-A "install_arch=Install (Arch)" \
|
||||||
|
"Can't switch to video wallpaper" \
|
||||||
|
"Missing dependencies: ${missing_deps[*]}")
|
||||||
|
if [[ "$action" == "install_arch" ]]; then
|
||||||
|
foot sudo pacman -S "${missing_deps[*]}"
|
||||||
|
if command -v mpvpaper &>/dev/null && command -v ffmpeg &>/dev/null; then
|
||||||
|
notify-send 'Wallpaper switcher' 'Alright, try again!'
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local video_path="$imgpath"
|
||||||
|
monitors=$(hyprctl monitors -j | jq -r '.[] | .name')
|
||||||
|
for monitor in $monitors; do
|
||||||
|
mpvpaper -o "$VIDEO_OPTS" "$monitor" "$video_path" &
|
||||||
|
sleep 0.1
|
||||||
|
done
|
||||||
|
|
||||||
|
# Extract first frame for color generation
|
||||||
|
thumbnail="$THUMBNAIL_DIR/$(basename "$imgpath").jpg"
|
||||||
|
ffmpeg -y -i "$imgpath" -vframes 1 "$thumbnail" 2>/dev/null
|
||||||
|
|
||||||
|
if [ -f "$thumbnail" ]; then
|
||||||
|
matugen_args=(image "$thumbnail")
|
||||||
|
generate_colors_material_args=(--path "$thumbnail")
|
||||||
|
create_restore_script "$video_path"
|
||||||
|
else
|
||||||
|
echo "Cannot create image to colorgen"
|
||||||
|
remove_restore
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
matugen_args=(image "$imgpath")
|
||||||
|
generate_colors_material_args=(--path "$imgpath")
|
||||||
|
# Set wallpaper with swww
|
||||||
|
swww img "$imgpath" --transition-step 100 --transition-fps 120 \
|
||||||
|
--transition-type grow --transition-angle 30 --transition-duration 1 \
|
||||||
|
--transition-pos "$cursorposx, $cursorposy_inverted"
|
||||||
|
remove_restore
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Determine mode if not set
|
# Determine mode if not set
|
||||||
@@ -62,18 +197,14 @@ switch() {
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Dark/light mode, material scheme
|
|
||||||
[[ -n "$mode_flag" ]] && matugen_args+=(--mode "$mode_flag") && generate_colors_material_args+=(--mode "$mode_flag")
|
[[ -n "$mode_flag" ]] && matugen_args+=(--mode "$mode_flag") && generate_colors_material_args+=(--mode "$mode_flag")
|
||||||
[[ -n "$type_flag" ]] && matugen_args+=(--type "$type_flag") && generate_colors_material_args+=(--scheme "$type_flag")
|
[[ -n "$type_flag" ]] && matugen_args+=(--type "$type_flag") && generate_colors_material_args+=(--scheme "$type_flag")
|
||||||
# Terminal scheme
|
|
||||||
generate_colors_material_args+=(--termscheme "$terminalscheme" --blend_bg_fg)
|
generate_colors_material_args+=(--termscheme "$terminalscheme" --blend_bg_fg)
|
||||||
generate_colors_material_args+=(--cache "$STATE_DIR/user/color.txt")
|
generate_colors_material_args+=(--cache "$STATE_DIR/user/color.txt")
|
||||||
|
|
||||||
pre_process
|
pre_process
|
||||||
|
|
||||||
# Generate with matugen
|
|
||||||
matugen "${matugen_args[@]}"
|
matugen "${matugen_args[@]}"
|
||||||
# Use custom script for mixing (matugen can't D:)
|
|
||||||
source "$(eval echo $ILLOGICAL_IMPULSE_VIRTUAL_ENV)/bin/activate"
|
source "$(eval echo $ILLOGICAL_IMPULSE_VIRTUAL_ENV)/bin/activate"
|
||||||
python "$SCRIPT_DIR/generate_colors_material.py" "${generate_colors_material_args[@]}" \
|
python "$SCRIPT_DIR/generate_colors_material.py" "${generate_colors_material_args[@]}" \
|
||||||
> "$STATE_DIR"/user/generated/material_colors.scss
|
> "$STATE_DIR"/user/generated/material_colors.scss
|
||||||
|
|||||||
@@ -14,11 +14,22 @@ Singleton {
|
|||||||
readonly property string interfaceRole: "interface"
|
readonly property string interfaceRole: "interface"
|
||||||
readonly property string apiKeyEnvVarName: "API_KEY"
|
readonly property string apiKeyEnvVarName: "API_KEY"
|
||||||
property Component aiMessageComponent: AiMessageData {}
|
property Component aiMessageComponent: AiMessageData {}
|
||||||
property string systemPrompt: ConfigOptions.ai.systemPrompt ?? ""
|
property string systemPrompt: ConfigOptions?.ai?.systemPrompt ?? ""
|
||||||
property var messages: []
|
property var messages: []
|
||||||
|
property var messageIDs: []
|
||||||
|
property var messageByID: ({})
|
||||||
readonly property var apiKeys: KeyringStorage.keyringData?.apiKeys ?? {}
|
readonly property var apiKeys: KeyringStorage.keyringData?.apiKeys ?? {}
|
||||||
readonly property var apiKeysLoaded: KeyringStorage.loaded
|
readonly property var apiKeysLoaded: KeyringStorage.loaded
|
||||||
|
|
||||||
|
function idForMessage(message) {
|
||||||
|
// Generate a unique ID using timestamp and random value
|
||||||
|
return Date.now().toString(36) + Math.random().toString(36).substr(2, 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
function safeModelName(modelName) {
|
||||||
|
return modelName.replace(/:/g, "_").replace(/\./g, "_")
|
||||||
|
}
|
||||||
|
|
||||||
// Model properties:
|
// Model properties:
|
||||||
// - name: Name of the model
|
// - name: Name of the model
|
||||||
// - icon: Icon name of the model
|
// - icon: Icon name of the model
|
||||||
@@ -36,14 +47,14 @@ Singleton {
|
|||||||
"gemini-2.0-flash-search": {
|
"gemini-2.0-flash-search": {
|
||||||
"name": "Gemini 2.0 Flash",
|
"name": "Gemini 2.0 Flash",
|
||||||
"icon": "google-gemini-symbolic",
|
"icon": "google-gemini-symbolic",
|
||||||
"description": "Online | Google's model\nGives up-to-date information with search.",
|
"description": qsTr("Online | Google's model\nGives up-to-date information with search."),
|
||||||
"homepage": "https://aistudio.google.com",
|
"homepage": "https://aistudio.google.com",
|
||||||
"endpoint": "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:streamGenerateContent",
|
"endpoint": "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:streamGenerateContent",
|
||||||
"model": "gemini-2.0-flash",
|
"model": "gemini-2.0-flash",
|
||||||
"requires_key": true,
|
"requires_key": true,
|
||||||
"key_id": "gemini",
|
"key_id": "gemini",
|
||||||
"key_get_link": "https://aistudio.google.com/app/apikey",
|
"key_get_link": "https://aistudio.google.com/app/apikey",
|
||||||
"key_get_description": "**Pricing**: free. Data used for training.\n\n**Instructions**: Log into Google account, allow AI Studio to create Google Cloud project or whatever it asks, go back and click Get API key",
|
"key_get_description": qsTr("**Pricing**: free. Data used for training.\n\n**Instructions**: Log into Google account, allow AI Studio to create Google Cloud project or whatever it asks, go back and click Get API key"),
|
||||||
"api_format": "gemini",
|
"api_format": "gemini",
|
||||||
"tools": [
|
"tools": [
|
||||||
{
|
{
|
||||||
@@ -54,25 +65,26 @@ Singleton {
|
|||||||
"openrouter-llama4-maverick": {
|
"openrouter-llama4-maverick": {
|
||||||
"name": "Llama 4 Maverick",
|
"name": "Llama 4 Maverick",
|
||||||
"icon": "ollama-symbolic",
|
"icon": "ollama-symbolic",
|
||||||
"description": "Online via OpenRouter | Meta's model",
|
"description": StringUtils.format(qsTr("Online via {0} | {1}'s model"), "OpenRouter", "Meta"),
|
||||||
"homepage": "https://openrouter.ai/meta-llama/llama-4-maverick:free",
|
"homepage": "https://openrouter.ai/meta-llama/llama-4-maverick:free",
|
||||||
"endpoint": "https://openrouter.ai/api/v1/chat/completions",
|
"endpoint": "https://openrouter.ai/api/v1/chat/completions",
|
||||||
"model": "meta-llama/llama-4-maverick:free",
|
"model": "meta-llama/llama-4-maverick:free",
|
||||||
"requires_key": true,
|
"requires_key": true,
|
||||||
"key_id": "openrouter",
|
"key_id": "openrouter",
|
||||||
"key_get_link": "https://openrouter.ai/settings/keys",
|
"key_get_link": "https://openrouter.ai/settings/keys",
|
||||||
"key_get_description": "**Pricing**: free. Data use policy varies depending on your OpenRouter account settings.\n\n**Instructions**: Log into OpenRouter account, go to Keys on the topright menu, click Create API Key",
|
"key_get_description": qsTr("**Pricing**: free. Data use policy varies depending on your OpenRouter account settings.\n\n**Instructions**: Log into OpenRouter account, go to Keys on the topright menu, click Create API Key"),
|
||||||
},
|
},
|
||||||
"openrouter-deepseek-r1": {
|
"openrouter-deepseek-r1": {
|
||||||
"name": "DeepSeek R1",
|
"name": "DeepSeek R1",
|
||||||
"icon": "deepseek-symbolic",
|
"icon": "deepseek-symbolic",
|
||||||
"description": "Online via OpenRouter | DeepSeek's reasoning model",
|
"description": StringUtils.format(qsTr("Online via {0} | {1}'s model"), "OpenRouter", "DeepSeek"),
|
||||||
"homepage": "https://openrouter.ai/deepseek/deepseek-r1:free",
|
"homepage": "https://openrouter.ai/deepseek/deepseek-r1:free",
|
||||||
"endpoint": "https://openrouter.ai/api/v1/chat/completions",
|
"endpoint": "https://openrouter.ai/api/v1/chat/completions",
|
||||||
"model": "deepseek/deepseek-r1:free",
|
"model": "deepseek/deepseek-r1:free",
|
||||||
"requires_key": true,
|
"requires_key": true,
|
||||||
"key_id": "openrouter",
|
"key_id": "openrouter",
|
||||||
"key_get_link": "https://openrouter.ai/settings/keys",
|
"key_get_link": "https://openrouter.ai/settings/keys",
|
||||||
|
"key_get_description": qsTr("**Pricing**: free. Data use policy varies depending on your OpenRouter account settings.\n\n**Instructions**: Log into OpenRouter account, go to Keys on the topright menu, click Create API Key"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
property var modelList: Object.keys(root.models)
|
property var modelList: Object.keys(root.models)
|
||||||
@@ -114,10 +126,11 @@ Singleton {
|
|||||||
const dataJson = JSON.parse(data);
|
const dataJson = JSON.parse(data);
|
||||||
root.modelList = [...root.modelList, ...dataJson];
|
root.modelList = [...root.modelList, ...dataJson];
|
||||||
dataJson.forEach(model => {
|
dataJson.forEach(model => {
|
||||||
root.models[model] = {
|
const safeModelName = root.safeModelName(model);
|
||||||
|
root.models[safeModelName] = {
|
||||||
"name": guessModelName(model),
|
"name": guessModelName(model),
|
||||||
"icon": guessModelLogo(model),
|
"icon": guessModelLogo(model),
|
||||||
"description": `Local Ollama model: ${model}`,
|
"description": StringUtils.format(qsTr("Local Ollama model | {0}"), model),
|
||||||
"homepage": `https://ollama.com/library/${model}`,
|
"homepage": `https://ollama.com/library/${model}`,
|
||||||
"endpoint": "http://localhost:11434/v1/chat/completions",
|
"endpoint": "http://localhost:11434/v1/chat/completions",
|
||||||
"model": model,
|
"model": model,
|
||||||
@@ -141,13 +154,17 @@ Singleton {
|
|||||||
"thinking": false,
|
"thinking": false,
|
||||||
"done": true,
|
"done": true,
|
||||||
});
|
});
|
||||||
root.messages = [...root.messages, aiMessage];
|
const id = idForMessage(aiMessage);
|
||||||
|
root.messageIDs = [...root.messageIDs, id];
|
||||||
|
root.messageByID[id] = aiMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeMessage(index) {
|
function removeMessage(index) {
|
||||||
if (index < 0 || index >= messages.length) return;
|
if (index < 0 || index >= messageIDs.length) return;
|
||||||
root.messages.splice(index, 1);
|
const id = root.messageIDs[index];
|
||||||
root.messages = [...root.messages];
|
root.messageIDs.splice(index, 1);
|
||||||
|
root.messageIDs = [...root.messageIDs];
|
||||||
|
delete root.messageByID[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
function addApiKeyAdvice(model) {
|
function addApiKeyAdvice(model) {
|
||||||
@@ -167,7 +184,7 @@ Singleton {
|
|||||||
modelId = modelId.toLowerCase()
|
modelId = modelId.toLowerCase()
|
||||||
if (modelList.indexOf(modelId) !== -1) {
|
if (modelList.indexOf(modelId) !== -1) {
|
||||||
PersistentStateManager.setState("ai.model", modelId);
|
PersistentStateManager.setState("ai.model", modelId);
|
||||||
if (feedback) root.addMessage("Model set to " + models[modelId].name, Ai.interfaceRole)
|
if (feedback) root.addMessage(StringUtils.format(StringUtils.format("Model set to {0}"), models[modelId].name, Ai.interfaceRole))
|
||||||
if (models[modelId].requires_key) {
|
if (models[modelId].requires_key) {
|
||||||
// If key not there show advice
|
// If key not there show advice
|
||||||
if (root.apiKeysLoaded && (!root.apiKeys[models[modelId].key_id] || root.apiKeys[models[modelId].key_id].length === 0)) {
|
if (root.apiKeysLoaded && (!root.apiKeys[models[modelId].key_id] || root.apiKeys[models[modelId].key_id].length === 0)) {
|
||||||
@@ -185,7 +202,7 @@ Singleton {
|
|||||||
function setApiKey(key) {
|
function setApiKey(key) {
|
||||||
const model = models[currentModelId];
|
const model = models[currentModelId];
|
||||||
if (!model.requires_key) {
|
if (!model.requires_key) {
|
||||||
root.addMessage(`${model.name} does not require an API key`, Ai.interfaceRole);
|
root.addMessage(StringUtils.format(qsTr("{0} does not require an API key"), model.name), Ai.interfaceRole);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!key || key.length === 0) {
|
if (!key || key.length === 0) {
|
||||||
@@ -194,7 +211,7 @@ Singleton {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
KeyringStorage.setNestedField(["apiKeys", model.key_id], key.trim());
|
KeyringStorage.setNestedField(["apiKeys", model.key_id], key.trim());
|
||||||
root.addMessage("API key set for " + model.name, Ai.interfaceRole);
|
root.addMessage(StringUtils.format(qsTr("API key set for {0}"), model.name, Ai.interfaceRole));
|
||||||
}
|
}
|
||||||
|
|
||||||
function printApiKey() {
|
function printApiKey() {
|
||||||
@@ -207,12 +224,13 @@ Singleton {
|
|||||||
root.addMessage(StringUtils.format(qsTr("No API key set for {0}"), model.name), Ai.interfaceRole);
|
root.addMessage(StringUtils.format(qsTr("No API key set for {0}"), model.name), Ai.interfaceRole);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
root.addMessage(`This model (${model.name}) does not require an API key`, Ai.interfaceRole);
|
root.addMessage(StringUtils.format(qsTr("{0} does not require an API key"), model.name), Ai.interfaceRole);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearMessages() {
|
function clearMessages() {
|
||||||
messages = [];
|
root.messageIDs = [];
|
||||||
|
root.messageByID = ({});
|
||||||
}
|
}
|
||||||
|
|
||||||
Process {
|
Process {
|
||||||
@@ -274,7 +292,8 @@ Singleton {
|
|||||||
|
|
||||||
/* Build endpoint, request data */
|
/* Build endpoint, request data */
|
||||||
const endpoint = (apiFormat === "gemini") ? buildGeminiEndpoint(model) : buildOpenAIEndpoint(model);
|
const endpoint = (apiFormat === "gemini") ? buildGeminiEndpoint(model) : buildOpenAIEndpoint(model);
|
||||||
const data = (apiFormat === "gemini") ? buildGeminiRequestData(model, root.messages) : buildOpenAIRequestData(model, root.messages);
|
const messageArray = root.messageIDs.map(id => root.messageByID[id]);
|
||||||
|
const data = (apiFormat === "gemini") ? buildGeminiRequestData(model, messageArray) : buildOpenAIRequestData(model, messageArray);
|
||||||
|
|
||||||
let requestHeaders = {
|
let requestHeaders = {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
@@ -288,7 +307,9 @@ Singleton {
|
|||||||
"thinking": true,
|
"thinking": true,
|
||||||
"done": false,
|
"done": false,
|
||||||
});
|
});
|
||||||
root.messages = [...root.messages, requester.message];
|
const id = idForMessage(requester.message);
|
||||||
|
root.messageIDs = [...root.messageIDs, id];
|
||||||
|
root.messageByID[id] = requester.message;
|
||||||
|
|
||||||
/* Build header string for curl */
|
/* Build header string for curl */
|
||||||
let headerString = Object.entries(requestHeaders)
|
let headerString = Object.entries(requestHeaders)
|
||||||
@@ -318,14 +339,14 @@ Singleton {
|
|||||||
const dataJson = JSON.parse(requester.geminiBuffer);
|
const dataJson = JSON.parse(requester.geminiBuffer);
|
||||||
const responseContent = dataJson.candidates[0]?.content?.parts[0]?.text
|
const responseContent = dataJson.candidates[0]?.content?.parts[0]?.text
|
||||||
requester.message.content += responseContent;
|
requester.message.content += responseContent;
|
||||||
const annotationSources = dataJson.candidates[0]?.groundingMetadata.groundingChunks?.map(chunk => {
|
const annotationSources = dataJson.candidates[0]?.groundingMetadata?.groundingChunks?.map(chunk => {
|
||||||
return {
|
return {
|
||||||
"type": "url_citation",
|
"type": "url_citation",
|
||||||
"text": chunk?.web?.title,
|
"text": chunk?.web?.title,
|
||||||
"url": chunk?.web?.uri,
|
"url": chunk?.web?.uri,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const annotations = dataJson.candidates[0]?.groundingMetadata.groundingSupports?.map(citation => {
|
const annotations = dataJson.candidates[0]?.groundingMetadata?.groundingSupports?.map(citation => {
|
||||||
return {
|
return {
|
||||||
"type": "url_citation",
|
"type": "url_citation",
|
||||||
"start_index": citation.segment?.startIndex,
|
"start_index": citation.segment?.startIndex,
|
||||||
@@ -446,15 +467,7 @@ Singleton {
|
|||||||
|
|
||||||
function sendUserMessage(message) {
|
function sendUserMessage(message) {
|
||||||
if (message.length === 0) return;
|
if (message.length === 0) return;
|
||||||
|
root.addMessage(message, "user");
|
||||||
const userMessage = aiMessageComponent.createObject(root, {
|
|
||||||
"role": "user",
|
|
||||||
"content": message,
|
|
||||||
"thinking": false,
|
|
||||||
"done": true,
|
|
||||||
});
|
|
||||||
root.messages = [...root.messages, userMessage];
|
|
||||||
|
|
||||||
requester.makeRequest();
|
requester.makeRequest();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,20 +13,13 @@ Singleton {
|
|||||||
|
|
||||||
signal tagSuggestion(string query, var suggestions)
|
signal tagSuggestion(string query, var suggestions)
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: ConfigOptions.sidebar.booru
|
|
||||||
function onAllowNsfwChanged() {
|
|
||||||
root.addSystemMessage(PersistentStates.booru.allowNsfw ? qsTr("Tiddies enabled") : qsTr("No horny"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
property string failMessage: qsTr("That didn't work. Tips:\n- Check your tags and NSFW settings\n- If you don't have a tag in mind, type a page number")
|
property string failMessage: qsTr("That didn't work. Tips:\n- Check your tags and NSFW settings\n- If you don't have a tag in mind, type a page number")
|
||||||
property var responses: []
|
property var responses: []
|
||||||
property int runningRequests: 0
|
property int runningRequests: 0
|
||||||
property var defaultUserAgent: ConfigOptions.networking.userAgent
|
property var defaultUserAgent: ConfigOptions?.networking?.userAgent || "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36"
|
||||||
property var providerList: ["yandere", "konachan", "zerochan", "danbooru", "gelbooru", "waifu.im"]
|
property var providerList: ["yandere", "konachan", "zerochan", "danbooru", "gelbooru", "waifu.im"]
|
||||||
property var providers: {
|
property var providers: {
|
||||||
"system": { "name": "System" },
|
"system": { "name": qsTr("System") },
|
||||||
"yandere": {
|
"yandere": {
|
||||||
"name": "yande.re",
|
"name": "yande.re",
|
||||||
"url": "https://yande.re",
|
"url": "https://yande.re",
|
||||||
@@ -347,7 +340,7 @@ Singleton {
|
|||||||
xhr.setRequestHeader("User-Agent", defaultUserAgent)
|
xhr.setRequestHeader("User-Agent", defaultUserAgent)
|
||||||
}
|
}
|
||||||
else if (currentProvider == "zerochan") {
|
else if (currentProvider == "zerochan") {
|
||||||
const userAgent = ConfigOptions.sidebar.booru.zerochan.username ? `Desktop sidebar booru viewer - ${ConfigOptions.sidebar.booru.zerochan.username}` : defaultUserAgent
|
const userAgent = ConfigOptions?.sidebar?.booru?.zerochan?.username ? `Desktop sidebar booru viewer - username: ${ConfigOptions.sidebar.booru.zerochan.username}` : defaultUserAgent
|
||||||
xhr.setRequestHeader("User-Agent", userAgent)
|
xhr.setRequestHeader("User-Agent", userAgent)
|
||||||
}
|
}
|
||||||
root.runningRequests++;
|
root.runningRequests++;
|
||||||
|
|||||||
@@ -134,13 +134,13 @@ Singleton {
|
|||||||
|
|
||||||
GlobalShortcut {
|
GlobalShortcut {
|
||||||
name: "brightnessIncrease"
|
name: "brightnessIncrease"
|
||||||
description: "Increase brightness"
|
description: qsTr("Increase brightness")
|
||||||
onPressed: root.increaseBrightness()
|
onPressed: root.increaseBrightness()
|
||||||
}
|
}
|
||||||
|
|
||||||
GlobalShortcut {
|
GlobalShortcut {
|
||||||
name: "brightnessDecrease"
|
name: "brightnessDecrease"
|
||||||
description: "Decrease brightness"
|
description: qsTr("Decrease brightness")
|
||||||
onPressed: root.decreaseBrightness()
|
onPressed: root.decreaseBrightness()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,11 +29,11 @@ Singleton {
|
|||||||
if (root.firstLoad) {
|
if (root.firstLoad) {
|
||||||
root.firstLoad = false;
|
root.firstLoad = false;
|
||||||
} else {
|
} else {
|
||||||
Hyprland.dispatch(`exec notify-send "Shell configuration reloaded" "${root.filePath}"`)
|
Hyprland.dispatch(`exec notify-send "${qsTr("Shell configuration reloaded")}" "${root.filePath}"`)
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("[ConfigLoader] Error reading file:", e);
|
console.error("[ConfigLoader] Error reading file:", e);
|
||||||
Hyprland.dispatch(`exec notify-send "Shell configuration failed to load" "${root.filePath}"`)
|
Hyprland.dispatch(`exec notify-send "${qsTr("Shell configuration failed to load")}" "${root.filePath}"`)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -66,9 +66,9 @@ Singleton {
|
|||||||
console.log("[ConfigLoader] File not found, creating new file.")
|
console.log("[ConfigLoader] File not found, creating new file.")
|
||||||
const plainConfig = ObjectUtils.toPlainObject(ConfigOptions)
|
const plainConfig = ObjectUtils.toPlainObject(ConfigOptions)
|
||||||
configFileView.setText(JSON.stringify(plainConfig, null, 2))
|
configFileView.setText(JSON.stringify(plainConfig, null, 2))
|
||||||
Hyprland.dispatch(`exec notify-send "Shell configuration created" "${root.filePath}"`)
|
Hyprland.dispatch(`exec notify-send "${qsTr("Shell configuration created")}" "${root.filePath}"`)
|
||||||
} else {
|
} else {
|
||||||
Hyprland.dispatch(`exec notify-send "Shell configuration failed to load" "${root.filePath}"`)
|
Hyprland.dispatch(`exec notify-send "${qsTr("Shell configuration failed to load")}" "${root.filePath}"`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ Singleton {
|
|||||||
if (hours > 0) formatted += `${formatted ? ", " : ""}${hours}h`
|
if (hours > 0) formatted += `${formatted ? ", " : ""}${hours}h`
|
||||||
if (minutes > 0 || !formatted) formatted += `${formatted ? ", " : ""}${minutes}m`
|
if (minutes > 0 || !formatted) formatted += `${formatted ? ", " : ""}${minutes}m`
|
||||||
uptime = formatted
|
uptime = formatted
|
||||||
interval = ConfigOptions.resources.updateInterval;
|
interval = ConfigOptions?.resources?.updateInterval ?? 3000
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,19 +11,15 @@ import Quickshell.Hyprland
|
|||||||
|
|
||||||
Singleton {
|
Singleton {
|
||||||
id: root
|
id: root
|
||||||
property var defaultKeybinds: []
|
property var defaultKeybinds: {"children": []}
|
||||||
property var userKeybinds: []
|
property var userKeybinds: {"children": []}
|
||||||
property var keybinds: ({
|
property var keybinds: ({
|
||||||
children: [
|
children: [
|
||||||
...defaultKeybinds.children,
|
...(defaultKeybinds.children ?? []),
|
||||||
...userKeybinds.children,
|
...(userKeybinds.children ?? []),
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
// onKeybindsChanged: {
|
|
||||||
// console.log("[CheatsheetKeybinds] Keybinds changed:", JSON.stringify(keybinds, null, 2))
|
|
||||||
// }
|
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: Hyprland
|
target: Hyprland
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ pragma Singleton
|
|||||||
pragma ComponentBehavior: Bound
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
import "root:/modules/common"
|
import "root:/modules/common"
|
||||||
|
import "root:/modules/common/functions/string_utils.js" as StringUtils
|
||||||
import Quickshell;
|
import Quickshell;
|
||||||
import Quickshell.Io;
|
import Quickshell.Io;
|
||||||
import Qt.labs.platform
|
import Qt.labs.platform
|
||||||
@@ -18,14 +19,14 @@ Singleton {
|
|||||||
|
|
||||||
property var properties: {
|
property var properties: {
|
||||||
"application": "illogical-impulse",
|
"application": "illogical-impulse",
|
||||||
"explanation": "For storing API keys and other sensitive information",
|
"explanation": qsTr("For storing API keys and other sensitive information"),
|
||||||
}
|
}
|
||||||
property var propertiesAsArgs: Object.keys(root.properties).reduce(
|
property var propertiesAsArgs: Object.keys(root.properties).reduce(
|
||||||
function(arr, key) {
|
function(arr, key) {
|
||||||
return arr.concat([key, root.properties[key]]);
|
return arr.concat([key, root.properties[key]]);
|
||||||
}, []
|
}, []
|
||||||
)
|
)
|
||||||
property string keyringLabel: "illogical-impulse Safe Storage"
|
property string keyringLabel: StringUtils.format(qsTr("{0} Safe Storage"), "illogical-impulse")
|
||||||
|
|
||||||
function setNestedField(path, value) {
|
function setNestedField(path, value) {
|
||||||
if (!root.keyringData) root.keyringData = {};
|
if (!root.keyringData) root.keyringData = {};
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ Singleton {
|
|||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: delayedFileRead
|
id: delayedFileRead
|
||||||
interval: ConfigOptions.hacks.arbitraryRaceConditionDelay
|
interval: ConfigOptions?.hacks?.arbitraryRaceConditionDelay ?? 100
|
||||||
repeat: false
|
repeat: false
|
||||||
running: false
|
running: false
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ Singleton {
|
|||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: delayedFileRead
|
id: delayedFileRead
|
||||||
interval: ConfigOptions.hacks.arbitraryRaceConditionDelay
|
interval: ConfigOptions?.hacks?.arbitraryRaceConditionDelay ?? 100
|
||||||
repeat: false
|
repeat: false
|
||||||
running: false
|
running: false
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ Singleton {
|
|||||||
|
|
||||||
previousCpuStats = { total, idle }
|
previousCpuStats = { total, idle }
|
||||||
}
|
}
|
||||||
interval = ConfigOptions.resources.updateInterval
|
interval = ConfigOptions?.resources?.updateInterval ?? 3000
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
//@ pragma UseQApplication
|
//@ pragma UseQApplication
|
||||||
|
//@ pragma Env QT_QUICK_CONTROLS_STYLE=Basic
|
||||||
|
//@ pragma Env QS_NO_RELOAD_POPUP=1
|
||||||
|
|
||||||
import "./modules/bar/"
|
import "./modules/bar/"
|
||||||
import "./modules/cheatsheet/"
|
import "./modules/cheatsheet/"
|
||||||
|
import "./modules/mediaControls/"
|
||||||
import "./modules/notificationPopup/"
|
import "./modules/notificationPopup/"
|
||||||
import "./modules/onScreenDisplay/"
|
import "./modules/onScreenDisplay/"
|
||||||
import "./modules/overview/"
|
import "./modules/overview/"
|
||||||
@@ -25,6 +28,7 @@ ShellRoot {
|
|||||||
|
|
||||||
Bar {}
|
Bar {}
|
||||||
Cheatsheet {}
|
Cheatsheet {}
|
||||||
|
MediaControls {}
|
||||||
NotificationPopup {}
|
NotificationPopup {}
|
||||||
OnScreenDisplayBrightness {}
|
OnScreenDisplayBrightness {}
|
||||||
OnScreenDisplayVolume {}
|
OnScreenDisplayVolume {}
|
||||||
|
|||||||
+22
-6
@@ -1,10 +1,26 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
if [ $? -eq 0 ]
|
set -euo pipefail
|
||||||
then
|
|
||||||
sed '1,/^### DATA ###$/d' $0 | fuzzel --match-mode fzf --dmenu | cut -d ' ' -f 1 | tr -d '\n' | wl-copy
|
MODE="${1:-type}"
|
||||||
else
|
|
||||||
sed '1,/^### DATA ###$/d' $0 | fuzzel --match-mode fzf --dmenu | cut -d ' ' -f 1 | tr -d '\n' | wl-copy
|
emoji="$(sed '1,/^### DATA ###$/d' "$0" | fuzzel --match-mode fzf --dmenu | cut -d ' ' -f 1 | tr -d '\n')"
|
||||||
fi
|
|
||||||
|
case "$MODE" in
|
||||||
|
type)
|
||||||
|
wtype "${emoji}" || wl-copy "${emoji}"
|
||||||
|
;;
|
||||||
|
copy)
|
||||||
|
wl-copy "${emoji}"
|
||||||
|
;;
|
||||||
|
both)
|
||||||
|
wtype "${emoji}" || true
|
||||||
|
wl-copy "${emoji}"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Usage: $0 [type|copy|both]"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
exit
|
exit
|
||||||
### DATA ###
|
### DATA ###
|
||||||
😀 grinning face face smile happy joy :D grin
|
😀 grinning face face smile happy joy :D grin
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
#!/usr/bin/bash
|
|
||||||
|
|
||||||
WORKSPACES="$(hyprctl monitors -j | jq -r 'map(.activeWorkspace.id)')"
|
|
||||||
WINDOWS="$(hyprctl clients -j | jq -r --argjson workspaces "$WORKSPACES" 'map(select([.workspace.id] | inside($workspaces)))' )"
|
|
||||||
GEOM=$(echo "$WINDOWS" | jq -r '.[] | "\(.at[0]),\(.at[1]) \(.size[0])x\(.size[1])"' | slurp -f '%x %y %w %h')
|
|
||||||
wayshot -s "$GEOM" --stdout ${#:+"$@"}
|
|
||||||
@@ -2,8 +2,8 @@
|
|||||||
## Not ready, but feel free to try it. It's simple:
|
## Not ready, but feel free to try it. It's simple:
|
||||||
|
|
||||||
- **Assumption**: You are already using the AGS illogical-impulse
|
- **Assumption**: You are already using the AGS illogical-impulse
|
||||||
- **Install Qt packages** (idk which are actually needed so this is everything I have): `qt5-base qt5-declarative qt5-graphicaleffects qt5-imageformats qt5-quickcontrols qt5-quickcontrols2 qt5-svg qt5-translations qt5-wayland qt5-x11extras qt6-5compat qt6-base qt6-declarative qt6-imageformats qt6-multimedia qt6-positioning qt6-quicktimeline qt6-sensors qt6-svg qt6-tools qt6-translations qt6-virtualkeyboard qt6-wayland qt6-webchannel qt6-webengine qt6-websockets qt6-webview syntax-highlighting`
|
- **Install Qt packages** (idk which are actually needed so this is everything I have): `qt5-base qt5-declarative qt5-graphicaleffects qt5-imageformats qt5-svg qt5-translations qt5-wayland qt6-5compat qt6-base qt6-declarative qt6-imageformats qt6-multimedia qt6-positioning qt6-quicktimeline qt6-sensors qt6-svg qt6-tools qt6-translations qt6-virtualkeyboard qt6-wayland syntax-highlighting`
|
||||||
- **Install quickshell and more stuff**: `yay -S quickshell matugen-bin grimblast`
|
- **Install quickshell and more stuff**: `yay -S quickshell matugen-bin grimblast wtype`
|
||||||
- **Copy** `.config/quickshell` folder and hyprland config files in `.config/hypr/hyprland/` (backing up is your responsibility) (or you can create a new user)
|
- **Copy** `.config/quickshell` folder and hyprland config files in `.config/hypr/hyprland/` (backing up is your responsibility) (or you can create a new user)
|
||||||
- **Run quickshell** with `qs` and see how things are - it's not finished, but **feedback is very welcome**
|
- **Run quickshell** with `qs` and see how things are - it's not finished, but **feedback is very welcome**
|
||||||
- We currently have bar, right sidebar, search/overview
|
- We currently have bar, right sidebar, search/overview
|
||||||
|
|||||||
@@ -7,6 +7,6 @@ license=(None)
|
|||||||
depends=(
|
depends=(
|
||||||
polkit-gnome
|
polkit-gnome
|
||||||
gnome-keyring
|
gnome-keyring
|
||||||
gnome-control-center
|
networkmanager
|
||||||
blueberry networkmanager
|
better-control-git
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user