Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added Material-Clock.plasmoid
Binary file not shown.
33 changes: 33 additions & 0 deletions build_plasmoid.sh
Original file line number Diff line number Diff line change
@@ -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
3 changes: 3 additions & 0 deletions package/contents/config/main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,8 @@
<entry name="timeFormat" type="String">
<default>HH:mm</default>
</entry>
<entry name="selectedTimeZone" type="String">
<default>Local</default>
</entry>
</group>
</kcfg>
35 changes: 34 additions & 1 deletion package/contents/ui/GeneralConfig.qml
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ 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

// Properties for configuration
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

Expand Down Expand Up @@ -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.)")
Expand Down
80 changes: 80 additions & 0 deletions package/contents/ui/TimeZoneData.js
Original file line number Diff line number Diff line change
@@ -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" }
];
146 changes: 128 additions & 18 deletions package/contents/ui/main.qml
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
Expand All @@ -33,15 +134,15 @@ PlasmoidItem {
ColumnLayout {
anchors.centerIn: parent
width: parent.width
spacing: 5
spacing: 2

// Date Text (small, centered)
Text {
id: dateText
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
}
Expand All @@ -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 !== ""
}
}
}
}
9 changes: 3 additions & 6 deletions package/metadata.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"KPackageStructure": "Plasma/Applet",
"KPlugin": {
"Authors": [
{
Expand All @@ -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"
}
}
Loading