851 lines
29 KiB
Plaintext
851 lines
29 KiB
Plaintext
struct HourData {
|
|
hour: string,
|
|
temp: int,
|
|
icon-type: int,
|
|
is-now: bool,
|
|
}
|
|
|
|
// ═══ API Key Dialog ═════════════════════════════════════════
|
|
export component ApiKeyDialog inherits Window {
|
|
title: "OpenWeatherMap API Key";
|
|
preferred-width: 480px;
|
|
preferred-height: 320px;
|
|
background: @linear-gradient(180deg, #1a1a2e, #0f0f1e);
|
|
|
|
in-out property <string> api-key-input: "";
|
|
callback submit-key(string);
|
|
callback skip;
|
|
|
|
VerticalLayout {
|
|
padding: 40px;
|
|
spacing: 20px;
|
|
alignment: center;
|
|
|
|
Text {
|
|
text: "Configurazione API Key";
|
|
color: white;
|
|
font-size: 24px;
|
|
font-weight: 600;
|
|
horizontal-alignment: center;
|
|
}
|
|
|
|
Text {
|
|
text: "Inserisci la tua chiave API di OpenWeatherMap\nper visualizzare i dati meteo in tempo reale.";
|
|
color: #ffffffaa;
|
|
font-size: 13px;
|
|
horizontal-alignment: center;
|
|
wrap: word-wrap;
|
|
}
|
|
|
|
Rectangle { height: 10px; }
|
|
|
|
// Input field
|
|
Rectangle {
|
|
height: 50px;
|
|
border-radius: 12px;
|
|
background: #ffffff0a;
|
|
border-width: 1px;
|
|
border-color: #ffffff22;
|
|
|
|
HorizontalLayout {
|
|
padding: 14px;
|
|
|
|
input := TextInput {
|
|
text <=> root.api-key-input;
|
|
font-size: 14px;
|
|
color: white;
|
|
horizontal-alignment: left;
|
|
single-line: true;
|
|
|
|
accepted => {
|
|
root.submit-key(self.text);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Text {
|
|
text: "Ottieni una chiave gratuita su:\nopenweathermap.org/api";
|
|
color: #66bbff;
|
|
font-size: 11px;
|
|
horizontal-alignment: center;
|
|
}
|
|
|
|
Rectangle { vertical-stretch: 1; }
|
|
|
|
HorizontalLayout {
|
|
spacing: 12px;
|
|
|
|
Rectangle {
|
|
horizontal-stretch: 1;
|
|
height: 44px;
|
|
border-radius: 12px;
|
|
background: #ffffff12;
|
|
border-width: 1px;
|
|
border-color: #ffffff1a;
|
|
|
|
Text {
|
|
text: "Salta (usa dati fake)";
|
|
color: #ffffffaa;
|
|
font-size: 13px;
|
|
vertical-alignment: center;
|
|
horizontal-alignment: center;
|
|
}
|
|
|
|
TouchArea {
|
|
clicked => {
|
|
root.skip();
|
|
}
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
horizontal-stretch: 1;
|
|
height: 44px;
|
|
border-radius: 12px;
|
|
background: @linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
|
|
drop-shadow-blur: 12px;
|
|
drop-shadow-color: #4facfe44;
|
|
|
|
Text {
|
|
text: "Salva e Continua";
|
|
color: white;
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
vertical-alignment: center;
|
|
horizontal-alignment: center;
|
|
}
|
|
|
|
TouchArea {
|
|
clicked => {
|
|
root.submit-key(root.api-key-input);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ─── Decorative Cloud ────────────────────────────────────────
|
|
component CloudShape inherits Rectangle {
|
|
in property <float> tick: 0;
|
|
in property <float> speed: 0.3;
|
|
in property <float> phase: 0;
|
|
in property <float> base-opacity: 0.25;
|
|
|
|
background: transparent;
|
|
opacity: base-opacity;
|
|
property <length> bob: Math.sin((tick * speed * 0.7 + phase) * 1rad) * 6px;
|
|
|
|
Rectangle {
|
|
x: parent.width * 0.15; y: parent.height * 0.35 + parent.bob;
|
|
width: parent.width * 0.7; height: parent.height * 0.55;
|
|
border-radius: self.height / 2; background: white;
|
|
}
|
|
Rectangle {
|
|
x: parent.width * 0.30; y: parent.height * 0.05 + parent.bob;
|
|
width: parent.width * 0.4; height: parent.height * 0.55;
|
|
border-radius: self.height / 2; background: white;
|
|
}
|
|
Rectangle {
|
|
x: parent.width * 0.05; y: parent.height * 0.40 + parent.bob;
|
|
width: parent.width * 0.32; height: parent.height * 0.42;
|
|
border-radius: self.height / 2; background: white;
|
|
}
|
|
Rectangle {
|
|
x: parent.width * 0.58; y: parent.height * 0.28 + parent.bob;
|
|
width: parent.width * 0.32; height: parent.height * 0.42;
|
|
border-radius: self.height / 2; background: white;
|
|
}
|
|
}
|
|
|
|
// ─── Rain Drop ───────────────────────────────────────────────
|
|
component RainDrop inherits Rectangle {
|
|
in property <float> tick;
|
|
in property <float> speed: 3.0;
|
|
in property <float> phase: 0;
|
|
|
|
width: 2px; height: 18px;
|
|
border-radius: 1px;
|
|
background: @linear-gradient(180deg, #aaddff00, #aaddffaa);
|
|
opacity: Math.max(0, Math.sin((tick * speed + phase) * 1rad)) * 0.6;
|
|
}
|
|
|
|
// ═══ Weather Display Icons (drawn with shapes) ══════════════
|
|
|
|
// ─── Sun Display ─────────────────────────────────────────────
|
|
component SunDisplay inherits Rectangle {
|
|
in property <float> tick;
|
|
background: transparent;
|
|
|
|
// Pulsing outer ring 1
|
|
Rectangle {
|
|
width: parent.width * 0.92; height: parent.height * 0.92;
|
|
x: parent.width * 0.04; y: parent.height * 0.04;
|
|
border-radius: self.width / 2;
|
|
background: transparent;
|
|
border-width: 1.5px; border-color: #FFD70022;
|
|
opacity: 0.4 + Math.sin(tick * 1.5rad) * 0.4;
|
|
}
|
|
|
|
// Pulsing outer ring 2 (counter-phase)
|
|
Rectangle {
|
|
width: parent.width * 0.82; height: parent.height * 0.82;
|
|
x: parent.width * 0.09; y: parent.height * 0.09;
|
|
border-radius: self.width / 2;
|
|
background: transparent;
|
|
border-width: 1.5px; border-color: #FFD70033;
|
|
opacity: 0.5 + Math.sin((tick * 2.0 + 1.0) * 1rad) * 0.35;
|
|
}
|
|
|
|
// Pulsing outer ring 3
|
|
Rectangle {
|
|
width: parent.width * 0.72; height: parent.height * 0.72;
|
|
x: parent.width * 0.14; y: parent.height * 0.14;
|
|
border-radius: self.width / 2;
|
|
background: transparent;
|
|
border-width: 1px; border-color: #FFD70044;
|
|
opacity: 0.4 + Math.sin((tick * 2.5 + 2.0) * 1rad) * 0.4;
|
|
}
|
|
|
|
// Warm glow halo
|
|
Rectangle {
|
|
width: parent.width * 0.65; height: parent.height * 0.65;
|
|
x: parent.width * 0.175; y: parent.height * 0.175;
|
|
border-radius: self.width / 2;
|
|
background: #FFD70018;
|
|
drop-shadow-blur: 15px; drop-shadow-color: #FFD70033;
|
|
opacity: 0.6 + Math.sin(tick * 1.8rad) * 0.3;
|
|
}
|
|
|
|
// Main circle
|
|
Rectangle {
|
|
width: parent.width * 0.50; height: parent.height * 0.50;
|
|
x: parent.width * 0.25; y: parent.height * 0.25;
|
|
border-radius: self.width / 2;
|
|
background: @radial-gradient(circle, #FFE066 0%, #FFD700 50%, #FFA500 100%);
|
|
drop-shadow-blur: 22px; drop-shadow-color: #FFD70088;
|
|
}
|
|
|
|
// Bright core
|
|
Rectangle {
|
|
width: parent.width * 0.22; height: parent.height * 0.22;
|
|
x: parent.width * 0.39; y: parent.height * 0.39;
|
|
border-radius: self.width / 2;
|
|
background: #FFF8E1;
|
|
opacity: 0.55 + Math.sin(tick * 4rad) * 0.3;
|
|
}
|
|
}
|
|
|
|
// ─── Cloud Display ───────────────────────────────────────────
|
|
component CloudDisplay inherits Rectangle {
|
|
in property <float> tick;
|
|
background: transparent;
|
|
property <length> bob: Math.sin(tick * 0.8rad) * 3px;
|
|
|
|
Rectangle {
|
|
x: parent.width * 0.10; y: parent.height * 0.45 + parent.bob;
|
|
width: parent.width * 0.80; height: parent.height * 0.42;
|
|
border-radius: self.height / 2;
|
|
background: @linear-gradient(180deg, #ffffff, #e0e0ee);
|
|
drop-shadow-blur: 12px; drop-shadow-color: #00000015;
|
|
}
|
|
Rectangle {
|
|
x: parent.width * 0.14; y: parent.height * 0.28 + parent.bob;
|
|
width: parent.width * 0.34; height: parent.height * 0.42;
|
|
border-radius: self.height / 2; background: #ffffff;
|
|
}
|
|
Rectangle {
|
|
x: parent.width * 0.30; y: parent.height * 0.08 + parent.bob;
|
|
width: parent.width * 0.40; height: parent.height * 0.52;
|
|
border-radius: self.height / 2; background: #ffffff;
|
|
}
|
|
Rectangle {
|
|
x: parent.width * 0.52; y: parent.height * 0.22 + parent.bob;
|
|
width: parent.width * 0.34; height: parent.height * 0.42;
|
|
border-radius: self.height / 2; background: #fafaff;
|
|
}
|
|
}
|
|
|
|
// ─── Rain Display ────────────────────────────────────────────
|
|
component RainDisplay inherits Rectangle {
|
|
in property <float> tick;
|
|
background: transparent;
|
|
property <length> bob: Math.sin(tick * 0.6rad) * 2px;
|
|
|
|
// Dark cloud
|
|
Rectangle {
|
|
x: parent.width * 0.10; y: parent.height * 0.10 + bob;
|
|
width: parent.width * 0.80; height: parent.height * 0.32;
|
|
border-radius: self.height / 2; background: #8899aa;
|
|
}
|
|
Rectangle {
|
|
x: parent.width * 0.20; y: parent.height * 0.0 + bob;
|
|
width: parent.width * 0.32; height: parent.height * 0.30;
|
|
border-radius: self.height / 2; background: #8899aa;
|
|
}
|
|
Rectangle {
|
|
x: parent.width * 0.42; y: parent.height * -0.04 + bob;
|
|
width: parent.width * 0.34; height: parent.height * 0.32;
|
|
border-radius: self.height / 2; background: #8899aa;
|
|
}
|
|
|
|
// Animated drops
|
|
Rectangle {
|
|
x: parent.width * 0.22; y: parent.height * 0.50;
|
|
width: 2px; height: 14px; border-radius: 1px; background: #aaddff;
|
|
opacity: Math.max(0.0, Math.sin((tick * 3.0) * 1rad)) * 0.7;
|
|
}
|
|
Rectangle {
|
|
x: parent.width * 0.38; y: parent.height * 0.55;
|
|
width: 2px; height: 12px; border-radius: 1px; background: #aaddff;
|
|
opacity: Math.max(0.0, Math.sin((tick * 3.2 + 1.5) * 1rad)) * 0.7;
|
|
}
|
|
Rectangle {
|
|
x: parent.width * 0.54; y: parent.height * 0.48;
|
|
width: 2px; height: 14px; border-radius: 1px; background: #aaddff;
|
|
opacity: Math.max(0.0, Math.sin((tick * 2.8 + 3.0) * 1rad)) * 0.7;
|
|
}
|
|
Rectangle {
|
|
x: parent.width * 0.70; y: parent.height * 0.53;
|
|
width: 2px; height: 12px; border-radius: 1px; background: #aaddff;
|
|
opacity: Math.max(0.0, Math.sin((tick * 3.5 + 2.0) * 1rad)) * 0.6;
|
|
}
|
|
Rectangle {
|
|
x: parent.width * 0.30; y: parent.height * 0.68;
|
|
width: 2px; height: 10px; border-radius: 1px; background: #aaddff;
|
|
opacity: Math.max(0.0, Math.sin((tick * 3.1 + 4.0) * 1rad)) * 0.5;
|
|
}
|
|
Rectangle {
|
|
x: parent.width * 0.62; y: parent.height * 0.70;
|
|
width: 2px; height: 10px; border-radius: 1px; background: #aaddff;
|
|
opacity: Math.max(0.0, Math.sin((tick * 2.9 + 5.5) * 1rad)) * 0.5;
|
|
}
|
|
}
|
|
|
|
// ═══ UI Components ══════════════════════════════════════════
|
|
|
|
// ─── Forecast Card (glassmorphism) ──────────────────────────
|
|
component ForecastCard inherits Rectangle {
|
|
in property <string> hour;
|
|
in property <int> temp;
|
|
in property <int> icon-type;
|
|
in property <bool> is-now: false;
|
|
in property <float> tick;
|
|
|
|
width: 64px;
|
|
height: 110px;
|
|
border-radius: 18px;
|
|
background: is-now ? #ffffff22 : #ffffff0c;
|
|
border-width: 1px;
|
|
border-color: is-now ? #ffffff30 : #ffffff0a;
|
|
drop-shadow-blur: is-now ? 12px : 0px;
|
|
drop-shadow-color: #ffffff11;
|
|
|
|
animate background { duration: 400ms; easing: ease-in-out; }
|
|
animate border-color { duration: 400ms; easing: ease-in-out; }
|
|
animate drop-shadow-blur { duration: 400ms; easing: ease-in-out; }
|
|
|
|
VerticalLayout {
|
|
alignment: center;
|
|
spacing: 6px;
|
|
padding-top: 10px;
|
|
padding-bottom: 10px;
|
|
padding-left: 6px;
|
|
padding-right: 6px;
|
|
|
|
Text {
|
|
text: hour;
|
|
color: is-now ? #ffffffee : #ffffff88;
|
|
font-size: 11px;
|
|
font-weight: is-now ? 600 : 400;
|
|
horizontal-alignment: center;
|
|
}
|
|
|
|
// Mini icon area
|
|
Rectangle {
|
|
height: 30px;
|
|
|
|
if icon-type == 0: Rectangle {
|
|
width: 20px; height: 20px;
|
|
x: (parent.width - self.width) / 2; y: 5px;
|
|
border-radius: 10px;
|
|
background: @radial-gradient(circle, #FFE066 0%, #FFA500 100%);
|
|
drop-shadow-blur: 6px; drop-shadow-color: #FFD70055;
|
|
}
|
|
|
|
if icon-type == 1: Rectangle {
|
|
x: (parent.width - 28px) / 2; y: 4px;
|
|
width: 28px; height: 18px; background: transparent;
|
|
|
|
Rectangle {
|
|
x: 2px; y: 8px; width: 24px; height: 10px;
|
|
border-radius: 5px; background: #ffffffbb;
|
|
}
|
|
Rectangle {
|
|
x: 6px; y: 2px; width: 14px; height: 12px;
|
|
border-radius: 6px; background: #ffffffbb;
|
|
}
|
|
}
|
|
|
|
if icon-type == 2: Rectangle {
|
|
x: (parent.width - 28px) / 2; y: 2px;
|
|
width: 28px; height: 24px; background: transparent;
|
|
|
|
Rectangle {
|
|
x: 2px; y: 4px; width: 24px; height: 10px;
|
|
border-radius: 5px; background: #8899aacc;
|
|
}
|
|
Rectangle {
|
|
x: 6px; y: 0px; width: 14px; height: 10px;
|
|
border-radius: 5px; background: #8899aacc;
|
|
}
|
|
Rectangle {
|
|
x: 8px; y: 16px; width: 1.5px; height: 6px;
|
|
border-radius: 0.75px; background: #aaddff88;
|
|
}
|
|
Rectangle {
|
|
x: 16px; y: 17px; width: 1.5px; height: 5px;
|
|
border-radius: 0.75px; background: #aaddff88;
|
|
}
|
|
}
|
|
}
|
|
|
|
Text {
|
|
text: "\{temp}°";
|
|
color: white;
|
|
font-size: 15px;
|
|
font-weight: 700;
|
|
horizontal-alignment: center;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ─── Info Pill (glassmorphism + accent) ─────────────────────
|
|
component InfoPill inherits Rectangle {
|
|
in property <string> label;
|
|
in property <string> value;
|
|
in property <color> accent: #66bbff;
|
|
|
|
horizontal-stretch: 1;
|
|
height: 72px;
|
|
border-radius: 18px;
|
|
background: #ffffff0c;
|
|
border-width: 1px;
|
|
border-color: #ffffff0a;
|
|
|
|
VerticalLayout {
|
|
alignment: center;
|
|
spacing: 4px;
|
|
padding: 10px;
|
|
|
|
// Accent dot
|
|
HorizontalLayout {
|
|
alignment: center;
|
|
Rectangle {
|
|
width: 6px; height: 6px;
|
|
border-radius: 3px;
|
|
background: accent;
|
|
drop-shadow-blur: 4px; drop-shadow-color: accent;
|
|
}
|
|
}
|
|
|
|
Text {
|
|
text: value;
|
|
color: white;
|
|
font-size: 18px;
|
|
font-weight: 600;
|
|
horizontal-alignment: center;
|
|
}
|
|
Text {
|
|
text: label;
|
|
color: #ffffff55;
|
|
font-size: 9px;
|
|
horizontal-alignment: center;
|
|
letter-spacing: 1.2px;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ─── Premium Slider ─────────────────────────────────────────
|
|
component PremiumSlider inherits Rectangle {
|
|
in-out property <float> value: 12;
|
|
in property <float> minimum: 0;
|
|
in property <float> maximum: 24;
|
|
|
|
height: 44px;
|
|
background: transparent;
|
|
|
|
property <float> ratio: Math.clamp((value - minimum) / (maximum - minimum), 0, 1);
|
|
|
|
// Track
|
|
Rectangle {
|
|
y: (parent.height - 4px) / 2;
|
|
width: parent.width; height: 4px;
|
|
border-radius: 2px;
|
|
background: #ffffff12;
|
|
}
|
|
|
|
// Fill
|
|
Rectangle {
|
|
y: (parent.height - 4px) / 2;
|
|
width: parent.width * ratio; height: 4px;
|
|
border-radius: 2px;
|
|
background: @linear-gradient(90deg, #ffffff20, #ffffff88);
|
|
}
|
|
|
|
// Thumb glow
|
|
Rectangle {
|
|
x: (parent.width - 28px) * ratio;
|
|
y: (parent.height - 28px) / 2;
|
|
width: 28px; height: 28px;
|
|
border-radius: 14px;
|
|
background: #ffffff08;
|
|
drop-shadow-blur: 16px; drop-shadow-color: #ffffff22;
|
|
}
|
|
|
|
// Thumb
|
|
Rectangle {
|
|
x: (parent.width - 22px) * ratio;
|
|
y: (parent.height - 22px) / 2;
|
|
width: 22px; height: 22px;
|
|
border-radius: 11px;
|
|
background: @radial-gradient(circle, #ffffff 0%, #e8e8f0 100%);
|
|
drop-shadow-blur: 8px; drop-shadow-color: #00000022;
|
|
|
|
Rectangle {
|
|
width: 6px; height: 6px;
|
|
x: 8px; y: 8px;
|
|
border-radius: 3px;
|
|
background: #888899;
|
|
}
|
|
}
|
|
|
|
// Touch
|
|
TouchArea {
|
|
clicked => {
|
|
root.value = root.minimum + (root.maximum - root.minimum) * Math.clamp(self.mouse-x / root.width, 0, 1);
|
|
}
|
|
moved => {
|
|
if self.pressed {
|
|
root.value = root.minimum + (root.maximum - root.minimum) * Math.clamp(self.mouse-x / root.width, 0, 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ─── Separator ──────────────────────────────────────────────
|
|
component Separator inherits HorizontalLayout {
|
|
alignment: center;
|
|
Rectangle {
|
|
width: 40px; height: 1px;
|
|
border-radius: 0.5px;
|
|
background: @linear-gradient(90deg, #ffffff00, #ffffff22, #ffffff00);
|
|
}
|
|
}
|
|
|
|
// ═════════════════════════════════════════════════════════════
|
|
// Main Window
|
|
// ═════════════════════════════════════════════════════════════
|
|
export component MainWindow inherits Window {
|
|
title: "Meteo Milano";
|
|
preferred-width: 420px;
|
|
preferred-height: 780px;
|
|
background: black;
|
|
|
|
in-out property <float> tick: 0;
|
|
in-out property <float> time-of-day: 14.0;
|
|
in-out property <color> sky-top: #1e90ff;
|
|
in-out property <color> sky-bottom: #87CEEB;
|
|
in-out property <int> current-temp: 22;
|
|
in-out property <string> weather-desc: "Parzialmente Nuvoloso";
|
|
in-out property <string> humidity: "65%";
|
|
in-out property <string> wind: "12 km/h";
|
|
in-out property <string> feels-like: "20°";
|
|
in-out property <int> weather-type: 1;
|
|
in-out property <float> sun-y-factor: 0.3;
|
|
in-out property <[HourData]> forecast: [];
|
|
in-out property <bool> show-rain: false;
|
|
|
|
callback open-settings;
|
|
|
|
// ── Background ──
|
|
Rectangle {
|
|
width: 100%; height: 100%;
|
|
background: @linear-gradient(180deg, sky-top, sky-bottom);
|
|
}
|
|
|
|
// ── Sky Sun ──
|
|
Rectangle {
|
|
property <length> s: 90px;
|
|
x: root.width * 0.70;
|
|
y: root.height * root.sun-y-factor;
|
|
width: s; height: s;
|
|
border-radius: s / 2;
|
|
background: @radial-gradient(circle, #FFD700 0%, #FF8C0060 60%, #FF450000 100%);
|
|
opacity: weather-type == 0 ? 0.95 : weather-type == 1 ? 0.35 : 0.0;
|
|
drop-shadow-blur: 40px + Math.sin(tick * 2rad) * 10px;
|
|
drop-shadow-color: #FFD70055;
|
|
animate opacity { duration: 600ms; easing: ease-in-out; }
|
|
|
|
Rectangle {
|
|
width: parent.width * 0.35; height: parent.height * 0.35;
|
|
x: parent.width * 0.325; y: parent.height * 0.325;
|
|
border-radius: self.width / 2;
|
|
background: white;
|
|
opacity: 0.35 + Math.sin(tick * 3rad) * 0.2;
|
|
}
|
|
}
|
|
|
|
// ── Moon ──
|
|
Rectangle {
|
|
property <length> m: 48px;
|
|
x: root.width * 0.76; y: root.height * 0.10;
|
|
width: m; height: m;
|
|
border-radius: m / 2;
|
|
background: #E8E8D0;
|
|
opacity: time-of-day < 5.5 || time-of-day > 20.5 ? 0.85 : 0.0;
|
|
drop-shadow-blur: 18px; drop-shadow-color: #E8E8D044;
|
|
animate opacity { duration: 800ms; easing: ease-in-out; }
|
|
|
|
Rectangle {
|
|
width: parent.width * 0.7; height: parent.height * 0.7;
|
|
x: parent.width * 0.38; y: parent.height * 0.08;
|
|
border-radius: self.width / 2;
|
|
background: sky-top;
|
|
}
|
|
}
|
|
|
|
// ── Floating clouds ──
|
|
CloudShape {
|
|
width: 150px; height: 75px;
|
|
x: root.width * 0.02 + Math.sin((tick * 0.2) * 1rad) * 25px;
|
|
y: root.height * 0.05;
|
|
tick: root.tick; speed: 0.3; phase: 0; base-opacity: 0.20;
|
|
}
|
|
CloudShape {
|
|
width: 115px; height: 58px;
|
|
x: root.width * 0.52 + Math.sin((tick * 0.15 + 2.0) * 1rad) * 20px;
|
|
y: root.height * 0.12;
|
|
tick: root.tick; speed: 0.25; phase: 2.0; base-opacity: 0.16;
|
|
}
|
|
CloudShape {
|
|
width: 100px; height: 50px;
|
|
x: root.width * 0.25 + Math.sin((tick * 0.18 + 4.5) * 1rad) * 30px;
|
|
y: root.height * 0.19;
|
|
tick: root.tick; speed: 0.35; phase: 4.5; base-opacity: 0.13;
|
|
}
|
|
|
|
// ── Rain ──
|
|
if show-rain: Rectangle {
|
|
width: 100%; height: 100%;
|
|
RainDrop { x: root.width * 0.06; y: root.height * 0.28; tick: root.tick; speed: 3.2; phase: 0.0; }
|
|
RainDrop { x: root.width * 0.13; y: root.height * 0.40; tick: root.tick; speed: 2.8; phase: 0.7; }
|
|
RainDrop { x: root.width * 0.20; y: root.height * 0.34; tick: root.tick; speed: 3.5; phase: 1.4; }
|
|
RainDrop { x: root.width * 0.28; y: root.height * 0.48; tick: root.tick; speed: 3.0; phase: 2.1; }
|
|
RainDrop { x: root.width * 0.35; y: root.height * 0.37; tick: root.tick; speed: 3.3; phase: 2.8; }
|
|
RainDrop { x: root.width * 0.42; y: root.height * 0.44; tick: root.tick; speed: 2.9; phase: 3.5; }
|
|
RainDrop { x: root.width * 0.50; y: root.height * 0.31; tick: root.tick; speed: 3.4; phase: 4.2; }
|
|
RainDrop { x: root.width * 0.57; y: root.height * 0.46; tick: root.tick; speed: 3.1; phase: 4.9; }
|
|
RainDrop { x: root.width * 0.64; y: root.height * 0.39; tick: root.tick; speed: 2.7; phase: 5.6; }
|
|
RainDrop { x: root.width * 0.72; y: root.height * 0.50; tick: root.tick; speed: 3.6; phase: 6.3; }
|
|
RainDrop { x: root.width * 0.80; y: root.height * 0.35; tick: root.tick; speed: 3.0; phase: 7.0; }
|
|
RainDrop { x: root.width * 0.88; y: root.height * 0.42; tick: root.tick; speed: 3.3; phase: 7.7; }
|
|
RainDrop { x: root.width * 0.09; y: root.height * 0.54; tick: root.tick; speed: 2.8; phase: 8.4; }
|
|
RainDrop { x: root.width * 0.38; y: root.height * 0.57; tick: root.tick; speed: 3.1; phase: 9.8; }
|
|
RainDrop { x: root.width * 0.68; y: root.height * 0.55; tick: root.tick; speed: 3.4; phase: 11.2; }
|
|
RainDrop { x: root.width * 0.84; y: root.height * 0.60; tick: root.tick; speed: 3.2; phase: 11.9; }
|
|
RainDrop { x: root.width * 0.17; y: root.height * 0.63; tick: root.tick; speed: 2.6; phase: 1.1; }
|
|
RainDrop { x: root.width * 0.46; y: root.height * 0.66; tick: root.tick; speed: 3.7; phase: 3.3; }
|
|
RainDrop { x: root.width * 0.76; y: root.height * 0.62; tick: root.tick; speed: 2.9; phase: 5.0; }
|
|
RainDrop { x: root.width * 0.93; y: root.height * 0.52; tick: root.tick; speed: 3.1; phase: 8.8; }
|
|
}
|
|
|
|
// ── Main content ──
|
|
VerticalLayout {
|
|
padding-top: 48px;
|
|
padding-bottom: 24px;
|
|
padding-left: 30px;
|
|
padding-right: 30px;
|
|
spacing: 2px;
|
|
|
|
// City name
|
|
Text {
|
|
text: "MILANO";
|
|
color: white;
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
letter-spacing: 6px;
|
|
horizontal-alignment: center;
|
|
}
|
|
|
|
Text {
|
|
text: "Sabato 31 Gennaio";
|
|
color: #ffffff77;
|
|
font-size: 12px;
|
|
font-weight: 400;
|
|
horizontal-alignment: center;
|
|
}
|
|
|
|
Rectangle { vertical-stretch: 1; }
|
|
|
|
// Central weather icon
|
|
HorizontalLayout {
|
|
alignment: center;
|
|
if weather-type == 0: SunDisplay {
|
|
width: 100px; height: 100px;
|
|
tick: root.tick;
|
|
}
|
|
if weather-type == 1: CloudDisplay {
|
|
width: 110px; height: 70px;
|
|
tick: root.tick;
|
|
}
|
|
if weather-type == 2: RainDisplay {
|
|
width: 110px; height: 80px;
|
|
tick: root.tick;
|
|
}
|
|
}
|
|
|
|
Rectangle { height: 8px; }
|
|
|
|
// Temperature
|
|
Text {
|
|
text: "\{current-temp}°";
|
|
color: white;
|
|
font-size: 92px;
|
|
font-weight: 200;
|
|
horizontal-alignment: center;
|
|
}
|
|
|
|
Text {
|
|
text: weather-desc;
|
|
color: #ffffffbb;
|
|
font-size: 16px;
|
|
font-weight: 400;
|
|
horizontal-alignment: center;
|
|
}
|
|
|
|
Rectangle { height: 16px; }
|
|
Separator { }
|
|
Rectangle { height: 16px; }
|
|
|
|
// Info pills
|
|
HorizontalLayout {
|
|
spacing: 10px;
|
|
InfoPill { label: "UMIDITA"; value: humidity; accent: #66bbff; }
|
|
InfoPill { label: "VENTO"; value: wind; accent: #88ddaa; }
|
|
InfoPill { label: "PERCEPITA"; value: feels-like; accent: #ffaa66; }
|
|
}
|
|
|
|
Rectangle { height: 20px; }
|
|
|
|
// Forecast section
|
|
Text {
|
|
text: "PREVISIONI ORARIE";
|
|
color: #ffffff55;
|
|
font-size: 10px;
|
|
font-weight: 500;
|
|
letter-spacing: 2px;
|
|
}
|
|
|
|
Rectangle { height: 8px; }
|
|
|
|
HorizontalLayout {
|
|
spacing: 7px;
|
|
for data in forecast: ForecastCard {
|
|
hour: data.hour;
|
|
temp: data.temp;
|
|
icon-type: data.icon-type;
|
|
is-now: data.is-now;
|
|
tick: root.tick;
|
|
}
|
|
}
|
|
|
|
Rectangle { vertical-stretch: 2; }
|
|
|
|
// Time control
|
|
Text {
|
|
text: "Scorri per cambiare l'ora";
|
|
color: #ffffff33;
|
|
font-size: 10px;
|
|
font-weight: 400;
|
|
horizontal-alignment: center;
|
|
}
|
|
|
|
Rectangle { height: 4px; }
|
|
|
|
PremiumSlider {
|
|
minimum: 0;
|
|
maximum: 23.9;
|
|
value <=> root.time-of-day;
|
|
}
|
|
|
|
HorizontalLayout {
|
|
Text { text: "00:00"; color: #ffffff33; font-size: 9px; font-weight: 400; }
|
|
Rectangle { horizontal-stretch: 1; }
|
|
Text { text: "12:00"; color: #ffffff33; font-size: 9px; font-weight: 400; horizontal-alignment: center; }
|
|
Rectangle { horizontal-stretch: 1; }
|
|
Text { text: "24:00"; color: #ffffff33; font-size: 9px; font-weight: 400; horizontal-alignment: right; }
|
|
}
|
|
}
|
|
|
|
// ── Settings button (top-right) ──
|
|
Rectangle {
|
|
x: root.width - 52px;
|
|
y: 12px;
|
|
width: 40px;
|
|
height: 40px;
|
|
border-radius: 20px;
|
|
background: #ffffff12;
|
|
border-width: 1px;
|
|
border-color: #ffffff1a;
|
|
|
|
// Gear icon (simplified)
|
|
Rectangle {
|
|
width: 20px;
|
|
height: 20px;
|
|
x: 10px;
|
|
y: 10px;
|
|
|
|
Rectangle {
|
|
width: 12px; height: 12px;
|
|
x: 4px; y: 4px;
|
|
border-radius: 6px;
|
|
background: transparent;
|
|
border-width: 2px;
|
|
border-color: #ffffffaa;
|
|
}
|
|
Rectangle {
|
|
width: 3px; height: 6px;
|
|
x: 8.5px; y: 0px;
|
|
border-radius: 1.5px;
|
|
background: #ffffffaa;
|
|
}
|
|
Rectangle {
|
|
width: 3px; height: 6px;
|
|
x: 8.5px; y: 14px;
|
|
border-radius: 1.5px;
|
|
background: #ffffffaa;
|
|
}
|
|
Rectangle {
|
|
width: 6px; height: 3px;
|
|
x: 0px; y: 8.5px;
|
|
border-radius: 1.5px;
|
|
background: #ffffffaa;
|
|
}
|
|
Rectangle {
|
|
width: 6px; height: 3px;
|
|
x: 14px; y: 8.5px;
|
|
border-radius: 1.5px;
|
|
background: #ffffffaa;
|
|
}
|
|
}
|
|
|
|
TouchArea {
|
|
clicked => {
|
|
root.open-settings();
|
|
}
|
|
}
|
|
}
|
|
}
|