1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2025-12-07 14:05:38 -05:00

iBluetooth repair, input device support, some media player improvements

This commit is contained in:
bbedward
2025-07-11 13:50:10 -04:00
parent b4f73ceb7b
commit d169f5d4a3
14 changed files with 1533 additions and 829 deletions

View File

@@ -0,0 +1,174 @@
import QtQuick
import QtQuick.Controls
import "../../Common"
Column {
id: calendarWidget
property var theme: Theme
property date displayDate: new Date()
property date selectedDate: new Date()
spacing: theme.spacingM
// Month navigation header
Row {
width: parent.width
height: 40
Rectangle {
width: 40
height: 40
radius: theme.cornerRadius
color: prevMonthArea.containsMouse ? Qt.rgba(theme.primary.r, theme.primary.g, theme.primary.b, 0.12) : "transparent"
Text {
anchors.centerIn: parent
text: "chevron_left"
font.family: theme.iconFont
font.pixelSize: theme.iconSize
color: theme.primary
font.weight: theme.iconFontWeight
}
MouseArea {
id: prevMonthArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
let newDate = new Date(displayDate)
newDate.setMonth(newDate.getMonth() - 1)
displayDate = newDate
}
}
}
Text {
width: parent.width - 80
height: 40
text: Qt.formatDate(displayDate, "MMMM yyyy")
font.pixelSize: theme.fontSizeLarge
color: theme.surfaceText
font.weight: Font.Medium
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
Rectangle {
width: 40
height: 40
radius: theme.cornerRadius
color: nextMonthArea.containsMouse ? Qt.rgba(theme.primary.r, theme.primary.g, theme.primary.b, 0.12) : "transparent"
Text {
anchors.centerIn: parent
text: "chevron_right"
font.family: theme.iconFont
font.pixelSize: theme.iconSize
color: theme.primary
font.weight: theme.iconFontWeight
}
MouseArea {
id: nextMonthArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
let newDate = new Date(displayDate)
newDate.setMonth(newDate.getMonth() + 1)
displayDate = newDate
}
}
}
}
// Days of week header
Row {
width: parent.width
height: 32
Repeater {
model: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
Rectangle {
width: parent.width / 7
height: 32
color: "transparent"
Text {
anchors.centerIn: parent
text: modelData
font.pixelSize: theme.fontSizeSmall
color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.6)
font.weight: Font.Medium
}
}
}
}
// Calendar grid
Grid {
width: parent.width
height: 200 // Fixed height for calendar
columns: 7
rows: 6
property date firstDay: {
let date = new Date(displayDate.getFullYear(), displayDate.getMonth(), 1)
let dayOfWeek = date.getDay()
date.setDate(date.getDate() - dayOfWeek)
return date
}
Repeater {
model: 42
Rectangle {
width: parent.width / 7
height: parent.height / 6
property date dayDate: {
let date = new Date(parent.firstDay)
date.setDate(date.getDate() + index)
return date
}
property bool isCurrentMonth: dayDate.getMonth() === displayDate.getMonth()
property bool isToday: dayDate.toDateString() === new Date().toDateString()
property bool isSelected: dayDate.toDateString() === selectedDate.toDateString()
color: isSelected ? theme.primary :
isToday ? Qt.rgba(theme.primary.r, theme.primary.g, theme.primary.b, 0.12) :
dayArea.containsMouse ? Qt.rgba(theme.primary.r, theme.primary.g, theme.primary.b, 0.08) : "transparent"
radius: theme.cornerRadiusSmall
Text {
anchors.centerIn: parent
text: dayDate.getDate()
font.pixelSize: theme.fontSizeMedium
color: isSelected ? theme.surface :
isToday ? theme.primary :
isCurrentMonth ? theme.surfaceText :
Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.4)
font.weight: isToday || isSelected ? Font.Medium : Font.Normal
}
MouseArea {
id: dayArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
selectedDate = dayDate
}
}
}
}
}
}

View File

@@ -0,0 +1,117 @@
import QtQuick
import QtQuick.Controls
import Qt5Compat.GraphicalEffects
import Quickshell
import Quickshell.Widgets
import Quickshell.Wayland
import Quickshell.Services.Mpris
import "../../Common"
import "../../Services"
PanelWindow {
id: centerCommandCenter
property var theme: Theme
property bool hasActiveMedia: root.hasActiveMedia
property var weather: root.weather
property bool useFahrenheit: false
// Prevent media player from disappearing during track changes
property bool showMediaPlayer: hasActiveMedia || hideMediaTimer.running
Timer {
id: hideMediaTimer
interval: 3000 // 3 second grace period
running: false
repeat: false
}
onHasActiveMediaChanged: {
if (hasActiveMedia) {
hideMediaTimer.stop()
} else {
hideMediaTimer.start()
}
}
visible: root.calendarVisible
implicitWidth: 320
implicitHeight: 400
WlrLayershell.layer: WlrLayershell.Overlay
WlrLayershell.exclusiveZone: -1
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
color: "transparent"
anchors {
top: true
left: true
right: true
bottom: true
}
Rectangle {
width: 400
height: showMediaPlayer ? 540 : (weather?.available ? 480 : 400)
x: (parent.width - width) / 2
y: theme.barHeight + theme.spacingS
color: theme.surfaceContainer
radius: theme.cornerRadiusLarge
border.color: Qt.rgba(theme.outline.r, theme.outline.g, theme.outline.b, 0.12)
border.width: 1
opacity: root.calendarVisible ? 1.0 : 0.0
scale: root.calendarVisible ? 1.0 : 0.85
Behavior on opacity {
NumberAnimation {
duration: theme.mediumDuration
easing.type: theme.emphasizedEasing
}
}
Behavior on scale {
NumberAnimation {
duration: theme.mediumDuration
easing.type: theme.emphasizedEasing
}
}
Column {
anchors.fill: parent
anchors.margins: theme.spacingL
spacing: theme.spacingM
// Media Player (when active)
MediaPlayerWidget {
visible: showMediaPlayer
theme: centerCommandCenter.theme
}
// Weather header (when available and no media)
WeatherWidget {
visible: weather?.available && !showMediaPlayer
theme: centerCommandCenter.theme
weather: centerCommandCenter.weather
useFahrenheit: centerCommandCenter.useFahrenheit
}
// Calendar
CalendarWidget {
width: parent.width
height: showMediaPlayer ? parent.height - 200 : (weather?.available ? parent.height - 120 : parent.height - 40)
theme: centerCommandCenter.theme
}
}
}
MouseArea {
anchors.fill: parent
z: -1
onClicked: {
root.calendarVisible = false
}
}
}

View File

@@ -0,0 +1,269 @@
import QtQuick
import QtQuick.Controls
import Qt5Compat.GraphicalEffects
import Quickshell
import Quickshell.Services.Mpris
import "../../Common"
import "../../Services"
Rectangle {
id: mediaPlayerWidget
property MprisPlayer activePlayer: MprisController.activePlayer
property var theme: Theme
width: parent.width
height: 160 // Reduced height to prevent overflow
radius: theme.cornerRadius
color: Qt.rgba(theme.primary.r, theme.primary.g, theme.primary.b, 0.08)
border.color: Qt.rgba(theme.primary.r, theme.primary.g, theme.primary.b, 0.2)
border.width: 1
// Timer to update MPRIS position
property bool justSeeked: false
property real seekTargetPosition: 0
Timer {
id: positionTimer
running: activePlayer?.playbackState === MprisPlaybackState.Playing && !justSeeked
interval: 1000
repeat: true
onTriggered: {
if (activePlayer) {
activePlayer.positionChanged()
}
}
}
// Timer to resume position updates after seeking
Timer {
id: seekCooldownTimer
interval: 1000 // Reduced from 2000
repeat: false
onTriggered: {
justSeeked = false
// Force position update after seek
if (activePlayer) {
activePlayer.positionChanged()
}
}
}
Column {
anchors.fill: parent
anchors.margins: theme.spacingM
spacing: theme.spacingM
// Album art and track info
Row {
width: parent.width
height: 70 // Reduced height
spacing: theme.spacingM
// Album Art
Rectangle {
width: 70
height: 70
radius: theme.cornerRadius
color: Qt.rgba(theme.surfaceVariant.r, theme.surfaceVariant.g, theme.surfaceVariant.b, 0.3)
Item {
anchors.fill: parent
clip: true
Image {
id: albumArt
anchors.fill: parent
source: activePlayer?.trackArtUrl || ""
fillMode: Image.PreserveAspectCrop
smooth: true
}
Rectangle {
anchors.fill: parent
visible: albumArt.status !== Image.Ready
color: "transparent"
Text {
anchors.centerIn: parent
text: "album"
font.family: theme.iconFont
font.pixelSize: 28
color: theme.surfaceVariantText
}
}
}
}
// Track Info
Column {
width: parent.width - 70 - theme.spacingM
spacing: theme.spacingXS
anchors.verticalCenter: parent.verticalCenter
Text {
text: activePlayer?.trackTitle || "Unknown Track"
font.pixelSize: theme.fontSizeMedium
font.weight: Font.Bold
color: theme.surfaceText
width: parent.width
elide: Text.ElideRight
}
Text {
text: activePlayer?.trackArtist || "Unknown Artist"
font.pixelSize: theme.fontSizeSmall
color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.8)
width: parent.width
elide: Text.ElideRight
}
Text {
text: activePlayer?.trackAlbum || ""
font.pixelSize: theme.fontSizeSmall
color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.6)
width: parent.width
elide: Text.ElideRight
visible: text.length > 0
}
}
}
// Simple progress bar - click to seek only
Rectangle {
width: parent.width
height: 6
radius: 3
color: Qt.rgba(theme.surfaceVariant.r, theme.surfaceVariant.g, theme.surfaceVariant.b, 0.3)
Rectangle {
width: {
if (!activePlayer || !activePlayer.length || activePlayer.length === 0) return 0
// Use seek target position if we just seeked
const currentPos = justSeeked ? seekTargetPosition : activePlayer.position
return Math.max(0, Math.min(parent.width, parent.width * (currentPos / activePlayer.length)))
}
height: parent.height
radius: parent.radius
color: theme.primary
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: (mouse) => {
if (activePlayer && activePlayer.length > 0 && activePlayer.canSeek) {
const ratio = mouse.x / width
const targetPosition = Math.floor(ratio * activePlayer.length)
const currentPosition = activePlayer.position || 0
const seekOffset = targetPosition - currentPosition
console.log("Simple seek - offset:", seekOffset, "target:", targetPosition, "current:", currentPosition)
// Store target position for visual feedback
seekTargetPosition = targetPosition
justSeeked = true
seekCooldownTimer.restart()
activePlayer.seek(seekOffset)
}
}
}
}
// Control buttons - compact to fit
Row {
anchors.horizontalCenter: parent.horizontalCenter
spacing: theme.spacingL
// Previous button
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: {
if (!activePlayer) return
// >8 s → jump to start, otherwise previous track
if (activePlayer.position > 8000000) {
console.log("Jumping to start - current position:", activePlayer.position)
// Store target position for visual feedback
seekTargetPosition = 0
justSeeked = true
seekCooldownTimer.restart()
// Seek to the beginning
activePlayer.seek(-activePlayer.position)
} else {
activePlayer.previous()
}
}
}
}
// Play/Pause button
Rectangle {
width: 36
height: 36
radius: 18
color: theme.primary
Text {
anchors.centerIn: parent
text: activePlayer?.playbackState === MprisPlaybackState.Playing ? "pause" : "play_arrow"
font.family: theme.iconFont
font.pixelSize: 20
color: theme.background
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: activePlayer?.togglePlaying()
}
}
// Next button
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: activePlayer?.next()
}
}
}
}
}

View File

@@ -0,0 +1,135 @@
import QtQuick
import QtQuick.Controls
import "../../Common"
import "../../Services"
Rectangle {
id: weatherWidget
property var theme: Theme
property var weather
property bool useFahrenheit: false
width: parent.width
height: 80
radius: theme.cornerRadius
color: Qt.rgba(theme.primary.r, theme.primary.g, theme.primary.b, 0.08)
border.color: Qt.rgba(theme.primary.r, theme.primary.g, theme.primary.b, 0.2)
border.width: 1
Row {
anchors.centerIn: parent
spacing: theme.spacingL
// Weather icon and temp
Column {
spacing: 2
anchors.verticalCenter: parent.verticalCenter
Text {
text: WeatherService.getWeatherIcon(weather.wCode)
font.family: theme.iconFont
font.pixelSize: theme.iconSize + 4
color: theme.primary
anchors.horizontalCenter: parent.horizontalCenter
}
Text {
text: (useFahrenheit ? weather.tempF : weather.temp) + "°" + (useFahrenheit ? "F" : "C")
font.pixelSize: theme.fontSizeLarge
color: theme.surfaceText
font.weight: Font.Bold
anchors.horizontalCenter: parent.horizontalCenter
MouseArea {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: useFahrenheit = !useFahrenheit
}
}
Text {
text: weather.city
font.pixelSize: theme.fontSizeSmall
color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.7)
anchors.horizontalCenter: parent.horizontalCenter
}
}
// Weather details grid
Grid {
columns: 2
spacing: theme.spacingS
anchors.verticalCenter: parent.verticalCenter
Row {
spacing: theme.spacingXS
Text {
text: "humidity_low"
font.family: theme.iconFont
font.pixelSize: theme.fontSizeSmall
color: theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
Text {
text: weather.humidity + "%"
font.pixelSize: theme.fontSizeSmall
color: theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
}
Row {
spacing: theme.spacingXS
Text {
text: "air"
font.family: theme.iconFont
font.pixelSize: theme.fontSizeSmall
color: theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
Text {
text: weather.wind
font.pixelSize: theme.fontSizeSmall
color: theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
}
Row {
spacing: theme.spacingXS
Text {
text: "wb_twilight"
font.family: theme.iconFont
font.pixelSize: theme.fontSizeSmall
color: theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
Text {
text: weather.sunrise
font.pixelSize: theme.fontSizeSmall
color: theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
}
Row {
spacing: theme.spacingXS
Text {
text: "bedtime"
font.family: theme.iconFont
font.pixelSize: theme.fontSizeSmall
color: theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
Text {
text: weather.sunset
font.pixelSize: theme.fontSizeSmall
color: theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
}
}
}
}

View File

@@ -0,0 +1,4 @@
CenterCommandCenter 1.0 CenterCommandCenter.qml
MediaPlayerWidget 1.0 MediaPlayerWidget.qml
WeatherWidget 1.0 WeatherWidget.qml
CalendarWidget 1.0 CalendarWidget.qml