added quickshell and updated zsh
This commit is contained in:
338
home/hypr/quickshell/components/Dock.qml
Normal file
338
home/hypr/quickshell/components/Dock.qml
Normal file
@@ -0,0 +1,338 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
import "../theme"
|
||||
import ".."
|
||||
|
||||
PanelWindow {
|
||||
id: dockWindow
|
||||
property var modelData
|
||||
screen: modelData
|
||||
|
||||
anchors {
|
||||
bottom: true
|
||||
}
|
||||
|
||||
margins {
|
||||
bottom: 6
|
||||
}
|
||||
|
||||
// Adjust width to fit content
|
||||
implicitWidth: dockTray.implicitWidth + 32
|
||||
implicitHeight: 90
|
||||
|
||||
color: "transparent"
|
||||
WlrLayershell.layer: WlrLayer.Top
|
||||
WlrLayershell.namespace: "quickshell-dock"
|
||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
|
||||
|
||||
// Reserve 70px exclusive zone so windows don't overlap the dock
|
||||
exclusiveZone: 70
|
||||
|
||||
// Tooltip overlay container
|
||||
Item {
|
||||
id: tooltipContainer
|
||||
anchors.fill: parent
|
||||
z: 100
|
||||
}
|
||||
|
||||
// 2.5D Frosted Glass Dock Tray
|
||||
Rectangle {
|
||||
id: dockTray
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
implicitWidth: dockLayout.implicitWidth + 24
|
||||
height: 68
|
||||
radius: Theme.radiusDock
|
||||
color: Theme.dockBg
|
||||
border.color: Theme.dockBorder
|
||||
border.width: 1
|
||||
|
||||
// Top glossy reflection edge line (Catalina shelf characteristic)
|
||||
Rectangle {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.leftMargin: 12
|
||||
anchors.rightMargin: 12
|
||||
height: 1
|
||||
color: Theme.dockHighlight
|
||||
opacity: 0.8
|
||||
}
|
||||
|
||||
// Inner items layout
|
||||
RowLayout {
|
||||
id: dockLayout
|
||||
anchors.centerIn: parent
|
||||
spacing: 6
|
||||
|
||||
// =================================================================
|
||||
// 1. PINNED APPS
|
||||
// =================================================================
|
||||
DockItem {
|
||||
id: finderItem
|
||||
name: "Finder"
|
||||
iconSource: "../assets/finder.svg"
|
||||
execCmd: "foot -e sh -c 'cd ~; lf || yazi || ls -la; exec zsh'"
|
||||
appClass: "foot"
|
||||
isPinned: true
|
||||
}
|
||||
|
||||
DockItem {
|
||||
name: "Launchpad"
|
||||
iconSource: "../assets/launchpad.svg"
|
||||
execCmd: ""
|
||||
isPinned: true
|
||||
onCustomClicked: GlobalState.toggleSpotlight()
|
||||
}
|
||||
|
||||
DockItem {
|
||||
name: "Brave Browser"
|
||||
iconSource: "../assets/browser.svg"
|
||||
execCmd: "brave"
|
||||
appClass: "brave-browser"
|
||||
isPinned: true
|
||||
}
|
||||
|
||||
DockItem {
|
||||
name: "Terminal"
|
||||
iconSource: "../assets/terminal.svg"
|
||||
execCmd: "foot"
|
||||
appClass: "foot"
|
||||
isPinned: true
|
||||
}
|
||||
|
||||
DockItem {
|
||||
name: "Neovim"
|
||||
iconSource: "../assets/code.svg"
|
||||
execCmd: "foot -e nvim"
|
||||
appClass: "nvim"
|
||||
isPinned: true
|
||||
}
|
||||
|
||||
DockItem {
|
||||
name: "System Preferences"
|
||||
iconSource: "../assets/preferences.svg"
|
||||
execCmd: "pavucontrol"
|
||||
appClass: "pavucontrol"
|
||||
isPinned: true
|
||||
}
|
||||
|
||||
DockItem {
|
||||
name: "App Store"
|
||||
iconSource: "../assets/appstore.svg"
|
||||
execCmd: "foot -e zsh -c 'echo NixOS Packages; nix-env -qaP | head -n 50; exec zsh'"
|
||||
appClass: ""
|
||||
isPinned: true
|
||||
}
|
||||
|
||||
DockItem {
|
||||
name: "Music"
|
||||
iconSource: "../assets/music.svg"
|
||||
execCmd: ""
|
||||
appClass: ""
|
||||
isPinned: true
|
||||
onCustomClicked: GlobalState.mediaPlayPause()
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// 2. DYNAMIC RUNNING APPS (Not pinned)
|
||||
// =================================================================
|
||||
Repeater {
|
||||
model: {
|
||||
const pinnedClasses = ["foot", "brave-browser", "pavucontrol", "nvim"];
|
||||
const list = [];
|
||||
const seen = {};
|
||||
if (Array.isArray(GlobalState.runningApps)) {
|
||||
for (let i = 0; i < GlobalState.runningApps.length; i++) {
|
||||
const client = GlobalState.runningApps[i];
|
||||
const cls = (client.class || "").toLowerCase();
|
||||
if (cls && !pinnedClasses.includes(cls) && !seen[cls]) {
|
||||
seen[cls] = true;
|
||||
list.push({
|
||||
name: client.title || client.class || "App",
|
||||
cls: client.class,
|
||||
address: client.address
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
delegate: DockItem {
|
||||
required property var modelData
|
||||
name: modelData.name
|
||||
iconSource: "../assets/code.svg"
|
||||
appClass: modelData.cls
|
||||
isPinned: false
|
||||
onCustomClicked: {
|
||||
if (modelData.address) {
|
||||
Hyprland.dispatch(`focuswindow address:${modelData.address}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// 3. DOCK SEPARATOR DIVIDER
|
||||
// =================================================================
|
||||
Rectangle {
|
||||
Layout.preferredWidth: 1
|
||||
Layout.preferredHeight: 44
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.leftMargin: 4
|
||||
Layout.rightMargin: 4
|
||||
color: Qt.rgba(1.0, 1.0, 1.0, 0.22)
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// 4. DOWNLOADS FOLDER & TRASH
|
||||
// =================================================================
|
||||
DockItem {
|
||||
name: "Downloads"
|
||||
iconSource: "../assets/folder-downloads.svg"
|
||||
execCmd: "foot -e sh -c 'cd ~/Downloads; ls -la; exec zsh'"
|
||||
isPinned: true
|
||||
}
|
||||
|
||||
DockItem {
|
||||
name: "Trash"
|
||||
iconSource: "../assets/trash-empty.svg"
|
||||
execCmd: "foot -e sh -c 'cd ~/.local/share/Trash/files 2>/dev/null || cd ~; ls -la; exec zsh'"
|
||||
isPinned: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reusable Dock Item Component
|
||||
component DockItem: Item {
|
||||
id: itemRoot
|
||||
property string name: ""
|
||||
property string iconSource: ""
|
||||
property string execCmd: ""
|
||||
property string appClass: ""
|
||||
property bool isPinned: true
|
||||
signal customClicked()
|
||||
|
||||
// Check if app is running
|
||||
readonly property bool isRunning: {
|
||||
if (!itemRoot.appClass) return false;
|
||||
const target = itemRoot.appClass.toLowerCase();
|
||||
if (GlobalState.activeWindowClass.toLowerCase().includes(target)) return true;
|
||||
if (Array.isArray(GlobalState.runningApps)) {
|
||||
for (let i = 0; i < GlobalState.runningApps.length; i++) {
|
||||
const c = GlobalState.runningApps[i];
|
||||
if ((c.class || "").toLowerCase().includes(target)) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
implicitWidth: 50
|
||||
implicitHeight: 64
|
||||
Layout.alignment: Qt.AlignBottom
|
||||
|
||||
// Container with scale & bounce transform
|
||||
Item {
|
||||
id: iconWrapper
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 8
|
||||
width: 48
|
||||
height: 48
|
||||
|
||||
scale: itemArea.containsMouse ? 1.28 : 1.0
|
||||
y: itemArea.containsMouse ? -8 : 0
|
||||
|
||||
Behavior on scale {
|
||||
NumberAnimation { duration: 180; easing.type: Easing.OutCubic }
|
||||
}
|
||||
Behavior on y {
|
||||
NumberAnimation { duration: 180; easing.type: Easing.OutCubic }
|
||||
}
|
||||
|
||||
// Icon Image
|
||||
Image {
|
||||
anchors.centerIn: parent
|
||||
width: 46
|
||||
height: 46
|
||||
source: itemRoot.iconSource
|
||||
sourceSize: Qt.size(92, 92)
|
||||
smooth: true
|
||||
mipmap: true
|
||||
}
|
||||
|
||||
// Click Bounce Sequential Animation
|
||||
SequentialAnimation {
|
||||
id: bounceAnim
|
||||
NumberAnimation { target: iconWrapper; property: "y"; to: -24; duration: 150; easing.type: Easing.OutQuad }
|
||||
NumberAnimation { target: iconWrapper; property: "y"; to: -4; duration: 130; easing.type: Easing.InQuad }
|
||||
NumberAnimation { target: iconWrapper; property: "y"; to: -14; duration: 100; easing.type: Easing.OutQuad }
|
||||
NumberAnimation { target: iconWrapper; property: "y"; to: 0; duration: 100; easing.type: Easing.InQuad }
|
||||
}
|
||||
}
|
||||
|
||||
// Active App Running Indicator Dot (White glowing pill)
|
||||
Rectangle {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 2
|
||||
width: 4
|
||||
height: 4
|
||||
radius: 2
|
||||
color: Theme.dockDot
|
||||
visible: itemRoot.isRunning
|
||||
}
|
||||
|
||||
// Hover Tooltip Badge (macOS Catalina Style Black Pill)
|
||||
Rectangle {
|
||||
id: tooltipPill
|
||||
visible: itemArea.containsMouse
|
||||
opacity: itemArea.containsMouse ? 1.0 : 0.0
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: parent.top
|
||||
anchors.bottomMargin: 6
|
||||
implicitWidth: tooltipText.implicitWidth + 16
|
||||
height: 24
|
||||
radius: 6
|
||||
color: Qt.rgba(0.08, 0.08, 0.10, 0.92)
|
||||
border.color: Qt.rgba(1.0, 1.0, 1.0, 0.18)
|
||||
border.width: 1
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation { duration: 120 }
|
||||
}
|
||||
|
||||
Text {
|
||||
id: tooltipText
|
||||
anchors.centerIn: parent
|
||||
text: itemRoot.name
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.Medium
|
||||
color: "#FFFFFF"
|
||||
}
|
||||
}
|
||||
|
||||
// Mouse interaction
|
||||
MouseArea {
|
||||
id: itemArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
|
||||
onClicked: {
|
||||
bounceAnim.start();
|
||||
if (itemRoot.execCmd !== "") {
|
||||
GlobalState.launchApp(itemRoot.execCmd);
|
||||
} else {
|
||||
itemRoot.customClicked();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user