Fix weather panel clipping and units

This commit is contained in:
David Heinemeier Hansson
2026-06-22 20:52:02 +02:00
parent 4773202ab0
commit e8e3b7ef54
5 changed files with 48 additions and 10 deletions
+4 -2
View File
@@ -40,6 +40,7 @@ PanelWindow {
property int padding: Style.spacing.popupPadding
property int contentWidth: Style.space(280)
property int contentHeight: Style.space(200)
property var borderSpec: Border.surfaceSpec("popups", "border", Color.popups.border, Math.max(1, Style.space(2)))
property bool centerOnBar: false
property bool open: false
property int gap: Style.gapsOut // distance between bar edge and panel
@@ -135,6 +136,7 @@ PanelWindow {
readonly property real availableCardHeight: screenH > 0
? Math.max(120, screenH - ((barPos === "top" || barPos === "bottom") ? barH + gap + margin : margin * 2))
: 0
readonly property real verticalContentInset: padding * 2 + Border.top(borderSpec) + Border.bottom(borderSpec)
function fittedContentWidth(width, cap) {
var desired = Math.max(1, Number(width) || 1)
@@ -144,7 +146,7 @@ PanelWindow {
}
function fittedContentHeight(implicitHeight, cap) {
var desired = Math.max(root.padding * 2, (Number(implicitHeight) || 0) + root.padding * 2)
var desired = Math.max(root.verticalContentInset, (Number(implicitHeight) || 0) + root.verticalContentInset)
var maxHeight = root.availableCardHeight > 0 ? root.availableCardHeight : desired
if (cap !== undefined && Number(cap) > 0) maxHeight = Math.min(maxHeight, Number(cap))
return Math.round(Math.min(desired, maxHeight))
@@ -297,7 +299,7 @@ PanelWindow {
width: root.contentWidth
height: root.contentHeight
color: Color.popups.background
borderSpec: Border.surfaceSpec("popups", "border", Color.popups.border, Math.max(1, Style.space(2)))
borderSpec: root.borderSpec
padding: root.padding
radius: Style.cornerRadius
opacity: root.open || root.popoutSwitching ? 1.0 : 0
+2 -1
View File
@@ -35,6 +35,7 @@ PopupWindow {
readonly property real availableCardHeight: screenH > 0
? Math.max(120, screenH - ((bar && (bar.position === "top" || bar.position === "bottom")) ? barH : 0) - root.margin * 2)
: 0
readonly property real verticalContentInset: padding * 2 + Border.top(borderSpec) + Border.bottom(borderSpec)
function fittedContentWidth(width, cap) {
var desired = Math.max(1, Number(width) || 1)
@@ -44,7 +45,7 @@ PopupWindow {
}
function fittedContentHeight(implicitHeight, cap) {
var desired = Math.max(root.padding * 2, (Number(implicitHeight) || 0) + root.padding * 2)
var desired = Math.max(root.verticalContentInset, (Number(implicitHeight) || 0) + root.verticalContentInset)
var maxHeight = root.availableCardHeight > 0 ? root.availableCardHeight : desired
if (cap !== undefined && Number(cap) > 0) maxHeight = Math.min(maxHeight, Number(cap))
return Math.round(Math.min(desired, maxHeight))
+35
View File
@@ -32,6 +32,37 @@ function formatTemp(value, useImperial) {
return value + "°" + (useImperial ? "F" : "C")
}
function normalizedUnit(value) {
return String(value || "").replace(/^\s+|\s+$/g, "").toLowerCase()
}
function localeUsesImperial(localeName) {
var name = String(localeName || "").replace(".", "_")
return /^en[_-]US($|[_.-])/.test(name) || /^en[_-]LR($|[_.-])/.test(name) || /^my($|[_.-])/.test(name)
}
function countryUsesImperial(countryName) {
var country = String(countryName || "")
.replace(/^\s+|\s+$/g, "")
.replace(/[._-]+/g, " ")
.toLowerCase()
if (!country) return null
if (country === "us" || country === "usa" || country === "united states" || country === "united states of america") return true
if (country === "liberia" || country === "myanmar" || country === "burma") return true
return false
}
function shouldUseImperial(unitOverride, localeName, countryName) {
var unit = normalizedUnit(unitOverride)
if (unit === "imperial") return true
if (unit === "metric") return false
var countryPreference = countryUsesImperial(countryName)
if (countryPreference !== null) return countryPreference
return localeUsesImperial(localeName)
}
function dayName(dateString, formatter) {
if (!dateString) return ""
var d = new Date(dateString + "T12:00:00")
@@ -143,6 +174,10 @@ if (typeof module !== "undefined") {
roundedTemp: roundedTemp,
celsiusToFahrenheit: celsiusToFahrenheit,
formatTemp: formatTemp,
normalizedUnit: normalizedUnit,
localeUsesImperial: localeUsesImperial,
countryUsesImperial: countryUsesImperial,
shouldUseImperial: shouldUseImperial,
dayName: dayName,
openMeteoForecastDays: openMeteoForecastDays,
wttrNextForecastDays: wttrNextForecastDays,
+2 -7
View File
@@ -61,14 +61,9 @@ Panel {
readonly property var current: report && report.current_condition && report.current_condition[0] ? report.current_condition[0] : null
readonly property var areaInfo: report && report.nearest_area && report.nearest_area[0] ? report.nearest_area[0] : null
readonly property var forecastDays: buildForecastDays()
readonly property string reportCountry: areaInfo && areaInfo.country && areaInfo.country[0] ? areaInfo.country[0].value : ""
readonly property bool useImperial: {
var override = setting("unit", "")
if (override === "imperial") return true
if (override === "metric") return false
var name = String(Qt.locale().name || "")
return /^en_US/.test(name) || /^en_LR/.test(name) || /^my/.test(name)
}
readonly property bool useImperial: Model.shouldUseImperial(setting("unit", ""), Qt.locale().name, reportCountry)
// Auto-refresh interval in minutes; clamped to a sane minimum.
readonly property int refreshMinutes: Math.max(1, parseInt(setting("refreshMinutes", 15), 10) || 15)
+5
View File
@@ -18,6 +18,11 @@ assertEqual(weather.roundedTemp('21.6'), '22', 'weather rounds temperatures')
assertEqual(weather.roundedTemp('nope'), '', 'weather ignores invalid temperatures')
assertEqual(weather.formatTemp(72, true), '72°F', 'weather formats imperial temperatures')
assertEqual(weather.formatTemp(22, false), '22°C', 'weather formats metric temperatures')
assertEqual(weather.shouldUseImperial('', 'en_US', ''), true, 'weather falls back to US locale for imperial units')
assertEqual(weather.shouldUseImperial('', 'en_US', 'Denmark'), false, 'weather prefers reported metric country over US locale')
assertEqual(weather.shouldUseImperial('', 'da_DK', 'United States of America'), true, 'weather prefers reported imperial country over metric locale')
assertEqual(weather.shouldUseImperial('metric', 'en_US', 'United States of America'), false, 'weather metric override wins')
assertEqual(weather.shouldUseImperial('imperial', 'da_DK', 'Denmark'), true, 'weather imperial override wins')
assertEqual(weather.dayName('2026-05-25'), 'Monday', 'weather derives day names')
const openMeteo = {