mirror of
https://github.com/end-4/dots-hyprland.git
synced 2026-06-05 23:09:26 -05:00
merge upstream
This commit is contained in:
@@ -46,10 +46,10 @@
|
||||
"fakeScreenRounding": 2 // 0: None | 1: Always | 2: When not fullscreen
|
||||
},
|
||||
"apps": {
|
||||
"bluetooth": "blueberry",
|
||||
"bluetooth": "better-control --bluetooth",
|
||||
"imageViewer": "loupe",
|
||||
"network": "XDG_CURRENT_DESKTOP=\"gnome\" gnome-control-center wifi",
|
||||
"settings": "XDG_CURRENT_DESKTOP=\"gnome\" gnome-control-center",
|
||||
"network": "better-control --wifi",
|
||||
"settings": "better-control",
|
||||
"taskManager": "gnome-usage",
|
||||
"terminal": "foot" // This is only for shell actions
|
||||
},
|
||||
|
||||
@@ -22,8 +22,8 @@ var lastCoverPath = '';
|
||||
function isRealPlayer(player) {
|
||||
return (
|
||||
// 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.chromium')) &&
|
||||
// !(hasPlasmaIntegration && player.busName.startsWith('org.mpris.MediaPlayer2.firefox')) &&
|
||||
// !(hasPlasmaIntegration && player.busName.startsWith('org.mpris.MediaPlayer2.chromium')) &&
|
||||
// playerctld just copies other buses and we don't need duplicates
|
||||
!player.busName.startsWith('org.mpris.MediaPlayer2.playerctld') &&
|
||||
// Non-instance mpd bus
|
||||
@@ -209,8 +209,15 @@ const CoverArt = ({ player, ...rest }) => {
|
||||
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`])
|
||||
.then(() => {
|
||||
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`);
|
||||
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"`)}`
|
||||
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}"`);
|
||||
Utils.timeout(200, () => {
|
||||
// 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%);
|
||||
@if $darkmode == False {
|
||||
$onSecondaryContainer: mix($onSecondaryContainer, black, 50%);
|
||||
} @else {
|
||||
$onSecondaryContainer: mix($onSecondaryContainer, white, 50%);
|
||||
}
|
||||
|
||||
|
||||
.osd-music {
|
||||
@include menu_decel;
|
||||
@include elevation2;
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
# See https://wiki.hyprland.org/Configuring/Binds/
|
||||
#!
|
||||
##! User keybinds
|
||||
bind = Ctrl+Super+Alt, Slash, exec, xdg-open ~/.config/hypr/custom/keybinds.conf # Edit custom keybinds
|
||||
##! Extra keybinds
|
||||
bind = Ctrl+Super+Alt, Slash, exec, xdg-open ~/.config/hypr/custom/keybinds.conf # Edit extra keybinds
|
||||
|
||||
# Add stuff here
|
||||
# Use #! to add an extra column on the cheatsheet
|
||||
# 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
|
||||
# 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
|
||||
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]
|
||||
bindd = Super, N, Toggle right sidebar, global, quickshell:sidebarRightToggle # Toggle right sidebar
|
||||
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
|
||||
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
|
||||
# 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, Period, Copy an emoji, exec, pkill fuzzel || ~/.local/bin/fuzzel-emoji # Pick emoji >> 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 # Emoji
|
||||
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
|
||||
# OCR
|
||||
@@ -198,8 +199,8 @@ bind = Super+Alt, E, exec, thunar # [hidden]
|
||||
bind = Super, W, exec, zen-browser # [hidden]
|
||||
bind = Super+Shift, W, exec, wps # WPS Office
|
||||
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, I, exec, XDG_CURRENT_DESKTOP="gnome" gnome-control-center # GNOME Settings
|
||||
bind = Ctrl+Shift, Escape, exec, gnome-system-monitor # GNOME System monitor
|
||||
|
||||
# Cursed stuff
|
||||
|
||||
@@ -105,15 +105,14 @@ layerrule = ignorealpha 0.6, osk[0-9]*
|
||||
|
||||
# Quickshell
|
||||
## My stuff
|
||||
layerrule = animation slide, quickshell:bar
|
||||
layerrule = animation fade, quickshell:screenCorners
|
||||
layerrule = animation slide right, quickshell:sidebarRight
|
||||
layerrule = animation slide left, quickshell:sidebarLeft
|
||||
layerrule = animation slide top, quickshell:onScreenDisplay
|
||||
layerrule = blur, quickshell:session
|
||||
layerrule = noanim, quickshell:session
|
||||
# Launchers need to be FAST
|
||||
layerrule = noanim, quickshell:overview
|
||||
layerrule = noanim, launcher
|
||||
layerrule = noanim, gtk4-layer-shell
|
||||
## outfoxxed's stuff
|
||||
layerrule = blur, shell:bar
|
||||
|
||||
@@ -8,8 +8,8 @@ pragma ComponentBehavior: Bound
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
property int sidebarLeftOpenCount: 0
|
||||
property int sidebarRightOpenCount: 0
|
||||
property bool sidebarLeftOpen: false
|
||||
property bool sidebarRightOpen: false
|
||||
property bool overviewOpen: false
|
||||
property bool workspaceShowNumbers: false
|
||||
property bool superReleaseMightTrigger: true
|
||||
@@ -31,7 +31,7 @@ Singleton {
|
||||
|
||||
GlobalShortcut {
|
||||
name: "workspaceNumber"
|
||||
description: "Hold to show workspace numbers, release to show icons"
|
||||
description: qsTr("Hold to show workspace numbers, release to show icons")
|
||||
|
||||
onPressed: {
|
||||
workspaceShowNumbersTimer.start()
|
||||
|
||||
@@ -108,7 +108,7 @@ Scope {
|
||||
ScrollHint {
|
||||
reveal: barLeftSideMouseArea.hovered
|
||||
icon: "light_mode"
|
||||
tooltipText: "Scroll to change brightness"
|
||||
tooltipText: qsTr("Scroll to change brightness")
|
||||
side: "left"
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
@@ -127,7 +127,7 @@ Scope {
|
||||
|
||||
// Layout.fillHeight: true
|
||||
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
|
||||
implicitHeight: distroIcon.height + 5*2
|
||||
|
||||
@@ -283,7 +283,7 @@ Scope {
|
||||
ScrollHint {
|
||||
reveal: barRightSideMouseArea.hovered
|
||||
icon: "volume_up"
|
||||
tooltipText: "Scroll to change volume"
|
||||
tooltipText: qsTr("Scroll to change volume")
|
||||
side: "right"
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
@@ -301,7 +301,7 @@ Scope {
|
||||
Layout.fillHeight: true
|
||||
implicitWidth: indicatorsRowLayout.implicitWidth + 10*2
|
||||
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 {
|
||||
id: indicatorsRowLayout
|
||||
anchors.centerIn: parent
|
||||
|
||||
@@ -8,6 +8,7 @@ import Quickshell.Services.UPower
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
property bool borderless: ConfigOptions.bar.borderless
|
||||
readonly property var chargeState: UPower.displayDevice.state
|
||||
readonly property bool isCharging: chargeState == UPowerDeviceState.Charging
|
||||
readonly property bool isPluggedIn: isCharging || chargeState == UPowerDeviceState.PendingCharge
|
||||
@@ -18,7 +19,7 @@ Rectangle {
|
||||
|
||||
implicitWidth: rowLayout.implicitWidth + rowLayout.spacing * 2
|
||||
implicitHeight: 32
|
||||
color: Appearance.colors.colLayer1
|
||||
color: borderless ? "transparent" : Appearance.colors.colLayer1
|
||||
radius: Appearance.rounding.small
|
||||
|
||||
RowLayout {
|
||||
@@ -31,18 +32,14 @@ Rectangle {
|
||||
implicitWidth: (isCharging ? (boltIconLoader?.item?.width ?? 0) : 0)
|
||||
|
||||
Behavior on implicitWidth {
|
||||
NumberAnimation {
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
color: Appearance.colors.colOnLayer1
|
||||
text: `${Math.round(percentage * 100)}%`
|
||||
text: `${Math.round(percentage * 100)}`
|
||||
}
|
||||
|
||||
CircularProgress {
|
||||
@@ -56,6 +53,7 @@ Rectangle {
|
||||
|
||||
MaterialSymbol {
|
||||
anchors.centerIn: parent
|
||||
fill: 1
|
||||
text: "battery_full"
|
||||
iconSize: Appearance.font.pixelSize.normal
|
||||
color: (isLow && !isCharging) ? batteryLowOnBackground : Appearance.m3colors.m3onSecondaryContainer
|
||||
@@ -91,12 +89,7 @@ Rectangle {
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import "root:/modules/common"
|
||||
import "root:/modules/common/widgets/"
|
||||
import "root:/modules/common/functions/color_utils.js" as ColorUtils
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
@@ -21,7 +22,9 @@ Button {
|
||||
background: Rectangle {
|
||||
anchors.fill: parent
|
||||
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
|
||||
|
||||
Rectangle {
|
||||
property bool borderless: ConfigOptions.bar.borderless
|
||||
implicitWidth: rowLayout.implicitWidth + rowLayout.spacing * 6 // idk, text seems nicer w/ more padding
|
||||
implicitHeight: 32
|
||||
color: Appearance.colors.colLayer1
|
||||
color: borderless ? "transparent" : Appearance.colors.colLayer1
|
||||
radius: Appearance.rounding.small
|
||||
|
||||
RowLayout {
|
||||
|
||||
@@ -1,29 +1,24 @@
|
||||
import "root:/modules/common"
|
||||
import "root:/modules/common/widgets"
|
||||
import "root:/services"
|
||||
import "root:/modules/common/functions/string_utils.js" as StringUtils
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Services.Mpris
|
||||
import Quickshell.Hyprland
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property bool borderless: ConfigOptions.bar.borderless
|
||||
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
|
||||
implicitWidth: rowLayout.implicitWidth + rowLayout.spacing * 2
|
||||
implicitHeight: 40
|
||||
|
||||
// Background
|
||||
Rectangle {
|
||||
anchors.centerIn: parent
|
||||
width: parent.width
|
||||
implicitHeight: 32
|
||||
color: Appearance.colors.colLayer1
|
||||
radius: Appearance.rounding.small
|
||||
}
|
||||
|
||||
Timer {
|
||||
running: activePlayer?.playbackState == MprisPlaybackState.Playing
|
||||
interval: 1000
|
||||
@@ -33,7 +28,7 @@ Item {
|
||||
|
||||
MouseArea {
|
||||
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) => {
|
||||
if (event.button === Qt.MiddleButton) {
|
||||
activePlayer.togglePlaying();
|
||||
@@ -41,11 +36,21 @@ Item {
|
||||
activePlayer.previous();
|
||||
} else if (event.button === Qt.ForwardButton || event.button === Qt.RightButton) {
|
||||
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
|
||||
|
||||
spacing: 4
|
||||
@@ -62,6 +67,7 @@ Item {
|
||||
|
||||
MaterialSymbol {
|
||||
anchors.centerIn: parent
|
||||
fill: 1
|
||||
text: activePlayer?.isPlaying ? "pause" : "play_arrow"
|
||||
iconSize: Appearance.font.pixelSize.normal
|
||||
color: Appearance.m3colors.m3onSecondaryContainer
|
||||
|
||||
@@ -28,6 +28,7 @@ Item {
|
||||
|
||||
MaterialSymbol {
|
||||
anchors.centerIn: parent
|
||||
fill: 1
|
||||
text: iconName
|
||||
iconSize: Appearance.font.pixelSize.normal
|
||||
color: Appearance.m3colors.m3onSecondaryContainer
|
||||
@@ -38,15 +39,11 @@ Item {
|
||||
StyledText {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
color: Appearance.colors.colOnLayer1
|
||||
text: `${Math.round(percentage * 100)}%`
|
||||
text: `${Math.round(percentage * 100)}`
|
||||
}
|
||||
|
||||
Behavior on x {
|
||||
NumberAnimation {
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,9 +8,10 @@ import Quickshell.Io
|
||||
import Quickshell.Services.Mpris
|
||||
|
||||
Rectangle {
|
||||
property bool borderless: ConfigOptions.bar.borderless
|
||||
implicitWidth: rowLayout.implicitWidth + rowLayout.anchors.leftMargin + rowLayout.anchors.rightMargin
|
||||
implicitHeight: 32
|
||||
color: Appearance.colors.colLayer1
|
||||
color: borderless ? "transparent" : Appearance.colors.colLayer1
|
||||
radius: Appearance.rounding.small
|
||||
|
||||
RowLayout {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import "root:/modules/common/"
|
||||
import "root:/modules/common/functions/color_utils.js" as ColorUtils
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
@@ -57,7 +58,7 @@ MouseArea {
|
||||
ColorOverlay {
|
||||
anchors.fill: 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
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
property bool borderless: ConfigOptions.bar.borderless
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
implicitWidth: rowLayout.implicitWidth + rowLayout.spacing * 2
|
||||
implicitHeight: 32
|
||||
color: Appearance.colors.colLayer1
|
||||
color: borderless ? "transparent" : Appearance.colors.colLayer1
|
||||
radius: Appearance.rounding.small
|
||||
|
||||
RowLayout {
|
||||
@@ -24,7 +26,8 @@ Rectangle {
|
||||
onClicked: Hyprland.dispatch("exec grimblast copy area")
|
||||
|
||||
MaterialSymbol {
|
||||
anchors.centerIn: parent
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
fill: 1
|
||||
text: "screenshot_region"
|
||||
iconSize: Appearance.font.pixelSize.normal
|
||||
color: Appearance.colors.colOnLayer2
|
||||
@@ -37,7 +40,8 @@ Rectangle {
|
||||
onClicked: Hyprland.dispatch("exec hyprpicker -a")
|
||||
|
||||
MaterialSymbol {
|
||||
anchors.centerIn: parent
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
fill: 1
|
||||
text: "colorize"
|
||||
iconSize: Appearance.font.pixelSize.normal
|
||||
color: Appearance.colors.colOnLayer2
|
||||
|
||||
@@ -15,6 +15,7 @@ import Qt5Compat.GraphicalEffects
|
||||
|
||||
Item {
|
||||
required property var bar
|
||||
property bool borderless: ConfigOptions.bar.borderless
|
||||
readonly property HyprlandMonitor monitor: Hyprland.monitorFor(bar.screen)
|
||||
readonly property Toplevel activeWindow: ToplevelManager.activeToplevel
|
||||
|
||||
@@ -66,7 +67,7 @@ Item {
|
||||
implicitHeight: 32
|
||||
implicitWidth: rowLayout.implicitWidth + widgetPadding * 2
|
||||
radius: Appearance.rounding.small
|
||||
color: Appearance.colors.colLayer1
|
||||
color: borderless ? "transparent" : Appearance.colors.colLayer1
|
||||
}
|
||||
|
||||
// Scroll to switch workspaces
|
||||
@@ -119,26 +120,14 @@ Item {
|
||||
opacity: (workspaceOccupied[index] && !(!activeWindow?.activated && monitor.activeWorkspace?.id === index+1)) ? 1 : 0
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
}
|
||||
Behavior on radiusLeft {
|
||||
NumberAnimation {
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
Behavior on radiusRight {
|
||||
NumberAnimation {
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -206,15 +195,6 @@ Item {
|
||||
(workspaceOccupied[index] ? Appearance.colors.colOnLayer1 :
|
||||
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 {
|
||||
NumberAnimation {
|
||||
duration: Appearance.animation.elementMoveFast.duration
|
||||
|
||||
@@ -2,10 +2,11 @@ import "root:/"
|
||||
import "root:/services"
|
||||
import "root:/modules/common"
|
||||
import "root:/modules/common/widgets"
|
||||
import "root:/modules/common/functions/color_utils.js" as ColorUtils
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Effects
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import Quickshell.Io
|
||||
import Quickshell
|
||||
import Quickshell.Widgets
|
||||
@@ -15,18 +16,24 @@ import Quickshell.Hyprland
|
||||
Scope { // Scope
|
||||
id: root
|
||||
|
||||
Variants { // Window repeater
|
||||
id: cheatsheetVariants
|
||||
model: Quickshell.screens
|
||||
|
||||
PanelWindow { // Window
|
||||
Loader {
|
||||
id: cheatsheetLoader
|
||||
active: false
|
||||
|
||||
sourceComponent: PanelWindow { // Window
|
||||
id: cheatsheetRoot
|
||||
visible: false
|
||||
focusable: true
|
||||
visible: cheatsheetLoader.active
|
||||
|
||||
property var modelData
|
||||
anchors {
|
||||
top: true
|
||||
bottom: true
|
||||
left: true
|
||||
right: true
|
||||
}
|
||||
|
||||
screen: modelData
|
||||
function hide() {
|
||||
cheatsheetLoader.active = false
|
||||
}
|
||||
exclusiveZone: 0
|
||||
implicitWidth: cheatsheetBackground.width + Appearance.sizes.elevationMargin * 2
|
||||
implicitHeight: cheatsheetBackground.height + Appearance.sizes.elevationMargin * 2
|
||||
@@ -42,25 +49,9 @@ Scope { // Scope
|
||||
HyprlandFocusGrab { // Click outside to close
|
||||
id: grab
|
||||
windows: [ cheatsheetRoot ]
|
||||
active: false
|
||||
active: cheatsheetRoot.visible
|
||||
onCleared: () => {
|
||||
if (!active) cheatsheetRoot.visible = false
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: cheatsheetRoot
|
||||
function onVisibleChanged() {
|
||||
delayedGrabTimer.start()
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: delayedGrabTimer
|
||||
interval: ConfigOptions.hacks.arbitraryRaceConditionDelay
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
grab.active = cheatsheetRoot.visible
|
||||
if (!active) cheatsheetRoot.hide()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,9 +65,19 @@ Scope { // Scope
|
||||
implicitWidth: cheatsheetColumnLayout.implicitWidth + 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
|
||||
if (event.key === Qt.Key_Escape) {
|
||||
cheatsheetRoot.visible = false
|
||||
cheatsheetRoot.hide()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,23 +95,19 @@ Scope { // Scope
|
||||
|
||||
PointingHandInteraction {}
|
||||
onClicked: {
|
||||
cheatsheetRoot.visible = false
|
||||
cheatsheetRoot.hide()
|
||||
}
|
||||
|
||||
background: Item {}
|
||||
background: null
|
||||
contentItem: Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: Appearance.rounding.full
|
||||
color: closeButton.pressed ? Appearance.colors.colLayer0Active :
|
||||
closeButton.hovered ? Appearance.colors.colLayer0Hover :
|
||||
Appearance.transparentize(Appearance.colors.colLayer0, 1)
|
||||
ColorUtils.transparentize(Appearance.colors.colLayer0, 1)
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Appearance.animation.elementMoveFast.duration
|
||||
easing.type: Appearance.animation.elementMoveFast.type
|
||||
easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
|
||||
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 {
|
||||
target: "cheatsheet"
|
||||
|
||||
function toggle(): void {
|
||||
for (let i = 0; i < cheatsheetVariants.instances.length; i++) {
|
||||
let panelWindow = cheatsheetVariants.instances[i];
|
||||
if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
|
||||
panelWindow.visible = !panelWindow.visible;
|
||||
if(panelWindow.visible) Notifications.timeoutAll();
|
||||
}
|
||||
}
|
||||
cheatsheetLoader.active = !cheatsheetLoader.active
|
||||
}
|
||||
|
||||
function close(): void {
|
||||
for (let i = 0; i < cheatsheetVariants.instances.length; i++) {
|
||||
let panelWindow = cheatsheetVariants.instances[i];
|
||||
if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
|
||||
panelWindow.visible = false;
|
||||
}
|
||||
}
|
||||
cheatsheetLoader.active = false
|
||||
}
|
||||
|
||||
function open(): void {
|
||||
for (let i = 0; i < cheatsheetVariants.instances.length; i++) {
|
||||
let panelWindow = cheatsheetVariants.instances[i];
|
||||
if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
|
||||
panelWindow.visible = true;
|
||||
if(panelWindow.visible) Notifications.timeoutAll();
|
||||
}
|
||||
}
|
||||
cheatsheetLoader.active = true
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "cheatsheetToggle"
|
||||
description: "Toggles cheatsheet on press"
|
||||
description: qsTr("Toggles cheatsheet on press")
|
||||
|
||||
onPressed: {
|
||||
for (let i = 0; i < cheatsheetVariants.instances.length; i++) {
|
||||
let panelWindow = cheatsheetVariants.instances[i];
|
||||
if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
|
||||
panelWindow.visible = !panelWindow.visible;
|
||||
if(panelWindow.visible) Notifications.timeoutAll();
|
||||
}
|
||||
}
|
||||
cheatsheetLoader.active = !cheatsheetLoader.active;
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "cheatsheetOpen"
|
||||
description: "Opens cheatsheet on press"
|
||||
description: qsTr("Opens cheatsheet on press")
|
||||
|
||||
onPressed: {
|
||||
for (let i = 0; i < cheatsheetVariants.instances.length; i++) {
|
||||
let panelWindow = cheatsheetVariants.instances[i];
|
||||
if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
|
||||
panelWindow.visible = true;
|
||||
if(panelWindow.visible) Notifications.timeoutAll();
|
||||
}
|
||||
}
|
||||
cheatsheetLoader.active = true;
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "cheatsheetClose"
|
||||
description: "Closes cheatsheet on press"
|
||||
description: qsTr("Closes cheatsheet on press")
|
||||
|
||||
onPressed: {
|
||||
for (let i = 0; i < cheatsheetVariants.instances.length; i++) {
|
||||
let panelWindow = cheatsheetVariants.instances[i];
|
||||
if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
|
||||
panelWindow.visible = false;
|
||||
}
|
||||
}
|
||||
cheatsheetLoader.active = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import "root:/modules/common/functions/color_utils.js" as ColorUtils
|
||||
pragma Singleton
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
property QtObject m3colors
|
||||
property QtObject animation
|
||||
property QtObject animationCurves
|
||||
@@ -13,18 +15,6 @@ Singleton {
|
||||
property QtObject sizes
|
||||
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 {
|
||||
property bool darkmode: false
|
||||
property bool transparent: false
|
||||
@@ -108,37 +98,37 @@ Singleton {
|
||||
property color colSubtext: m3colors.m3outline
|
||||
property color colLayer0: m3colors.m3background
|
||||
property color colOnLayer0: m3colors.m3onBackground
|
||||
property color colLayer0Hover: mix(colLayer0, colOnLayer0, 0.9)
|
||||
property color colLayer0Active: mix(colLayer0, colOnLayer0, 0.8)
|
||||
property color colLayer0Hover: ColorUtils.mix(colLayer0, colOnLayer0, 0.9)
|
||||
property color colLayer0Active: ColorUtils.mix(colLayer0, colOnLayer0, 0.8)
|
||||
property color colLayer1: m3colors.m3surfaceContainerLow;
|
||||
property color colOnLayer1: m3colors.m3onSurfaceVariant;
|
||||
property color colOnLayer1Inactive: mix(colOnLayer1, colLayer1, 0.45);
|
||||
property color colLayer2: mix(m3colors.m3surfaceContainer, m3colors.m3surfaceContainerHigh, 0.55);
|
||||
property color colOnLayer1Inactive: ColorUtils.mix(colOnLayer1, colLayer1, 0.45);
|
||||
property color colLayer2: ColorUtils.mix(m3colors.m3surfaceContainer, m3colors.m3surfaceContainerHigh, 0.55);
|
||||
property color colOnLayer2: m3colors.m3onSurface;
|
||||
property color colOnLayer2Disabled: mix(colOnLayer2, m3colors.m3background, 0.4);
|
||||
property color colLayer3: mix(m3colors.m3surfaceContainerHigh, m3colors.m3onSurface, 0.96);
|
||||
property color colOnLayer2Disabled: ColorUtils.mix(colOnLayer2, m3colors.m3background, 0.4);
|
||||
property color colLayer3: ColorUtils.mix(m3colors.m3surfaceContainerHigh, m3colors.m3onSurface, 0.96);
|
||||
property color colOnLayer3: m3colors.m3onSurface;
|
||||
property color colLayer1Hover: mix(colLayer1, colOnLayer1, 0.88);
|
||||
property color colLayer1Active: mix(colLayer1, colOnLayer1, 0.77);
|
||||
property color colLayer2Hover: mix(colLayer2, colOnLayer2, 0.90);
|
||||
property color colLayer2Active: mix(colLayer2, colOnLayer2, 0.80);
|
||||
property color colLayer2Disabled: mix(colLayer2, m3colors.m3background, 0.8);
|
||||
property color colLayer3Hover: mix(colLayer3, colOnLayer3, 0.90);
|
||||
property color colLayer3Active: mix(colLayer3, colOnLayer3, 0.80);
|
||||
property color colPrimaryHover: mix(m3colors.m3primary, colLayer1Hover, 0.85)
|
||||
property color colPrimaryActive: mix(m3colors.m3primary, colLayer1Active, 0.7)
|
||||
property color colPrimaryContainerHover: mix(m3colors.m3primaryContainer, colLayer1Hover, 0.7)
|
||||
property color colPrimaryContainerActive: mix(m3colors.m3primaryContainer, colLayer1Active, 0.6)
|
||||
property color colSecondaryHover: mix(m3colors.m3secondary, colLayer1Hover, 0.85)
|
||||
property color colSecondaryActive: mix(m3colors.m3secondary, colLayer1Active, 0.4)
|
||||
property color colSecondaryContainerHover: mix(m3colors.m3secondaryContainer, colLayer1Hover, 0.6)
|
||||
property color colSecondaryContainerActive: mix(m3colors.m3secondaryContainer, colLayer1Active, 0.54)
|
||||
property color colSurfaceContainerHighestHover: mix(m3colors.m3surfaceContainerHighest, m3colors.m3onSurface, 0.95)
|
||||
property color colSurfaceContainerHighestActive: mix(m3colors.m3surfaceContainerHighest, m3colors.m3onSurface, 0.85)
|
||||
property color colLayer1Hover: ColorUtils.mix(colLayer1, colOnLayer1, 0.92);
|
||||
property color colLayer1Active: ColorUtils.mix(colLayer1, colOnLayer1, 0.85);
|
||||
property color colLayer2Hover: ColorUtils.mix(colLayer2, colOnLayer2, 0.90);
|
||||
property color colLayer2Active: ColorUtils.mix(colLayer2, colOnLayer2, 0.80);
|
||||
property color colLayer2Disabled: ColorUtils.mix(colLayer2, m3colors.m3background, 0.8);
|
||||
property color colLayer3Hover: ColorUtils.mix(colLayer3, colOnLayer3, 0.90);
|
||||
property color colLayer3Active: ColorUtils.mix(colLayer3, colOnLayer3, 0.80);
|
||||
property color colPrimaryHover: ColorUtils.mix(m3colors.m3primary, colLayer1Hover, 0.85)
|
||||
property color colPrimaryActive: ColorUtils.mix(m3colors.m3primary, colLayer1Active, 0.7)
|
||||
property color colPrimaryContainerHover: ColorUtils.mix(m3colors.m3primaryContainer, colLayer1Hover, 0.7)
|
||||
property color colPrimaryContainerActive: ColorUtils.mix(m3colors.m3primaryContainer, colLayer1Active, 0.6)
|
||||
property color colSecondaryHover: ColorUtils.mix(m3colors.m3secondary, colLayer1Hover, 0.85)
|
||||
property color colSecondaryActive: ColorUtils.mix(m3colors.m3secondary, colLayer1Active, 0.4)
|
||||
property color colSecondaryContainerHover: ColorUtils.mix(m3colors.m3secondaryContainer, colLayer1Hover, 0.6)
|
||||
property color colSecondaryContainerActive: ColorUtils.mix(m3colors.m3secondaryContainer, colLayer1Active, 0.54)
|
||||
property color colSurfaceContainerHighestHover: ColorUtils.mix(m3colors.m3surfaceContainerHighest, m3colors.m3onSurface, 0.95)
|
||||
property color colSurfaceContainerHighestActive: ColorUtils.mix(m3colors.m3surfaceContainerHighest, m3colors.m3onSurface, 0.85)
|
||||
property color colTooltip: "#3C4043" // m3colors.m3inverseSurface in the specs, but the m3 website actually uses this color
|
||||
property color 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 colShadow: transparentize(m3colors.m3shadow, 0.75)
|
||||
property color colScrim: ColorUtils.transparentize(m3colors.m3scrim, 0.5)
|
||||
property color colShadow: ColorUtils.transparentize(m3colors.m3shadow, 0.75)
|
||||
}
|
||||
|
||||
rounding: QtObject {
|
||||
@@ -189,24 +179,57 @@ Singleton {
|
||||
property int type: Easing.BezierSpline
|
||||
property list<real> bezierCurve: animationCurves.emphasized
|
||||
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 int duration: 400
|
||||
property int type: Easing.BezierSpline
|
||||
property list<real> bezierCurve: animationCurves.emphasizedDecel
|
||||
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 int duration: 200
|
||||
property int type: Easing.BezierSpline
|
||||
property list<real> bezierCurve: animationCurves.emphasizedAccel
|
||||
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 int duration: 200
|
||||
property int type: Easing.BezierSpline
|
||||
property list<real> bezierCurve: animationCurves.standardDecel
|
||||
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 int duration: 400
|
||||
@@ -226,18 +249,21 @@ Singleton {
|
||||
}
|
||||
|
||||
sizes: QtObject {
|
||||
property int barHeight: 40
|
||||
property int barCenterSideModuleWidth: 360
|
||||
property int barPreferredSideSectionWidth: 400
|
||||
property int sidebarWidth: 450
|
||||
property int sidebarWidthExtended: 750
|
||||
property int notificationPopupWidth: 410
|
||||
property int searchWidthCollapsed: 260
|
||||
property int searchWidth: 450
|
||||
property int hyprlandGapsOut: 5
|
||||
property int elevationMargin: 7
|
||||
property int fabShadowRadius: 5
|
||||
property int fabHoveredShadowRadius: 7
|
||||
property real barHeight: 40
|
||||
property real barCenterSideModuleWidth: 360
|
||||
property real barPreferredSideSectionWidth: 400
|
||||
property real sidebarWidth: 460
|
||||
property real sidebarWidthExtended: 750
|
||||
property real osdWidth: 200
|
||||
property real mediaControlsWidth: 440
|
||||
property real mediaControlsHeight: 160
|
||||
property real notificationPopupWidth: 410
|
||||
property real searchWidthCollapsed: 260
|
||||
property real searchWidth: 450
|
||||
property real hyprlandGapsOut: 5
|
||||
property real elevationMargin: 8
|
||||
property real fabShadowRadius: 5
|
||||
property real fabHoveredShadowRadius: 7
|
||||
}
|
||||
|
||||
syntaxHighlightingTheme: Appearance.m3colors.darkmode ? "Monokai" : "ayu Light"
|
||||
|
||||
@@ -5,7 +5,7 @@ pragma ComponentBehavior: Bound
|
||||
|
||||
Singleton {
|
||||
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 {
|
||||
@@ -13,10 +13,10 @@ Singleton {
|
||||
}
|
||||
|
||||
property QtObject apps: QtObject {
|
||||
property string bluetooth: "blueberry"
|
||||
property string bluetooth: "better-control --bluetooth"
|
||||
property string imageViewer: "loupe"
|
||||
property string network: "XDG_CURRENT_DESKTOP=\"gnome\" gnome-control-center wifi"
|
||||
property string settings: "XDG_CURRENT_DESKTOP=\"gnome\" gnome-control-center"
|
||||
property string network: "better-control --wifi"
|
||||
property string settings: "better-control"
|
||||
property string taskManager: "gnome-usage"
|
||||
property string terminal: "foot" // This is only for shell actions
|
||||
}
|
||||
@@ -25,6 +25,7 @@ Singleton {
|
||||
property int batteryLowThreshold: 20
|
||||
property string topLeftIcon: "spark" // Options: distro, spark
|
||||
property bool showBackground: true
|
||||
property bool borderless: false
|
||||
property QtObject resources: QtObject {
|
||||
property bool alwaysShowSwap: true
|
||||
property bool alwaysShowCpu: false
|
||||
@@ -70,7 +71,7 @@ Singleton {
|
||||
property string defaultProvider: "yandere"
|
||||
property int limit: 20
|
||||
property QtObject zerochan: QtObject {
|
||||
property string username: ""
|
||||
property string username: "[unset]"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,12 +9,6 @@ Singleton {
|
||||
}
|
||||
|
||||
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 bool collapsed: false
|
||||
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));
|
||||
}
|
||||
@@ -147,4 +147,32 @@ function wordWrap(str, maxLen) {
|
||||
}
|
||||
if (current.length > 0) lines.push(current);
|
||||
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/functions/color_utils.js" as ColorUtils
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
@@ -17,14 +18,12 @@ Button {
|
||||
background: Rectangle {
|
||||
anchors.fill: parent
|
||||
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 {
|
||||
ColorAnimation {
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
|
||||
}
|
||||
|
||||
@@ -41,11 +40,7 @@ Button {
|
||||
color: button.enabled ? Appearance.m3colors.m3primary : Appearance.m3colors.m3outline
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import "root:/modules/common"
|
||||
import "root:/modules/common/functions/color_utils.js" as ColorUtils
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
@@ -16,17 +17,12 @@ Button {
|
||||
|
||||
background: Rectangle {
|
||||
anchors.fill: parent
|
||||
color: (button.down && button.enabled) ? Appearance.transparentize(Appearance.m3colors.m3onSurface, 0.84) :
|
||||
((button.hovered && button.enabled) ? Appearance.transparentize(Appearance.m3colors.m3onSurface, 0.92) :
|
||||
Appearance.transparentize(Appearance.m3colors.m3onSurface, 1))
|
||||
color: (button.down && button.enabled) ? ColorUtils.transparentize(Appearance.m3colors.m3onSurface, 0.84) :
|
||||
((button.hovered && button.enabled) ? ColorUtils.transparentize(Appearance.m3colors.m3onSurface, 0.92) :
|
||||
ColorUtils.transparentize(Appearance.m3colors.m3onSurface, 1))
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -42,11 +38,7 @@ Button {
|
||||
color: button.enabled ? Appearance.m3colors.m3onSurface : Appearance.m3colors.m3outline
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import "root:/modules/common"
|
||||
import "root:/modules/common/widgets"
|
||||
import "root:/modules/common/functions/color_utils.js" as ColorUtils
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
@@ -16,7 +17,7 @@ Button {
|
||||
implicitHeight: columnLayout.implicitHeight
|
||||
implicitWidth: columnLayout.implicitWidth
|
||||
|
||||
background: Item {}
|
||||
background: null
|
||||
PointingHandInteraction {}
|
||||
|
||||
// Real stuff
|
||||
@@ -30,14 +31,10 @@ Button {
|
||||
radius: Appearance.rounding.full
|
||||
color: toggled ?
|
||||
(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 {
|
||||
ColorAnimation {
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
MaterialSymbol {
|
||||
id: navRailButtonIcon
|
||||
@@ -48,11 +45,7 @@ Button {
|
||||
color: toggled ? Appearance.m3colors.m3onSecondaryContainer : Appearance.colors.colOnLayer1
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import "root:/modules/common"
|
||||
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.Controls
|
||||
import QtQuick.Effects
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
@@ -126,7 +128,7 @@ Item {
|
||||
onPressAndHold: (mouse) => {
|
||||
if (mouse.button === Qt.LeftButton) {
|
||||
Hyprland.dispatch(`exec wl-copy '${StringUtils.shellSingleQuoteEscape(notificationObject.body)}'`)
|
||||
notificationSummaryText.text = `${notificationObject.summary} (copied)`
|
||||
notificationSummaryText.text = String.format(qsTr("{0} (copied)"), notificationObject.summary)
|
||||
}
|
||||
}
|
||||
onDragStartedChanged: () => {
|
||||
@@ -187,9 +189,19 @@ Item {
|
||||
height: notificationColumnLayout.implicitHeight
|
||||
|
||||
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
|
||||
|
||||
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 {
|
||||
enabled: enableAnimation
|
||||
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
|
||||
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
|
||||
iconSize: 27
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
@@ -422,17 +420,11 @@ Item {
|
||||
background: Rectangle {
|
||||
anchors.fill: parent
|
||||
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 {
|
||||
ColorAnimation {
|
||||
duration: Appearance.animation.elementMoveFast.duration
|
||||
easing.type: Appearance.animation.elementMoveFast.type
|
||||
easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve
|
||||
}
|
||||
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
contentItem: MaterialSymbol {
|
||||
|
||||
@@ -52,38 +52,31 @@ ColumnLayout {
|
||||
root.enableIndicatorAnimation = true
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
radius: Appearance.rounding.full
|
||||
z: 2
|
||||
|
||||
anchors.fill: parent
|
||||
// TODO: make the end point in the moving direction go first
|
||||
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;
|
||||
Behavior on x {
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
}
|
||||
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 implicitWidth {
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import "root:/modules/common"
|
||||
import "root:/modules/common/widgets"
|
||||
import "root:/modules/common/functions/color_utils.js" as ColorUtils
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
@@ -12,24 +14,108 @@ TabButton {
|
||||
property string buttonIcon
|
||||
property bool selected: false
|
||||
property int tabContentWidth: contentItem.children[0].implicitWidth
|
||||
property int rippleDuration: 1200
|
||||
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 {
|
||||
id: buttonBackground
|
||||
radius: Appearance.rounding.small
|
||||
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 {
|
||||
ColorAnimation {
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
|
||||
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 {
|
||||
anchors.centerIn: buttonBackground
|
||||
ColumnLayout {
|
||||
@@ -44,11 +130,7 @@ TabButton {
|
||||
fill: selected ? 1 : 0
|
||||
color: selected ? Appearance.m3colors.m3primary : Appearance.colors.colOnLayer1
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
}
|
||||
StyledText {
|
||||
@@ -59,11 +141,7 @@ TabButton {
|
||||
color: selected ? Appearance.m3colors.m3primary : Appearance.colors.colOnLayer1
|
||||
text: buttonText
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,18 +16,10 @@ Item {
|
||||
|
||||
Behavior on implicitWidth {
|
||||
enabled: !vertical
|
||||
NumberAnimation {
|
||||
duration: Appearance.animation.elementMoveEnter.duration
|
||||
easing.type: Appearance.animation.elementMoveEnter.type
|
||||
easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||
}
|
||||
Behavior on implicitHeight {
|
||||
enabled: vertical
|
||||
NumberAnimation {
|
||||
duration: Appearance.animation.elementMoveEnter.duration
|
||||
easing.type: Appearance.animation.elementMoveEnter.type
|
||||
easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import "root:/modules/common"
|
||||
import "root:/modules/common/widgets"
|
||||
import "root:/modules/common/functions/color_utils.js" as ColorUtils
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
@@ -11,25 +13,111 @@ TabButton {
|
||||
property string buttonText
|
||||
property string buttonIcon
|
||||
property bool selected: false
|
||||
property int rippleDuration: 1200
|
||||
height: buttonBackground.height
|
||||
property int tabContentWidth: buttonBackground.width - buttonBackground.radius*2
|
||||
|
||||
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 {
|
||||
id: buttonBackground
|
||||
radius: Appearance.rounding.small
|
||||
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 {
|
||||
ColorAnimation {
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
|
||||
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 {
|
||||
anchors.centerIn: buttonBackground
|
||||
RowLayout {
|
||||
@@ -52,11 +140,7 @@ TabButton {
|
||||
fill: selected ? 1 : 0
|
||||
color: selected ? Appearance.m3colors.m3primary : Appearance.colors.colOnLayer1
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -67,11 +151,7 @@ TabButton {
|
||||
color: selected ? Appearance.m3colors.m3primary : Appearance.colors.colOnLayer1
|
||||
text: buttonText
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,13 +13,11 @@ ProgressBar {
|
||||
property real valueBarWidth: 120
|
||||
property real valueBarHeight: 4
|
||||
property real valueBarGap: 4
|
||||
property color highlightColor: Appearance.m3colors.m3primary
|
||||
property color trackColor: Appearance.m3colors.m3secondaryContainer
|
||||
|
||||
Behavior on value {
|
||||
NumberAnimation {
|
||||
duration: Appearance.animation.elementMoveEnter.duration
|
||||
easing.type: Appearance.animation.elementMoveEnter.type
|
||||
easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
@@ -38,21 +36,21 @@ ProgressBar {
|
||||
width: root.visualPosition * parent.width
|
||||
height: parent.height
|
||||
radius: Appearance.rounding.full
|
||||
color: Appearance.m3colors.m3primary
|
||||
color: root.highlightColor
|
||||
}
|
||||
Rectangle { // Right remaining part fill
|
||||
anchors.right: parent.right
|
||||
width: (1 - root.visualPosition) * parent.width - valueBarGap
|
||||
height: parent.height
|
||||
radius: Appearance.rounding.full
|
||||
color: Appearance.m3colors.m3secondaryContainer
|
||||
color: root.trackColor
|
||||
}
|
||||
Rectangle { // Stop point
|
||||
anchors.right: parent.right
|
||||
width: valueBarGap
|
||||
height: valueBarGap
|
||||
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 handleHeight: 44 * 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 string tooltipContent: `${Math.round(value * 100)}%`
|
||||
Layout.fillWidth: true
|
||||
from: 0
|
||||
to: 1
|
||||
@@ -44,14 +49,15 @@ Slider {
|
||||
|
||||
background: Item {
|
||||
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
|
||||
Rectangle {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
width: slider.handleLimit + slider.visualPosition * slider.limitedHandleRangeWidth - (slider.handleMargins + slider.handleWidth / 2)
|
||||
height: parent.height
|
||||
color: Appearance.m3colors.m3primary
|
||||
height: trackHeight
|
||||
color: slider.highlightColor
|
||||
topLeftRadius: Appearance.rounding.full
|
||||
bottomLeftRadius: Appearance.rounding.full
|
||||
topRightRadius: Appearance.rounding.unsharpen
|
||||
@@ -60,10 +66,11 @@ Slider {
|
||||
|
||||
// Fill right
|
||||
Rectangle {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: parent.right
|
||||
width: slider.handleLimit + (1 - slider.visualPosition) * slider.limitedHandleRangeWidth - (slider.handleMargins + slider.handleWidth / 2)
|
||||
height: parent.height
|
||||
color: Appearance.m3colors.m3secondaryContainer
|
||||
height: trackHeight
|
||||
color: slider.trackColor
|
||||
topLeftRadius: Appearance.rounding.unsharpen
|
||||
bottomLeftRadius: Appearance.rounding.unsharpen
|
||||
topRightRadius: Appearance.rounding.full
|
||||
@@ -78,7 +85,7 @@ Slider {
|
||||
width: slider.backgroundDotSize
|
||||
height: slider.backgroundDotSize
|
||||
radius: Appearance.rounding.full
|
||||
color: Appearance.m3colors.m3onSecondaryContainer
|
||||
color: slider.handleColor
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,7 +96,7 @@ Slider {
|
||||
implicitWidth: slider.handleWidth
|
||||
implicitHeight: slider.handleHeight
|
||||
radius: Appearance.rounding.full
|
||||
color: Appearance.m3colors.m3onSecondaryContainer
|
||||
color: slider.handleColor
|
||||
|
||||
Behavior on implicitWidth {
|
||||
NumberAnimation {
|
||||
@@ -101,7 +108,7 @@ Slider {
|
||||
|
||||
StyledToolTip {
|
||||
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
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMove.colorAnimation.createObject(this)
|
||||
}
|
||||
Behavior on border.color {
|
||||
ColorAnimation {
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMove.colorAnimation.createObject(this)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
Behavior on anchors.leftMargin {
|
||||
NumberAnimation {
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
}
|
||||
Behavior on width {
|
||||
NumberAnimation {
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
}
|
||||
Behavior on height {
|
||||
NumberAnimation {
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
}
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMove.colorAnimation.createObject(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ ToolTip {
|
||||
}
|
||||
}
|
||||
|
||||
background: Item {}
|
||||
background: null
|
||||
|
||||
contentItem: Item {
|
||||
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 Quickshell
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
|
||||
Scope {
|
||||
id: screenCorners
|
||||
readonly property Toplevel activeWindow: ToplevelManager.activeToplevel
|
||||
id: notificationPopup
|
||||
|
||||
Variants {
|
||||
model: Quickshell.screens
|
||||
LazyLoader {
|
||||
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 var modelData
|
||||
loading: true
|
||||
PanelWindow {
|
||||
id: root
|
||||
visible: (columnLayout.children.length > 0 || notificationWidgetList.length > 0)
|
||||
property Component notifComponent: NotificationWidget {}
|
||||
property list<NotificationWidget> notificationWidgetList: []
|
||||
|
||||
property Component notifComponent: NotificationWidget {}
|
||||
property list<NotificationWidget> notificationWidgetList: []
|
||||
|
||||
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
|
||||
}
|
||||
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.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 {
|
||||
id: root
|
||||
property bool showOsdValues: false
|
||||
property var focusedScreen: Quickshell.screens.find(s => s.name === Hyprland.focusedMonitor?.name)
|
||||
property var brightnessMonitor: Brightness.getMonitorForScreen(focusedScreen)
|
||||
|
||||
function triggerOsd() {
|
||||
showOsdValues = true
|
||||
@@ -36,81 +38,79 @@ Scope {
|
||||
}
|
||||
}
|
||||
|
||||
Variants {
|
||||
model: Quickshell.screens
|
||||
Connections {
|
||||
target: Brightness
|
||||
function onBrightnessChanged() {
|
||||
if (!root.brightnessMonitor.ready) return
|
||||
root.triggerOsd()
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: osdLoader
|
||||
property var modelData
|
||||
active: showOsdValues
|
||||
property var brightnessMonitor: Brightness.getMonitorForScreen(modelData)
|
||||
Loader {
|
||||
id: osdLoader
|
||||
active: showOsdValues
|
||||
|
||||
PanelWindow {
|
||||
id: osdRoot
|
||||
|
||||
Connections {
|
||||
target: brightnessMonitor
|
||||
function onBrightnessChanged() {
|
||||
if (!brightnessMonitor.ready) return
|
||||
root.triggerOsd()
|
||||
target: root
|
||||
function onFocusedScreenChanged() {
|
||||
osdRoot.screen = root.focusedScreen
|
||||
}
|
||||
}
|
||||
|
||||
PanelWindow {
|
||||
screen: modelData
|
||||
exclusionMode: ExclusionMode.Normal
|
||||
WlrLayershell.namespace: "quickshell:onScreenDisplay"
|
||||
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")
|
||||
}
|
||||
}
|
||||
}
|
||||
exclusionMode: ExclusionMode.Normal
|
||||
WlrLayershell.namespace: "quickshell:onScreenDisplay"
|
||||
WlrLayershell.layer: WlrLayer.Overlay
|
||||
color: "transparent"
|
||||
|
||||
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 {
|
||||
@@ -131,7 +131,7 @@ Scope {
|
||||
|
||||
GlobalShortcut {
|
||||
name: "osdBrightnessTrigger"
|
||||
description: "Triggers brightness OSD on press"
|
||||
description: qsTr("Triggers brightness OSD on press")
|
||||
|
||||
onPressed: {
|
||||
root.triggerOsd()
|
||||
@@ -139,7 +139,7 @@ Scope {
|
||||
}
|
||||
GlobalShortcut {
|
||||
name: "osdBrightnessHide"
|
||||
description: "Hides brightness OSD on press"
|
||||
description: qsTr("Hides brightness OSD on press")
|
||||
|
||||
onPressed: {
|
||||
root.showOsdValues = false
|
||||
|
||||
@@ -12,6 +12,7 @@ import Quickshell.Hyprland
|
||||
Scope {
|
||||
id: root
|
||||
property bool showOsdValues: false
|
||||
property var focusedScreen: Quickshell.screens.find(s => s.name === Hyprland.focusedMonitor?.name)
|
||||
|
||||
function triggerOsd() {
|
||||
showOsdValues = true
|
||||
@@ -47,70 +48,68 @@ Scope {
|
||||
}
|
||||
}
|
||||
|
||||
Variants {
|
||||
model: Quickshell.screens
|
||||
Loader {
|
||||
id: osdLoader
|
||||
active: showOsdValues
|
||||
|
||||
Loader {
|
||||
id: osdLoader
|
||||
property var modelData
|
||||
active: showOsdValues
|
||||
PanelWindow {
|
||||
screen: modelData
|
||||
exclusionMode: ExclusionMode.Normal
|
||||
WlrLayershell.namespace: "quickshell:onScreenDisplay"
|
||||
WlrLayershell.layer: WlrLayer.Overlay
|
||||
color: "transparent"
|
||||
PanelWindow {
|
||||
id: osdRoot
|
||||
|
||||
anchors {
|
||||
top: true
|
||||
}
|
||||
mask: Region {
|
||||
item: osdValuesWrapper
|
||||
Connections {
|
||||
target: root
|
||||
function onFocusedScreenChanged() {
|
||||
osdRoot.screen = root.focusedScreen
|
||||
}
|
||||
}
|
||||
|
||||
implicitWidth: columnLayout.implicitWidth
|
||||
implicitHeight: columnLayout.implicitHeight
|
||||
visible: osdLoader.active
|
||||
exclusionMode: ExclusionMode.Normal
|
||||
WlrLayershell.namespace: "quickshell:onScreenDisplay"
|
||||
WlrLayershell.layer: WlrLayer.Overlay
|
||||
color: "transparent"
|
||||
|
||||
ColumnLayout {
|
||||
id: columnLayout
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
Item {
|
||||
height: 1 // Prevent Wayland protocol error
|
||||
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
|
||||
}
|
||||
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: Audio.sink?.audio.volume ?? 0
|
||||
icon: Audio.sink?.audio.muted ? "volume_off" : "volume_up"
|
||||
name: qsTr("Volume")
|
||||
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: Audio.sink?.audio.volume ?? 0
|
||||
icon: Audio.sink?.audio.muted ? "volume_off" : "volume_up"
|
||||
name: qsTr("Volume")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
@@ -130,7 +129,7 @@ Scope {
|
||||
}
|
||||
GlobalShortcut {
|
||||
name: "osdVolumeTrigger"
|
||||
description: "Triggers volume OSD on press"
|
||||
description: qsTr("Triggers volume OSD on press")
|
||||
|
||||
onPressed: {
|
||||
root.triggerOsd()
|
||||
@@ -138,7 +137,7 @@ Scope {
|
||||
}
|
||||
GlobalShortcut {
|
||||
name: "osdVolumeHide"
|
||||
description: "Hides volume OSD on press"
|
||||
description: qsTr("Hides volume OSD on press")
|
||||
|
||||
onPressed: {
|
||||
root.showOsdValues = false
|
||||
|
||||
@@ -3,10 +3,11 @@ import "root:/modules/common"
|
||||
import "root:/modules/common/widgets"
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Effects
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Widgets
|
||||
import Qt5Compat.GraphicalEffects
|
||||
// import Qt5Compat.GraphicalEffects
|
||||
|
||||
Item {
|
||||
id: root
|
||||
@@ -14,27 +15,40 @@ Item {
|
||||
required property string icon
|
||||
required property string name
|
||||
property bool rotateIcon: false
|
||||
property bool scaleIcon: false
|
||||
|
||||
property real valueIndicatorVerticalPadding: 5
|
||||
property real valueIndicatorVerticalPadding: 9
|
||||
property real valueIndicatorLeftPadding: 10
|
||||
property real valueIndicatorRightPadding: 20 // An icon is circle ish, a column isn't, hence the extra padding
|
||||
|
||||
Layout.margins: Appearance.sizes.elevationMargin
|
||||
implicitWidth: valueIndicator.implicitWidth
|
||||
implicitWidth: Appearance.sizes.osdWidth
|
||||
implicitHeight: valueIndicator.implicitHeight
|
||||
|
||||
WrapperRectangle {
|
||||
id: valueIndicator
|
||||
anchors.fill: parent
|
||||
radius: Appearance.rounding.full
|
||||
color: Appearance.colors.colLayer0
|
||||
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
|
||||
id: valueRow
|
||||
Layout.margins: 10
|
||||
anchors.fill: parent
|
||||
spacing: 10
|
||||
|
||||
Item {
|
||||
Item {
|
||||
implicitWidth: 30
|
||||
implicitHeight: 30
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
@@ -47,22 +61,14 @@ Item {
|
||||
renderType: Text.QtRendering
|
||||
|
||||
text: root.icon
|
||||
iconSize: 20 + 10 * (root.rotateIcon ? value : 1)
|
||||
iconSize: 20 + 10 * (root.scaleIcon ? value : 1)
|
||||
rotation: 180 * (root.rotateIcon ? value : 0)
|
||||
|
||||
Behavior on iconSize {
|
||||
NumberAnimation {
|
||||
duration: Appearance.animation.elementMoveEnter.duration
|
||||
easing.type: Appearance.animation.elementMoveEnter.type
|
||||
easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||
}
|
||||
Behavior on rotation {
|
||||
NumberAnimation {
|
||||
duration: Appearance.animation.elementMoveEnter.duration
|
||||
easing.type: Appearance.animation.elementMoveEnter.type
|
||||
easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -93,20 +99,10 @@ Item {
|
||||
|
||||
StyledProgressBar {
|
||||
id: valueProgressBar
|
||||
Layout.fillWidth: true
|
||||
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 {
|
||||
name: "overviewToggle"
|
||||
description: "Toggles overview on press"
|
||||
description: qsTr("Toggles overview on press")
|
||||
|
||||
onPressed: {
|
||||
GlobalStates.overviewOpen = !GlobalStates.overviewOpen
|
||||
@@ -136,7 +136,7 @@ Scope {
|
||||
}
|
||||
GlobalShortcut {
|
||||
name: "overviewClose"
|
||||
description: "Closes overview"
|
||||
description: qsTr("Closes overview")
|
||||
|
||||
onPressed: {
|
||||
GlobalStates.overviewOpen = false
|
||||
@@ -144,7 +144,7 @@ Scope {
|
||||
}
|
||||
GlobalShortcut {
|
||||
name: "overviewToggleRelease"
|
||||
description: "Toggles overview on release"
|
||||
description: qsTr("Toggles overview on release")
|
||||
|
||||
onPressed: {
|
||||
GlobalStates.superReleaseMightTrigger = true
|
||||
@@ -160,9 +160,9 @@ Scope {
|
||||
}
|
||||
GlobalShortcut {
|
||||
name: "overviewToggleReleaseInterrupt"
|
||||
description: "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. " +
|
||||
"To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything."
|
||||
description: qsTr("Interrupts possibility of overview being toggled on release. ") +
|
||||
qsTr("This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key. ") +
|
||||
qsTr("To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything.")
|
||||
|
||||
onPressed: {
|
||||
GlobalStates.superReleaseMightTrigger = false
|
||||
|
||||
@@ -2,8 +2,9 @@ import "root:/"
|
||||
import "root:/services/"
|
||||
import "root:/modules/common"
|
||||
import "root:/modules/common/widgets"
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import "root:/modules/common/functions/color_utils.js" as ColorUtils
|
||||
import QtQuick
|
||||
import QtQuick.Effects
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
@@ -58,6 +59,16 @@ Item {
|
||||
color: Appearance.colors.colLayer0
|
||||
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 {
|
||||
id: workspaceColumnLayout
|
||||
|
||||
@@ -78,7 +89,7 @@ Item {
|
||||
property int colIndex: index
|
||||
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 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 activeBorderColor: Appearance.m3colors.m3secondary
|
||||
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/widgets"
|
||||
import "root:/modules/common/functions/icons.js" as Icons
|
||||
import "root:/modules/common/functions/color_utils.js" as ColorUtils
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
@@ -42,37 +43,21 @@ Rectangle { // Window
|
||||
|
||||
radius: Appearance.rounding.windowRounding * root.scale
|
||||
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.width : 1
|
||||
|
||||
Behavior on x {
|
||||
NumberAnimation {
|
||||
duration: Appearance.animation.elementMoveEnter.duration
|
||||
easing.type: Appearance.animation.elementMoveEnter.type
|
||||
easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||
}
|
||||
Behavior on y {
|
||||
NumberAnimation {
|
||||
duration: Appearance.animation.elementMoveEnter.duration
|
||||
easing.type: Appearance.animation.elementMoveEnter.type
|
||||
easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||
}
|
||||
Behavior on width {
|
||||
NumberAnimation {
|
||||
duration: Appearance.animation.elementMoveEnter.duration
|
||||
easing.type: Appearance.animation.elementMoveEnter.type
|
||||
easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||
}
|
||||
Behavior on height {
|
||||
NumberAnimation {
|
||||
duration: Appearance.animation.elementMoveEnter.duration
|
||||
easing.type: Appearance.animation.elementMoveEnter.type
|
||||
easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
@@ -88,11 +73,7 @@ Rectangle { // Window
|
||||
implicitSize: Math.min(targetWindowWidth, targetWindowHeight) * (root.compactMode ? root.iconToWindowRatioCompact : root.iconToWindowRatio)
|
||||
|
||||
Behavior on implicitSize {
|
||||
NumberAnimation {
|
||||
duration: Appearance.animation.elementMoveEnter.duration
|
||||
easing.type: Appearance.animation.elementMoveEnter.type
|
||||
easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
IconImage {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import "root:/"
|
||||
import "root:/modules/common"
|
||||
import "root:/modules/common/widgets"
|
||||
import "root:/modules/common/functions/color_utils.js" as ColorUtils
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
@@ -57,7 +58,9 @@ Button {
|
||||
anchors.leftMargin: root.horizontalMargin
|
||||
anchors.rightMargin: root.horizontalMargin
|
||||
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 {
|
||||
@@ -100,7 +103,7 @@ Button {
|
||||
StyledText {
|
||||
font.pixelSize: Appearance.font.pixelSize.small
|
||||
color: Appearance.colors.colSubtext
|
||||
visible: root.itemType && root.itemType != "App"
|
||||
visible: root.itemType && root.itemType != qsTr("App")
|
||||
text: root.itemType
|
||||
}
|
||||
StyledText {
|
||||
|
||||
@@ -7,6 +7,7 @@ import Qt5Compat.GraphicalEffects
|
||||
import Qt.labs.platform
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Effects
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
@@ -160,6 +161,15 @@ Item { // Wrapper
|
||||
implicitHeight: columnLayout.implicitHeight
|
||||
radius: Appearance.rounding.large
|
||||
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 {
|
||||
id: columnLayout
|
||||
@@ -227,7 +237,7 @@ Item { // Wrapper
|
||||
}
|
||||
}
|
||||
|
||||
background: Item {}
|
||||
background: null
|
||||
|
||||
cursorDelegate: Rectangle {
|
||||
width: 1
|
||||
@@ -276,7 +286,7 @@ Item { // Wrapper
|
||||
nonAppResultsTimer.restart();
|
||||
const mathResultObject = {
|
||||
name: root.mathResult,
|
||||
clickActionName: "Copy",
|
||||
clickActionName: qsTr("Copy"),
|
||||
type: qsTr("Math result"),
|
||||
fontType: "monospace",
|
||||
materialSymbol: 'calculate',
|
||||
@@ -286,7 +296,7 @@ Item { // Wrapper
|
||||
}
|
||||
const commandResultObject = {
|
||||
name: searchingText,
|
||||
clickActionName: "Run",
|
||||
clickActionName: qsTr("Run"),
|
||||
type: qsTr("Run command"),
|
||||
fontType: "monospace",
|
||||
materialSymbol: 'terminal',
|
||||
@@ -300,8 +310,8 @@ Item { // Wrapper
|
||||
if (actionString.startsWith(root.searchingText) || root.searchingText.startsWith(actionString)) {
|
||||
return {
|
||||
name: root.searchingText.startsWith(actionString) ? root.searchingText : actionString,
|
||||
clickActionName: "Run",
|
||||
type: "Action",
|
||||
clickActionName: qsTr("Run"),
|
||||
type: qsTr("Action"),
|
||||
materialSymbol: 'settings_suggest',
|
||||
execute: () => {
|
||||
action.execute(root.searchingText.split(" ").slice(1).join(" "))
|
||||
@@ -319,8 +329,8 @@ Item { // Wrapper
|
||||
result = result.concat(
|
||||
AppSearch.fuzzyQuery(root.searchingText)
|
||||
.map((entry) => {
|
||||
entry.clickActionName = "Launch";
|
||||
entry.type = "App"
|
||||
entry.clickActionName = qsTr("Launch");
|
||||
entry.type = qsTr("App");
|
||||
return entry;
|
||||
})
|
||||
);
|
||||
@@ -344,8 +354,8 @@ Item { // Wrapper
|
||||
// Web search
|
||||
result.push({
|
||||
name: root.searchingText,
|
||||
clickActionName: "Search",
|
||||
type: "Search the web",
|
||||
clickActionName: qsTr("Search"),
|
||||
type: qsTr("Search the web"),
|
||||
materialSymbol: 'travel_explore',
|
||||
execute: () => {
|
||||
let url = ConfigOptions.search.engineBaseUrl + root.searchingText
|
||||
@@ -361,22 +371,9 @@ Item { // Wrapper
|
||||
}
|
||||
delegate: SearchItem {
|
||||
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/widgets"
|
||||
import "root:/modules/common/functions/color_utils.js" as ColorUtils
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import Quickshell.Io
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Widgets
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
|
||||
Scope {
|
||||
id: root
|
||||
readonly property Toplevel activeWindow: ToplevelManager.activeToplevel
|
||||
property var focusedScreen: Quickshell.screens.find(s => s.name === Hyprland.focusedMonitor?.name)
|
||||
|
||||
Variants {
|
||||
id: sessionVariants
|
||||
model: Quickshell.screens
|
||||
|
||||
Loader {
|
||||
id: sessionLoader
|
||||
property var modelData
|
||||
active: false
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
Loader {
|
||||
id: sessionLoader
|
||||
active: false
|
||||
|
||||
PanelWindow { // Session menu
|
||||
id: sessionRoot
|
||||
visible: sessionLoader.active
|
||||
property string 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"
|
||||
|
||||
function toggle(): void {
|
||||
for (let i = 0; i < sessionVariants.instances.length; i++) {
|
||||
let loader = sessionVariants.instances[i];
|
||||
loader.active = !loader.active;
|
||||
}
|
||||
sessionLoader.active = !sessionLoader.active;
|
||||
}
|
||||
|
||||
function close(): void {
|
||||
for (let i = 0; i < sessionVariants.instances.length; i++) {
|
||||
let loader = sessionVariants.instances[i];
|
||||
loader.active = false;
|
||||
}
|
||||
sessionLoader.active = false;
|
||||
}
|
||||
|
||||
function open(): void {
|
||||
for (let i = 0; i < sessionVariants.instances.length; i++) {
|
||||
let loader = sessionVariants.instances[i];
|
||||
loader.active = true;
|
||||
}
|
||||
sessionLoader.active = true;
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "sessionToggle"
|
||||
description: "Toggles session screen on press"
|
||||
description: qsTr("Toggles session screen on press")
|
||||
|
||||
onPressed: {
|
||||
for (let i = 0; i < sessionVariants.instances.length; i++) {
|
||||
let loader = sessionVariants.instances[i];
|
||||
loader.active = !loader.active;
|
||||
}
|
||||
sessionLoader.active = !sessionLoader.active;
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "sessionOpen"
|
||||
description: "Opens session screen on press"
|
||||
description: qsTr("Opens session screen on press")
|
||||
|
||||
onPressed: {
|
||||
for (let i = 0; i < sessionVariants.instances.length; i++) {
|
||||
let loader = sessionVariants.instances[i];
|
||||
loader.active = !loader.active;
|
||||
}
|
||||
sessionLoader.active = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,12 +37,7 @@ Button {
|
||||
color: (button.down || button.keyboardDown) ? Appearance.colors.colLayer2Active : ((button.hovered || button.focus) ? Appearance.colors.colLayer2Hover : Appearance.colors.colLayer2)
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
|
||||
animation: Appearance.animation.elementMove.colorAnimation.createObject(this)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ Item {
|
||||
id: root
|
||||
property var panelWindow
|
||||
property var inputField: messageInputField
|
||||
readonly property var messages: Ai.messages
|
||||
property string commandPrefix: "/"
|
||||
property string faviconDownloadPath: FileUtils.trimFileProtocol(`${XdgDirectories.cache}/media/favicons`)
|
||||
|
||||
@@ -31,7 +30,7 @@ Item {
|
||||
|
||||
onFocusChanged: (focus) => {
|
||||
if (focus) {
|
||||
messageInputField.forceActiveFocus()
|
||||
root.inputField.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,46 +178,42 @@ int main(int argc, char* argv[]) {
|
||||
}
|
||||
|
||||
add: Transition {
|
||||
NumberAnimation {
|
||||
property: "opacity"
|
||||
from: 0; to: 1
|
||||
duration: Appearance.animation.elementMoveEnter.duration
|
||||
easing.type: Appearance.animation.elementMoveEnter.type
|
||||
easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve
|
||||
}
|
||||
animations: [Appearance.animation.elementMoveEnter.numberAnimation.createObject(this, {
|
||||
property: "opacity",
|
||||
from: 0,
|
||||
to: 1
|
||||
})]
|
||||
}
|
||||
remove: Transition {
|
||||
NumberAnimation {
|
||||
property: "opacity"
|
||||
from: 1; to: 0
|
||||
duration: Appearance.animation.elementMoveEnter.duration
|
||||
easing.type: Appearance.animation.elementMoveEnter.type
|
||||
easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve
|
||||
}
|
||||
animations: [Appearance.animation.elementMoveEnter.numberAnimation.createObject(this, {
|
||||
property: "opacity",
|
||||
from: 1,
|
||||
to: 0
|
||||
})]
|
||||
}
|
||||
|
||||
model: ScriptModel {
|
||||
values: root.messages
|
||||
values: Ai.messageIDs
|
||||
}
|
||||
delegate: AiMessage {
|
||||
required property var modelData
|
||||
required property int index
|
||||
messageIndex: index
|
||||
messageData: modelData
|
||||
messageData: {
|
||||
Ai.messageByID[modelData]
|
||||
}
|
||||
messageInputField: root.inputField
|
||||
faviconDownloadPath: root.faviconDownloadPath
|
||||
}
|
||||
}
|
||||
|
||||
Item { // Placeholder when list is empty
|
||||
opacity: root.messages.length === 0 ? 1 : 0
|
||||
opacity: Ai.messageIDs.length === 0 ? 1 : 0
|
||||
visible: opacity > 0
|
||||
anchors.fill: parent
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
@@ -356,11 +351,7 @@ int main(int argc, char* argv[]) {
|
||||
border.width: 1
|
||||
|
||||
Behavior on implicitHeight {
|
||||
NumberAnimation {
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
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)
|
||||
placeholderTextColor: Appearance.m3colors.m3outline
|
||||
|
||||
background: Item {}
|
||||
background: null
|
||||
|
||||
onTextChanged: { // Handle suggestions
|
||||
if(messageInputField.text.length === 0) {
|
||||
@@ -474,11 +465,7 @@ int main(int argc, char* argv[]) {
|
||||
Appearance.m3colors.m3primary) : Appearance.colors.colLayer2Disabled
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -566,11 +553,7 @@ int main(int argc, char* argv[]) {
|
||||
Appearance.colors.colLayer2
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
}
|
||||
onClicked: {
|
||||
|
||||
@@ -4,6 +4,7 @@ import "root:/modules/common"
|
||||
import "root:/modules/common/widgets"
|
||||
import "root:/modules/common/functions/fuzzysort.js" as Fuzzy
|
||||
import "root:/modules/common/functions/string_utils.js" as StringUtils
|
||||
import "root:/modules/common/functions/file_utils.js" as FileUtils
|
||||
import "./anime/"
|
||||
import Qt.labs.platform
|
||||
import QtQuick
|
||||
@@ -19,9 +20,9 @@ Item {
|
||||
property var panelWindow
|
||||
property var inputField: tagInputField
|
||||
readonly property var responses: Booru.responses
|
||||
property string previewDownloadPath: `${XdgDirectories.cache}/media/waifus`.replace("file://", "")
|
||||
property string downloadPath: (XdgDirectories.pictures + "/homework").replace("file://", "")
|
||||
property string nsfwPath: (XdgDirectories.pictures + "/homework/🌶️").replace("file://", "")
|
||||
property string previewDownloadPath: FileUtils.trimFileProtocol(`${XdgDirectories.cache}/media/waifus`)
|
||||
property string downloadPath: FileUtils.trimFileProtocol(XdgDirectories.pictures + "/homework")
|
||||
property string nsfwPath: FileUtils.trimFileProtocol(XdgDirectories.pictures + "/homework/🌶️")
|
||||
property string commandPrefix: "/"
|
||||
property real scrollOnNewResponse: 100
|
||||
property int tagSuggestionDelay: 210
|
||||
@@ -37,7 +38,8 @@ Item {
|
||||
}
|
||||
|
||||
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: [
|
||||
@@ -114,12 +116,6 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: panelWindow
|
||||
function onVisibleChanged(visible) {
|
||||
tagInputField.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
onFocusChanged: (focus) => {
|
||||
if (focus) {
|
||||
tagInputField.forceActiveFocus()
|
||||
@@ -174,13 +170,11 @@ Item {
|
||||
}
|
||||
|
||||
add: Transition {
|
||||
NumberAnimation {
|
||||
property: "opacity"
|
||||
from: 0; to: 1
|
||||
duration: Appearance.animation.elementMoveEnter.duration
|
||||
easing.type: Appearance.animation.elementMoveEnter.type
|
||||
easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve
|
||||
}
|
||||
animations: [Appearance.animation.elementMoveEnter.numberAnimation.createObject(this, {
|
||||
property: "opacity",
|
||||
from: 0,
|
||||
to: 1
|
||||
})]
|
||||
}
|
||||
|
||||
model: ScriptModel {
|
||||
@@ -208,11 +202,7 @@ Item {
|
||||
anchors.fill: parent
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
@@ -246,11 +236,7 @@ Item {
|
||||
visible: opacity > 0
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
@@ -392,11 +378,7 @@ Item {
|
||||
border.width: 1
|
||||
|
||||
Behavior on implicitHeight {
|
||||
NumberAnimation {
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
RowLayout { // Input field and send button
|
||||
@@ -419,7 +401,7 @@ Item {
|
||||
placeholderText: StringUtils.format(qsTr('Enter tags, or "{0}" for commands'), root.commandPrefix)
|
||||
placeholderTextColor: Appearance.m3colors.m3outline
|
||||
|
||||
background: Item {}
|
||||
background: null
|
||||
|
||||
property Timer searchTimer: Timer { // Timer for tag suggestions
|
||||
interval: root.tagSuggestionDelay
|
||||
@@ -530,11 +512,7 @@ Item {
|
||||
Appearance.m3colors.m3primary) : Appearance.colors.colLayer2Disabled
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -666,11 +644,7 @@ Item {
|
||||
Appearance.colors.colLayer2
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
}
|
||||
onClicked: {
|
||||
|
||||
@@ -5,6 +5,7 @@ import "root:/modules/common/widgets"
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Effects
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import Quickshell.Io
|
||||
import Quickshell
|
||||
@@ -17,30 +18,31 @@ Scope { // Scope
|
||||
property int sidebarPadding: 15
|
||||
property var tabButtonList: [{"icon": "neurology", "name": qsTr("Intelligence")}, {"icon": "bookmark_heart", "name": qsTr("Anime")}]
|
||||
|
||||
Variants { // Window repeater
|
||||
id: sidebarVariants
|
||||
model: Quickshell.screens
|
||||
|
||||
Loader {
|
||||
id: sidebarLoader
|
||||
active: false
|
||||
onActiveChanged: {
|
||||
GlobalStates.sidebarLeftOpen = sidebarLoader.active
|
||||
}
|
||||
|
||||
PanelWindow { // Window
|
||||
id: sidebarRoot
|
||||
visible: false
|
||||
focusable: true
|
||||
property int selectedTab: PersistentStates.sidebar.leftSide.selectedTab
|
||||
visible: sidebarLoader.active
|
||||
|
||||
property int selectedTab: 0
|
||||
property bool extend: false
|
||||
property bool pin: false
|
||||
property real sidebarWidth: sidebarRoot.extend ? Appearance.sizes.sidebarWidthExtended : Appearance.sizes.sidebarWidth
|
||||
|
||||
onVisibleChanged: {
|
||||
GlobalStates.sidebarLeftOpenCount += visible ? 1 : -1
|
||||
function hide() {
|
||||
sidebarLoader.active = false
|
||||
}
|
||||
|
||||
property var modelData
|
||||
|
||||
screen: modelData
|
||||
exclusiveZone: 0
|
||||
exclusiveZone: sidebarRoot.pin ? sidebarWidth : 0
|
||||
implicitWidth: Appearance.sizes.sidebarWidthExtended
|
||||
WlrLayershell.namespace: "quickshell:sidebarLeft"
|
||||
// Hyprland 0.49: Focus is always exclusive and setting this breaks mouse focus grab
|
||||
// WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
|
||||
// Hyprland 0.49: OnDemand is Exclusive, Exclusive just breaks click-outside-to-close
|
||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
|
||||
color: "transparent"
|
||||
|
||||
anchors {
|
||||
@@ -56,26 +58,12 @@ Scope { // Scope
|
||||
HyprlandFocusGrab { // Click outside to close
|
||||
id: grab
|
||||
windows: [ sidebarRoot ]
|
||||
active: false
|
||||
active: sidebarRoot.visible && !sidebarRoot.pin
|
||||
onActiveChanged: { // Focus the selected tab
|
||||
if (active) swipeView.currentItem.forceActiveFocus()
|
||||
}
|
||||
onCleared: () => {
|
||||
if (!active) sidebarRoot.visible = false
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
if (!active) sidebarRoot.hide()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,41 +75,50 @@ Scope { // Scope
|
||||
anchors.left: parent.left
|
||||
anchors.topMargin: 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
|
||||
color: Appearance.colors.colLayer0
|
||||
radius: Appearance.rounding.screenRounding - Appearance.sizes.elevationMargin + 1
|
||||
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 {
|
||||
NumberAnimation {
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
Keys.onPressed: (event) => {
|
||||
// console.log("Key pressed: " + event.key)
|
||||
if (event.key === Qt.Key_Escape) {
|
||||
sidebarRoot.visible = false;
|
||||
sidebarRoot.hide();
|
||||
}
|
||||
if (event.modifiers === Qt.ControlModifier) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
sidebarRoot.extend = !sidebarRoot.extend;
|
||||
}
|
||||
else if (event.key === Qt.Key_P) {
|
||||
sidebarRoot.pin = !sidebarRoot.pin;
|
||||
}
|
||||
event.accepted = true;
|
||||
}
|
||||
}
|
||||
@@ -137,7 +134,7 @@ Scope { // Scope
|
||||
tabButtonList: root.tabButtonList
|
||||
externalTrackedTab: sidebarRoot.selectedTab
|
||||
function onCurrentIndexChanged(currentIndex) {
|
||||
PersistentStateManager.setState("sidebar.leftSide.selectedTab", currentIndex)
|
||||
sidebarRoot.selectedTab = currentIndex
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,10 +143,12 @@ Scope { // Scope
|
||||
Layout.topMargin: 5
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
currentIndex: sidebarRoot.selectedTab
|
||||
spacing: 10
|
||||
|
||||
currentIndex: tabBar.externalTrackedTab
|
||||
onCurrentIndexChanged: {
|
||||
tabBar.enableIndicatorAnimation = true
|
||||
PersistentStateManager.setState("sidebar.leftSide.selectedTab", currentIndex)
|
||||
sidebarRoot.selectedTab = currentIndex
|
||||
}
|
||||
|
||||
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 {
|
||||
target: "sidebarLeft"
|
||||
|
||||
function toggle(): void {
|
||||
for (let i = 0; i < sidebarVariants.instances.length; i++) {
|
||||
let panelWindow = sidebarVariants.instances[i];
|
||||
if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
|
||||
panelWindow.visible = !panelWindow.visible;
|
||||
if(panelWindow.visible) Notifications.timeoutAll();
|
||||
}
|
||||
}
|
||||
sidebarLoader.active = !sidebarLoader.active
|
||||
if(sidebarLoader.active) Notifications.timeoutAll();
|
||||
}
|
||||
|
||||
function close(): void {
|
||||
for (let i = 0; i < sidebarVariants.instances.length; i++) {
|
||||
let panelWindow = sidebarVariants.instances[i];
|
||||
if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
|
||||
panelWindow.visible = false;
|
||||
}
|
||||
}
|
||||
sidebarLoader.active = false
|
||||
}
|
||||
|
||||
function open(): void {
|
||||
for (let i = 0; i < sidebarVariants.instances.length; i++) {
|
||||
let panelWindow = sidebarVariants.instances[i];
|
||||
if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
|
||||
panelWindow.visible = true;
|
||||
if(panelWindow.visible) Notifications.timeoutAll();
|
||||
}
|
||||
}
|
||||
sidebarLoader.active = true
|
||||
if(sidebarLoader.active) Notifications.timeoutAll();
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "sidebarLeftToggle"
|
||||
description: "Toggles left sidebar on press"
|
||||
description: qsTr("Toggles left sidebar on press")
|
||||
|
||||
onPressed: {
|
||||
for (let i = 0; i < sidebarVariants.instances.length; i++) {
|
||||
let panelWindow = sidebarVariants.instances[i];
|
||||
if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
|
||||
panelWindow.visible = !panelWindow.visible;
|
||||
if(panelWindow.visible) Notifications.timeoutAll();
|
||||
}
|
||||
}
|
||||
sidebarLoader.active = !sidebarLoader.active;
|
||||
if(sidebarLoader.active) Notifications.timeoutAll();
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "sidebarLeftOpen"
|
||||
description: "Opens left sidebar on press"
|
||||
description: qsTr("Opens left sidebar on press")
|
||||
|
||||
onPressed: {
|
||||
for (let i = 0; i < sidebarVariants.instances.length; i++) {
|
||||
let panelWindow = sidebarVariants.instances[i];
|
||||
if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
|
||||
panelWindow.visible = true;
|
||||
if(panelWindow.visible) Notifications.timeoutAll();
|
||||
}
|
||||
}
|
||||
sidebarLoader.active = true;
|
||||
if(sidebarLoader.active) Notifications.timeoutAll();
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "sidebarLeftClose"
|
||||
description: "Closes left sidebar on press"
|
||||
description: qsTr("Closes left sidebar on press")
|
||||
|
||||
onPressed: {
|
||||
for (let i = 0; i < sidebarVariants.instances.length; i++) {
|
||||
let panelWindow = sidebarVariants.instances[i];
|
||||
if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
|
||||
panelWindow.visible = false;
|
||||
}
|
||||
}
|
||||
sidebarLoader.active = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -151,7 +151,7 @@ Rectangle {
|
||||
color: Appearance.m3colors.m3onSecondaryContainer
|
||||
text: messageData.role == 'assistant' ? Ai.models[messageData.model].name :
|
||||
(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/widgets"
|
||||
import "root:/services"
|
||||
import "root:/modules/common/functions/color_utils.js" as ColorUtils
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
@@ -18,20 +19,16 @@ Button {
|
||||
|
||||
background: Rectangle {
|
||||
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.hovered ? Appearance.colors.colPrimaryHover :
|
||||
Appearance.m3colors.m3primary) :
|
||||
(button.down ? Appearance.colors.colSurfaceContainerHighestActive :
|
||||
button.hovered ? Appearance.colors.colSurfaceContainerHighestHover :
|
||||
Appearance.transparentize(Appearance.m3colors.m3surfaceContainerHighest, 1))
|
||||
ColorUtils.transparentize(Appearance.m3colors.m3surfaceContainerHighest, 1))
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Appearance.animation.elementMoveFast.duration
|
||||
easing.type: Appearance.animation.elementMoveFast.type
|
||||
easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,11 +41,7 @@ Button {
|
||||
Appearance.colors.colOnLayer1Inactive
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Appearance.animation.elementMoveFast.duration
|
||||
easing.type: Appearance.animation.elementMoveFast.type
|
||||
easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import "root:/modules/common/"
|
||||
import "root:/modules/common/widgets"
|
||||
import "../"
|
||||
import "root:/modules/common/functions/string_utils.js" as StringUtils
|
||||
import "root:/modules/common/functions/color_utils.js" as ColorUtils
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
@@ -98,7 +99,7 @@ Item {
|
||||
id: thinkBlockLanguage
|
||||
Layout.fillWidth: false
|
||||
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 }
|
||||
Button { // Expand button
|
||||
@@ -117,7 +118,7 @@ Item {
|
||||
radius: Appearance.rounding.full
|
||||
color: (headerMouseArea.pressed) ? Appearance.colors.colLayer2Active
|
||||
: (headerMouseArea.containsMouse ? Appearance.colors.colLayer2Hover
|
||||
: Appearance.transparentize(Appearance.colors.colLayer2, 1))
|
||||
: ColorUtils.transparentize(Appearance.colors.colLayer2, 1))
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
|
||||
@@ -2,15 +2,17 @@ import "root:/"
|
||||
import "root:/modules/common"
|
||||
import "root:/modules/common/widgets"
|
||||
import "root:/modules/common/functions/string_utils.js" as StringUtils
|
||||
import "root:/modules/common/functions/color_utils.js" as ColorUtils
|
||||
import QtQml
|
||||
import Qt.labs.platform
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Effects
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Hyprland
|
||||
import Qt5Compat.GraphicalEffects
|
||||
|
||||
Button {
|
||||
id: root
|
||||
@@ -78,11 +80,7 @@ Button {
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Appearance.animation.elementMoveEnter.duration
|
||||
easing.type: Appearance.animation.elementMoveEnter.type
|
||||
easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,13 +95,13 @@ Button {
|
||||
PointingHandInteraction {}
|
||||
|
||||
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 {
|
||||
color: menuButton.down ? Appearance.transparentize(Appearance.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) :
|
||||
Appearance.transparentize(Appearance.m3colors.m3surface, 0.3)
|
||||
color: menuButton.down ? ColorUtils.transparentize(ColorUtils.mix(Appearance.m3colors.m3surface, Appearance.m3colors.m3onSurface, 0.6), 0.1) :
|
||||
menuButton.hovered ? ColorUtils.transparentize(ColorUtils.mix(Appearance.m3colors.m3surface, Appearance.m3colors.m3onSurface, 0.8), 0.2) :
|
||||
ColorUtils.transparentize(Appearance.m3colors.m3surface, 0.3)
|
||||
radius: Appearance.rounding.full
|
||||
}
|
||||
|
||||
@@ -140,6 +138,16 @@ Button {
|
||||
implicitHeight: contextMenuColumnLayout.implicitHeight + radius * 2
|
||||
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 {
|
||||
NumberAnimation {
|
||||
duration: Appearance.animation.elementMoveFast.duration
|
||||
@@ -180,7 +188,7 @@ Button {
|
||||
MenuButton {
|
||||
id: downloadButton
|
||||
Layout.fillWidth: true
|
||||
buttonText: "Download"
|
||||
buttonText: qsTr("Download")
|
||||
onClicked: {
|
||||
root.showActions = false
|
||||
Hyprland.dispatch(`exec curl '${root.imageData.file_url}' -o '${root.imageData.is_nsfw ? root.nsfwPath : root.downloadPath}/${root.fileName}' && notify-send '${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:/modules/common"
|
||||
import "root:/modules/common/widgets"
|
||||
import "root:/modules/common/functions/string_utils.js" as StringUtils
|
||||
import "../"
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
@@ -93,7 +94,8 @@ Rectangle {
|
||||
anchors.centerIn: parent
|
||||
font.pixelSize: Appearance.font.pixelSize.smaller
|
||||
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 {
|
||||
NumberAnimation {
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
}
|
||||
Behavior on implicitHeight {
|
||||
NumberAnimation {
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
|
||||
@@ -16,27 +16,23 @@ Rectangle {
|
||||
radius: Appearance.rounding.normal
|
||||
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")}]
|
||||
|
||||
onSelectedTabChanged: {
|
||||
PersistentStateManager.setState("sidebar.centerGroup.selectedTab", selectedTab)
|
||||
}
|
||||
|
||||
Keys.onPressed: (event) => {
|
||||
if (event.key === Qt.Key_PageDown || event.key === Qt.Key_PageUp) {
|
||||
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) {
|
||||
PersistentStateManager.setState("sidebar.centerGroup.selectedTab", Math.max(root.selectedTab - 1, 0))
|
||||
root.selectedTab = Math.max(root.selectedTab - 1, 0)
|
||||
}
|
||||
event.accepted = true;
|
||||
}
|
||||
if (event.modifiers === Qt.ControlModifier) {
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
@@ -53,7 +49,7 @@ Rectangle {
|
||||
externalTrackedTab: root.selectedTab
|
||||
|
||||
function onCurrentIndexChanged(currentIndex) {
|
||||
PersistentStateManager.setState("sidebar.centerGroup.selectedTab", currentIndex)
|
||||
root.selectedTab = currentIndex
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,10 +58,11 @@ Rectangle {
|
||||
Layout.topMargin: 5
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
spacing: 10
|
||||
currentIndex: root.selectedTab
|
||||
onCurrentIndexChanged: {
|
||||
tabBar.enableIndicatorAnimation = true
|
||||
PersistentStateManager.setState("sidebar.centerGroup.selectedTab", currentIndex)
|
||||
root.selectedTab = currentIndex
|
||||
}
|
||||
|
||||
clip: true
|
||||
|
||||
@@ -2,10 +2,12 @@ import "root:/"
|
||||
import "root:/services"
|
||||
import "root:/modules/common"
|
||||
import "root:/modules/common/widgets"
|
||||
import "root:/modules/common/functions/string_utils.js" as StringUtils
|
||||
import "./quickToggles/"
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Effects
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import Quickshell.Io
|
||||
import Quickshell
|
||||
@@ -17,251 +19,207 @@ Scope {
|
||||
property int sidebarWidth: Appearance.sizes.sidebarWidth
|
||||
property int sidebarPadding: 15
|
||||
|
||||
Variants {
|
||||
id: sidebarVariants
|
||||
model: Quickshell.screens
|
||||
|
||||
Loader {
|
||||
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
|
||||
}
|
||||
|
||||
}
|
||||
Loader {
|
||||
id: sidebarLoader
|
||||
active: false
|
||||
onActiveChanged: {
|
||||
GlobalStates.sidebarRightOpen = sidebarLoader.active
|
||||
}
|
||||
|
||||
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 {
|
||||
target: "sidebarRight"
|
||||
|
||||
function toggle(): void {
|
||||
for (let i = 0; i < sidebarVariants.instances.length; i++) {
|
||||
let loader = sidebarVariants.instances[i];
|
||||
loader.active = !loader.active;
|
||||
}
|
||||
sidebarLoader.active = !sidebarLoader.active;
|
||||
if(sidebarLoader.active) Notifications.timeoutAll();
|
||||
}
|
||||
|
||||
function close(): void {
|
||||
for (let i = 0; i < sidebarVariants.instances.length; i++) {
|
||||
let loader = sidebarVariants.instances[i];
|
||||
loader.active = false;
|
||||
}
|
||||
sidebarLoader.active = false;
|
||||
}
|
||||
|
||||
function open(): void {
|
||||
for (let i = 0; i < sidebarVariants.instances.length; i++) {
|
||||
let loader = sidebarVariants.instances[i];
|
||||
loader.active = true;
|
||||
}
|
||||
sidebarLoader.active = true;
|
||||
Notifications.timeoutAll();
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "sidebarRightToggle"
|
||||
description: "Toggles right sidebar on press"
|
||||
description: qsTr("Toggles right sidebar on press")
|
||||
|
||||
onPressed: {
|
||||
for (let i = 0; i < sidebarVariants.instances.length; i++) {
|
||||
let loader = sidebarVariants.instances[i];
|
||||
loader.active = !loader.active;
|
||||
}
|
||||
sidebarLoader.active = !sidebarLoader.active;
|
||||
if(sidebarLoader.active) Notifications.timeoutAll();
|
||||
}
|
||||
}
|
||||
GlobalShortcut {
|
||||
name: "sidebarRightOpen"
|
||||
description: "Opens right sidebar on press"
|
||||
description: qsTr("Opens right sidebar on press")
|
||||
|
||||
onPressed: {
|
||||
for (let i = 0; i < sidebarVariants.instances.length; i++) {
|
||||
let loader = sidebarVariants.instances[i];
|
||||
loader.active = true;
|
||||
}
|
||||
sidebarLoader.active = true;
|
||||
Notifications.timeoutAll();
|
||||
}
|
||||
}
|
||||
GlobalShortcut {
|
||||
name: "sidebarRightClose"
|
||||
description: "Closes right sidebar on press"
|
||||
description: qsTr("Closes right sidebar on press")
|
||||
|
||||
onPressed: {
|
||||
for (let i = 0; i < sidebarVariants.instances.length; i++) {
|
||||
let loader = sidebarVariants.instances[i];
|
||||
loader.active = false;
|
||||
}
|
||||
sidebarLoader.active = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import "root:/modules/common"
|
||||
import "root:/modules/common/widgets"
|
||||
import "root:/modules/common/functions/color_utils.js" as ColorUtils
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
@@ -24,15 +25,10 @@ Button {
|
||||
Appearance.m3colors.m3primary) :
|
||||
(interactable && button.down) ? Appearance.colors.colLayer1Active :
|
||||
(interactable && button.hovered) ? Appearance.colors.colLayer1Hover :
|
||||
Appearance.transparentize(Appearance.colors.colLayer1, 1)
|
||||
ColorUtils.transparentize(Appearance.colors.colLayer1, 1)
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,12 +42,7 @@ Button {
|
||||
Appearance.m3colors.m3outlineVariant
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,12 +26,7 @@ Button {
|
||||
color: (button.down) ? Appearance.colors.colLayer2Active : (button.hovered ? Appearance.colors.colLayer2Hover : Appearance.colors.colLayer2)
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -142,11 +142,7 @@ Item {
|
||||
opacity: notificationWidgetList.length > 0 ? 1 : 0
|
||||
visible: opacity > 0
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ Button {
|
||||
property string buttonText: ""
|
||||
property string buttonIcon: ""
|
||||
|
||||
implicitHeight: 30
|
||||
// implicitHeight: 30
|
||||
implicitWidth: contentRowLayout.implicitWidth + 10 * 2
|
||||
Behavior on implicitWidth {
|
||||
SmoothedAnimation {
|
||||
@@ -25,19 +25,13 @@ Button {
|
||||
color: (button.down) ? Appearance.colors.colLayer2Active : (button.hovered ? Appearance.colors.colLayer2Hover : Appearance.colors.colLayer2)
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
|
||||
}
|
||||
contentItem: RowLayout {
|
||||
id: contentRowLayout
|
||||
// anchors.centerIn: parent
|
||||
anchors.right: parent.right
|
||||
anchors.centerIn: parent
|
||||
spacing: 0
|
||||
MaterialSymbol {
|
||||
text: buttonIcon
|
||||
|
||||
@@ -2,6 +2,7 @@ import "../"
|
||||
import "root:/services"
|
||||
import "root:/modules/common"
|
||||
import "root:/modules/common/widgets"
|
||||
import "root:/modules/common/functions/string_utils.js" as StringUtils
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
@@ -37,8 +38,9 @@ QuickToggleButton {
|
||||
}
|
||||
}
|
||||
StyledToolTip {
|
||||
content: `${(Bluetooth.bluetoothEnabled && Bluetooth.bluetoothDeviceName.length > 0) ?
|
||||
Bluetooth.bluetoothDeviceName : "Bluetooth"} | ${qsTr("Right-click to configure")}`
|
||||
content: StringUtils.format(qsTr("{0} | 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/widgets"
|
||||
import "root:/services"
|
||||
import "root:/modules/common/functions/string_utils.js" as StringUtils
|
||||
import "../"
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
@@ -42,6 +43,6 @@ QuickToggleButton {
|
||||
}
|
||||
}
|
||||
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/widgets"
|
||||
import "root:/modules/common/functions/color_utils.js" as ColorUtils
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Quickshell.Io
|
||||
@@ -20,14 +21,10 @@ Button {
|
||||
radius: Appearance.rounding.full
|
||||
color: toggled ?
|
||||
(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 {
|
||||
ColorAnimation {
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
|
||||
}
|
||||
|
||||
@@ -39,11 +36,7 @@ Button {
|
||||
color: toggled ? Appearance.m3colors.m3onPrimary : Appearance.colors.colOnLayer1
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -155,11 +155,7 @@ Item {
|
||||
anchors.fill: parent
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import "root:/modules/common"
|
||||
import "root:/modules/common/widgets"
|
||||
import "root:/modules/common/functions/color_utils.js" as ColorUtils
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
@@ -23,15 +24,10 @@ Button {
|
||||
background: Rectangle {
|
||||
anchors.fill: parent
|
||||
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 {
|
||||
ColorAnimation {
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import "root:/modules/common"
|
||||
import "root:/modules/common/widgets"
|
||||
import "root:/services"
|
||||
import "root:/modules/common/functions/color_utils.js" as ColorUtils
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Effects
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
|
||||
Item {
|
||||
id: root
|
||||
@@ -79,37 +80,33 @@ Item {
|
||||
tabIndicator.enableIndicatorAnimation = true
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
radius: Appearance.rounding.full
|
||||
z: 2
|
||||
|
||||
anchors.fill: parent
|
||||
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 {
|
||||
Behavior on x {
|
||||
enabled: tabIndicator.enableIndicatorAnimation
|
||||
SmoothedAnimation {
|
||||
velocity: Appearance.animation.positionShift.velocity
|
||||
}
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
}
|
||||
Behavior on anchors.rightMargin {
|
||||
|
||||
Behavior on implicitWidth {
|
||||
enabled: tabIndicator.enableIndicatorAnimation
|
||||
SmoothedAnimation {
|
||||
velocity: Appearance.animation.positionShift.velocity
|
||||
}
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,6 +122,7 @@ Item {
|
||||
Layout.topMargin: 10
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
spacing: 10
|
||||
clip: true
|
||||
currentIndex: currentTab
|
||||
onCurrentIndexChanged: {
|
||||
@@ -173,31 +171,17 @@ Item {
|
||||
color: (fabButton.down) ? Appearance.colors.colPrimaryContainerActive : (fabButton.hovered ? Appearance.colors.colPrimaryContainerHover : Appearance.m3colors.m3primaryContainer)
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
}
|
||||
|
||||
DropShadow {
|
||||
id: fabShadow
|
||||
anchors.fill: fabBackground
|
||||
source: fabBackground
|
||||
horizontalOffset: 0
|
||||
verticalOffset: fabButton.hovered ? 4 : 2
|
||||
radius: fabButton.hovered ? Appearance.sizes.fabHoveredShadowRadius : Appearance.sizes.fabShadowRadius
|
||||
samples: fabShadow.radius * 2 + 1
|
||||
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
|
||||
}
|
||||
layer.enabled: true
|
||||
layer.effect: MultiEffect {
|
||||
source: fabBackground
|
||||
anchors.fill: fabBackground
|
||||
shadowEnabled: true
|
||||
shadowColor: Appearance.colors.colShadow
|
||||
shadowBlur: 0.6
|
||||
shadowVerticalOffset: fabButton.hovered ? 4 : 2
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,12 +18,7 @@ Button {
|
||||
color: (button.down) ? Appearance.colors.colLayer2Active : (button.hovered ? Appearance.colors.colLayer2Hover : Appearance.colors.colLayer2)
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Appearance.animation.elementMove.duration
|
||||
easing.type: Appearance.animation.elementMove.type
|
||||
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
|
||||
}
|
||||
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,82 @@ post_process() {
|
||||
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() {
|
||||
imgpath="$1"
|
||||
mode_flag="$2"
|
||||
@@ -48,8 +124,67 @@ switch() {
|
||||
echo 'Aborted'
|
||||
exit 0
|
||||
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
|
||||
|
||||
# Determine mode if not set
|
||||
@@ -62,18 +197,14 @@ switch() {
|
||||
fi
|
||||
fi
|
||||
|
||||
# Dark/light mode, material scheme
|
||||
[[ -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")
|
||||
# Terminal scheme
|
||||
generate_colors_material_args+=(--termscheme "$terminalscheme" --blend_bg_fg)
|
||||
generate_colors_material_args+=(--cache "$STATE_DIR/user/color.txt")
|
||||
|
||||
|
||||
pre_process
|
||||
|
||||
# Generate with matugen
|
||||
matugen "${matugen_args[@]}"
|
||||
# Use custom script for mixing (matugen can't D:)
|
||||
source "$(eval echo $ILLOGICAL_IMPULSE_VIRTUAL_ENV)/bin/activate"
|
||||
python "$SCRIPT_DIR/generate_colors_material.py" "${generate_colors_material_args[@]}" \
|
||||
> "$STATE_DIR"/user/generated/material_colors.scss
|
||||
|
||||
@@ -14,11 +14,22 @@ Singleton {
|
||||
readonly property string interfaceRole: "interface"
|
||||
readonly property string apiKeyEnvVarName: "API_KEY"
|
||||
property Component aiMessageComponent: AiMessageData {}
|
||||
property string systemPrompt: ConfigOptions.ai.systemPrompt ?? ""
|
||||
property string systemPrompt: ConfigOptions?.ai?.systemPrompt ?? ""
|
||||
property var messages: []
|
||||
property var messageIDs: []
|
||||
property var messageByID: ({})
|
||||
readonly property var apiKeys: KeyringStorage.keyringData?.apiKeys ?? {}
|
||||
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:
|
||||
// - name: Name of the model
|
||||
// - icon: Icon name of the model
|
||||
@@ -36,14 +47,14 @@ Singleton {
|
||||
"gemini-2.0-flash-search": {
|
||||
"name": "Gemini 2.0 Flash",
|
||||
"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",
|
||||
"endpoint": "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:streamGenerateContent",
|
||||
"model": "gemini-2.0-flash",
|
||||
"requires_key": true,
|
||||
"key_id": "gemini",
|
||||
"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",
|
||||
"tools": [
|
||||
{
|
||||
@@ -54,25 +65,26 @@ Singleton {
|
||||
"openrouter-llama4-maverick": {
|
||||
"name": "Llama 4 Maverick",
|
||||
"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",
|
||||
"endpoint": "https://openrouter.ai/api/v1/chat/completions",
|
||||
"model": "meta-llama/llama-4-maverick:free",
|
||||
"requires_key": true,
|
||||
"key_id": "openrouter",
|
||||
"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": {
|
||||
"name": "DeepSeek R1",
|
||||
"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",
|
||||
"endpoint": "https://openrouter.ai/api/v1/chat/completions",
|
||||
"model": "deepseek/deepseek-r1:free",
|
||||
"requires_key": true,
|
||||
"key_id": "openrouter",
|
||||
"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)
|
||||
@@ -114,10 +126,11 @@ Singleton {
|
||||
const dataJson = JSON.parse(data);
|
||||
root.modelList = [...root.modelList, ...dataJson];
|
||||
dataJson.forEach(model => {
|
||||
root.models[model] = {
|
||||
const safeModelName = root.safeModelName(model);
|
||||
root.models[safeModelName] = {
|
||||
"name": guessModelName(model),
|
||||
"icon": guessModelLogo(model),
|
||||
"description": `Local Ollama model: ${model}`,
|
||||
"description": StringUtils.format(qsTr("Local Ollama model | {0}"), model),
|
||||
"homepage": `https://ollama.com/library/${model}`,
|
||||
"endpoint": "http://localhost:11434/v1/chat/completions",
|
||||
"model": model,
|
||||
@@ -141,13 +154,17 @@ Singleton {
|
||||
"thinking": false,
|
||||
"done": true,
|
||||
});
|
||||
root.messages = [...root.messages, aiMessage];
|
||||
const id = idForMessage(aiMessage);
|
||||
root.messageIDs = [...root.messageIDs, id];
|
||||
root.messageByID[id] = aiMessage;
|
||||
}
|
||||
|
||||
function removeMessage(index) {
|
||||
if (index < 0 || index >= messages.length) return;
|
||||
root.messages.splice(index, 1);
|
||||
root.messages = [...root.messages];
|
||||
if (index < 0 || index >= messageIDs.length) return;
|
||||
const id = root.messageIDs[index];
|
||||
root.messageIDs.splice(index, 1);
|
||||
root.messageIDs = [...root.messageIDs];
|
||||
delete root.messageByID[id];
|
||||
}
|
||||
|
||||
function addApiKeyAdvice(model) {
|
||||
@@ -167,7 +184,7 @@ Singleton {
|
||||
modelId = modelId.toLowerCase()
|
||||
if (modelList.indexOf(modelId) !== -1) {
|
||||
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 key not there show advice
|
||||
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) {
|
||||
const model = models[currentModelId];
|
||||
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;
|
||||
}
|
||||
if (!key || key.length === 0) {
|
||||
@@ -194,7 +211,7 @@ Singleton {
|
||||
return;
|
||||
}
|
||||
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() {
|
||||
@@ -207,12 +224,13 @@ Singleton {
|
||||
root.addMessage(StringUtils.format(qsTr("No API key set for {0}"), model.name), Ai.interfaceRole);
|
||||
}
|
||||
} 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() {
|
||||
messages = [];
|
||||
root.messageIDs = [];
|
||||
root.messageByID = ({});
|
||||
}
|
||||
|
||||
Process {
|
||||
@@ -274,7 +292,8 @@ Singleton {
|
||||
|
||||
/* Build endpoint, request data */
|
||||
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 = {
|
||||
"Content-Type": "application/json",
|
||||
@@ -288,7 +307,9 @@ Singleton {
|
||||
"thinking": true,
|
||||
"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 */
|
||||
let headerString = Object.entries(requestHeaders)
|
||||
@@ -318,14 +339,14 @@ Singleton {
|
||||
const dataJson = JSON.parse(requester.geminiBuffer);
|
||||
const responseContent = dataJson.candidates[0]?.content?.parts[0]?.text
|
||||
requester.message.content += responseContent;
|
||||
const annotationSources = dataJson.candidates[0]?.groundingMetadata.groundingChunks?.map(chunk => {
|
||||
const annotationSources = dataJson.candidates[0]?.groundingMetadata?.groundingChunks?.map(chunk => {
|
||||
return {
|
||||
"type": "url_citation",
|
||||
"text": chunk?.web?.title,
|
||||
"url": chunk?.web?.uri,
|
||||
}
|
||||
});
|
||||
const annotations = dataJson.candidates[0]?.groundingMetadata.groundingSupports?.map(citation => {
|
||||
const annotations = dataJson.candidates[0]?.groundingMetadata?.groundingSupports?.map(citation => {
|
||||
return {
|
||||
"type": "url_citation",
|
||||
"start_index": citation.segment?.startIndex,
|
||||
@@ -446,15 +467,7 @@ Singleton {
|
||||
|
||||
function sendUserMessage(message) {
|
||||
if (message.length === 0) return;
|
||||
|
||||
const userMessage = aiMessageComponent.createObject(root, {
|
||||
"role": "user",
|
||||
"content": message,
|
||||
"thinking": false,
|
||||
"done": true,
|
||||
});
|
||||
root.messages = [...root.messages, userMessage];
|
||||
|
||||
root.addMessage(message, "user");
|
||||
requester.makeRequest();
|
||||
}
|
||||
|
||||
|
||||
@@ -13,20 +13,13 @@ Singleton {
|
||||
|
||||
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 var responses: []
|
||||
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 providers: {
|
||||
"system": { "name": "System" },
|
||||
"system": { "name": qsTr("System") },
|
||||
"yandere": {
|
||||
"name": "yande.re",
|
||||
"url": "https://yande.re",
|
||||
@@ -347,7 +340,7 @@ Singleton {
|
||||
xhr.setRequestHeader("User-Agent", defaultUserAgent)
|
||||
}
|
||||
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)
|
||||
}
|
||||
root.runningRequests++;
|
||||
|
||||
@@ -134,13 +134,13 @@ Singleton {
|
||||
|
||||
GlobalShortcut {
|
||||
name: "brightnessIncrease"
|
||||
description: "Increase brightness"
|
||||
description: qsTr("Increase brightness")
|
||||
onPressed: root.increaseBrightness()
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "brightnessDecrease"
|
||||
description: "Decrease brightness"
|
||||
description: qsTr("Decrease brightness")
|
||||
onPressed: root.decreaseBrightness()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,11 +29,11 @@ Singleton {
|
||||
if (root.firstLoad) {
|
||||
root.firstLoad = false;
|
||||
} else {
|
||||
Hyprland.dispatch(`exec notify-send "Shell configuration reloaded" "${root.filePath}"`)
|
||||
Hyprland.dispatch(`exec notify-send "${qsTr("Shell configuration reloaded")}" "${root.filePath}"`)
|
||||
}
|
||||
} catch (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;
|
||||
}
|
||||
}
|
||||
@@ -66,9 +66,9 @@ Singleton {
|
||||
console.log("[ConfigLoader] File not found, creating new file.")
|
||||
const plainConfig = ObjectUtils.toPlainObject(ConfigOptions)
|
||||
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 {
|
||||
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 (minutes > 0 || !formatted) formatted += `${formatted ? ", " : ""}${minutes}m`
|
||||
uptime = formatted
|
||||
interval = ConfigOptions.resources.updateInterval;
|
||||
interval = ConfigOptions?.resources?.updateInterval ?? 3000
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,19 +11,15 @@ import Quickshell.Hyprland
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
property var defaultKeybinds: []
|
||||
property var userKeybinds: []
|
||||
property var defaultKeybinds: {"children": []}
|
||||
property var userKeybinds: {"children": []}
|
||||
property var keybinds: ({
|
||||
children: [
|
||||
...defaultKeybinds.children,
|
||||
...userKeybinds.children,
|
||||
...(defaultKeybinds.children ?? []),
|
||||
...(userKeybinds.children ?? []),
|
||||
]
|
||||
})
|
||||
|
||||
// onKeybindsChanged: {
|
||||
// console.log("[CheatsheetKeybinds] Keybinds changed:", JSON.stringify(keybinds, null, 2))
|
||||
// }
|
||||
|
||||
Connections {
|
||||
target: Hyprland
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ pragma Singleton
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import "root:/modules/common"
|
||||
import "root:/modules/common/functions/string_utils.js" as StringUtils
|
||||
import Quickshell;
|
||||
import Quickshell.Io;
|
||||
import Qt.labs.platform
|
||||
@@ -18,14 +19,14 @@ Singleton {
|
||||
|
||||
property var properties: {
|
||||
"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(
|
||||
function(arr, 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) {
|
||||
if (!root.keyringData) root.keyringData = {};
|
||||
|
||||
@@ -30,7 +30,7 @@ Singleton {
|
||||
|
||||
Timer {
|
||||
id: delayedFileRead
|
||||
interval: ConfigOptions.hacks.arbitraryRaceConditionDelay
|
||||
interval: ConfigOptions?.hacks?.arbitraryRaceConditionDelay ?? 100
|
||||
repeat: false
|
||||
running: false
|
||||
onTriggered: {
|
||||
|
||||
@@ -72,7 +72,7 @@ Singleton {
|
||||
|
||||
Timer {
|
||||
id: delayedFileRead
|
||||
interval: ConfigOptions.hacks.arbitraryRaceConditionDelay
|
||||
interval: ConfigOptions?.hacks?.arbitraryRaceConditionDelay ?? 100
|
||||
repeat: false
|
||||
running: false
|
||||
onTriggered: {
|
||||
|
||||
@@ -50,7 +50,7 @@ Singleton {
|
||||
|
||||
previousCpuStats = { total, idle }
|
||||
}
|
||||
interval = ConfigOptions.resources.updateInterval
|
||||
interval = ConfigOptions?.resources?.updateInterval ?? 3000
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
//@ pragma UseQApplication
|
||||
//@ pragma Env QT_QUICK_CONTROLS_STYLE=Basic
|
||||
//@ pragma Env QS_NO_RELOAD_POPUP=1
|
||||
|
||||
import "./modules/bar/"
|
||||
import "./modules/cheatsheet/"
|
||||
import "./modules/mediaControls/"
|
||||
import "./modules/notificationPopup/"
|
||||
import "./modules/onScreenDisplay/"
|
||||
import "./modules/overview/"
|
||||
@@ -25,6 +28,7 @@ ShellRoot {
|
||||
|
||||
Bar {}
|
||||
Cheatsheet {}
|
||||
MediaControls {}
|
||||
NotificationPopup {}
|
||||
OnScreenDisplayBrightness {}
|
||||
OnScreenDisplayVolume {}
|
||||
|
||||
+22
-6
@@ -1,10 +1,26 @@
|
||||
#!/bin/bash
|
||||
if [ $? -eq 0 ]
|
||||
then
|
||||
sed '1,/^### DATA ###$/d' $0 | fuzzel --match-mode fzf --dmenu | cut -d ' ' -f 1 | tr -d '\n' | wl-copy
|
||||
else
|
||||
sed '1,/^### DATA ###$/d' $0 | fuzzel --match-mode fzf --dmenu | cut -d ' ' -f 1 | tr -d '\n' | wl-copy
|
||||
fi
|
||||
set -euo pipefail
|
||||
|
||||
MODE="${1:-type}"
|
||||
|
||||
emoji="$(sed '1,/^### DATA ###$/d' "$0" | fuzzel --match-mode fzf --dmenu | cut -d ' ' -f 1 | tr -d '\n')"
|
||||
|
||||
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
|
||||
### DATA ###
|
||||
😀 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:
|
||||
|
||||
- **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 quickshell and more stuff**: `yay -S quickshell matugen-bin grimblast`
|
||||
- **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 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)
|
||||
- **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
|
||||
|
||||
@@ -7,6 +7,6 @@ license=(None)
|
||||
depends=(
|
||||
polkit-gnome
|
||||
gnome-keyring
|
||||
gnome-control-center
|
||||
blueberry networkmanager
|
||||
networkmanager
|
||||
better-control-git
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user