diff --git a/Material-Clock.plasmoid b/Material-Clock.plasmoid new file mode 100644 index 0000000..51160f5 Binary files /dev/null and b/Material-Clock.plasmoid differ diff --git a/build_plasmoid.sh b/build_plasmoid.sh new file mode 100755 index 0000000..270aa25 --- /dev/null +++ b/build_plasmoid.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +# Name of the output file +PROJECT_NAME="Material-Clock" +OUTPUT_FILE="../$PROJECT_NAME.plasmoid" + +# Navigate to package directory +cd package || { echo "Directory 'package' not found"; exit 1; } + +# Remove old package if it exists +if [ -f "$OUTPUT_FILE" ]; then + rm "$OUTPUT_FILE" +fi + +echo "Packaging $PROJECT_NAME..." + +# Try using zip if available +if command -v zip &> /dev/null; then + zip -r "$OUTPUT_FILE" . -x "*.git*" + status=$? +else + # Fallback to Python 3 if zip is not available + echo "zip command not found, falling back to Python..." + python3 -c "import shutil, os; shutil.make_archive('../${PROJECT_NAME}', 'zip', '.'); os.rename('../${PROJECT_NAME}.zip', '${OUTPUT_FILE}')" + status=$? +fi + +if [ $status -eq 0 ]; then + echo "Successfully created $PROJECT_NAME.plasmoid in the root directory." +else + echo "Failed to create package." + exit 1 +fi diff --git a/package/contents/config/main.xml b/package/contents/config/main.xml index c542700..89a5eea 100644 --- a/package/contents/config/main.xml +++ b/package/contents/config/main.xml @@ -14,5 +14,8 @@ HH:mm + + Local + diff --git a/package/contents/ui/GeneralConfig.qml b/package/contents/ui/GeneralConfig.qml index 549edab..7d0bb0e 100644 --- a/package/contents/ui/GeneralConfig.qml +++ b/package/contents/ui/GeneralConfig.qml @@ -2,7 +2,7 @@ import QtQuick 2.12 import QtQuick.Controls 2.15 import QtQuick.Layouts 1.11 import org.kde.plasma.core 2.0 as PlasmaCore - +import "TimeZoneData.js" as TimeZoneData Item { id: configRoot @@ -10,6 +10,7 @@ Item { property alias cfg_colorHex: colorhex.text property alias cfg_dateFormat: dateFormatField.text property alias cfg_timeFormat: timeFormatField.text + property string cfg_selectedTimeZone: "Local" signal configurationChanged @@ -109,6 +110,38 @@ Item { } } + // Time Zone Selector + ColumnLayout { + Label { + text: i18n("Time Zone") + } + ComboBox { + id: timeZoneComboBox + Layout.fillWidth: true + // Curated list of major timezones + model: TimeZoneData.timeZones + textRole: "text" + valueRole: "value" + + // Helper to safely find index + function getTimeZoneIndex(val) { + for (var i = 0; i < TimeZoneData.timeZones.length; i++) { + if (TimeZoneData.timeZones[i].value === val) { + return i; + } + } + return 0; // Default to Local (index 0) if not found + } + + currentIndex: getTimeZoneIndex(cfg_selectedTimeZone) + + onActivated: { + cfg_selectedTimeZone = currentValue + configurationChanged() + } + } + } + // Tip Text Label { text: i18n("Format Help: Use Qt date formatting codes (ddd, MMM, HH, mm, etc.)") diff --git a/package/contents/ui/TimeZoneData.js b/package/contents/ui/TimeZoneData.js new file mode 100644 index 0000000..13f203e --- /dev/null +++ b/package/contents/ui/TimeZoneData.js @@ -0,0 +1,80 @@ + +var timeZones = [ + { text: "Local System Time", value: "Local" }, + { text: "UTC", value: "UTC" }, + { text: "UTC-12:00", value: "Etc/GMT+12" }, + { text: "UTC-11:00 (Midway)", value: "Pacific/Midway" }, + { text: "UTC-10:00 (Hawaii)", value: "Pacific/Honolulu" }, + { text: "UTC-09:30 (Marquesas)", value: "Pacific/Marquesas" }, + { text: "UTC-09:00 (Alaska)", value: "America/Anchorage" }, + { text: "UTC-08:00 (Los Angeles - PST/PDT)", value: "America/Los_Angeles" }, + { text: "UTC-08:00 (Tijuana)", value: "America/Tijuana" }, + { text: "UTC-07:00 (Denver - MST/MDT)", value: "America/Denver" }, + { text: "UTC-07:00 (Phoenix)", value: "America/Phoenix" }, + { text: "UTC-06:00 (Chicago - CST/CDT)", value: "America/Chicago" }, + { text: "UTC-06:00 (Mexico City)", value: "America/Mexico_City" }, + { text: "UTC-06:00 (Saskatchewan)", value: "America/Regina" }, + { text: "UTC-05:00 (New York - EST/EDT)", value: "America/New_York" }, + { text: "UTC-05:00 (Bogota)", value: "America/Bogota" }, + { text: "UTC-05:00 (Lima)", value: "America/Lima" }, + { text: "UTC-04:00 (Santiago)", value: "America/Santiago" }, + { text: "UTC-04:00 (Caracas)", value: "America/Caracas" }, + { text: "UTC-04:00 (Halifax)", value: "America/Halifax" }, + { text: "UTC-03:30 (Newfoundland)", value: "America/St_Johns" }, + { text: "UTC-03:00 (Sao Paulo - BRT/BRST)", value: "America/Sao_Paulo" }, + { text: "UTC-03:00 (Buenos Aires)", value: "America/Buenos_Aires" }, + { text: "UTC-03:00 (Greenland)", value: "America/Godthab" }, + { text: "UTC-02:00 (South Georgia)", value: "Atlantic/South_Georgia" }, + { text: "UTC-02:00 (General)", value: "Etc/GMT+2" }, + { text: "UTC-01:00 (Azores)", value: "Atlantic/Azores" }, + { text: "UTC-01:00 (Cape Verde)", value: "Atlantic/Cape_Verde" }, + { text: "UTC+00:00 (London - GMT/BST)", value: "Europe/London" }, + { text: "UTC+00:00 (Lisbon)", value: "Europe/Lisbon" }, + { text: "UTC+00:00 (Casablanca)", value: "Africa/Casablanca" }, + { text: "UTC+01:00 (Paris - CET/CEST)", value: "Europe/Paris" }, + { text: "UTC+01:00 (Berlin)", value: "Europe/Berlin" }, + { text: "UTC+01:00 (Rome)", value: "Europe/Rome" }, + { text: "UTC+01:00 (Lagos)", value: "Africa/Lagos" }, + { text: "UTC+02:00 (Athens)", value: "Europe/Athens" }, + { text: "UTC+02:00 (Cairo)", value: "Africa/Cairo" }, + { text: "UTC+02:00 (Jerusalem)", value: "Asia/Jerusalem" }, + { text: "UTC+02:00 (Helsinki)", value: "Europe/Helsinki" }, + { text: "UTC+02:00 (Johannesburg)", value: "Africa/Johannesburg" }, + { text: "UTC+03:00 (Moscow)", value: "Europe/Moscow" }, + { text: "UTC+03:00 (Istanbul)", value: "Europe/Istanbul" }, + { text: "UTC+03:00 (Nairobi)", value: "Africa/Nairobi" }, + { text: "UTC+03:00 (Baghdad)", value: "Asia/Baghdad" }, + { text: "UTC+03:00 (Riyadh)", value: "Asia/Riyadh" }, + { text: "UTC+03:30 (Tehran)", value: "Asia/Tehran" }, + { text: "UTC+04:00 (Dubai)", value: "Asia/Dubai" }, + { text: "UTC+04:00 (Baku)", value: "Asia/Baku" }, + { text: "UTC+04:30 (Kabul)", value: "Asia/Kabul" }, + { text: "UTC+05:00 (Karachi)", value: "Asia/Karachi" }, + { text: "UTC+05:00 (Tashkent)", value: "Asia/Tashkent" }, + { text: "UTC+05:30 (Kolkata)", value: "Asia/Kolkata" }, + { text: "UTC+05:30 (Colombo)", value: "Asia/Colombo" }, + { text: "UTC+05:45 (Kathmandu)", value: "Asia/Kathmandu" }, + { text: "UTC+06:00 (Dhaka)", value: "Asia/Dhaka" }, + { text: "UTC+06:00 (Almaty)", value: "Asia/Almaty" }, + { text: "UTC+06:30 (Yangon)", value: "Asia/Yangon" }, + { text: "UTC+07:00 (Bangkok)", value: "Asia/Bangkok" }, + { text: "UTC+07:00 (Jakarta)", value: "Asia/Jakarta" }, + { text: "UTC+07:00 (Novosibirsk)", value: "Asia/Novosibirsk" }, + { text: "UTC+08:00 (Singapore)", value: "Asia/Singapore" }, + { text: "UTC+08:00 (Shanghai)", value: "Asia/Shanghai" }, + { text: "UTC+08:00 (Taipei)", value: "Asia/Taipei" }, + { text: "UTC+08:00 (Hong Kong)", value: "Asia/Hong_Kong" }, + { text: "UTC+08:00 (Perth)", value: "Australia/Perth" }, + { text: "UTC+09:00 (Tokyo)", value: "Asia/Tokyo" }, + { text: "UTC+09:00 (Seoul)", value: "Asia/Seoul" }, + { text: "UTC+09:30 (Adelaide)", value: "Australia/Adelaide" }, + { text: "UTC+09:30 (Darwin)", value: "Australia/Darwin" }, + { text: "UTC+10:00 (Sydney)", value: "Australia/Sydney" }, + { text: "UTC+10:00 (Brisbane)", value: "Australia/Brisbane" }, + { text: "UTC+10:00 (Vladivostok)", value: "Asia/Vladivostok" }, + { text: "UTC+11:00 (Noumea)", value: "Pacific/Noumea" }, + { text: "UTC+12:00 (Auckland)", value: "Pacific/Auckland" }, + { text: "UTC+12:00 (Fiji)", value: "Pacific/Fiji" }, + { text: "UTC+13:00 (Tongatapu)", value: "Pacific/Tongatapu" }, + { text: "UTC+14:00 (Kiritimati)", value: "Pacific/Kiritimati" } +]; diff --git a/package/contents/ui/main.qml b/package/contents/ui/main.qml index f1e7881..ccc36db 100644 --- a/package/contents/ui/main.qml +++ b/package/contents/ui/main.qml @@ -2,10 +2,10 @@ import QtQuick 2.12 import QtQuick.Controls 2.15 import QtQuick.Layouts 1.12 import org.kde.plasma.plasmoid 2.0 -import org.kde.plasma.core 2.0 as PlasmaCore - -PlasmoidItem { - id: root +import org.kde.plasma.plasma5support as Plasma5Support +import "TimeZoneData.js" as TimeZoneData + PlasmoidItem { + id: root property color colorPlasmoid: Plasmoid.configuration.colorHex @@ -23,6 +23,107 @@ PlasmoidItem { source: Qt.resolvedUrl("../fonts/poppins-regular.ttf") } + property var currentDateTime: new Date() + + Timer { + interval: 1000 + running: true + repeat: true + onTriggered: root.currentDateTime = new Date() + } + + property string activeTimeZone: { + var tz = Plasmoid.configuration.selectedTimeZone + return tz || "Local" + } + property int timeZoneOffset: 0 + + + + property var displayDateTime: { + // If Local, just return current time + if (activeTimeZone === "Local" || activeTimeZone === "") { + return currentDateTime + } + + // Manual Shift Strategy: + // DateTime from DataEngine is the correct "Moment" (UTC-based). + // Qt.formatDateTime uses System Local Timezone. + // We want: Format(ShiftedDate) in Local = Remote Time String. + // UTC(Shifted) + LocalOffset = UTC(Real) + RemoteOffset. + // Shifted = Real + RemoteOffset - LocalOffset. + + var localOffsetMin = new Date().getTimezoneOffset() // (UTC - Local) in minutes. e.g. Brazil(+3h because 180). + // Actually getTimezoneOffset returns positive if behind UTC. + // Brazil (GMT-3) -> 180. London (GMT+1) -> -60. + // LocalOffsetSeconds = -1 * localOffsetMin * 60. + + var localOffsetSeconds = -localOffsetMin * 60 + var targetOffsetSeconds = root.timeZoneOffset + + var diffSeconds = targetOffsetSeconds - localOffsetSeconds + + // Snap to nearest 15 minutes (900 seconds) to avoid small drifts or 1-minute errors + if (Math.abs(diffSeconds) % 900 !== 0) { + diffSeconds = Math.round(diffSeconds / 900) * 900 + } + + return new Date(currentDateTime.getTime() + diffSeconds * 1000) + } + + function updateTimeZoneData() { + var src = activeTimeZone + var engineSource = src === "Local" ? "Local" : src + + timeSource.connectSource(engineSource) + + // Check immediate availability + if (timeSource.data[engineSource]) { + if (timeSource.data[engineSource]["Offset"] !== undefined) { + root.timeZoneOffset = timeSource.data[engineSource]["Offset"] + } + } + } + + onActiveTimeZoneChanged: { + updateTimeZoneData() + } + + + + Plasma5Support.DataSource { + id: timeSource + engine: "time" + // connectedSources: [activeTimeZone === "Local" ? "Local" : activeTimeZone] // We manage manually to be safe + interval: 60000 + onDataChanged: function(sourceName, data) { + if (sourceName === (activeTimeZone === "Local" ? "Local" : activeTimeZone)) { + if (data["Offset"] !== undefined) { + root.timeZoneOffset = data["Offset"] + } + } + } + Component.onCompleted: { + updateTimeZoneData() + } + } + + // Force initial update + Component.onCompleted: { + updateTimeZoneData() + } + + // Config watcher + Connections { + target: Plasmoid.configuration + function onSelectedTimeZoneChanged() { + // updateTimeZoneData is called by activeTimeZone binding change + } + } + + // Timer to update UI every second locally if needed, or rely on DataSource (minutely) + // The previous code had 60000 interval. DataSource also has 60000. + // Centered layout container Item { id: wrapper @@ -33,7 +134,7 @@ PlasmoidItem { ColumnLayout { anchors.centerIn: parent width: parent.width - spacing: 5 + spacing: 2 // Date Text (small, centered) Text { @@ -41,7 +142,7 @@ PlasmoidItem { Layout.alignment: Qt.AlignHCenter font.family: poppinsThin.name font.pixelSize: root.height * 0.1 - text: Qt.formatDateTime(new Date(), Plasmoid.configuration.dateFormat).toLowerCase() + text: root.displayDateTime.toLocaleString(Qt.locale(), Plasmoid.configuration.dateFormat).toLowerCase() color: colorPlasmoid horizontalAlignment: Text.AlignHCenter } @@ -52,22 +153,31 @@ PlasmoidItem { Layout.alignment: Qt.AlignHCenter font.family: poppinsRegular.name font.pixelSize: root.height * 0.4 - text: Qt.formatDateTime(new Date(), Plasmoid.configuration.timeFormat) + text: root.displayDateTime.toLocaleString(Qt.locale(), Plasmoid.configuration.timeFormat) color: colorPlasmoid horizontalAlignment: Text.AlignHCenter } - } - } - // Timer for updating time and date - Timer { - interval: 60000 - running: true - repeat: true - onTriggered: { - var now = new Date() - dateText.text = Qt.formatDateTime(now, Plasmoid.configuration.dateFormat).toLowerCase() - timeText.text = Qt.formatDateTime(now, Plasmoid.configuration.timeFormat) + // Time Zone Label + Text { + id: timeZoneText + Layout.alignment: Qt.AlignHCenter + font.family: poppinsThin.name + font.pixelSize: root.height * 0.07 + text: { + var tz = root.activeTimeZone; + if (tz === "Local" || tz === "") return ""; + for (var i = 0; i < TimeZoneData.timeZones.length; i++) { + if (TimeZoneData.timeZones[i].value === tz) { + return TimeZoneData.timeZones[i].text; + } + } + return tz; + } + color: colorPlasmoid + horizontalAlignment: Text.AlignHCenter + visible: root.activeTimeZone !== "Local" && root.activeTimeZone !== "" + } } } } diff --git a/package/metadata.json b/package/metadata.json index 6797bed..03fd231 100644 --- a/package/metadata.json +++ b/package/metadata.json @@ -1,4 +1,5 @@ { + "KPackageStructure": "Plasma/Applet", "KPlugin": { "Authors": [ { @@ -12,15 +13,11 @@ "Icon": "clock", "EnabledByDefault": true, "License": "GPL-3.0+", - "ServiceTypes": [ - "Plasma/Applet" - ], - "Version": "0.0.4", + "Version": "0.0.5", "Website": "https://github.com/cesp99/Material-Clock" }, "X-Plasma-API-Minimum-Version": "6.0", "X-Plasma-MainScript": "ui/main.qml", - "X-Plasma-WidgetType": "Configuration", "X-KDE-Plasma-MainScript": "ui/main.qml", "X-KDE-Plasma-ConfigurationScript": "config/config.qml" -} +} \ No newline at end of file diff --git a/rebuild.sh b/rebuild.sh new file mode 100755 index 0000000..0a51553 --- /dev/null +++ b/rebuild.sh @@ -0,0 +1,45 @@ +#!/bin/bash + +PROJECT_NAME="Material-Clock" +PLASMOID_FILE="${PROJECT_NAME}.plasmoid" +INSTALL_DIR="$HOME/.local/share/kpackage/generic/material.clock" + +echo "🚧 Building $PROJECT_NAME..." + +# Ensure we are in the project root +if [ ! -d "package" ]; then + echo "❌ Error: 'package' directory not found. Please run this from the project root." + exit 1 +fi + +# Create the package +rm -f "$PLASMOID_FILE" +cd package +zip -r "../$PLASMOID_FILE" . -x "*.git*" +cd .. + +if [ ! -f "$PLASMOID_FILE" ]; then + echo "❌ Build failed!" + exit 1 +fi +echo "✅ Build successful: $PLASMOID_FILE" + +echo "🔄 Reinstalling..." +# Using kpackagetool6 to upgrade if exists, or install +# Note: 'upgrade' sometimes fails if structure changed, so we can clean install +kpackagetool6 --upgrade "$PLASMOID_FILE" || (kpackagetool6 -r material.clock; kpackagetool6 -i "$PLASMOID_FILE") + +echo "✅ Installed successfully." + +echo "" +echo "---------------------------------------------------------" +echo "⚠️ IMPORTANT: Plasma caches files aggressively." +echo "To see changes immediately, try ONE of the following:" +echo "" +echo "1. Run specific viewer (Recommended for testing):" +echo " plasmoidviewer -a package" +echo "" +echo "2. Restart Plasma Shell (Resets your desktop):" +echo " kquitapp5 plasmashell || kquitapp6 plasmashell" +echo " kstart5 plasmashell || kstart6 plasmashell" +echo "---------------------------------------------------------"