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 "---------------------------------------------------------"