mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-24 21:42:51 -05:00
add shell
This commit is contained in:
58
Widgets/AppLauncherButton.qml
Normal file
58
Widgets/AppLauncherButton.qml
Normal file
@@ -0,0 +1,58 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
|
||||
Rectangle {
|
||||
id: archLauncher
|
||||
|
||||
property var theme
|
||||
property var root
|
||||
|
||||
width: Math.max(120, launcherRow.implicitWidth + theme.spacingM * 2)
|
||||
height: 32
|
||||
radius: theme.cornerRadius
|
||||
color: launcherArea.containsMouse ? Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.12) : Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.08)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
Row {
|
||||
id: launcherRow
|
||||
anchors.centerIn: parent
|
||||
spacing: theme.spacingS
|
||||
|
||||
Text {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: root.osLogo || "apps"
|
||||
font.family: root.osLogo ? "NerdFont" : theme.iconFont
|
||||
font.pixelSize: root.osLogo ? theme.iconSize - 2 : theme.iconSize - 2
|
||||
font.weight: theme.iconFontWeight
|
||||
color: theme.surfaceText
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: "Applications"
|
||||
font.pixelSize: theme.fontSizeMedium
|
||||
font.weight: Font.Medium
|
||||
color: theme.surfaceText
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: launcherArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
|
||||
onClicked: {
|
||||
root.appLauncher.toggle()
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: theme.shortDuration
|
||||
easing.type: theme.standardEasing
|
||||
}
|
||||
}
|
||||
}
|
||||
40
Widgets/ClipboardButton.qml
Normal file
40
Widgets/ClipboardButton.qml
Normal file
@@ -0,0 +1,40 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
|
||||
Rectangle {
|
||||
property var theme
|
||||
property var root
|
||||
|
||||
width: 40
|
||||
height: 32
|
||||
radius: theme.cornerRadius
|
||||
color: clipboardArea.containsMouse ? Qt.rgba(theme.primary.r, theme.primary.g, theme.primary.b, 0.12) : Qt.rgba(theme.secondary.r, theme.secondary.g, theme.secondary.b, 0.08)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: "content_paste"
|
||||
font.family: theme.iconFont
|
||||
font.pixelSize: theme.iconSize - 6
|
||||
font.weight: theme.iconFontWeight
|
||||
color: theme.surfaceText
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: clipboardArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
|
||||
onClicked: {
|
||||
root.clipboardHistoryPopup.toggle()
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: theme.shortDuration
|
||||
easing.type: theme.standardEasing
|
||||
}
|
||||
}
|
||||
}
|
||||
291
Widgets/ClockWidget.qml
Normal file
291
Widgets/ClockWidget.qml
Normal file
@@ -0,0 +1,291 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Quickshell.Services.Mpris
|
||||
|
||||
Rectangle {
|
||||
id: clockContainer
|
||||
|
||||
property var theme
|
||||
property var root
|
||||
|
||||
width: Math.min(root.hasActiveMedia ? 500 : (root.weather.available ? 280 : 200), parent.width - theme.spacingL * 2)
|
||||
height: root.hasActiveMedia ? 80 : 32
|
||||
radius: theme.cornerRadius
|
||||
color: clockMouseArea.containsMouse && root.hasActiveMedia ?
|
||||
Qt.rgba(theme.primary.r, theme.primary.g, theme.primary.b, 0.12) :
|
||||
Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.08)
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: theme.shortDuration
|
||||
easing.type: theme.standardEasing
|
||||
}
|
||||
}
|
||||
|
||||
property date currentDate: new Date()
|
||||
|
||||
// Media player content (when active)
|
||||
Column {
|
||||
visible: root.hasActiveMedia
|
||||
anchors.centerIn: parent
|
||||
width: parent.width - theme.spacingM * 2
|
||||
spacing: theme.spacingXS
|
||||
|
||||
Row {
|
||||
width: parent.width
|
||||
spacing: theme.spacingS
|
||||
|
||||
Rectangle {
|
||||
width: 48
|
||||
height: 48
|
||||
radius: theme.cornerRadiusSmall
|
||||
color: Qt.rgba(theme.surfaceVariant.r, theme.surfaceVariant.g, theme.surfaceVariant.b, 0.3)
|
||||
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
clip: true
|
||||
|
||||
Image {
|
||||
anchors.fill: parent
|
||||
source: root.activePlayer?.trackArtUrl || ""
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
smooth: true
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
visible: parent.children[0].status !== Image.Ready
|
||||
color: "transparent"
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: "music_note"
|
||||
font.family: theme.iconFont
|
||||
font.pixelSize: theme.iconSize
|
||||
color: theme.surfaceVariantText
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
width: parent.width - 48 - theme.spacingS - 120
|
||||
spacing: 2
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
Text {
|
||||
text: root.activePlayer?.trackTitle || "Unknown Track"
|
||||
font.pixelSize: theme.fontSizeMedium
|
||||
color: theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
width: parent.width
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
Text {
|
||||
text: root.activePlayer?.trackArtist || "Unknown Artist"
|
||||
font.pixelSize: theme.fontSizeSmall
|
||||
color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.7)
|
||||
width: parent.width
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: theme.spacingS
|
||||
|
||||
Rectangle {
|
||||
width: 28
|
||||
height: 28
|
||||
radius: 14
|
||||
color: prevBtnArea.containsMouse ? Qt.rgba(theme.surfaceVariant.r, theme.surfaceVariant.g, theme.surfaceVariant.b, 0.12) : "transparent"
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: "skip_previous"
|
||||
font.family: theme.iconFont
|
||||
font.pixelSize: 16
|
||||
color: theme.surfaceText
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: prevBtnArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: root.activePlayer?.previous()
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 28
|
||||
height: 28
|
||||
radius: 14
|
||||
color: theme.primary
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: root.activePlayer?.playbackState === MprisPlaybackState.Playing ? "pause" : "play_arrow"
|
||||
font.family: theme.iconFont
|
||||
font.pixelSize: 16
|
||||
color: theme.background
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: root.activePlayer?.togglePlaying()
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 28
|
||||
height: 28
|
||||
radius: 14
|
||||
color: nextBtnArea.containsMouse ? Qt.rgba(theme.surfaceVariant.r, theme.surfaceVariant.g, theme.surfaceVariant.b, 0.12) : "transparent"
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: "skip_next"
|
||||
font.family: theme.iconFont
|
||||
font.pixelSize: 16
|
||||
color: theme.surfaceText
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: nextBtnArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: root.activePlayer?.next()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 4
|
||||
radius: 2
|
||||
color: Qt.rgba(theme.surfaceVariant.r, theme.surfaceVariant.g, theme.surfaceVariant.b, 0.3)
|
||||
|
||||
Rectangle {
|
||||
id: progressFill
|
||||
width: parent.width * (root.activePlayer?.position / Math.max(root.activePlayer?.length || 1, 1))
|
||||
height: parent.height
|
||||
radius: parent.radius
|
||||
color: theme.primary
|
||||
|
||||
Behavior on width {
|
||||
NumberAnimation {
|
||||
duration: 200
|
||||
easing.type: Easing.OutQuad
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
|
||||
onClicked: (mouse) => {
|
||||
if (root.activePlayer && root.activePlayer.length > 0) {
|
||||
const newPosition = (mouse.x / width) * root.activePlayer.length
|
||||
root.activePlayer.setPosition(newPosition)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Normal clock/weather content (when no media)
|
||||
Row {
|
||||
anchors.centerIn: parent
|
||||
spacing: theme.spacingM
|
||||
visible: !root.hasActiveMedia
|
||||
|
||||
// Weather info (when available)
|
||||
Row {
|
||||
spacing: theme.spacingXS
|
||||
visible: root.weather.available
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
Text {
|
||||
text: root.weatherIcons[root.weather.wCode] || "clear_day"
|
||||
font.family: theme.iconFont
|
||||
font.pixelSize: theme.iconSize - 2
|
||||
color: theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Text {
|
||||
text: (root.useFahrenheit ? root.weather.tempF : root.weather.temp) + "°" + (root.useFahrenheit ? "F" : "C")
|
||||
font.pixelSize: theme.fontSizeMedium
|
||||
color: theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
// Separator when weather is available
|
||||
Text {
|
||||
text: "•"
|
||||
font.pixelSize: theme.fontSizeMedium
|
||||
color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.5)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: root.weather.available
|
||||
}
|
||||
|
||||
// Time and date
|
||||
Row {
|
||||
spacing: theme.spacingS
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
Text {
|
||||
text: Qt.formatTime(clockContainer.currentDate, "h:mm AP")
|
||||
font.pixelSize: theme.fontSizeMedium
|
||||
color: theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "•"
|
||||
font.pixelSize: theme.fontSizeMedium
|
||||
color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.5)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Text {
|
||||
text: Qt.formatDate(clockContainer.currentDate, "ddd d")
|
||||
font.pixelSize: theme.fontSizeMedium
|
||||
color: theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
interval: 1000
|
||||
running: true
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
clockContainer.currentDate = new Date()
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: clockMouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: !root.hasActiveMedia ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
enabled: !root.hasActiveMedia
|
||||
|
||||
onClicked: {
|
||||
root.calendarVisible = !root.calendarVisible
|
||||
}
|
||||
}
|
||||
}
|
||||
40
Widgets/ColorPickerButton.qml
Normal file
40
Widgets/ColorPickerButton.qml
Normal file
@@ -0,0 +1,40 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
|
||||
Rectangle {
|
||||
property var theme
|
||||
property var root
|
||||
|
||||
width: 40
|
||||
height: 32
|
||||
radius: theme.cornerRadius
|
||||
color: colorPickerArea.containsMouse ? Qt.rgba(theme.primary.r, theme.primary.g, theme.primary.b, 0.12) : Qt.rgba(theme.secondary.r, theme.secondary.g, theme.secondary.b, 0.08)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: "colorize"
|
||||
font.family: theme.iconFont
|
||||
font.pixelSize: theme.iconSize - 6
|
||||
font.weight: theme.iconFontWeight
|
||||
color: theme.surfaceText
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: colorPickerArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
|
||||
onClicked: {
|
||||
root.colorPickerProcess.running = true
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: theme.shortDuration
|
||||
easing.type: theme.standardEasing
|
||||
}
|
||||
}
|
||||
}
|
||||
57
Widgets/NotificationButton.qml
Normal file
57
Widgets/NotificationButton.qml
Normal file
@@ -0,0 +1,57 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
|
||||
Rectangle {
|
||||
property var theme
|
||||
property var root
|
||||
|
||||
width: 40
|
||||
height: 32
|
||||
radius: theme.cornerRadius
|
||||
color: notificationArea.containsMouse || root.notificationHistoryVisible ?
|
||||
Qt.rgba(theme.primary.r, theme.primary.g, theme.primary.b, 0.16) :
|
||||
Qt.rgba(theme.secondary.r, theme.secondary.g, theme.secondary.b, 0.08)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
property bool hasUnread: root.notificationHistory.count > 0
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: "notifications"
|
||||
font.family: theme.iconFont
|
||||
font.pixelSize: theme.iconSize - 6
|
||||
font.weight: theme.iconFontWeight
|
||||
color: notificationArea.containsMouse || root.notificationHistoryVisible ?
|
||||
theme.primary : theme.surfaceText
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 8
|
||||
height: 8
|
||||
radius: 4
|
||||
color: theme.error
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.rightMargin: 6
|
||||
anchors.topMargin: 6
|
||||
visible: parent.hasUnread
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: notificationArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
|
||||
onClicked: {
|
||||
root.notificationHistoryVisible = !root.notificationHistoryVisible
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: theme.shortDuration
|
||||
easing.type: theme.standardEasing
|
||||
}
|
||||
}
|
||||
}
|
||||
111
Widgets/SystemTrayWidget.qml
Normal file
111
Widgets/SystemTrayWidget.qml
Normal file
@@ -0,0 +1,111 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import Quickshell.Services.SystemTray
|
||||
|
||||
Rectangle {
|
||||
property var theme
|
||||
property var root
|
||||
|
||||
width: Math.max(40, systemTrayRow.implicitWidth + theme.spacingS * 2)
|
||||
height: 32
|
||||
radius: theme.cornerRadius
|
||||
color: Qt.rgba(theme.secondary.r, theme.secondary.g, theme.secondary.b, 0.08)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: systemTrayRow.children.length > 0
|
||||
|
||||
Row {
|
||||
id: systemTrayRow
|
||||
anchors.centerIn: parent
|
||||
spacing: theme.spacingXS
|
||||
|
||||
Repeater {
|
||||
model: SystemTray.items
|
||||
delegate: Rectangle {
|
||||
width: 24
|
||||
height: 24
|
||||
radius: theme.cornerRadiusSmall
|
||||
color: trayItemArea.containsMouse ? Qt.rgba(theme.primary.r, theme.primary.g, theme.primary.b, 0.12) : "transparent"
|
||||
|
||||
property var trayItem: modelData
|
||||
|
||||
Image {
|
||||
anchors.centerIn: parent
|
||||
width: 18
|
||||
height: 18
|
||||
source: {
|
||||
let icon = trayItem?.icon || "";
|
||||
if (!icon) return "";
|
||||
|
||||
if (icon.includes("?path=")) {
|
||||
const [name, path] = icon.split("?path=");
|
||||
const fileName = name.substring(name.lastIndexOf("/") + 1);
|
||||
return `file://${path}/${fileName}`;
|
||||
}
|
||||
return icon;
|
||||
}
|
||||
asynchronous: true
|
||||
smooth: true
|
||||
fillMode: Image.PreserveAspectFit
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: trayItemArea
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
|
||||
onClicked: (mouse) => {
|
||||
if (!trayItem) return;
|
||||
|
||||
if (mouse.button === Qt.LeftButton) {
|
||||
if (!trayItem.onlyMenu) {
|
||||
trayItem.activate()
|
||||
}
|
||||
} else if (mouse.button === Qt.RightButton) {
|
||||
if (trayItem.hasMenu) {
|
||||
console.log("Right-click detected, showing menu for:", trayItem.title || "Unknown")
|
||||
customTrayMenu.showMenu(mouse.x, mouse.y)
|
||||
} else {
|
||||
console.log("No menu available for:", trayItem.title || "Unknown")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: customTrayMenu
|
||||
|
||||
property bool menuVisible: false
|
||||
|
||||
function showMenu(x, y) {
|
||||
root.currentTrayMenu = customTrayMenu
|
||||
root.currentTrayItem = trayItem
|
||||
|
||||
root.trayMenuX = parent.parent.parent.parent.x + parent.parent.parent.parent.width - 180 - theme.spacingL
|
||||
root.trayMenuY = theme.barHeight + theme.spacingS
|
||||
|
||||
console.log("Showing menu at:", root.trayMenuX, root.trayMenuY)
|
||||
menuVisible = true
|
||||
root.showTrayMenu = true
|
||||
}
|
||||
|
||||
function hideMenu() {
|
||||
menuVisible = false
|
||||
root.showTrayMenu = false
|
||||
root.currentTrayMenu = null
|
||||
root.currentTrayItem = null
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: theme.shortDuration
|
||||
easing.type: theme.standardEasing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
117
Widgets/TopBar.qml
Normal file
117
Widgets/TopBar.qml
Normal file
@@ -0,0 +1,117 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import Quickshell.Widgets
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Services.SystemTray
|
||||
import "../Services"
|
||||
|
||||
PanelWindow {
|
||||
id: topBar
|
||||
|
||||
property var theme
|
||||
property var root
|
||||
|
||||
anchors {
|
||||
top: true
|
||||
left: true
|
||||
right: true
|
||||
}
|
||||
|
||||
implicitHeight: theme.barHeight
|
||||
color: "transparent"
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: Qt.rgba(theme.surfaceContainer.r, theme.surfaceContainer.g, theme.surfaceContainer.b, 0.95)
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: "transparent"
|
||||
border.color: Qt.rgba(theme.outline.r, theme.outline.g, theme.outline.b, 0.12)
|
||||
border.width: 1
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: Qt.rgba(theme.surfaceTint.r, theme.surfaceTint.g, theme.surfaceTint.b, 0.08)
|
||||
|
||||
SequentialAnimation on opacity {
|
||||
running: true
|
||||
loops: Animation.Infinite
|
||||
NumberAnimation {
|
||||
to: 0.12
|
||||
duration: theme.extraLongDuration
|
||||
easing.type: theme.standardEasing
|
||||
}
|
||||
NumberAnimation {
|
||||
to: 0.06
|
||||
duration: theme.extraLongDuration
|
||||
easing.type: theme.standardEasing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: theme.spacingL
|
||||
anchors.rightMargin: theme.spacingL
|
||||
|
||||
// Left section - Apps and Workspace Switcher
|
||||
Row {
|
||||
id: leftSection
|
||||
height: parent.height
|
||||
spacing: theme.spacingL
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
AppLauncherButton {
|
||||
theme: topBar.theme
|
||||
root: topBar.root
|
||||
}
|
||||
|
||||
WorkspaceSwitcher {
|
||||
theme: topBar.theme
|
||||
root: topBar.root
|
||||
}
|
||||
}
|
||||
|
||||
// Center section - Clock/Media Player
|
||||
ClockWidget {
|
||||
id: clockWidget
|
||||
theme: topBar.theme
|
||||
root: topBar.root
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
// Right section - System controls
|
||||
Row {
|
||||
id: rightSection
|
||||
height: parent.height
|
||||
spacing: theme.spacingXS
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
SystemTrayWidget {
|
||||
theme: topBar.theme
|
||||
root: topBar.root
|
||||
}
|
||||
|
||||
ClipboardButton {
|
||||
theme: topBar.theme
|
||||
root: topBar.root
|
||||
}
|
||||
|
||||
ColorPickerButton {
|
||||
theme: topBar.theme
|
||||
root: topBar.root
|
||||
}
|
||||
|
||||
NotificationButton {
|
||||
theme: topBar.theme
|
||||
root: topBar.root
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
111
Widgets/TopBarSimple.qml
Normal file
111
Widgets/TopBarSimple.qml
Normal file
@@ -0,0 +1,111 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import Quickshell.Widgets
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Services.SystemTray
|
||||
|
||||
PanelWindow {
|
||||
id: topBar
|
||||
|
||||
property var theme
|
||||
property var root
|
||||
|
||||
anchors {
|
||||
top: true
|
||||
left: true
|
||||
right: true
|
||||
}
|
||||
|
||||
implicitHeight: theme.barHeight
|
||||
color: "transparent"
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: Qt.rgba(theme.surfaceContainer.r, theme.surfaceContainer.g, theme.surfaceContainer.b, 0.95)
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: "transparent"
|
||||
border.color: Qt.rgba(theme.outline.r, theme.outline.g, theme.outline.b, 0.12)
|
||||
border.width: 1
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: Qt.rgba(theme.surfaceTint.r, theme.surfaceTint.g, theme.surfaceTint.b, 0.08)
|
||||
|
||||
SequentialAnimation on opacity {
|
||||
running: true
|
||||
loops: Animation.Infinite
|
||||
NumberAnimation {
|
||||
to: 0.12
|
||||
duration: theme.extraLongDuration
|
||||
easing.type: theme.standardEasing
|
||||
}
|
||||
NumberAnimation {
|
||||
to: 0.06
|
||||
duration: theme.extraLongDuration
|
||||
easing.type: theme.standardEasing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: theme.spacingL
|
||||
anchors.rightMargin: theme.spacingL
|
||||
|
||||
// Left section - Apps and Workspace Switcher
|
||||
Row {
|
||||
id: leftSection
|
||||
height: parent.height
|
||||
spacing: theme.spacingL
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
AppLauncherButton {
|
||||
theme: topBar.theme
|
||||
root: topBar.root
|
||||
}
|
||||
|
||||
WorkspaceSwitcher {
|
||||
theme: topBar.theme
|
||||
root: topBar.root
|
||||
}
|
||||
}
|
||||
|
||||
// Center section - Clock/Media Player
|
||||
ClockWidget {
|
||||
id: clockWidget
|
||||
theme: topBar.theme
|
||||
root: topBar.root
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
// Right section - System controls
|
||||
Row {
|
||||
id: rightSection
|
||||
height: parent.height
|
||||
spacing: theme.spacingXS
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
ClipboardButton {
|
||||
theme: topBar.theme
|
||||
root: topBar.root
|
||||
}
|
||||
|
||||
ColorPickerButton {
|
||||
theme: topBar.theme
|
||||
root: topBar.root
|
||||
}
|
||||
|
||||
NotificationButton {
|
||||
theme: topBar.theme
|
||||
root: topBar.root
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
145
Widgets/WorkspaceSwitcher.qml
Normal file
145
Widgets/WorkspaceSwitcher.qml
Normal file
@@ -0,0 +1,145 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
|
||||
Rectangle {
|
||||
id: workspaceSwitcher
|
||||
|
||||
property var theme
|
||||
property var root
|
||||
|
||||
width: Math.max(120, workspaceRow.implicitWidth + theme.spacingL * 2)
|
||||
height: 32
|
||||
radius: theme.cornerRadiusLarge
|
||||
color: Qt.rgba(theme.surfaceContainerHigh.r, theme.surfaceContainerHigh.g, theme.surfaceContainerHigh.b, 0.8)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
property int currentWorkspace: 1
|
||||
property var workspaceList: []
|
||||
|
||||
Process {
|
||||
id: workspaceQuery
|
||||
command: ["niri", "msg", "workspaces"]
|
||||
running: true
|
||||
|
||||
stdout: SplitParser {
|
||||
splitMarker: "\n"
|
||||
onRead: (data) => {
|
||||
if (data.trim()) {
|
||||
workspaceSwitcher.parseWorkspaceOutput(data.trim())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function parseWorkspaceOutput(data) {
|
||||
const lines = data.split('\n')
|
||||
let currentOutputName = ""
|
||||
let focusedOutput = ""
|
||||
let focusedWorkspace = 1
|
||||
let outputWorkspaces = {}
|
||||
|
||||
for (const line of lines) {
|
||||
if (line.startsWith('Output "')) {
|
||||
const outputMatch = line.match(/Output "(.+)"/)
|
||||
if (outputMatch) {
|
||||
currentOutputName = outputMatch[1]
|
||||
outputWorkspaces[currentOutputName] = []
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if (line.trim() && line.match(/^\s*\*?\s*(\d+)$/)) {
|
||||
const wsMatch = line.match(/^\s*(\*?)\s*(\d+)$/)
|
||||
if (wsMatch) {
|
||||
const isActive = wsMatch[1] === '*'
|
||||
const wsNum = parseInt(wsMatch[2])
|
||||
|
||||
if (currentOutputName && outputWorkspaces[currentOutputName]) {
|
||||
outputWorkspaces[currentOutputName].push(wsNum)
|
||||
}
|
||||
|
||||
if (isActive) {
|
||||
focusedOutput = currentOutputName
|
||||
focusedWorkspace = wsNum
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
currentWorkspace = focusedWorkspace
|
||||
|
||||
if (focusedOutput && outputWorkspaces[focusedOutput]) {
|
||||
workspaceList = outputWorkspaces[focusedOutput]
|
||||
} else {
|
||||
workspaceList = [1, 2]
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
interval: 500
|
||||
running: true
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
workspaceQuery.running = true
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: workspaceRow
|
||||
anchors.centerIn: parent
|
||||
spacing: theme.spacingS
|
||||
|
||||
Repeater {
|
||||
model: workspaceSwitcher.workspaceList
|
||||
|
||||
Rectangle {
|
||||
property bool isActive: modelData === workspaceSwitcher.currentWorkspace
|
||||
property bool isHovered: mouseArea.containsMouse
|
||||
|
||||
width: isActive ? theme.spacingXL + theme.spacingS : theme.spacingL
|
||||
height: theme.spacingS
|
||||
radius: height / 2
|
||||
color: isActive ? theme.primary :
|
||||
isHovered ? Qt.rgba(theme.primary.r, theme.primary.g, theme.primary.b, 0.5) :
|
||||
Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.3)
|
||||
|
||||
Behavior on width {
|
||||
NumberAnimation {
|
||||
duration: theme.mediumDuration
|
||||
easing.type: theme.emphasizedEasing
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: theme.mediumDuration
|
||||
easing.type: theme.emphasizedEasing
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
|
||||
onClicked: {
|
||||
switchProcess.command = ["niri", "msg", "action", "focus-workspace", modelData.toString()]
|
||||
switchProcess.running = true
|
||||
workspaceSwitcher.currentWorkspace = modelData
|
||||
Qt.callLater(() => {
|
||||
workspaceQuery.running = true
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: switchProcess
|
||||
running: false
|
||||
}
|
||||
}
|
||||
9
Widgets/qmldir
Normal file
9
Widgets/qmldir
Normal file
@@ -0,0 +1,9 @@
|
||||
TopBar 1.0 TopBar.qml
|
||||
TopBarSimple 1.0 TopBarSimple.qml
|
||||
AppLauncherButton 1.0 AppLauncherButton.qml
|
||||
WorkspaceSwitcher 1.0 WorkspaceSwitcher.qml
|
||||
ClockWidget 1.0 ClockWidget.qml
|
||||
SystemTrayWidget 1.0 SystemTrayWidget.qml
|
||||
ClipboardButton 1.0 ClipboardButton.qml
|
||||
ColorPickerButton 1.0 ColorPickerButton.qml
|
||||
NotificationButton 1.0 NotificationButton.qml
|
||||
Reference in New Issue
Block a user