[gnome-weather] ui: Make thermometer labels sticky to the scale
- From: Christopher Davis <christopherdavis src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-weather] ui: Make thermometer labels sticky to the scale
- Date: Thu, 7 Jul 2022 14:14:57 +0000 (UTC)
commit d438a23bf717a9296b6ad68dad327782d307dcbf
Author: Vitaly Dyachkov <obyknovenius me com>
Date: Sat Jun 18 16:06:10 2022 +0200
ui: Make thermometer labels sticky to the scale
src/app/thermometer.js | 300 +++++++++++++++++++----------
src/app/thermometer.ui | 27 ---
src/org.gnome.Weather.src.gresource.xml.in | 1 -
3 files changed, 195 insertions(+), 133 deletions(-)
---
diff --git a/src/app/thermometer.js b/src/app/thermometer.js
index 9605ed6..dca14af 100644
--- a/src/app/thermometer.js
+++ b/src/app/thermometer.js
@@ -1,6 +1,6 @@
/* thermometer.js
*
- * Copyright 2021 Vitaly Dyachkov <obyknovenius me com>
+ * Copyright 2021-2022 Vitaly Dyachkov <obyknovenius me com>
* Copyright 2022 Evan Welsh <contact evanwelsh com>
*
* This program is free software: you can redistribute it and/or modify
@@ -22,143 +22,233 @@
import GLib from 'gi://GLib';
import GObject from 'gi://GObject';
import Gtk from 'gi://Gtk';
+import Gdk from 'gi://Gdk';
import Graphene from 'gi://Graphene';
import Gsk from 'gi://Gsk';
import * as Util from '../misc/util.js';
export class TemperatureRange {
- dailyLow;
- dailyHigh;
- weeklyLow;
- weeklyHigh;
-
- constructor({ dailyLow, dailyHigh, weeklyLow, weeklyHigh }) {
- this.dailyLow = dailyLow;
- this.dailyHigh = dailyHigh;
- this.weeklyLow = weeklyLow;
- this.weeklyHigh = weeklyHigh;
- }
+
+ dailyLow;
+ dailyHigh;
+ weeklyLow;
+ weeklyHigh;
+
+ constructor({ dailyLow, dailyHigh, weeklyLow, weeklyHigh }) {
+ this.dailyLow = dailyLow;
+ this.dailyHigh = dailyHigh;
+ this.weeklyLow = weeklyLow;
+ this.weeklyHigh = weeklyHigh;
+ }
}
-GObject.registerClass({
- CssName: 'WeatherThermometerScale',
- Properties: {
- 'range': GObject.ParamSpec.jsobject(
- 'range',
- 'range',
- 'The TemperatureRange instance representing this thermometer scale',
- GObject.ParamFlags.READWRITE,
- ),
- },
+const ThermometerScale = GObject.registerClass({
+ CssName: 'WeatherThermometerScale',
+ Properties: {
+ 'range': GObject.ParamSpec.jsobject(
+ 'range',
+ 'range',
+ 'The TemperatureRange instance representing this thermometer scale',
+ GObject.ParamFlags.READWRITE,
+ ),
+ },
}, class ThermometerScale extends Gtk.Widget {
- constructor({ range = null, ...params }) {
- super({
- vexpand: true,
- halign: Gtk.Align.FILL,
- overflow: Gtk.Overflow.HIDDEN,
- ...params
- });
+ minHeight = 64;
+ radius = 12;
+
+ constructor({ range = null, ...params }) {
+ super(params);
- this.range = range;
- }
+ this.range = range;
+ }
- vfunc_map() {
- super.vfunc_map();
+ vfunc_map() {
+ super.vfunc_map();
- this._rangeChangedId = this.connect('notify::range', () => {
- this.queue_draw();
- });
- }
+ this._rangeChangedId = this.connect('notify::range', () => {
+ this.queue_draw();
+ });
+ }
- vfunc_unmap() {
- this.disconnect(this._rangeChangedId);
+ vfunc_unmap() {
+ this.disconnect(this._rangeChangedId);
- super.vfunc_unmap();
- }
+ super.vfunc_unmap();
+ }
- vfunc_snapshot(snapshot) {
- super.vfunc_snapshot(snapshot);
+ vfunc_snapshot(snapshot) {
+ super.vfunc_snapshot(snapshot);
- if (!this.range) return;
+ if (!this.range)
+ return;
- const { width, height } = this.get_allocation();
+ const { width, height } = this.get_allocation();
- // Don't render when allocation is shorter than 64
- if (height < 64) return;
+ // Don't render when allocation is shorter than the minimal height
+ if (height < this.minHeight)
+ return;
- const { dailyHigh, dailyLow, weeklyHigh, weeklyLow } = this.range;
+ const { dailyHigh, dailyLow, weeklyHigh, weeklyLow } = this.range;
- const scaleRadius = 12;
- const scaleFactor = (height - scaleRadius * 2) / (weeklyHigh - weeklyLow);
+ const radius = this.radius;
+ const factor = (height - 2 * radius) / (weeklyHigh - weeklyLow);
- const scaleWidth = 24;
- const scaleHeight = scaleFactor * (dailyHigh - dailyLow);
+ const gradientWidth = 2 * radius;
+ const gradientHeight = factor * (dailyHigh - dailyLow);
- const x = (width - scaleWidth) / 2;
- const y = scaleRadius + scaleFactor * (weeklyHigh - dailyHigh);
+ const x = (width - gradientWidth) / 2;
+ const y = radius + factor * (weeklyHigh - dailyHigh);
- const bounds = new Graphene.Rect();
- bounds.init(x, y - scaleRadius, scaleWidth, scaleHeight + 2 * scaleRadius);
+ const bounds = new Graphene.Rect();
+ bounds.init(x, y - radius, gradientWidth, gradientHeight + 2 * radius);
- const outline = new Gsk.RoundedRect();
- outline.init_from_rect(bounds, scaleRadius);
+ const outline = new Gsk.RoundedRect();
+ outline.init_from_rect(bounds, radius);
- snapshot.push_rounded_clip(outline);
+ snapshot.push_rounded_clip(outline);
- const [, warmColor] = this.get_style_context().lookup_color('weather_thermometer_warm_color');
- const [, coolColor] = this.get_style_context().lookup_color('weather_thermometer_cold_color');
+ const [, warmColor] = this.get_style_context()
+ .lookup_color('weather_thermometer_warm_color');
- snapshot.append_linear_gradient(
- bounds,
- new Graphene.Point({ x: x + scaleWidth / 2, y: 0 }),
- new Graphene.Point({ x: x + scaleWidth / 2, y: height }),
- [
- new Gsk.ColorStop({ offset: 0.0, color: warmColor }),
- new Gsk.ColorStop({ offset: 1.0, color: coolColor })
- ]
- );
+ const [, coolColor] = this.get_style_context()
+ .lookup_color('weather_thermometer_cold_color');
- snapshot.pop();
- }
+ snapshot.append_linear_gradient(
+ bounds,
+ new Graphene.Point({ x: x + gradientWidth / 2, y: 0 }),
+ new Graphene.Point({ x: x + gradientWidth / 2, y: height }),
+ [
+ new Gsk.ColorStop({ offset: 0.0, color: warmColor }),
+ new Gsk.ColorStop({ offset: 1.0, color: coolColor })
+ ]
+ );
+
+ snapshot.pop();
+ }
});
export const Thermometer = GObject.registerClass({
- CssName: 'WeatherThermometer',
- Template: GLib.Uri.resolve_relative(import.meta.url, './thermometer.ui', 0),
- InternalChildren: ['scale', 'highLabel', 'lowLabel'],
- Properties: {
- 'range': GObject.ParamSpec.jsobject(
- 'range',
- 'range',
- 'The TemperatureRange instance representing this thermometer scale',
- GObject.ParamFlags.READWRITE,
- ),
- },
+ CssName: 'WeatherThermometer',
+ Properties: {
+ 'range': GObject.ParamSpec.jsobject(
+ 'range',
+ 'range',
+ 'The TemperatureRange instance representing this thermometer scale',
+ GObject.ParamFlags.READWRITE,
+ ),
+ },
}, class Thermometer extends Gtk.Widget {
- constructor({ ...params }) {
- super(params);
-
- Object.assign(this.layoutManager, {
- orientation: Gtk.Orientation.VERTICAL,
- spacing: 20
- });
- }
-
- vfunc_root() {
- super.vfunc_root();
-
- this.bind_property('range', this._scale, 'range', GObject.BindingFlags.DEFAULT);
-
- this.bind_property_full('range', this._lowLabel, 'label', GObject.BindingFlags.DEFAULT, (_, range) => {
- return [!!range, Util.formatTemperature(range?.dailyLow) ?? ''];
- }, null);
- this.bind_property_full('range', this._highLabel, 'label', GObject.BindingFlags.DEFAULT, (_, range) => {
- return [!!range, Util.formatTemperature(range?.dailyHigh) ?? ''];
- }, null);
- }
+ #highLabel;
+ #lowLabel;
+ #scale;
+
+ spacing = 18;
+
+ constructor({ ...params }) {
+ super(params);
+
+ this.#highLabel = new Gtk.Label({
+ css_classes: ['high', 'body'],
+ });
+ this.#highLabel.set_parent(this);
+
+ this.#lowLabel = new Gtk.Label({
+ css_classes: ['low', 'body'],
+ });
+ this.#lowLabel.set_parent(this);
+
+ this.#scale = new ThermometerScale({});
+ this.#scale.set_parent(this);
+ }
+
+ vfunc_measure(orientation, for_size) {
+ const [highMin, highNat, highMinBaseline, highNatBaseline] =
+ this.#highLabel.measure(orientation, for_size);
+
+ const [lowMin, lowNat, lowMinBaseline, lowNatBaseline] =
+ this.#lowLabel.measure(orientation, for_size);
+
+ if (orientation === Gtk.Orientation.HORIZONTAL) {
+ return [
+ Math.max(highMin, lowMin),
+ Math.max(highNat, lowNat),
+ Math.max(highMinBaseline, lowMinBaseline),
+ Math.max(highNatBaseline, lowNatBaseline)
+ ];
+ } else {
+ const spacing = this.spacing;
+ return [
+ highMin + spacing + lowMin,
+ highNat + spacing + lowNat,
+ highMinBaseline + spacing + lowMinBaseline,
+ highNatBaseline + spacing + lowNatBaseline
+ ];
+ }
+ }
+
+ vfunc_size_allocate(width, height, baseline) {
+ const [highMin, highNat] = this.#highLabel.get_preferred_size();
+ const [lowMin, lowNat] = this.#lowLabel.get_preferred_size();
+
+ const spacing = this.spacing;
+
+ const scaleHeight = Math.max(
+ 0,
+ height - (highNat.height + lowNat.height) - 2 * spacing);
+
+ const scaleRect = new Gdk.Rectangle({
+ height: scaleHeight,
+ width,
+ x: 0,
+ y:highNat.height + spacing,
+ });
+ this.#scale.size_allocate(scaleRect, -1);
+
+ let highY = 0;
+ let lowY = height - lowNat.height;
+
+ if (scaleHeight >= this.#scale.minHeight) {
+ const { dailyHigh, dailyLow, weeklyHigh, weeklyLow } = this.range;
+
+ const radius = this.#scale.radius;
+ const factor = (scaleHeight - 2 * radius) / (weeklyHigh - weeklyLow);
+
+ highY += (weeklyHigh - dailyHigh) * factor;
+ lowY -= (dailyLow - weeklyLow) * factor;
+ }
+
+ const highRect = new Gdk.Rectangle({
+ height: highNat.height,
+ width: highNat.width,
+ x: (width - highNat.width) / 2,
+ y: highY,
+ });
+ this.#highLabel.size_allocate(highRect, -1);
+
+ const lowRect = new Gdk.Rectangle({
+ height: lowNat.height,
+ width: lowNat.width,
+ x: (width - lowNat.width) / 2,
+ y: lowY,
+ });
+ this.#lowLabel.size_allocate(lowRect, -1);
+ }
+
+ vfunc_root() {
+ super.vfunc_root();
+
+ this.bind_property('range', this.#scale,'range', GObject.BindingFlags.DEFAULT);
+
+ this.bind_property_full('range', this.#lowLabel, 'label', GObject.BindingFlags.DEFAULT, (_, range)
=> {
+ return [!!range, Util.formatTemperature(range?.dailyLow) ?? ''];
+ }, null);
+
+ this.bind_property_full('range', this.#highLabel, 'label', GObject.BindingFlags.DEFAULT, (_, range)
=> {
+ return [!!range, Util.formatTemperature(range?.dailyHigh) ?? ''];
+ }, null);
+ }
});
-Thermometer.set_layout_manager_type(Gtk.BoxLayout);
diff --git a/src/org.gnome.Weather.src.gresource.xml.in b/src/org.gnome.Weather.src.gresource.xml.in
index 1631802..2b2178e 100644
--- a/src/org.gnome.Weather.src.gresource.xml.in
+++ b/src/org.gnome.Weather.src.gresource.xml.in
@@ -8,7 +8,6 @@
<file>app/locationRow.js</file>
<file>app/locationRow.ui</file>
<file>app/thermometer.js</file>
- <file>app/thermometer.ui</file>
<file>app/dailyForecast.js</file>
<file>app/entry.js</file>
<file>app/main.js</file>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]