diff --git a/.gitattributes b/.gitattributes index 6313b56..4f9b388 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,4 @@ * text=auto eol=lf + +# Compiled Qt translation catalogs +*.qm binary diff --git a/Changelog.txt b/Changelog.txt index f00bd87..43a0df0 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -1,3 +1,7 @@ +1.1.1 +- Abort Waze requests that stall, and show a dash instead of a stale travel time +- Mark user-visible strings with qsTr and add an English translation + 1.1.0 - Add new option for center layout - Add option to move the day text to the date row diff --git a/README.md b/README.md index 208f579..eb7e95a 100644 --- a/README.md +++ b/README.md @@ -102,8 +102,14 @@ duration can never linger on the tile. A second timer refreshes the active route Travel times come from Waze's public live-map routing endpoint (`routing-livemap-row.waze.com/RoutingManager/routingRequest`), summing `crossTime` over -the returned route segments. Failures are logged to the Toon's console and leave the -previous value in place. +the returned route segments. + +Each request is guarded by a 15-second `Timer` that aborts it — Qt's QML +`XMLHttpRequest` has no `timeout` property of its own. A request that times out, returns +a non-200 status or yields unparseable JSON logs the reason to the Toon's console and +replaces the duration with `—`, so an unreachable Waze reads as unavailable rather than +freezing on a stale number. Only one request per route is ever in flight; a refresh or a +settings change aborts the previous one. | File | Role | | ------------------------------ | ------------------------------------------------------------ | @@ -111,10 +117,30 @@ previous value in place. | `TscClockTile.qml` | The tile — both layouts, switched by `app.centerLayout` | | `TscClockSettings.qml` | Clock settings screen | | `TscClockTravelSettings.qml` | Two-column travel-time settings screen | +| `lang/` | Qt translation catalogs, one per locale | | `version.txt`, `Changelog.txt` | Read by the ToonStore to detect and describe updates | -`isNxt` selects the larger font and margin values for Toon 2 (NXT) hardware. The UI is in -Dutch, matching the rest of the Toon interface. +`isNxt` selects the larger font and margin values for Toon 2 (NXT) hardware. + +## Translations + +The source language is Dutch, matching the rest of the Toon interface. Every user-visible +string goes through `qsTr()`, and `lang/lang_en_GB.qm` translates them to English; the +Toon loads the catalog matching its own locale, following the same `lang/lang_.qm` +convention as the other ToonStore apps. Dutch needs no catalog, being the source language. + +`lang/lang_en_GB.ts` is the editable source and `lang/lang_en_GB.qm` the compiled form the +Toon actually reads, so **both must be committed**. After changing or adding a string: + +```sh +pip install PySide6 +pyside6-lupdate *.qml -ts lang/lang_en_GB.ts # pick up new strings +pyside6-lrelease lang/lang_en_GB.ts -qm lang/lang_en_GB.qm +``` + +`lupdate` merges rather than overwrites, marking new entries `type="unfinished"` for +translation and stale ones `type="vanished"`. Adding a language means copying the `.ts`, +changing its `language` attribute and compiling it to a matching `.qm`. ## Differences from upstream @@ -167,9 +193,8 @@ There is no way to run the app off-device: it depends on Toon-private QML module requests at any time, and every fetch sends your route coordinates to Waze. - `thumbnailIcon` points at `drawables/clock.svg`, which is not in this repository — inherited from upstream, where the file is likewise absent. -- Travel times are fetched over HTTP without a timeout; an unreachable Waze leaves the - last value on the tile until the route goes inactive. -- The settings screens are Dutch-only and not translated via `qsTr`. +- Only Dutch and English are provided. Any other locale falls back to the Dutch source + strings. ## License diff --git a/TscClockApp.qml b/TscClockApp.qml index fe87d2c..b347b89 100644 --- a/TscClockApp.qml +++ b/TscClockApp.qml @@ -1,284 +1,392 @@ import QtQuick 2.1 import qb.components 1.0 -import qb.base 1.0; +import qb.base 1.0 import FileIO 1.0 App { - id: tscClockApp - - property url tileUrl : "TscClockTile.qml"; - property url settingsUrl : "TscClockSettings.qml"; - property url travelSettingsUrl : "TscClockTravelSettings.qml"; - property url thumbnailIcon : "drawables/clock.svg"; - - property TscClockSettings tscClockSettings - property TscClockTravelSettings tscClockTravelSettings - - property string timeStr - property string timeSeconds - property string dateStr - property int leftMarginTime : 10 - property bool showSeconds : true - property bool showDate : true - property bool showMonthInText : true - property bool showDayOfWeek : true - property bool centerLayout : false - property bool showDayOnDate : false - - // Route 1 - property string route1Start : "06:30" - property string route1End : "09:00" - property string route1Label : "Thuis-Werk:" - property string route1FromLat : "" - property string route1FromLon : "" - property string route1ToLat : "" - property string route1ToLon : "" - property bool route1WorkdaysOnly : false - property bool route1WindowEnabled : false // false = ignore time window, always active - - // Route 2 - property string route2Start : "16:00" - property string route2End : "18:30" - property string route2Label : "Werk-Thuis:" - property string route2FromLat : "" - property string route2FromLon : "" - property string route2ToLat : "" - property string route2ToLon : "" - property bool route2WorkdaysOnly : false - property bool route2WindowEnabled : false // false = ignore time window, always active - - // Runtime — not persisted - property string travelTime1Str : "" - property string travelTime2Str : "" - property bool route1Active : false - property bool route2Active : false - - - FileIO { - id: tscClockSettingsFile - source: "file:///mnt/data/tsc/tscClock.userSettings.json" - } - - Component.onCompleted: { - try { - var settingsString = tscClockSettingsFile.read(); - var settings = JSON.parse(settingsString); - if (settings['showSeconds']) showSeconds = (settings['showSeconds'] == "true"); - if (settings['showDate']) showDate = (settings['showDate'] == "true"); - if (settings['showMonthInText']) showMonthInText = (settings['showMonthInText'] == "true"); - if (settings['showDayOfWeek']) showDayOfWeek = (settings['showDayOfWeek'] == "true"); - if (settings['centerLayout']) centerLayout = (settings['centerLayout'] == "true"); - if (settings['showDayOnDate']) showDayOnDate = (settings['showDayOnDate'] == "true"); - if (settings['route1Start']) route1Start = settings['route1Start']; - if (settings['route1End']) route1End = settings['route1End']; - if (settings['route1Label']) route1Label = settings['route1Label']; - if (settings['route1FromLat']) route1FromLat = settings['route1FromLat']; - if (settings['route1FromLon']) route1FromLon = settings['route1FromLon']; - if (settings['route1ToLat']) route1ToLat = settings['route1ToLat']; - if (settings['route1ToLon']) route1ToLon = settings['route1ToLon']; - if (settings['route2Start']) route2Start = settings['route2Start']; - if (settings['route2End']) route2End = settings['route2End']; - if (settings['route2Label']) route2Label = settings['route2Label']; - if (settings['route2FromLat']) route2FromLat = settings['route2FromLat']; - if (settings['route2FromLon']) route2FromLon = settings['route2FromLon']; - if (settings['route2ToLat']) route2ToLat = settings['route2ToLat']; - if (settings['route2ToLon']) route2ToLon = settings['route2ToLon']; - if (settings['route1WorkdaysOnly']) route1WorkdaysOnly = (settings['route1WorkdaysOnly'] == "true"); - if (settings['route2WorkdaysOnly']) route2WorkdaysOnly = (settings['route2WorkdaysOnly'] == "true"); - if (settings['route1WindowEnabled']) route1WindowEnabled = (settings['route1WindowEnabled'] == "true"); - if (settings['route2WindowEnabled']) route2WindowEnabled = (settings['route2WindowEnabled'] == "true"); - } catch(e) { - } - } - - function init() { - registry.registerWidget("tile", tileUrl, this, null, {thumbLabel: qsTr("Klok-TSC"), thumbIcon: thumbnailIcon, thumbCategory: "general", thumbWeight: 30, baseTileWeight: 10, thumbIconVAlignment: "center"}); - registry.registerWidget("screen", settingsUrl, this, "tscClockSettings"); - registry.registerWidget("screen", travelSettingsUrl, this, "tscClockTravelSettings"); - } - - function updateClockTiles() { - var now = new Date().getTime(); - timeSeconds = new Date().getSeconds(); - if (timeSeconds.length == 1) timeSeconds = "0" + timeSeconds; - - if (showDayOfWeek && !showDayOnDate) { - timeStr = i18n.dateTime(now, i18n.dow_full).substring(0,2).toLowerCase() + " " + i18n.dateTime(now, i18n.time_yes); - leftMarginTime = isNxt ? 8 : 6; - } else { - timeStr = i18n.dateTime(now, i18n.time_yes); - leftMarginTime = isNxt ? 60 : 48; - if (showSeconds) leftMarginTime = leftMarginTime - 15; - } - - var dateBase = showMonthInText ? i18n.dateTime(now, i18n.mon_full) : i18n.dateTime(now, i18n.mon_num); - if (showDayOfWeek && showDayOnDate) { - dateStr = i18n.dateTime(now, i18n.dow_full).substring(0,2).toLowerCase() + " " + dateBase; - } else { - dateStr = dateBase; - } - - checkTravelTimeWindow(); - } - - function minutesOfDay(timeStr) { - var parts = timeStr.split(":"); - if (parts.length !== 2) return -1; - return parseInt(parts[0]) * 60 + parseInt(parts[1]); - } - - function setTravelTime(slot, str) { - if (slot === 1) travelTime1Str = str; - else travelTime2Str = str; - } - - function fetchTravelTime(slot, fromLat, fromLon, toLat, toLon, label) { - if (!fromLat || !fromLon || !toLat || !toLon) { - console.log("Waze: skipping fetch, missing coordinates"); - return; - } - // Accept Dutch decimal commas and stray whitespace - fromLat = String(fromLat).trim().replace(",", "."); - fromLon = String(fromLon).trim().replace(",", "."); - toLat = String(toLat).trim().replace(",", "."); - toLon = String(toLon).trim().replace(",", "."); - var lbl = label || ""; - var url = "https://routing-livemap-row.waze.com/RoutingManager/routingRequest" + - "?from=x%3A" + fromLon + "+y%3A" + fromLat + - "&to=x%3A" + toLon + "+y%3A" + toLat + - "&at=0&returnJSON=true&returnGeometries=false" + - "&returnInstructions=false&timeout=60000&nPaths=1"; - console.log("Waze: GET " + url); - var xhr = new XMLHttpRequest(); - xhr.onreadystatechange = function() { - if (xhr.readyState !== XMLHttpRequest.DONE) return; - // Break the xhr <-> handler reference cycle so the request object - // (and its closure) can be garbage-collected instead of leaking. - xhr.onreadystatechange = null; - if (xhr.status !== 200) { - console.log("Waze: HTTP " + xhr.status + " — " + (xhr.responseText || "").substring(0, 200)); - return; - } - try { - var data = JSON.parse(xhr.responseText); - // nPaths=1 returns data.response.results; multi-path returns data.alternatives[]. - var results = data.alternatives - ? data.alternatives[0].response.results - : data.response.results; - var secs = 0; - for (var i = 0; i < results.length; i++) secs += results[i].crossTime; - var str = (lbl ? lbl + " " : "") + Math.round(secs / 60) + " min"; - setTravelTime(slot, str); - console.log("Waze: ok — " + str); - } catch(e) { - console.log("Waze: parse error — " + e + " — body: " + (xhr.responseText || "").substring(0, 200)); - } - }; - xhr.open("GET", url); - xhr.send(); - } - - // Whether a route should currently be shown. When its time window is - // disabled the route is always active (still subject to workdays-only); - // otherwise the current time must fall inside [start, end). - function routeIsActive(rs, re, windowEnabled, workdaysOnly, current, isWorkday) { - if (workdaysOnly && !isWorkday) return false; - if (!windowEnabled) return true; - return rs >= 0 && re > rs && current >= rs && current < re; - } - - function checkTravelTimeWindow() { - var now = new Date(); - var current = now.getHours() * 60 + now.getMinutes(); - var dow = now.getDay(); // 0=Sunday … 6=Saturday - var isWorkday = dow >= 1 && dow <= 5; - var r1s = minutesOfDay(route1Start), r1e = minutesOfDay(route1End); - var r2s = minutesOfDay(route2Start), r2e = minutesOfDay(route2End); - - // Each route is evaluated independently so overlapping windows show both. - var a1 = routeIsActive(r1s, r1e, route1WindowEnabled, route1WorkdaysOnly, current, isWorkday); - var a2 = routeIsActive(r2s, r2e, route2WindowEnabled, route2WorkdaysOnly, current, isWorkday); - - // Fetch only on the transition into the active state (so we don't refetch - // every tick), but always clear when inactive so a stale value can never - // linger — e.g. after a settings change flips a route to inactive. - if (a1) { - if (!route1Active) { - route1Active = true; - fetchTravelTime(1, route1FromLat, route1FromLon, route1ToLat, route1ToLon, route1Label); - } - } else { - route1Active = false; - travelTime1Str = ""; - } - if (a2) { - if (!route2Active) { - route2Active = true; - fetchTravelTime(2, route2FromLat, route2FromLon, route2ToLat, route2ToLon, route2Label); - } - } else { - route2Active = false; - travelTime2Str = ""; - } - } - - function saveSettings() { - var tmpshowSeconds = showSeconds ? "true" : "false"; - var tmpshowDate = showDate ? "true" : "false"; - var tmpshowMonthInText = showMonthInText ? "true" : "false"; - var tmpshowDayOfWeek = showDayOfWeek ? "true" : "false"; - var tmpCenterLayout = centerLayout ? "true" : "false"; - var tmpshowDayOnDate = showDayOnDate ? "true" : "false"; - - var tscClockSettingsJson = { - "showSeconds" : tmpshowSeconds, - "showDate" : tmpshowDate, - "showMonthInText": tmpshowMonthInText, - "showDayOfWeek" : tmpshowDayOfWeek, - "centerLayout" : tmpCenterLayout, - "showDayOnDate" : tmpshowDayOnDate, - "route1Start" : route1Start, - "route1End" : route1End, - "route1Label" : route1Label, - "route1FromLat" : route1FromLat, - "route1FromLon" : route1FromLon, - "route1ToLat" : route1ToLat, - "route1ToLon" : route1ToLon, - "route2Start" : route2Start, - "route2End" : route2End, - "route2Label" : route2Label, - "route2FromLat" : route2FromLat, - "route2FromLon" : route2FromLon, - "route2ToLat" : route2ToLat, - "route2ToLon" : route2ToLon, - "route1WorkdaysOnly" : route1WorkdaysOnly ? "true" : "false", - "route2WorkdaysOnly" : route2WorkdaysOnly ? "true" : "false", - "route1WindowEnabled" : route1WindowEnabled ? "true" : "false", - "route2WindowEnabled" : route2WindowEnabled ? "true" : "false" - } - - var doc3 = new XMLHttpRequest(); - doc3.open("PUT", "file:///mnt/data/tsc/tscClock.userSettings.json"); - doc3.send(JSON.stringify(tscClockSettingsJson)); - } - - Timer { - id: datetimeTimer - interval: 1000 - triggeredOnStart: true - running: true - repeat: true - onTriggered: updateClockTiles() - } - - Timer { - id: travelTimeRefreshTimer - interval: 300000 // 5 minutes - running: true - repeat: true - onTriggered: { - if (route1Active) fetchTravelTime(1, route1FromLat, route1FromLon, route1ToLat, route1ToLon, route1Label); - if (route2Active) fetchTravelTime(2, route2FromLat, route2FromLon, route2ToLat, route2ToLon, route2Label); - } - } + id: tscClockApp + + property url tileUrl: "TscClockTile.qml" + property url settingsUrl: "TscClockSettings.qml" + property url travelSettingsUrl: "TscClockTravelSettings.qml" + property url thumbnailIcon: "drawables/clock.svg" + + property TscClockSettings tscClockSettings + property TscClockTravelSettings tscClockTravelSettings + + property string timeStr + property string timeSeconds + property string dateStr + property int leftMarginTime: 10 + property bool showSeconds: true + property bool showDate: true + property bool showMonthInText: true + property bool showDayOfWeek: true + property bool centerLayout: false + property bool showDayOnDate: false + + // Route 1 + property string route1Start: "06:30" + property string route1End: "09:00" + property string route1Label: qsTr("Thuis-Werk:") + property string route1FromLat: "" + property string route1FromLon: "" + property string route1ToLat: "" + property string route1ToLon: "" + property bool route1WorkdaysOnly: false + property bool route1WindowEnabled: false // false = ignore time window, always active + + // Route 2 + property string route2Start: "16:00" + property string route2End: "18:30" + property string route2Label: qsTr("Werk-Thuis:") + property string route2FromLat: "" + property string route2FromLon: "" + property string route2ToLat: "" + property string route2ToLon: "" + property bool route2WorkdaysOnly: false + property bool route2WindowEnabled: false // false = ignore time window, always active + + // Runtime — not persisted + property string travelTime1Str: "" + property string travelTime2Str: "" + property bool route1Active: false + property bool route2Active: false + + // The in-flight request per slot, kept so a stalled one can be aborted. + property var pendingXhr1: null + property var pendingXhr2: null + // Qt's QML XMLHttpRequest has no timeout property, so each request is + // guarded by a Timer that aborts it. + property int travelRequestTimeout: 15000 + + FileIO { + id: tscClockSettingsFile + source: "file:///mnt/data/tsc/tscClock.userSettings.json" + } + + Component.onCompleted: { + try { + var settingsString = tscClockSettingsFile.read(); + var settings = JSON.parse(settingsString); + if (settings['showSeconds']) + showSeconds = (settings['showSeconds'] == "true"); + if (settings['showDate']) + showDate = (settings['showDate'] == "true"); + if (settings['showMonthInText']) + showMonthInText = (settings['showMonthInText'] == "true"); + if (settings['showDayOfWeek']) + showDayOfWeek = (settings['showDayOfWeek'] == "true"); + if (settings['centerLayout']) + centerLayout = (settings['centerLayout'] == "true"); + if (settings['showDayOnDate']) + showDayOnDate = (settings['showDayOnDate'] == "true"); + if (settings['route1Start']) + route1Start = settings['route1Start']; + if (settings['route1End']) + route1End = settings['route1End']; + if (settings['route1Label']) + route1Label = settings['route1Label']; + if (settings['route1FromLat']) + route1FromLat = settings['route1FromLat']; + if (settings['route1FromLon']) + route1FromLon = settings['route1FromLon']; + if (settings['route1ToLat']) + route1ToLat = settings['route1ToLat']; + if (settings['route1ToLon']) + route1ToLon = settings['route1ToLon']; + if (settings['route2Start']) + route2Start = settings['route2Start']; + if (settings['route2End']) + route2End = settings['route2End']; + if (settings['route2Label']) + route2Label = settings['route2Label']; + if (settings['route2FromLat']) + route2FromLat = settings['route2FromLat']; + if (settings['route2FromLon']) + route2FromLon = settings['route2FromLon']; + if (settings['route2ToLat']) + route2ToLat = settings['route2ToLat']; + if (settings['route2ToLon']) + route2ToLon = settings['route2ToLon']; + if (settings['route1WorkdaysOnly']) + route1WorkdaysOnly = (settings['route1WorkdaysOnly'] == "true"); + if (settings['route2WorkdaysOnly']) + route2WorkdaysOnly = (settings['route2WorkdaysOnly'] == "true"); + if (settings['route1WindowEnabled']) + route1WindowEnabled = (settings['route1WindowEnabled'] == "true"); + if (settings['route2WindowEnabled']) + route2WindowEnabled = (settings['route2WindowEnabled'] == "true"); + } catch (e) {} + } + + function init() { + registry.registerWidget("tile", tileUrl, this, null, { + thumbLabel: qsTr("Klok-TSC"), + thumbIcon: thumbnailIcon, + thumbCategory: "general", + thumbWeight: 30, + baseTileWeight: 10, + thumbIconVAlignment: "center" + }); + registry.registerWidget("screen", settingsUrl, this, "tscClockSettings"); + registry.registerWidget("screen", travelSettingsUrl, this, "tscClockTravelSettings"); + } + + function updateClockTiles() { + var now = new Date().getTime(); + timeSeconds = new Date().getSeconds(); + if (timeSeconds.length == 1) + timeSeconds = "0" + timeSeconds; + + if (showDayOfWeek && !showDayOnDate) { + timeStr = i18n.dateTime(now, i18n.dow_full).substring(0, 2).toLowerCase() + " " + i18n.dateTime(now, i18n.time_yes); + leftMarginTime = isNxt ? 8 : 6; + } else { + timeStr = i18n.dateTime(now, i18n.time_yes); + leftMarginTime = isNxt ? 60 : 48; + if (showSeconds) + leftMarginTime = leftMarginTime - 15; + } + + var dateBase = showMonthInText ? i18n.dateTime(now, i18n.mon_full) : i18n.dateTime(now, i18n.mon_num); + if (showDayOfWeek && showDayOnDate) { + dateStr = i18n.dateTime(now, i18n.dow_full).substring(0, 2).toLowerCase() + " " + dateBase; + } else { + dateStr = dateBase; + } + + checkTravelTimeWindow(); + } + + function minutesOfDay(timeStr) { + var parts = timeStr.split(":"); + if (parts.length !== 2) + return -1; + return parseInt(parts[0]) * 60 + parseInt(parts[1]); + } + + function setTravelTime(slot, str) { + if (slot === 1) + travelTime1Str = str; + else + travelTime2Str = str; + } + + function setPendingXhr(slot, xhr) { + if (slot === 1) + pendingXhr1 = xhr; + else + pendingXhr2 = xhr; + } + + function travelTimeoutTimer(slot) { + return slot === 1 ? travelTimeout1 : travelTimeout2; + } + + function routeLabel(slot) { + return slot === 1 ? route1Label : route2Label; + } + + function clearTravelRequest(slot) { + travelTimeoutTimer(slot).stop(); + setPendingXhr(slot, null); + } + + // Replace the duration with a dash, so a route that cannot be reached reads + // as unavailable instead of silently freezing on its last known value. + function showTravelFailure(slot, reason) { + var lbl = routeLabel(slot); + setTravelTime(slot, (lbl ? lbl + " " : "") + "—"); + console.log("Waze: route " + slot + " unavailable — " + reason); + } + + // Drop the in-flight request for a slot. Pass an empty reason to abandon it + // silently (route switched off, or a new fetch supersedes it). + function abortTravelRequest(slot, reason) { + var xhr = slot === 1 ? pendingXhr1 : pendingXhr2; + if (xhr) { + // Detach first: abort() itself fires a final readyState change. + xhr.onreadystatechange = null; + xhr.abort(); + } + clearTravelRequest(slot); + if (reason) + showTravelFailure(slot, reason); + } + + function fetchTravelTime(slot, fromLat, fromLon, toLat, toLon, label) { + // A refresh can land while the previous request is still open. + abortTravelRequest(slot, ""); + if (!fromLat || !fromLon || !toLat || !toLon) { + console.log("Waze: skipping fetch, missing coordinates"); + setTravelTime(slot, ""); + return; + } + // Accept Dutch decimal commas and stray whitespace + fromLat = String(fromLat).trim().replace(",", "."); + fromLon = String(fromLon).trim().replace(",", "."); + toLat = String(toLat).trim().replace(",", "."); + toLon = String(toLon).trim().replace(",", "."); + var lbl = label || ""; + var url = "https://routing-livemap-row.waze.com/RoutingManager/routingRequest" + "?from=x%3A" + fromLon + "+y%3A" + fromLat + "&to=x%3A" + toLon + "+y%3A" + toLat + "&at=0&returnJSON=true&returnGeometries=false" + "&returnInstructions=false&timeout=60000&nPaths=1"; + console.log("Waze: GET " + url); + var xhr = new XMLHttpRequest(); + xhr.onreadystatechange = function () { + if (xhr.readyState !== XMLHttpRequest.DONE) + return; + // Break the xhr <-> handler reference cycle so the request object + // (and its closure) can be garbage-collected instead of leaking. + xhr.onreadystatechange = null; + clearTravelRequest(slot); + if (xhr.status !== 200) { + showTravelFailure(slot, "HTTP " + xhr.status + " — " + (xhr.responseText || "").substring(0, 200)); + return; + } + try { + var data = JSON.parse(xhr.responseText); + // nPaths=1 returns data.response.results; multi-path returns data.alternatives[]. + var results = data.alternatives ? data.alternatives[0].response.results : data.response.results; + var secs = 0; + for (var i = 0; i < results.length; i++) + secs += results[i].crossTime; + var str = (lbl ? lbl + " " : "") + Math.round(secs / 60) + " " + qsTr("min"); + setTravelTime(slot, str); + console.log("Waze: ok — " + str); + } catch (e) { + showTravelFailure(slot, "parse error — " + e + " — body: " + (xhr.responseText || "").substring(0, 200)); + } + }; + xhr.open("GET", url); + // Arm the guard before sending: a request that fails immediately calls + // the handler from inside send(), and that handler must be able to + // clear the pending request and stop the timer. + setPendingXhr(slot, xhr); + travelTimeoutTimer(slot).restart(); + xhr.send(); + } + + // Whether a route should currently be shown. When its time window is + // disabled the route is always active (still subject to workdays-only); + // otherwise the current time must fall inside [start, end). + function routeIsActive(rs, re, windowEnabled, workdaysOnly, current, isWorkday) { + if (workdaysOnly && !isWorkday) + return false; + if (!windowEnabled) + return true; + return rs >= 0 && re > rs && current >= rs && current < re; + } + + function checkTravelTimeWindow() { + var now = new Date(); + var current = now.getHours() * 60 + now.getMinutes(); + var dow = now.getDay(); // 0=Sunday … 6=Saturday + var isWorkday = dow >= 1 && dow <= 5; + var r1s = minutesOfDay(route1Start), r1e = minutesOfDay(route1End); + var r2s = minutesOfDay(route2Start), r2e = minutesOfDay(route2End); + + // Each route is evaluated independently so overlapping windows show both. + var a1 = routeIsActive(r1s, r1e, route1WindowEnabled, route1WorkdaysOnly, current, isWorkday); + var a2 = routeIsActive(r2s, r2e, route2WindowEnabled, route2WorkdaysOnly, current, isWorkday); + + // Fetch only on the transition into the active state (so we don't refetch + // every tick), but always clear when inactive so a stale value can never + // linger — e.g. after a settings change flips a route to inactive. + if (a1) { + if (!route1Active) { + route1Active = true; + fetchTravelTime(1, route1FromLat, route1FromLon, route1ToLat, route1ToLon, route1Label); + } + } else { + route1Active = false; + abortTravelRequest(1, ""); + travelTime1Str = ""; + } + if (a2) { + if (!route2Active) { + route2Active = true; + fetchTravelTime(2, route2FromLat, route2FromLon, route2ToLat, route2ToLon, route2Label); + } + } else { + route2Active = false; + abortTravelRequest(2, ""); + travelTime2Str = ""; + } + } + + function saveSettings() { + var tmpshowSeconds = showSeconds ? "true" : "false"; + var tmpshowDate = showDate ? "true" : "false"; + var tmpshowMonthInText = showMonthInText ? "true" : "false"; + var tmpshowDayOfWeek = showDayOfWeek ? "true" : "false"; + var tmpCenterLayout = centerLayout ? "true" : "false"; + var tmpshowDayOnDate = showDayOnDate ? "true" : "false"; + + var tscClockSettingsJson = { + "showSeconds": tmpshowSeconds, + "showDate": tmpshowDate, + "showMonthInText": tmpshowMonthInText, + "showDayOfWeek": tmpshowDayOfWeek, + "centerLayout": tmpCenterLayout, + "showDayOnDate": tmpshowDayOnDate, + "route1Start": route1Start, + "route1End": route1End, + "route1Label": route1Label, + "route1FromLat": route1FromLat, + "route1FromLon": route1FromLon, + "route1ToLat": route1ToLat, + "route1ToLon": route1ToLon, + "route2Start": route2Start, + "route2End": route2End, + "route2Label": route2Label, + "route2FromLat": route2FromLat, + "route2FromLon": route2FromLon, + "route2ToLat": route2ToLat, + "route2ToLon": route2ToLon, + "route1WorkdaysOnly": route1WorkdaysOnly ? "true" : "false", + "route2WorkdaysOnly": route2WorkdaysOnly ? "true" : "false", + "route1WindowEnabled": route1WindowEnabled ? "true" : "false", + "route2WindowEnabled": route2WindowEnabled ? "true" : "false" + }; + + var doc3 = new XMLHttpRequest(); + doc3.open("PUT", "file:///mnt/data/tsc/tscClock.userSettings.json"); + doc3.send(JSON.stringify(tscClockSettingsJson)); + } + + Timer { + id: datetimeTimer + interval: 1000 + triggeredOnStart: true + running: true + repeat: true + onTriggered: updateClockTiles() + } + + Timer { + id: travelTimeout1 + interval: travelRequestTimeout + repeat: false + onTriggered: abortTravelRequest(1, "no response within " + travelRequestTimeout + " ms") + } + + Timer { + id: travelTimeout2 + interval: travelRequestTimeout + repeat: false + onTriggered: abortTravelRequest(2, "no response within " + travelRequestTimeout + " ms") + } + + Timer { + id: travelTimeRefreshTimer + interval: 300000 // 5 minutes + running: true + repeat: true + onTriggered: { + if (route1Active) + fetchTravelTime(1, route1FromLat, route1FromLon, route1ToLat, route1ToLon, route1Label); + if (route2Active) + fetchTravelTime(2, route2FromLat, route2FromLon, route2ToLat, route2ToLon, route2Label); + } + } } diff --git a/TscClockSettings.qml b/TscClockSettings.qml index b3eaa7d..7eefc05 100644 --- a/TscClockSettings.qml +++ b/TscClockSettings.qml @@ -3,204 +3,201 @@ import qb.components 1.0 import BxtClient 1.0 Screen { - id: tscClockSettingsScreen - - - screenTitle: "TSC Klok configuratie" - - onShown: { - addCustomTopRightButton("Opslaan"); - showDateToggle.isSwitchedOn = app.showDate; - showSecondsToggle.isSwitchedOn = app.showSeconds; - showMonthInTextToggle.isSwitchedOn = app.showMonthInText; - showDayOfWeekToggle.isSwitchedOn = app.showDayOfWeek; - centerLayoutToggle.isSwitchedOn = app.centerLayout; - showDayOnDateToggle.isSwitchedOn = app.showDayOnDate; - } - - onCustomButtonClicked: { - app.saveSettings(); - app.updateClockTiles(); - hide(); - } - - - Text { - id: showDateText - x: isNxt ? 36 : 30 - y: isNxt ? 60 : 50 - width: isNxt ? 500 : 400 - font.pixelSize: isNxt ? 20 : 16 - font.family: qfont.semiBold.name - text: "Toon datum" - } - - OnOffToggle { - id: showDateToggle - height: isNxt ? 45 : 36 - anchors.left: showDateText.right - anchors.leftMargin: isNxt ? 25 : 20 - anchors.top: showDateText.top - leftIsSwitchedOn: false - onSelectedChangedByUser: { - if (isSwitchedOn) { - app.showDate = true; - } else { - app.showDate = false; - } - } - } - - - Text { - id: showSecondsText - anchors { - top: showDateText.bottom - topMargin: isNxt ? 25 : 20 - left: showDateText.left - } - font.pixelSize: isNxt ? 20 : 16 - font.family: qfont.semiBold.name - text: "Toon seconden in de tijd" - } - - OnOffToggle { - id: showSecondsToggle - height: isNxt ? 45 : 36 - anchors.left: showDateToggle.left - anchors.top: showSecondsText.top - leftIsSwitchedOn: false - onSelectedChangedByUser: { - if (isSwitchedOn) { - app.showSeconds = true - } else { - app.showSeconds = false - } - } - } - - Text { - id: showMonthInTextText - anchors { - top: showSecondsText.bottom - topMargin: isNxt ? 25 : 20 - left: showSecondsText.left - } - font.pixelSize: isNxt ? 20 : 16 - font.family: qfont.semiBold.name - text: "Maand in datum voluit geschreven" - opacity: app.showDate ? 1.0 : 0.35 - } - - OnOffToggle { - id: showMonthInTextToggle - height: isNxt ? 45 : 36 - anchors.left: showSecondsToggle.left - anchors.top: showMonthInTextText.top - leftIsSwitchedOn: false - enabled: app.showDate - opacity: app.showDate ? 1.0 : 0.35 - onSelectedChangedByUser: { - if (isSwitchedOn) { - app.showMonthInText = true; - } else { - app.showMonthInText = false; - } - } - } - - Text { - id: showDayOfWeekText - anchors { - top: showMonthInTextText.bottom - topMargin: isNxt ? 25 : 20 - left: showSecondsText.left - } - font.pixelSize: isNxt ? 20 : 16 - font.family: qfont.semiBold.name - text: "Toon dag van de week" - } - - OnOffToggle { - id: showDayOfWeekToggle - height: isNxt ? 45 : 36 - anchors.left: showSecondsToggle.left - anchors.top: showDayOfWeekText.top - leftIsSwitchedOn: false - onSelectedChangedByUser: { - if (isSwitchedOn) { - app.showDayOfWeek = true; - } else { - app.showDayOfWeek = false; - } - } - } - - Text { - id: centerLayoutText - anchors { - top: showDayOfWeekText.bottom - topMargin: isNxt ? 25 : 20 - left: showDayOfWeekText.left - } - font.pixelSize: isNxt ? 20 : 16 - font.family: qfont.semiBold.name - text: "Verticaal gecentreerde weergave" - } - - OnOffToggle { - id: centerLayoutToggle - height: isNxt ? 45 : 36 - anchors.left: showDayOfWeekToggle.left - anchors.top: centerLayoutText.top - leftIsSwitchedOn: false - onSelectedChangedByUser: { - app.centerLayout = isSwitchedOn; - } - } - - Text { - id: showDayOnDateText - anchors { - top: centerLayoutText.bottom - topMargin: isNxt ? 25 : 20 - left: centerLayoutText.left - } - font.pixelSize: isNxt ? 20 : 16 - font.family: qfont.semiBold.name - text: "Dag voor datum (i.p.v. voor tijd)" - visible: app.showDayOfWeek - opacity: app.showDate ? 1.0 : 0.35 - } - - OnOffToggle { - id: showDayOnDateToggle - height: isNxt ? 45 : 36 - anchors.left: centerLayoutToggle.left - anchors.top: showDayOnDateText.top - leftIsSwitchedOn: false - visible: app.showDayOfWeek - enabled: app.showDate - opacity: app.showDate ? 1.0 : 0.35 - onSelectedChangedByUser: { - app.showDayOnDate = isSwitchedOn; - } - } - - Text { - id: travelSettingsNavText - anchors { - top: showDayOnDateText.bottom - topMargin: isNxt ? 30 : 22 - left: showDayOnDateText.left - } - font.pixelSize: isNxt ? 20 : 16 - font.family: qfont.semiBold.name - text: "Waze reistijd »" - - MouseArea { - anchors.fill: parent - onClicked: app.tscClockTravelSettings.show() - } - } + id: tscClockSettingsScreen + + screenTitle: qsTr("TSC Klok configuratie") + + onShown: { + addCustomTopRightButton(qsTr("Opslaan")); + showDateToggle.isSwitchedOn = app.showDate; + showSecondsToggle.isSwitchedOn = app.showSeconds; + showMonthInTextToggle.isSwitchedOn = app.showMonthInText; + showDayOfWeekToggle.isSwitchedOn = app.showDayOfWeek; + centerLayoutToggle.isSwitchedOn = app.centerLayout; + showDayOnDateToggle.isSwitchedOn = app.showDayOnDate; + } + + onCustomButtonClicked: { + app.saveSettings(); + app.updateClockTiles(); + hide(); + } + + Text { + id: showDateText + x: isNxt ? 36 : 30 + y: isNxt ? 60 : 50 + width: isNxt ? 500 : 400 + font.pixelSize: isNxt ? 20 : 16 + font.family: qfont.semiBold.name + text: qsTr("Toon datum") + } + + OnOffToggle { + id: showDateToggle + height: isNxt ? 45 : 36 + anchors.left: showDateText.right + anchors.leftMargin: isNxt ? 25 : 20 + anchors.top: showDateText.top + leftIsSwitchedOn: false + onSelectedChangedByUser: { + if (isSwitchedOn) { + app.showDate = true; + } else { + app.showDate = false; + } + } + } + + Text { + id: showSecondsText + anchors { + top: showDateText.bottom + topMargin: isNxt ? 25 : 20 + left: showDateText.left + } + font.pixelSize: isNxt ? 20 : 16 + font.family: qfont.semiBold.name + text: qsTr("Toon seconden in de tijd") + } + + OnOffToggle { + id: showSecondsToggle + height: isNxt ? 45 : 36 + anchors.left: showDateToggle.left + anchors.top: showSecondsText.top + leftIsSwitchedOn: false + onSelectedChangedByUser: { + if (isSwitchedOn) { + app.showSeconds = true; + } else { + app.showSeconds = false; + } + } + } + + Text { + id: showMonthInTextText + anchors { + top: showSecondsText.bottom + topMargin: isNxt ? 25 : 20 + left: showSecondsText.left + } + font.pixelSize: isNxt ? 20 : 16 + font.family: qfont.semiBold.name + text: qsTr("Maand in datum voluit geschreven") + opacity: app.showDate ? 1.0 : 0.35 + } + + OnOffToggle { + id: showMonthInTextToggle + height: isNxt ? 45 : 36 + anchors.left: showSecondsToggle.left + anchors.top: showMonthInTextText.top + leftIsSwitchedOn: false + enabled: app.showDate + opacity: app.showDate ? 1.0 : 0.35 + onSelectedChangedByUser: { + if (isSwitchedOn) { + app.showMonthInText = true; + } else { + app.showMonthInText = false; + } + } + } + + Text { + id: showDayOfWeekText + anchors { + top: showMonthInTextText.bottom + topMargin: isNxt ? 25 : 20 + left: showSecondsText.left + } + font.pixelSize: isNxt ? 20 : 16 + font.family: qfont.semiBold.name + text: qsTr("Toon dag van de week") + } + + OnOffToggle { + id: showDayOfWeekToggle + height: isNxt ? 45 : 36 + anchors.left: showSecondsToggle.left + anchors.top: showDayOfWeekText.top + leftIsSwitchedOn: false + onSelectedChangedByUser: { + if (isSwitchedOn) { + app.showDayOfWeek = true; + } else { + app.showDayOfWeek = false; + } + } + } + + Text { + id: centerLayoutText + anchors { + top: showDayOfWeekText.bottom + topMargin: isNxt ? 25 : 20 + left: showDayOfWeekText.left + } + font.pixelSize: isNxt ? 20 : 16 + font.family: qfont.semiBold.name + text: qsTr("Verticaal gecentreerde weergave") + } + + OnOffToggle { + id: centerLayoutToggle + height: isNxt ? 45 : 36 + anchors.left: showDayOfWeekToggle.left + anchors.top: centerLayoutText.top + leftIsSwitchedOn: false + onSelectedChangedByUser: { + app.centerLayout = isSwitchedOn; + } + } + + Text { + id: showDayOnDateText + anchors { + top: centerLayoutText.bottom + topMargin: isNxt ? 25 : 20 + left: centerLayoutText.left + } + font.pixelSize: isNxt ? 20 : 16 + font.family: qfont.semiBold.name + text: qsTr("Dag voor datum (i.p.v. voor tijd)") + visible: app.showDayOfWeek + opacity: app.showDate ? 1.0 : 0.35 + } + + OnOffToggle { + id: showDayOnDateToggle + height: isNxt ? 45 : 36 + anchors.left: centerLayoutToggle.left + anchors.top: showDayOnDateText.top + leftIsSwitchedOn: false + visible: app.showDayOfWeek + enabled: app.showDate + opacity: app.showDate ? 1.0 : 0.35 + onSelectedChangedByUser: { + app.showDayOnDate = isSwitchedOn; + } + } + + Text { + id: travelSettingsNavText + anchors { + top: showDayOnDateText.bottom + topMargin: isNxt ? 30 : 22 + left: showDayOnDateText.left + } + font.pixelSize: isNxt ? 20 : 16 + font.family: qfont.semiBold.name + text: qsTr("Waze reistijd »") + + MouseArea { + anchors.fill: parent + onClicked: app.tscClockTravelSettings.show() + } + } } diff --git a/TscClockTile.qml b/TscClockTile.qml index 51efcff..23f3d92 100644 --- a/TscClockTile.qml +++ b/TscClockTile.qml @@ -3,164 +3,164 @@ import QtQuick 2.1 import qb.components 1.0 Tile { - id: tscClockTile + id: tscClockTile - onClicked: { - if (app.tscClockSettings) - app.tscClockSettings.show(); - } + onClicked: { + if (app.tscClockSettings) + app.tscClockSettings.show(); + } - // --- CENTRED LAYOUT --- - Column { - width: parent.width - anchors.verticalCenter: parent.verticalCenter - spacing: isNxt ? 8 : 6 - visible: app.centerLayout + // --- CENTRED LAYOUT --- + Column { + width: parent.width + anchors.verticalCenter: parent.verticalCenter + spacing: isNxt ? 8 : 6 + visible: app.centerLayout - Item { - width: parent.width - height: txtTimeBigCentered.implicitHeight + Item { + width: parent.width + height: txtTimeBigCentered.implicitHeight - Text { - id: txtTimeBigCentered - text: app.timeStr - color: (typeof dimmableColors !== 'undefined') ? dimmableColors.clockTileColor : colors.clockTileColor - anchors.horizontalCenter: parent.horizontalCenter - anchors.horizontalCenterOffset: app.showSeconds ? -Math.round((txtSecondsCentered.implicitWidth + (isNxt ? 5 : 4)) / 2) : 0 - font.pixelSize: app.showDayOnDate ? (isNxt ? 75 : 58) : (isNxt ? 65 : 50) - font.family: qfont.regular.name - } + Text { + id: txtTimeBigCentered + text: app.timeStr + color: (typeof dimmableColors !== 'undefined') ? dimmableColors.clockTileColor : colors.clockTileColor + anchors.horizontalCenter: parent.horizontalCenter + anchors.horizontalCenterOffset: app.showSeconds ? -Math.round((txtSecondsCentered.implicitWidth + (isNxt ? 5 : 4)) / 2) : 0 + font.pixelSize: app.showDayOnDate ? (isNxt ? 75 : 58) : (isNxt ? 65 : 50) + font.family: qfont.regular.name + } - Text { - id: txtSecondsCentered - text: app.timeSeconds - color: (typeof dimmableColors !== 'undefined') ? dimmableColors.clockTileColor : colors.clockTileColor - anchors { - bottom: txtTimeBigCentered.bottom - bottomMargin: isNxt ? 11 : 9 - left: txtTimeBigCentered.right - leftMargin: isNxt ? 5 : 4 - } - font.pixelSize: isNxt ? 30 : 24 - font.family: qfont.regular.name - visible: app.showSeconds - } - } + Text { + id: txtSecondsCentered + text: app.timeSeconds + color: (typeof dimmableColors !== 'undefined') ? dimmableColors.clockTileColor : colors.clockTileColor + anchors { + bottom: txtTimeBigCentered.bottom + bottomMargin: isNxt ? 11 : 9 + left: txtTimeBigCentered.right + leftMargin: isNxt ? 5 : 4 + } + font.pixelSize: isNxt ? 30 : 24 + font.family: qfont.regular.name + visible: app.showSeconds + } + } - Text { - id: txtDateCentered - text: app.dateStr - color: (typeof dimmableColors !== 'undefined') ? dimmableColors.clockTileColor : colors.clockTileColor - anchors.horizontalCenter: parent.horizontalCenter - horizontalAlignment: Text.AlignHCenter - font.pixelSize: isNxt ? 32 : 25 - font.family: qfont.regular.name - visible: app.showDate - height: app.showDate ? implicitHeight : 0 - } + Text { + id: txtDateCentered + text: app.dateStr + color: (typeof dimmableColors !== 'undefined') ? dimmableColors.clockTileColor : colors.clockTileColor + anchors.horizontalCenter: parent.horizontalCenter + horizontalAlignment: Text.AlignHCenter + font.pixelSize: isNxt ? 32 : 25 + font.family: qfont.regular.name + visible: app.showDate + height: app.showDate ? implicitHeight : 0 + } - Text { - id: txtTravel1Centered - text: app.travelTime1Str - color: (typeof dimmableColors !== 'undefined') ? dimmableColors.clockTileColor : colors.clockTileColor - anchors.horizontalCenter: parent.horizontalCenter - font.pixelSize: isNxt ? 20 : 16 - font.family: qfont.regular.name - visible: app.travelTime1Str !== "" - height: app.travelTime1Str !== "" ? implicitHeight : 0 - } + Text { + id: txtTravel1Centered + text: app.travelTime1Str + color: (typeof dimmableColors !== 'undefined') ? dimmableColors.clockTileColor : colors.clockTileColor + anchors.horizontalCenter: parent.horizontalCenter + font.pixelSize: isNxt ? 20 : 16 + font.family: qfont.regular.name + visible: app.travelTime1Str !== "" + height: app.travelTime1Str !== "" ? implicitHeight : 0 + } - Text { - id: txtTravel2Centered - text: app.travelTime2Str - color: (typeof dimmableColors !== 'undefined') ? dimmableColors.clockTileColor : colors.clockTileColor - anchors.horizontalCenter: parent.horizontalCenter - font.pixelSize: isNxt ? 20 : 16 - font.family: qfont.regular.name - visible: app.travelTime2Str !== "" - height: app.travelTime2Str !== "" ? implicitHeight : 0 - } - } + Text { + id: txtTravel2Centered + text: app.travelTime2Str + color: (typeof dimmableColors !== 'undefined') ? dimmableColors.clockTileColor : colors.clockTileColor + anchors.horizontalCenter: parent.horizontalCenter + font.pixelSize: isNxt ? 20 : 16 + font.family: qfont.regular.name + visible: app.travelTime2Str !== "" + height: app.travelTime2Str !== "" ? implicitHeight : 0 + } + } - // --- ORIGINAL LAYOUT --- - Text { - id: txtTimeBig - text: app.timeStr - color: (typeof dimmableColors !== 'undefined') ? dimmableColors.clockTileColor: colors.clockTileColor - anchors { - baseline: parent.top - baselineOffset: isNxt ? 70 : 55 - left: parent.left - leftMargin: app.leftMarginTime - } - font.pixelSize: app.showDayOnDate ? (isNxt ? 75 : 58) : (isNxt ? 65 : 50) - font.family: qfont.regular.name - visible: !app.centerLayout - } + // --- ORIGINAL LAYOUT --- + Text { + id: txtTimeBig + text: app.timeStr + color: (typeof dimmableColors !== 'undefined') ? dimmableColors.clockTileColor : colors.clockTileColor + anchors { + baseline: parent.top + baselineOffset: isNxt ? 70 : 55 + left: parent.left + leftMargin: app.leftMarginTime + } + font.pixelSize: app.showDayOnDate ? (isNxt ? 75 : 58) : (isNxt ? 65 : 50) + font.family: qfont.regular.name + visible: !app.centerLayout + } - Text { - id: txtSeconds - text: app.timeSeconds - color: (typeof dimmableColors !== 'undefined') ? dimmableColors.clockTileColor: colors.clockTileColor - anchors { - bottom: txtTimeBig.bottom - bottomMargin: isNxt ? 11 : 9 - left: txtTimeBig.right - leftMargin: isNxt ? 5 : 4 - } - font.pixelSize: isNxt ? 30 : 24 - font.family: qfont.regular.name - visible: app.showSeconds && !app.centerLayout - } + Text { + id: txtSeconds + text: app.timeSeconds + color: (typeof dimmableColors !== 'undefined') ? dimmableColors.clockTileColor : colors.clockTileColor + anchors { + bottom: txtTimeBig.bottom + bottomMargin: isNxt ? 11 : 9 + left: txtTimeBig.right + leftMargin: isNxt ? 5 : 4 + } + font.pixelSize: isNxt ? 30 : 24 + font.family: qfont.regular.name + visible: app.showSeconds && !app.centerLayout + } - Text { - id: txtDate - text: app.dateStr - color: (typeof dimmableColors !== 'undefined') ? dimmableColors.clockTileColor: colors.clockTileColor - anchors { - baseline: parent.top - baselineOffset: isNxt ? 120 : 95 - horizontalCenter: parent.horizontalCenter - } - horizontalAlignment: Text.AlignHCenter - font.pixelSize: isNxt ? 32 : 25 - font.family: qfont.regular.name - visible: app.showDate && !app.centerLayout - } + Text { + id: txtDate + text: app.dateStr + color: (typeof dimmableColors !== 'undefined') ? dimmableColors.clockTileColor : colors.clockTileColor + anchors { + baseline: parent.top + baselineOffset: isNxt ? 120 : 95 + horizontalCenter: parent.horizontalCenter + } + horizontalAlignment: Text.AlignHCenter + font.pixelSize: isNxt ? 32 : 25 + font.family: qfont.regular.name + visible: app.showDate && !app.centerLayout + } - Column { - id: travelColumn - anchors { - // Approximate the old single-line baseline (158/125) as a top offset; - // the first line's ascent (~16/13 px) puts its baseline at that spot. - top: parent.top - topMargin: isNxt ? 142 : 112 - horizontalCenter: parent.horizontalCenter - } - visible: !app.centerLayout + Column { + id: travelColumn + anchors { + // Approximate the old single-line baseline (158/125) as a top offset; + // the first line's ascent (~16/13 px) puts its baseline at that spot. + top: parent.top + topMargin: isNxt ? 142 : 112 + horizontalCenter: parent.horizontalCenter + } + visible: !app.centerLayout - Text { - id: txtTravelTime1 - text: app.travelTime1Str - color: (typeof dimmableColors !== 'undefined') ? dimmableColors.clockTileColor: colors.clockTileColor - anchors.horizontalCenter: parent.horizontalCenter - horizontalAlignment: Text.AlignHCenter - font.pixelSize: isNxt ? 20 : 16 - font.family: qfont.regular.name - visible: app.travelTime1Str !== "" - height: app.travelTime1Str !== "" ? implicitHeight : 0 - } + Text { + id: txtTravelTime1 + text: app.travelTime1Str + color: (typeof dimmableColors !== 'undefined') ? dimmableColors.clockTileColor : colors.clockTileColor + anchors.horizontalCenter: parent.horizontalCenter + horizontalAlignment: Text.AlignHCenter + font.pixelSize: isNxt ? 20 : 16 + font.family: qfont.regular.name + visible: app.travelTime1Str !== "" + height: app.travelTime1Str !== "" ? implicitHeight : 0 + } - Text { - id: txtTravelTime2 - text: app.travelTime2Str - color: (typeof dimmableColors !== 'undefined') ? dimmableColors.clockTileColor: colors.clockTileColor - anchors.horizontalCenter: parent.horizontalCenter - horizontalAlignment: Text.AlignHCenter - font.pixelSize: isNxt ? 20 : 16 - font.family: qfont.regular.name - visible: app.travelTime2Str !== "" - height: app.travelTime2Str !== "" ? implicitHeight : 0 - } - } + Text { + id: txtTravelTime2 + text: app.travelTime2Str + color: (typeof dimmableColors !== 'undefined') ? dimmableColors.clockTileColor : colors.clockTileColor + anchors.horizontalCenter: parent.horizontalCenter + horizontalAlignment: Text.AlignHCenter + font.pixelSize: isNxt ? 20 : 16 + font.family: qfont.regular.name + visible: app.travelTime2Str !== "" + height: app.travelTime2Str !== "" ? implicitHeight : 0 + } + } } diff --git a/TscClockTravelSettings.qml b/TscClockTravelSettings.qml index adc0b67..80fd87b 100644 --- a/TscClockTravelSettings.qml +++ b/TscClockTravelSettings.qml @@ -2,534 +2,808 @@ import QtQuick 2.1 import qb.components 1.0 Screen { - id: tscClockTravelSettingsScreen + id: tscClockTravelSettingsScreen - screenTitle: "Waze reistijd configuratie" + screenTitle: qsTr("Waze reistijd configuratie") - onShown: { - addCustomTopRightButton("Opslaan"); - r1StartInput.text = app.route1Start; - r1EndInput.text = app.route1End; - r1FromLatInput.text = app.route1FromLat; - r1FromLonInput.text = app.route1FromLon; - r1ToLatInput.text = app.route1ToLat; - r1ToLonInput.text = app.route1ToLon; - r1LabelInput.text = app.route1Label; - r1WorkdaysToggle.isSwitchedOn = app.route1WorkdaysOnly; - r1WindowToggle.isSwitchedOn = app.route1WindowEnabled; - r2StartInput.text = app.route2Start; - r2EndInput.text = app.route2End; - r2FromLatInput.text = app.route2FromLat; - r2FromLonInput.text = app.route2FromLon; - r2ToLatInput.text = app.route2ToLat; - r2ToLonInput.text = app.route2ToLon; - r2LabelInput.text = app.route2Label; - r2WorkdaysToggle.isSwitchedOn = app.route2WorkdaysOnly; - r2WindowToggle.isSwitchedOn = app.route2WindowEnabled; - } + onShown: { + addCustomTopRightButton(qsTr("Opslaan")); + r1StartInput.text = app.route1Start; + r1EndInput.text = app.route1End; + r1FromLatInput.text = app.route1FromLat; + r1FromLonInput.text = app.route1FromLon; + r1ToLatInput.text = app.route1ToLat; + r1ToLonInput.text = app.route1ToLon; + r1LabelInput.text = app.route1Label; + r1WorkdaysToggle.isSwitchedOn = app.route1WorkdaysOnly; + r1WindowToggle.isSwitchedOn = app.route1WindowEnabled; + r2StartInput.text = app.route2Start; + r2EndInput.text = app.route2End; + r2FromLatInput.text = app.route2FromLat; + r2FromLonInput.text = app.route2FromLon; + r2ToLatInput.text = app.route2ToLat; + r2ToLonInput.text = app.route2ToLon; + r2LabelInput.text = app.route2Label; + r2WorkdaysToggle.isSwitchedOn = app.route2WorkdaysOnly; + r2WindowToggle.isSwitchedOn = app.route2WindowEnabled; + } - onCustomButtonClicked: { - app.route1Start = r1StartInput.text; - app.route1End = r1EndInput.text; - app.route1Label = r1LabelInput.text; - app.route1FromLat = r1FromLatInput.text; - app.route1FromLon = r1FromLonInput.text; - app.route1ToLat = r1ToLatInput.text; - app.route1ToLon = r1ToLonInput.text; - app.route1WorkdaysOnly = r1WorkdaysToggle.isSwitchedOn; - app.route1WindowEnabled = r1WindowToggle.isSwitchedOn; - app.route2Start = r2StartInput.text; - app.route2End = r2EndInput.text; - app.route2Label = r2LabelInput.text; - app.route2FromLat = r2FromLatInput.text; - app.route2FromLon = r2FromLonInput.text; - app.route2ToLat = r2ToLatInput.text; - app.route2ToLon = r2ToLonInput.text; - app.route2WorkdaysOnly = r2WorkdaysToggle.isSwitchedOn; - app.route2WindowEnabled = r2WindowToggle.isSwitchedOn; - app.saveSettings(); - // Re-evaluate right now so the tile reflects the new settings immediately - // (clearing routes that are no longer active, fetching ones that are), - // instead of waiting for the next 1-second tick. Resetting the active - // flags forces a re-fetch for routes that stay active with new coords. - app.route1Active = false; - app.route2Active = false; - app.checkTravelTimeWindow(); - hide(); - } + onCustomButtonClicked: { + app.route1Start = r1StartInput.text; + app.route1End = r1EndInput.text; + app.route1Label = r1LabelInput.text; + app.route1FromLat = r1FromLatInput.text; + app.route1FromLon = r1FromLonInput.text; + app.route1ToLat = r1ToLatInput.text; + app.route1ToLon = r1ToLonInput.text; + app.route1WorkdaysOnly = r1WorkdaysToggle.isSwitchedOn; + app.route1WindowEnabled = r1WindowToggle.isSwitchedOn; + app.route2Start = r2StartInput.text; + app.route2End = r2EndInput.text; + app.route2Label = r2LabelInput.text; + app.route2FromLat = r2FromLatInput.text; + app.route2FromLon = r2FromLonInput.text; + app.route2ToLat = r2ToLatInput.text; + app.route2ToLon = r2ToLonInput.text; + app.route2WorkdaysOnly = r2WorkdaysToggle.isSwitchedOn; + app.route2WindowEnabled = r2WindowToggle.isSwitchedOn; + app.saveSettings(); + // Re-evaluate right now so the tile reflects the new settings immediately + // (clearing routes that are no longer active, fetching ones that are), + // instead of waiting for the next 1-second tick. Resetting the active + // flags forces a re-fetch for routes that stay active with new coords. + app.route1Active = false; + app.route2Active = false; + app.checkTravelTimeWindow(); + hide(); + } - // ── Route 1 ────────────────────────────────────────────────────────────── + // ── Route 1 ────────────────────────────────────────────────────────────── - Text { - id: route1Header - x: isNxt ? 36 : 30 - y: isNxt ? 60 : 50 - font.pixelSize: isNxt ? 22 : 18 - font.family: qfont.semiBold.name - text: "Route 1" - } + Text { + id: route1Header + x: isNxt ? 36 : 30 + y: isNxt ? 60 : 50 + font.pixelSize: isNxt ? 22 : 18 + font.family: qfont.semiBold.name + text: qsTr("Route 1") + } - Text { - id: r1PeriodeLabel - anchors { top: route1Header.bottom; topMargin: isNxt ? 18 : 14; left: route1Header.left } - width: isNxt ? 100 : 80 - height: isNxt ? 36 : 28 - font.pixelSize: isNxt ? 18 : 14 - font.family: qfont.regular.name - text: "Periode:" - verticalAlignment: Text.AlignVCenter - } - Rectangle { - id: r1StartBox - anchors { left: r1PeriodeLabel.right; leftMargin: isNxt ? 10 : 8; verticalCenter: r1PeriodeLabel.verticalCenter } - opacity: r1WindowToggle.isSwitchedOn ? 1.0 : 0.35 - width: isNxt ? 90 : 70; height: isNxt ? 36 : 28 - border.color: "#aaaaaa"; border.width: 1; radius: 2; color: "white" - TextInput { - id: r1StartInput - anchors { fill: parent; margins: isNxt ? 6 : 4 } - font.pixelSize: isNxt ? 18 : 14 - font.family: qfont.regular.name - color: "#333333" - } - } - Text { - id: r1TotLabel - anchors { left: r1StartBox.right; leftMargin: isNxt ? 10 : 8; verticalCenter: r1PeriodeLabel.verticalCenter } - opacity: r1WindowToggle.isSwitchedOn ? 1.0 : 0.35 - height: isNxt ? 36 : 28 - font.pixelSize: isNxt ? 18 : 14 - font.family: qfont.regular.name - text: "tot" - verticalAlignment: Text.AlignVCenter - } - Rectangle { - id: r1EndBox - anchors { left: r1TotLabel.right; leftMargin: isNxt ? 10 : 8; verticalCenter: r1PeriodeLabel.verticalCenter } - opacity: r1WindowToggle.isSwitchedOn ? 1.0 : 0.35 - width: isNxt ? 90 : 70; height: isNxt ? 36 : 28 - border.color: "#aaaaaa"; border.width: 1; radius: 2; color: "white" - TextInput { - id: r1EndInput - anchors { fill: parent; margins: isNxt ? 6 : 4 } - font.pixelSize: isNxt ? 18 : 14 - font.family: qfont.regular.name - color: "#333333" - } - } + Text { + id: r1PeriodeLabel + anchors { + top: route1Header.bottom + topMargin: isNxt ? 18 : 14 + left: route1Header.left + } + width: isNxt ? 100 : 80 + height: isNxt ? 36 : 28 + font.pixelSize: isNxt ? 18 : 14 + font.family: qfont.regular.name + text: qsTr("Periode:") + verticalAlignment: Text.AlignVCenter + } + Rectangle { + id: r1StartBox + anchors { + left: r1PeriodeLabel.right + leftMargin: isNxt ? 10 : 8 + verticalCenter: r1PeriodeLabel.verticalCenter + } + opacity: r1WindowToggle.isSwitchedOn ? 1.0 : 0.35 + width: isNxt ? 90 : 70 + height: isNxt ? 36 : 28 + border.color: "#aaaaaa" + border.width: 1 + radius: 2 + color: "white" + TextInput { + id: r1StartInput + anchors { + fill: parent + margins: isNxt ? 6 : 4 + } + font.pixelSize: isNxt ? 18 : 14 + font.family: qfont.regular.name + color: "#333333" + } + } + Text { + id: r1TotLabel + anchors { + left: r1StartBox.right + leftMargin: isNxt ? 10 : 8 + verticalCenter: r1PeriodeLabel.verticalCenter + } + opacity: r1WindowToggle.isSwitchedOn ? 1.0 : 0.35 + height: isNxt ? 36 : 28 + font.pixelSize: isNxt ? 18 : 14 + font.family: qfont.regular.name + text: qsTr("tot") + verticalAlignment: Text.AlignVCenter + } + Rectangle { + id: r1EndBox + anchors { + left: r1TotLabel.right + leftMargin: isNxt ? 10 : 8 + verticalCenter: r1PeriodeLabel.verticalCenter + } + opacity: r1WindowToggle.isSwitchedOn ? 1.0 : 0.35 + width: isNxt ? 90 : 70 + height: isNxt ? 36 : 28 + border.color: "#aaaaaa" + border.width: 1 + radius: 2 + color: "white" + TextInput { + id: r1EndInput + anchors { + fill: parent + margins: isNxt ? 6 : 4 + } + font.pixelSize: isNxt ? 18 : 14 + font.family: qfont.regular.name + color: "#333333" + } + } - Text { - id: r1LabelText - anchors { top: r1PeriodeLabel.bottom; topMargin: isNxt ? 12 : 9; left: r1PeriodeLabel.left } - width: isNxt ? 100 : 80 - height: isNxt ? 36 : 28 - font.pixelSize: isNxt ? 18 : 14 - font.family: qfont.regular.name - text: "Label:" - verticalAlignment: Text.AlignVCenter - } - Rectangle { - id: r1LabelBox - anchors { left: r1LabelText.right; leftMargin: isNxt ? 10 : 8; verticalCenter: r1LabelText.verticalCenter } - width: isNxt ? 250 : 200; height: isNxt ? 36 : 28 - border.color: "#aaaaaa"; border.width: 1; radius: 2; color: "white" - TextInput { - id: r1LabelInput - anchors { fill: parent; margins: isNxt ? 6 : 4 } - font.pixelSize: isNxt ? 18 : 14 - font.family: qfont.regular.name - color: "#333333" - } - } + Text { + id: r1LabelText + anchors { + top: r1PeriodeLabel.bottom + topMargin: isNxt ? 12 : 9 + left: r1PeriodeLabel.left + } + width: isNxt ? 100 : 80 + height: isNxt ? 36 : 28 + font.pixelSize: isNxt ? 18 : 14 + font.family: qfont.regular.name + text: qsTr("Label:") + verticalAlignment: Text.AlignVCenter + } + Rectangle { + id: r1LabelBox + anchors { + left: r1LabelText.right + leftMargin: isNxt ? 10 : 8 + verticalCenter: r1LabelText.verticalCenter + } + width: isNxt ? 250 : 200 + height: isNxt ? 36 : 28 + border.color: "#aaaaaa" + border.width: 1 + radius: 2 + color: "white" + TextInput { + id: r1LabelInput + anchors { + fill: parent + margins: isNxt ? 6 : 4 + } + font.pixelSize: isNxt ? 18 : 14 + font.family: qfont.regular.name + color: "#333333" + } + } - Text { - id: r1VanLabel - anchors { top: r1LabelText.bottom; topMargin: isNxt ? 12 : 9; left: r1LabelText.left } - width: isNxt ? 100 : 80 - height: isNxt ? 36 : 28 - font.pixelSize: isNxt ? 18 : 14 - font.family: qfont.regular.name - text: "Van:" - verticalAlignment: Text.AlignVCenter - } - Rectangle { - id: r1FromLatBox - anchors { left: r1FromLatLabel.right; leftMargin: isNxt ? 6 : 5; verticalCenter: r1VanLabel.verticalCenter } - width: isNxt ? 105 : 85; height: isNxt ? 36 : 28 - border.color: "#aaaaaa"; border.width: 1; radius: 2; color: "white" - TextInput { - id: r1FromLatInput - anchors { fill: parent; margins: isNxt ? 6 : 4 } - font.pixelSize: isNxt ? 18 : 14 - font.family: qfont.regular.name - color: "#333333" - } - } - Rectangle { - id: r1FromLonBox - anchors { left: r1FromLonLabel.right; leftMargin: isNxt ? 6 : 5; verticalCenter: r1VanLabel.verticalCenter } - width: isNxt ? 105 : 85; height: isNxt ? 36 : 28 - border.color: "#aaaaaa"; border.width: 1; radius: 2; color: "white" - TextInput { - id: r1FromLonInput - anchors { fill: parent; margins: isNxt ? 6 : 4 } - font.pixelSize: isNxt ? 18 : 14 - font.family: qfont.regular.name - color: "#333333" - } - } + Text { + id: r1VanLabel + anchors { + top: r1LabelText.bottom + topMargin: isNxt ? 12 : 9 + left: r1LabelText.left + } + width: isNxt ? 100 : 80 + height: isNxt ? 36 : 28 + font.pixelSize: isNxt ? 18 : 14 + font.family: qfont.regular.name + text: qsTr("Van:") + verticalAlignment: Text.AlignVCenter + } + Rectangle { + id: r1FromLatBox + anchors { + left: r1FromLatLabel.right + leftMargin: isNxt ? 6 : 5 + verticalCenter: r1VanLabel.verticalCenter + } + width: isNxt ? 105 : 85 + height: isNxt ? 36 : 28 + border.color: "#aaaaaa" + border.width: 1 + radius: 2 + color: "white" + TextInput { + id: r1FromLatInput + anchors { + fill: parent + margins: isNxt ? 6 : 4 + } + font.pixelSize: isNxt ? 18 : 14 + font.family: qfont.regular.name + color: "#333333" + } + } + Rectangle { + id: r1FromLonBox + anchors { + left: r1FromLonLabel.right + leftMargin: isNxt ? 6 : 5 + verticalCenter: r1VanLabel.verticalCenter + } + width: isNxt ? 105 : 85 + height: isNxt ? 36 : 28 + border.color: "#aaaaaa" + border.width: 1 + radius: 2 + color: "white" + TextInput { + id: r1FromLonInput + anchors { + fill: parent + margins: isNxt ? 6 : 4 + } + font.pixelSize: isNxt ? 18 : 14 + font.family: qfont.regular.name + color: "#333333" + } + } - Text { - id: r1NaarLabel - anchors { top: r1VanLabel.bottom; topMargin: isNxt ? 12 : 9; left: r1VanLabel.left } - width: isNxt ? 100 : 80 - height: isNxt ? 36 : 28 - font.pixelSize: isNxt ? 18 : 14 - font.family: qfont.regular.name - text: "Naar:" - verticalAlignment: Text.AlignVCenter - } - Rectangle { - id: r1ToLatBox - anchors { left: r1ToLatLabel.right; leftMargin: isNxt ? 6 : 5; verticalCenter: r1NaarLabel.verticalCenter } - width: isNxt ? 105 : 85; height: isNxt ? 36 : 28 - border.color: "#aaaaaa"; border.width: 1; radius: 2; color: "white" - TextInput { - id: r1ToLatInput - anchors { fill: parent; margins: isNxt ? 6 : 4 } - font.pixelSize: isNxt ? 18 : 14 - font.family: qfont.regular.name - color: "#333333" - } - } - Rectangle { - id: r1ToLonBox - anchors { left: r1ToLonLabel.right; leftMargin: isNxt ? 6 : 5; verticalCenter: r1NaarLabel.verticalCenter } - width: isNxt ? 105 : 85; height: isNxt ? 36 : 28 - border.color: "#aaaaaa"; border.width: 1; radius: 2; color: "white" - TextInput { - id: r1ToLonInput - anchors { fill: parent; margins: isNxt ? 6 : 4 } - font.pixelSize: isNxt ? 18 : 14 - font.family: qfont.regular.name - color: "#333333" - } - } + Text { + id: r1NaarLabel + anchors { + top: r1VanLabel.bottom + topMargin: isNxt ? 12 : 9 + left: r1VanLabel.left + } + width: isNxt ? 100 : 80 + height: isNxt ? 36 : 28 + font.pixelSize: isNxt ? 18 : 14 + font.family: qfont.regular.name + text: qsTr("Naar:") + verticalAlignment: Text.AlignVCenter + } + Rectangle { + id: r1ToLatBox + anchors { + left: r1ToLatLabel.right + leftMargin: isNxt ? 6 : 5 + verticalCenter: r1NaarLabel.verticalCenter + } + width: isNxt ? 105 : 85 + height: isNxt ? 36 : 28 + border.color: "#aaaaaa" + border.width: 1 + radius: 2 + color: "white" + TextInput { + id: r1ToLatInput + anchors { + fill: parent + margins: isNxt ? 6 : 4 + } + font.pixelSize: isNxt ? 18 : 14 + font.family: qfont.regular.name + color: "#333333" + } + } + Rectangle { + id: r1ToLonBox + anchors { + left: r1ToLonLabel.right + leftMargin: isNxt ? 6 : 5 + verticalCenter: r1NaarLabel.verticalCenter + } + width: isNxt ? 105 : 85 + height: isNxt ? 36 : 28 + border.color: "#aaaaaa" + border.width: 1 + radius: 2 + color: "white" + TextInput { + id: r1ToLonInput + anchors { + fill: parent + margins: isNxt ? 6 : 4 + } + font.pixelSize: isNxt ? 18 : 14 + font.family: qfont.regular.name + color: "#333333" + } + } - Text { - id: r1WorkdaysLabel - anchors { top: r1WindowLabel.bottom; topMargin: isNxt ? 12 : 9; left: route1Header.left } - height: isNxt ? 36 : 28 - font.pixelSize: isNxt ? 18 : 14 - font.family: qfont.regular.name - text: "Alleen op werkdagen" - verticalAlignment: Text.AlignVCenter - } - OnOffToggle { - id: r1WorkdaysToggle - height: isNxt ? 36 : 28 - anchors { left: r1WorkdaysLabel.right; leftMargin: isNxt ? 12 : 10; verticalCenter: r1WorkdaysLabel.verticalCenter } - leftIsSwitchedOn: false - } + Text { + id: r1WorkdaysLabel + anchors { + top: r1WindowLabel.bottom + topMargin: isNxt ? 12 : 9 + left: route1Header.left + } + height: isNxt ? 36 : 28 + font.pixelSize: isNxt ? 18 : 14 + font.family: qfont.regular.name + text: qsTr("Alleen op werkdagen") + verticalAlignment: Text.AlignVCenter + } + OnOffToggle { + id: r1WorkdaysToggle + height: isNxt ? 36 : 28 + anchors { + left: r1WorkdaysLabel.right + leftMargin: isNxt ? 12 : 10 + verticalCenter: r1WorkdaysLabel.verticalCenter + } + leftIsSwitchedOn: false + } - // ── Route 2 ────────────────────────────────────────────────────────────── + // ── Route 2 ────────────────────────────────────────────────────────────── - Text { - id: route2Header - // Second column: aligned to Route 1's top, starting at the screen's - // horizontal centre so it adapts to the available width. - anchors { top: route1Header.top; left: parent.horizontalCenter; leftMargin: isNxt ? 20 : 16 } - font.pixelSize: isNxt ? 22 : 18 - font.family: qfont.semiBold.name - text: "Route 2" - } + Text { + id: route2Header + // Second column: aligned to Route 1's top, starting at the screen's + // horizontal centre so it adapts to the available width. + anchors { + top: route1Header.top + left: parent.horizontalCenter + leftMargin: isNxt ? 20 : 16 + } + font.pixelSize: isNxt ? 22 : 18 + font.family: qfont.semiBold.name + text: qsTr("Route 2") + } - Text { - id: r2PeriodeLabel - anchors { top: route2Header.bottom; topMargin: isNxt ? 18 : 14; left: route2Header.left } - width: isNxt ? 100 : 80 - height: isNxt ? 36 : 28 - font.pixelSize: isNxt ? 18 : 14 - font.family: qfont.regular.name - text: "Periode:" - verticalAlignment: Text.AlignVCenter - } - Rectangle { - id: r2StartBox - anchors { left: r2PeriodeLabel.right; leftMargin: isNxt ? 10 : 8; verticalCenter: r2PeriodeLabel.verticalCenter } - opacity: r2WindowToggle.isSwitchedOn ? 1.0 : 0.35 - width: isNxt ? 90 : 70; height: isNxt ? 36 : 28 - border.color: "#aaaaaa"; border.width: 1; radius: 2; color: "white" - TextInput { - id: r2StartInput - anchors { fill: parent; margins: isNxt ? 6 : 4 } - font.pixelSize: isNxt ? 18 : 14 - font.family: qfont.regular.name - color: "#333333" - } - } - Text { - id: r2TotLabel - anchors { left: r2StartBox.right; leftMargin: isNxt ? 10 : 8; verticalCenter: r2PeriodeLabel.verticalCenter } - opacity: r2WindowToggle.isSwitchedOn ? 1.0 : 0.35 - height: isNxt ? 36 : 28 - font.pixelSize: isNxt ? 18 : 14 - font.family: qfont.regular.name - text: "tot" - verticalAlignment: Text.AlignVCenter - } - Rectangle { - id: r2EndBox - anchors { left: r2TotLabel.right; leftMargin: isNxt ? 10 : 8; verticalCenter: r2PeriodeLabel.verticalCenter } - opacity: r2WindowToggle.isSwitchedOn ? 1.0 : 0.35 - width: isNxt ? 90 : 70; height: isNxt ? 36 : 28 - border.color: "#aaaaaa"; border.width: 1; radius: 2; color: "white" - TextInput { - id: r2EndInput - anchors { fill: parent; margins: isNxt ? 6 : 4 } - font.pixelSize: isNxt ? 18 : 14 - font.family: qfont.regular.name - color: "#333333" - } - } + Text { + id: r2PeriodeLabel + anchors { + top: route2Header.bottom + topMargin: isNxt ? 18 : 14 + left: route2Header.left + } + width: isNxt ? 100 : 80 + height: isNxt ? 36 : 28 + font.pixelSize: isNxt ? 18 : 14 + font.family: qfont.regular.name + text: qsTr("Periode:") + verticalAlignment: Text.AlignVCenter + } + Rectangle { + id: r2StartBox + anchors { + left: r2PeriodeLabel.right + leftMargin: isNxt ? 10 : 8 + verticalCenter: r2PeriodeLabel.verticalCenter + } + opacity: r2WindowToggle.isSwitchedOn ? 1.0 : 0.35 + width: isNxt ? 90 : 70 + height: isNxt ? 36 : 28 + border.color: "#aaaaaa" + border.width: 1 + radius: 2 + color: "white" + TextInput { + id: r2StartInput + anchors { + fill: parent + margins: isNxt ? 6 : 4 + } + font.pixelSize: isNxt ? 18 : 14 + font.family: qfont.regular.name + color: "#333333" + } + } + Text { + id: r2TotLabel + anchors { + left: r2StartBox.right + leftMargin: isNxt ? 10 : 8 + verticalCenter: r2PeriodeLabel.verticalCenter + } + opacity: r2WindowToggle.isSwitchedOn ? 1.0 : 0.35 + height: isNxt ? 36 : 28 + font.pixelSize: isNxt ? 18 : 14 + font.family: qfont.regular.name + text: qsTr("tot") + verticalAlignment: Text.AlignVCenter + } + Rectangle { + id: r2EndBox + anchors { + left: r2TotLabel.right + leftMargin: isNxt ? 10 : 8 + verticalCenter: r2PeriodeLabel.verticalCenter + } + opacity: r2WindowToggle.isSwitchedOn ? 1.0 : 0.35 + width: isNxt ? 90 : 70 + height: isNxt ? 36 : 28 + border.color: "#aaaaaa" + border.width: 1 + radius: 2 + color: "white" + TextInput { + id: r2EndInput + anchors { + fill: parent + margins: isNxt ? 6 : 4 + } + font.pixelSize: isNxt ? 18 : 14 + font.family: qfont.regular.name + color: "#333333" + } + } - Text { - id: r2LabelText - anchors { top: r2PeriodeLabel.bottom; topMargin: isNxt ? 12 : 9; left: r2PeriodeLabel.left } - width: isNxt ? 100 : 80 - height: isNxt ? 36 : 28 - font.pixelSize: isNxt ? 18 : 14 - font.family: qfont.regular.name - text: "Label:" - verticalAlignment: Text.AlignVCenter - } - Rectangle { - id: r2LabelBox - anchors { left: r2LabelText.right; leftMargin: isNxt ? 10 : 8; verticalCenter: r2LabelText.verticalCenter } - width: isNxt ? 250 : 200; height: isNxt ? 36 : 28 - border.color: "#aaaaaa"; border.width: 1; radius: 2; color: "white" - TextInput { - id: r2LabelInput - anchors { fill: parent; margins: isNxt ? 6 : 4 } - font.pixelSize: isNxt ? 18 : 14 - font.family: qfont.regular.name - color: "#333333" - } - } + Text { + id: r2LabelText + anchors { + top: r2PeriodeLabel.bottom + topMargin: isNxt ? 12 : 9 + left: r2PeriodeLabel.left + } + width: isNxt ? 100 : 80 + height: isNxt ? 36 : 28 + font.pixelSize: isNxt ? 18 : 14 + font.family: qfont.regular.name + text: qsTr("Label:") + verticalAlignment: Text.AlignVCenter + } + Rectangle { + id: r2LabelBox + anchors { + left: r2LabelText.right + leftMargin: isNxt ? 10 : 8 + verticalCenter: r2LabelText.verticalCenter + } + width: isNxt ? 250 : 200 + height: isNxt ? 36 : 28 + border.color: "#aaaaaa" + border.width: 1 + radius: 2 + color: "white" + TextInput { + id: r2LabelInput + anchors { + fill: parent + margins: isNxt ? 6 : 4 + } + font.pixelSize: isNxt ? 18 : 14 + font.family: qfont.regular.name + color: "#333333" + } + } - Text { - id: r2VanLabel - anchors { top: r2LabelText.bottom; topMargin: isNxt ? 12 : 9; left: r2LabelText.left } - width: isNxt ? 100 : 80 - height: isNxt ? 36 : 28 - font.pixelSize: isNxt ? 18 : 14 - font.family: qfont.regular.name - text: "Van:" - verticalAlignment: Text.AlignVCenter - } - Rectangle { - id: r2FromLatBox - anchors { left: r2FromLatLabel.right; leftMargin: isNxt ? 6 : 5; verticalCenter: r2VanLabel.verticalCenter } - width: isNxt ? 105 : 85; height: isNxt ? 36 : 28 - border.color: "#aaaaaa"; border.width: 1; radius: 2; color: "white" - TextInput { - id: r2FromLatInput - anchors { fill: parent; margins: isNxt ? 6 : 4 } - font.pixelSize: isNxt ? 18 : 14 - font.family: qfont.regular.name - color: "#333333" - } - } - Rectangle { - id: r2FromLonBox - anchors { left: r2FromLonLabel.right; leftMargin: isNxt ? 6 : 5; verticalCenter: r2VanLabel.verticalCenter } - width: isNxt ? 105 : 85; height: isNxt ? 36 : 28 - border.color: "#aaaaaa"; border.width: 1; radius: 2; color: "white" - TextInput { - id: r2FromLonInput - anchors { fill: parent; margins: isNxt ? 6 : 4 } - font.pixelSize: isNxt ? 18 : 14 - font.family: qfont.regular.name - color: "#333333" - } - } + Text { + id: r2VanLabel + anchors { + top: r2LabelText.bottom + topMargin: isNxt ? 12 : 9 + left: r2LabelText.left + } + width: isNxt ? 100 : 80 + height: isNxt ? 36 : 28 + font.pixelSize: isNxt ? 18 : 14 + font.family: qfont.regular.name + text: qsTr("Van:") + verticalAlignment: Text.AlignVCenter + } + Rectangle { + id: r2FromLatBox + anchors { + left: r2FromLatLabel.right + leftMargin: isNxt ? 6 : 5 + verticalCenter: r2VanLabel.verticalCenter + } + width: isNxt ? 105 : 85 + height: isNxt ? 36 : 28 + border.color: "#aaaaaa" + border.width: 1 + radius: 2 + color: "white" + TextInput { + id: r2FromLatInput + anchors { + fill: parent + margins: isNxt ? 6 : 4 + } + font.pixelSize: isNxt ? 18 : 14 + font.family: qfont.regular.name + color: "#333333" + } + } + Rectangle { + id: r2FromLonBox + anchors { + left: r2FromLonLabel.right + leftMargin: isNxt ? 6 : 5 + verticalCenter: r2VanLabel.verticalCenter + } + width: isNxt ? 105 : 85 + height: isNxt ? 36 : 28 + border.color: "#aaaaaa" + border.width: 1 + radius: 2 + color: "white" + TextInput { + id: r2FromLonInput + anchors { + fill: parent + margins: isNxt ? 6 : 4 + } + font.pixelSize: isNxt ? 18 : 14 + font.family: qfont.regular.name + color: "#333333" + } + } - Text { - id: r2NaarLabel - anchors { top: r2VanLabel.bottom; topMargin: isNxt ? 12 : 9; left: r2VanLabel.left } - width: isNxt ? 100 : 80 - height: isNxt ? 36 : 28 - font.pixelSize: isNxt ? 18 : 14 - font.family: qfont.regular.name - text: "Naar:" - verticalAlignment: Text.AlignVCenter - } - Rectangle { - id: r2ToLatBox - anchors { left: r2ToLatLabel.right; leftMargin: isNxt ? 6 : 5; verticalCenter: r2NaarLabel.verticalCenter } - width: isNxt ? 105 : 85; height: isNxt ? 36 : 28 - border.color: "#aaaaaa"; border.width: 1; radius: 2; color: "white" - TextInput { - id: r2ToLatInput - anchors { fill: parent; margins: isNxt ? 6 : 4 } - font.pixelSize: isNxt ? 18 : 14 - font.family: qfont.regular.name - color: "#333333" - } - } - Rectangle { - id: r2ToLonBox - anchors { left: r2ToLonLabel.right; leftMargin: isNxt ? 6 : 5; verticalCenter: r2NaarLabel.verticalCenter } - width: isNxt ? 105 : 85; height: isNxt ? 36 : 28 - border.color: "#aaaaaa"; border.width: 1; radius: 2; color: "white" - TextInput { - id: r2ToLonInput - anchors { fill: parent; margins: isNxt ? 6 : 4 } - font.pixelSize: isNxt ? 18 : 14 - font.family: qfont.regular.name - color: "#333333" - } - } + Text { + id: r2NaarLabel + anchors { + top: r2VanLabel.bottom + topMargin: isNxt ? 12 : 9 + left: r2VanLabel.left + } + width: isNxt ? 100 : 80 + height: isNxt ? 36 : 28 + font.pixelSize: isNxt ? 18 : 14 + font.family: qfont.regular.name + text: qsTr("Naar:") + verticalAlignment: Text.AlignVCenter + } + Rectangle { + id: r2ToLatBox + anchors { + left: r2ToLatLabel.right + leftMargin: isNxt ? 6 : 5 + verticalCenter: r2NaarLabel.verticalCenter + } + width: isNxt ? 105 : 85 + height: isNxt ? 36 : 28 + border.color: "#aaaaaa" + border.width: 1 + radius: 2 + color: "white" + TextInput { + id: r2ToLatInput + anchors { + fill: parent + margins: isNxt ? 6 : 4 + } + font.pixelSize: isNxt ? 18 : 14 + font.family: qfont.regular.name + color: "#333333" + } + } + Rectangle { + id: r2ToLonBox + anchors { + left: r2ToLonLabel.right + leftMargin: isNxt ? 6 : 5 + verticalCenter: r2NaarLabel.verticalCenter + } + width: isNxt ? 105 : 85 + height: isNxt ? 36 : 28 + border.color: "#aaaaaa" + border.width: 1 + radius: 2 + color: "white" + TextInput { + id: r2ToLonInput + anchors { + fill: parent + margins: isNxt ? 6 : 4 + } + font.pixelSize: isNxt ? 18 : 14 + font.family: qfont.regular.name + color: "#333333" + } + } - Text { - id: r2WorkdaysLabel - anchors { top: r2WindowLabel.bottom; topMargin: isNxt ? 12 : 9; left: route2Header.left } - height: isNxt ? 36 : 28 - font.pixelSize: isNxt ? 18 : 14 - font.family: qfont.regular.name - text: "Alleen op werkdagen" - verticalAlignment: Text.AlignVCenter - } - OnOffToggle { - id: r2WorkdaysToggle - height: isNxt ? 36 : 28 - anchors { left: r2WorkdaysLabel.right; leftMargin: isNxt ? 12 : 10; verticalCenter: r2WorkdaysLabel.verticalCenter } - leftIsSwitchedOn: false - } + Text { + id: r2WorkdaysLabel + anchors { + top: r2WindowLabel.bottom + topMargin: isNxt ? 12 : 9 + left: route2Header.left + } + height: isNxt ? 36 : 28 + font.pixelSize: isNxt ? 18 : 14 + font.family: qfont.regular.name + text: qsTr("Alleen op werkdagen") + verticalAlignment: Text.AlignVCenter + } + OnOffToggle { + id: r2WorkdaysToggle + height: isNxt ? 36 : 28 + anchors { + left: r2WorkdaysLabel.right + leftMargin: isNxt ? 12 : 10 + verticalCenter: r2WorkdaysLabel.verticalCenter + } + leftIsSwitchedOn: false + } - // ── Lat/Lon prefix labels (anchored to the coordinate boxes above) ──────── + // ── Lat/Lon prefix labels (anchored to the coordinate boxes above) ──────── - Text { - id: r1FromLatLabel - anchors { left: r1VanLabel.right; leftMargin: isNxt ? 8 : 6; verticalCenter: r1VanLabel.verticalCenter } - width: isNxt ? 30 : 24; height: isNxt ? 36 : 28 - font.pixelSize: isNxt ? 18 : 14 - font.family: qfont.regular.name - color: "#555555" - text: "Lat" - verticalAlignment: Text.AlignVCenter - } - Text { - id: r1FromLonLabel - anchors { left: r1FromLatBox.right; leftMargin: isNxt ? 10 : 8; verticalCenter: r1VanLabel.verticalCenter } - width: isNxt ? 30 : 24; height: isNxt ? 36 : 28 - font.pixelSize: isNxt ? 18 : 14 - font.family: qfont.regular.name - color: "#555555" - text: "Lon" - verticalAlignment: Text.AlignVCenter - } - Text { - id: r1ToLatLabel - anchors { left: r1NaarLabel.right; leftMargin: isNxt ? 8 : 6; verticalCenter: r1NaarLabel.verticalCenter } - width: isNxt ? 30 : 24; height: isNxt ? 36 : 28 - font.pixelSize: isNxt ? 18 : 14 - font.family: qfont.regular.name - color: "#555555" - text: "Lat" - verticalAlignment: Text.AlignVCenter - } - Text { - id: r1ToLonLabel - anchors { left: r1ToLatBox.right; leftMargin: isNxt ? 10 : 8; verticalCenter: r1NaarLabel.verticalCenter } - width: isNxt ? 30 : 24; height: isNxt ? 36 : 28 - font.pixelSize: isNxt ? 18 : 14 - font.family: qfont.regular.name - color: "#555555" - text: "Lon" - verticalAlignment: Text.AlignVCenter - } - Text { - id: r2FromLatLabel - anchors { left: r2VanLabel.right; leftMargin: isNxt ? 8 : 6; verticalCenter: r2VanLabel.verticalCenter } - width: isNxt ? 30 : 24; height: isNxt ? 36 : 28 - font.pixelSize: isNxt ? 18 : 14 - font.family: qfont.regular.name - color: "#555555" - text: "Lat" - verticalAlignment: Text.AlignVCenter - } - Text { - id: r2FromLonLabel - anchors { left: r2FromLatBox.right; leftMargin: isNxt ? 10 : 8; verticalCenter: r2VanLabel.verticalCenter } - width: isNxt ? 30 : 24; height: isNxt ? 36 : 28 - font.pixelSize: isNxt ? 18 : 14 - font.family: qfont.regular.name - color: "#555555" - text: "Lon" - verticalAlignment: Text.AlignVCenter - } - Text { - id: r2ToLatLabel - anchors { left: r2NaarLabel.right; leftMargin: isNxt ? 8 : 6; verticalCenter: r2NaarLabel.verticalCenter } - width: isNxt ? 30 : 24; height: isNxt ? 36 : 28 - font.pixelSize: isNxt ? 18 : 14 - font.family: qfont.regular.name - color: "#555555" - text: "Lat" - verticalAlignment: Text.AlignVCenter - } - Text { - id: r2ToLonLabel - anchors { left: r2ToLatBox.right; leftMargin: isNxt ? 10 : 8; verticalCenter: r2NaarLabel.verticalCenter } - width: isNxt ? 30 : 24; height: isNxt ? 36 : 28 - font.pixelSize: isNxt ? 18 : 14 - font.family: qfont.regular.name - color: "#555555" - text: "Lon" - verticalAlignment: Text.AlignVCenter - } + Text { + id: r1FromLatLabel + anchors { + left: r1VanLabel.right + leftMargin: isNxt ? 8 : 6 + verticalCenter: r1VanLabel.verticalCenter + } + width: isNxt ? 30 : 24 + height: isNxt ? 36 : 28 + font.pixelSize: isNxt ? 18 : 14 + font.family: qfont.regular.name + color: "#555555" + text: qsTr("Lat") + verticalAlignment: Text.AlignVCenter + } + Text { + id: r1FromLonLabel + anchors { + left: r1FromLatBox.right + leftMargin: isNxt ? 10 : 8 + verticalCenter: r1VanLabel.verticalCenter + } + width: isNxt ? 30 : 24 + height: isNxt ? 36 : 28 + font.pixelSize: isNxt ? 18 : 14 + font.family: qfont.regular.name + color: "#555555" + text: qsTr("Lon") + verticalAlignment: Text.AlignVCenter + } + Text { + id: r1ToLatLabel + anchors { + left: r1NaarLabel.right + leftMargin: isNxt ? 8 : 6 + verticalCenter: r1NaarLabel.verticalCenter + } + width: isNxt ? 30 : 24 + height: isNxt ? 36 : 28 + font.pixelSize: isNxt ? 18 : 14 + font.family: qfont.regular.name + color: "#555555" + text: qsTr("Lat") + verticalAlignment: Text.AlignVCenter + } + Text { + id: r1ToLonLabel + anchors { + left: r1ToLatBox.right + leftMargin: isNxt ? 10 : 8 + verticalCenter: r1NaarLabel.verticalCenter + } + width: isNxt ? 30 : 24 + height: isNxt ? 36 : 28 + font.pixelSize: isNxt ? 18 : 14 + font.family: qfont.regular.name + color: "#555555" + text: qsTr("Lon") + verticalAlignment: Text.AlignVCenter + } + Text { + id: r2FromLatLabel + anchors { + left: r2VanLabel.right + leftMargin: isNxt ? 8 : 6 + verticalCenter: r2VanLabel.verticalCenter + } + width: isNxt ? 30 : 24 + height: isNxt ? 36 : 28 + font.pixelSize: isNxt ? 18 : 14 + font.family: qfont.regular.name + color: "#555555" + text: qsTr("Lat") + verticalAlignment: Text.AlignVCenter + } + Text { + id: r2FromLonLabel + anchors { + left: r2FromLatBox.right + leftMargin: isNxt ? 10 : 8 + verticalCenter: r2VanLabel.verticalCenter + } + width: isNxt ? 30 : 24 + height: isNxt ? 36 : 28 + font.pixelSize: isNxt ? 18 : 14 + font.family: qfont.regular.name + color: "#555555" + text: qsTr("Lon") + verticalAlignment: Text.AlignVCenter + } + Text { + id: r2ToLatLabel + anchors { + left: r2NaarLabel.right + leftMargin: isNxt ? 8 : 6 + verticalCenter: r2NaarLabel.verticalCenter + } + width: isNxt ? 30 : 24 + height: isNxt ? 36 : 28 + font.pixelSize: isNxt ? 18 : 14 + font.family: qfont.regular.name + color: "#555555" + text: qsTr("Lat") + verticalAlignment: Text.AlignVCenter + } + Text { + id: r2ToLonLabel + anchors { + left: r2ToLatBox.right + leftMargin: isNxt ? 10 : 8 + verticalCenter: r2NaarLabel.verticalCenter + } + width: isNxt ? 30 : 24 + height: isNxt ? 36 : 28 + font.pixelSize: isNxt ? 18 : 14 + font.family: qfont.regular.name + color: "#555555" + text: qsTr("Lon") + verticalAlignment: Text.AlignVCenter + } - // ── Time-window enable toggles (per route, own row under the coordinates) ── + // ── Time-window enable toggles (per route, own row under the coordinates) ── - Text { - id: r1WindowLabel - anchors { top: r1NaarLabel.bottom; topMargin: isNxt ? 12 : 9; left: route1Header.left } - height: isNxt ? 36 : 28 - font.pixelSize: isNxt ? 18 : 14 - font.family: qfont.regular.name - text: "Tijdvenster" - verticalAlignment: Text.AlignVCenter - } - OnOffToggle { - id: r1WindowToggle - height: isNxt ? 36 : 28 - anchors { left: r1WindowLabel.right; leftMargin: isNxt ? 12 : 10; verticalCenter: r1WindowLabel.verticalCenter } - leftIsSwitchedOn: false - } - Text { - id: r2WindowLabel - anchors { top: r2NaarLabel.bottom; topMargin: isNxt ? 12 : 9; left: route2Header.left } - height: isNxt ? 36 : 28 - font.pixelSize: isNxt ? 18 : 14 - font.family: qfont.regular.name - text: "Tijdvenster" - verticalAlignment: Text.AlignVCenter - } - OnOffToggle { - id: r2WindowToggle - height: isNxt ? 36 : 28 - anchors { left: r2WindowLabel.right; leftMargin: isNxt ? 12 : 10; verticalCenter: r2WindowLabel.verticalCenter } - leftIsSwitchedOn: false - } + Text { + id: r1WindowLabel + anchors { + top: r1NaarLabel.bottom + topMargin: isNxt ? 12 : 9 + left: route1Header.left + } + height: isNxt ? 36 : 28 + font.pixelSize: isNxt ? 18 : 14 + font.family: qfont.regular.name + text: qsTr("Tijdvenster") + verticalAlignment: Text.AlignVCenter + } + OnOffToggle { + id: r1WindowToggle + height: isNxt ? 36 : 28 + anchors { + left: r1WindowLabel.right + leftMargin: isNxt ? 12 : 10 + verticalCenter: r1WindowLabel.verticalCenter + } + leftIsSwitchedOn: false + } + Text { + id: r2WindowLabel + anchors { + top: r2NaarLabel.bottom + topMargin: isNxt ? 12 : 9 + left: route2Header.left + } + height: isNxt ? 36 : 28 + font.pixelSize: isNxt ? 18 : 14 + font.family: qfont.regular.name + text: qsTr("Tijdvenster") + verticalAlignment: Text.AlignVCenter + } + OnOffToggle { + id: r2WindowToggle + height: isNxt ? 36 : 28 + anchors { + left: r2WindowLabel.right + leftMargin: isNxt ? 12 : 10 + verticalCenter: r2WindowLabel.verticalCenter + } + leftIsSwitchedOn: false + } - // ── Hint ───────────────────────────────────────────────────────────────── + // ── Hint ───────────────────────────────────────────────────────────────── - Text { - anchors { top: r1WorkdaysLabel.bottom; topMargin: isNxt ? 18 : 12; left: route1Header.left } - font.pixelSize: isNxt ? 15 : 12 - font.family: qfont.regular.name - color: "#888888" - text: "Coördinaten in decimaal formaat (bijv. 52.3704 / 4.8952), tijd als HH:MM" - } + Text { + anchors { + top: r1WorkdaysLabel.bottom + topMargin: isNxt ? 18 : 12 + left: route1Header.left + } + font.pixelSize: isNxt ? 15 : 12 + font.family: qfont.regular.name + color: "#888888" + text: qsTr("Coördinaten in decimaal formaat (bijv. 52.3704 / 4.8952), tijd als HH:MM") + } } diff --git a/lang/lang_en_GB.qm b/lang/lang_en_GB.qm new file mode 100644 index 0000000..2e70c20 Binary files /dev/null and b/lang/lang_en_GB.qm differ diff --git a/lang/lang_en_GB.ts b/lang/lang_en_GB.ts new file mode 100644 index 0000000..c73f8ec --- /dev/null +++ b/lang/lang_en_GB.ts @@ -0,0 +1,121 @@ + + + + + TscClockApp + + Klok-TSC + Clock-TSC + + + Thuis-Werk: + Home-Work: + + + Werk-Thuis: + Work-Home: + + + min + min + + + + TscClockSettings + + TSC Klok configuratie + TSC Clock settings + + + Opslaan + Save + + + Toon datum + Show date + + + Toon seconden in de tijd + Show seconds in the time + + + Maand in datum voluit geschreven + Write the month out in full + + + Toon dag van de week + Show day of the week + + + Verticaal gecentreerde weergave + Vertically centred layout + + + Dag voor datum (i.p.v. voor tijd) + Day before date (instead of before time) + + + Waze reistijd » + Waze travel time » + + + + TscClockTravelSettings + + Waze reistijd configuratie + Waze travel time settings + + + Opslaan + Save + + + Route 1 + Route 1 + + + Route 2 + Route 2 + + + Periode: + Period: + + + tot + to + + + Label: + Label: + + + Van: + From: + + + Naar: + To: + + + Lat + Lat + + + Lon + Lon + + + Tijdvenster + Time window + + + Alleen op werkdagen + Weekdays only + + + Coördinaten in decimaal formaat (bijv. 52.3704 / 4.8952), tijd als HH:MM + Coordinates in decimal format (e.g. 52.3704 / 4.8952), time as HH:MM + + + diff --git a/version.txt b/version.txt index 9084fa2..524cb55 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.1.0 +1.1.1