1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2025-12-07 14:05:38 -05:00
Files
DankMaterialShell/Widgets/CenterCommandCenter/EventsWidget.qml
2025-07-12 13:26:09 -04:00

316 lines
14 KiB
QML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import QtQuick
import QtQuick.Controls
import QtQuick.Effects
import "../../Common"
import "../../Services"
// Events widget for selected date - Material Design 3 style
Rectangle {
id: eventsWidget
property var theme: Theme
property date selectedDate: new Date()
property var selectedDateEvents: []
property bool hasEvents: selectedDateEvents && selectedDateEvents.length > 0
onSelectedDateEventsChanged: {
console.log("EventsWidget: selectedDateEvents changed, count:", selectedDateEvents.length)
eventsList.model = selectedDateEvents
}
property bool shouldShow: CalendarService && CalendarService.khalAvailable
width: parent.width
height: shouldShow ? (hasEvents ? Math.min(300, 80 + selectedDateEvents.length * 60) : 120) : 0
radius: theme.cornerRadiusLarge
color: Qt.rgba(theme.surfaceVariant.r, theme.surfaceVariant.g, theme.surfaceVariant.b, 0.12)
border.color: Qt.rgba(theme.outline.r, theme.outline.g, theme.outline.b, 0.08)
border.width: 1
visible: shouldShow
// Material elevation shadow
layer.enabled: true
layer.effect: MultiEffect {
shadowEnabled: true
shadowHorizontalOffset: 0
shadowVerticalOffset: 2
shadowBlur: 0.25
shadowColor: Qt.rgba(0, 0, 0, 0.1)
shadowOpacity: 0.1
}
Behavior on height {
NumberAnimation {
duration: theme.mediumDuration
easing.type: theme.emphasizedEasing
}
}
// Update events when selected date or events change
Connections {
target: CalendarService
enabled: CalendarService !== null
function onEventsByDateChanged() {
updateSelectedDateEvents()
}
function onKhalAvailableChanged() {
updateSelectedDateEvents()
}
}
Component.onCompleted: {
updateSelectedDateEvents()
}
onSelectedDateChanged: {
updateSelectedDateEvents()
}
function updateSelectedDateEvents() {
if (CalendarService && CalendarService.khalAvailable) {
let events = CalendarService.getEventsForDate(selectedDate)
console.log("EventsWidget: Updating events for", Qt.formatDate(selectedDate, "yyyy-MM-dd"), "found", events.length, "events")
selectedDateEvents = events
} else {
selectedDateEvents = []
}
}
Item {
anchors.fill: parent
anchors.margins: theme.spacingL
Column {
anchors.fill: parent
spacing: theme.spacingM
// Header - always visible when widget is shown
Item {
width: parent.width
height: headerRow.height
Row {
id: headerRow
width: parent.width
spacing: theme.spacingS
Text {
text: "event"
font.family: theme.iconFont
font.pixelSize: theme.iconSize - 2
color: theme.primary
anchors.verticalCenter: parent.verticalCenter
}
Text {
text: hasEvents ?
(Qt.formatDate(selectedDate, "MMM d") + " • " +
(selectedDateEvents.length === 1 ? "1 event" : selectedDateEvents.length + " events")) :
Qt.formatDate(selectedDate, "MMM d")
font.pixelSize: theme.fontSizeMedium
color: theme.surfaceText
font.weight: Font.Medium
anchors.verticalCenter: parent.verticalCenter
}
}
}
// Content area
Rectangle {
anchors.fill: parent
anchors.margins: theme.spacingL
color: "transparent"
// No events placeholder - absolutely centered in this gray container
Column {
anchors.centerIn: parent
spacing: theme.spacingXS
visible: !hasEvents
Text {
text: "event_busy"
font.family: theme.iconFont
font.pixelSize: theme.iconSize + 8
color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.3)
anchors.horizontalCenter: parent.horizontalCenter
}
Text {
text: "No events"
font.pixelSize: theme.fontSizeMedium
color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.5)
font.weight: Font.Normal
anchors.horizontalCenter: parent.horizontalCenter
}
}
// Events list
ListView {
id: eventsList
anchors.fill: parent
anchors.margins: theme.spacingM
visible: hasEvents
clip: true
spacing: theme.spacingS
boundsMovement: Flickable.StopAtBounds
boundsBehavior: Flickable.StopAtBounds
ScrollBar.vertical: ScrollBar {
policy: eventsList.contentHeight > eventsList.height ? ScrollBar.AsNeeded : ScrollBar.AlwaysOff
}
delegate: Rectangle {
width: eventsList.width
height: eventContent.implicitHeight + theme.spacingM
radius: theme.cornerRadius
color: {
if (modelData.url && eventMouseArea.containsMouse) {
return Qt.rgba(theme.primary.r, theme.primary.g, theme.primary.b, 0.12)
} else if (eventMouseArea.containsMouse) {
return Qt.rgba(theme.primary.r, theme.primary.g, theme.primary.b, 0.06)
}
return Qt.rgba(theme.surfaceVariant.r, theme.surfaceVariant.g, theme.surfaceVariant.b, 0.06)
}
border.color: {
if (modelData.url && eventMouseArea.containsMouse) {
return Qt.rgba(theme.primary.r, theme.primary.g, theme.primary.b, 0.3)
} else if (eventMouseArea.containsMouse) {
return Qt.rgba(theme.primary.r, theme.primary.g, theme.primary.b, 0.15)
}
return "transparent"
}
border.width: 1
// Event indicator strip
Rectangle {
width: 4
height: parent.height - 8
anchors.left: parent.left
anchors.leftMargin: 4
anchors.verticalCenter: parent.verticalCenter
radius: 2
color: theme.primary
opacity: 0.8
}
Column {
id: eventContent
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: theme.spacingL + 4
anchors.rightMargin: theme.spacingM
spacing: 6
Text {
width: parent.width
text: modelData.title
font.pixelSize: theme.fontSizeMedium
color: theme.surfaceText
font.weight: Font.Medium
elide: Text.ElideRight
wrapMode: Text.Wrap
maximumLineCount: 2
}
Item {
width: parent.width
height: Math.max(timeRow.height, locationRow.height)
Row {
id: timeRow
spacing: 4
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
Text {
text: "schedule"
font.family: theme.iconFont
font.pixelSize: theme.fontSizeSmall
color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.7)
anchors.verticalCenter: parent.verticalCenter
}
Text {
text: {
if (modelData.allDay) {
return "All day"
} else {
let startTime = Qt.formatTime(modelData.start, "h:mm AP")
if (modelData.start.toDateString() !== modelData.end.toDateString() ||
modelData.start.getTime() !== modelData.end.getTime()) {
return startTime + " " + Qt.formatTime(modelData.end, "h:mm AP")
}
return startTime
}
}
font.pixelSize: theme.fontSizeSmall
color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.7)
font.weight: Font.Normal
anchors.verticalCenter: parent.verticalCenter
}
}
Row {
id: locationRow
spacing: 4
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
visible: modelData.location !== ""
Text {
text: "location_on"
font.family: theme.iconFont
font.pixelSize: theme.fontSizeSmall
color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.7)
anchors.verticalCenter: parent.verticalCenter
}
Text {
text: modelData.location
font.pixelSize: theme.fontSizeSmall
color: Qt.rgba(theme.surfaceText.r, theme.surfaceText.g, theme.surfaceText.b, 0.7)
elide: Text.ElideRight
anchors.verticalCenter: parent.verticalCenter
maximumLineCount: 1
width: Math.min(implicitWidth, 200)
}
}
}
}
MouseArea {
id: eventMouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: modelData.url ? Qt.PointingHandCursor : Qt.ArrowCursor
enabled: modelData.url !== ""
onClicked: {
if (modelData.url && modelData.url !== "") {
if (Qt.openUrlExternally(modelData.url) === false) {
console.warn("Couldn't open", modelData.url)
}
}
}
}
Behavior on color {
ColorAnimation {
duration: theme.shortDuration
easing.type: theme.standardEasing
}
}
Behavior on border.color {
ColorAnimation {
duration: theme.shortDuration
easing.type: theme.standardEasing
}
}
}
}
}
}
}
}