[gnome-weather] Add a new Thermometer widget



commit 091f298c262dd59645feec5a25f03d81dab75f6e
Author: Vitaly Dyachkov <obyknovenius me com>
Date:   Wed Oct 27 17:07:58 2021 +0200

    Add a new Thermometer widget

 src/app/thermometer.js                     | 190 +++++++++++++++++++++++++++++
 src/org.gnome.Weather.src.gresource.xml.in |   1 +
 2 files changed, 191 insertions(+)
---
diff --git a/src/app/thermometer.js b/src/app/thermometer.js
new file mode 100644
index 0000000..725c528
--- /dev/null
+++ b/src/app/thermometer.js
@@ -0,0 +1,190 @@
+/* thermometer.js
+ *
+ * Copyright 2021 Vitaly Dyachkov <obyknovenius me com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+const GObject = imports.gi.GObject;
+const Gdk = imports.gi.Gdk;
+const Gtk = imports.gi.Gtk;
+const Pango = imports.gi.Pango;
+const Cairo = imports.cairo;
+
+const Thermometer = GObject.registerClass({
+  Properties: {
+    'adjustment': GObject.ParamSpec.object(
+      'adjustment',
+      'Adjustment',
+      'The GtkAdjustment that contains the current value of this thermometer object',
+      GObject.ParamFlags.READWRITE,
+      Gtk.Adjustment,
+    ),
+  },
+  CssName: 'thermometer',
+},class Thermometer extends Gtk.DrawingArea {
+
+  _init(params) {
+    super._init(params);
+
+    const styleContext = this.get_style_context();
+
+    const createStyleContext = (selector) => {
+      const path = styleContext.get_path().copy();
+
+      const pos = path.append_type(GObject.TYPE_NONE);
+      path.iter_set_object_name(pos, selector);
+
+      const context = Gtk.StyleContext.new();
+      context.set_parent(styleContext);
+      context.set_path(path);
+
+      return context;
+    }
+
+    this._highContext = createStyleContext('high');
+    this._lowContext = createStyleContext('low');
+
+    this._radius = 12;
+    this._margin = 12;
+  }
+
+  get adjustment() {
+    return this._adjustment;
+  }
+
+  set adjustment(adjustment) {
+    this._adjustment = adjustment;
+
+    this._updatePangoLayouts(adjustment);
+  }
+
+  vfunc_get_preferred_width() {
+    const [ highWidth ] = this._highLayout.get_pixel_size();
+    const [ lowWidth ] = this._lowLayout.get_pixel_size();
+
+    const width = Math.max(this._radius, highWidth, lowWidth);
+    return [ width, width ];
+  }
+
+  vfunc_get_preferred_height() {
+    const [ , highHeight ] = this._highLayout.get_pixel_size();
+    const [ , lowHeight ] = this._lowLayout.get_pixel_size();
+
+    const height = highHeight + this._maring + lowHeight;
+    return [ height, height ];
+  }
+
+  _updatePangoLayouts(adjustment) {
+    const value = adjustment.get_value();
+    const pageSize = adjustment.get_page_size();
+
+    const highLabel = Math.round(value + pageSize) + "°";
+    this._highLayout = this._createPangoLayout(this._highContext, highLabel);
+
+    const lowLabel = Math.round(value) + "°";
+    this._lowLayout = this._createPangoLayout(this._lowContext, lowLabel);
+  }
+
+  _createPangoLayout(styleContext, text) {
+    const context = this._createPangoContext(styleContext);
+    const layout = Pango.Layout.new(context);
+
+    layout.set_text(text, -1);
+
+    return layout;
+  }
+
+  _createPangoContext(styleContext) {
+    const display = this.get_display();
+    const context = Gdk.pango_context_get_for_display(display);
+
+    const font = styleContext.get_property('font', styleContext.get_state());
+    context.set_font_description (font);
+
+    return context;
+  }
+
+  vfunc_draw(cr) {
+    const lower = this._adjustment.get_lower();
+    const upper = this._adjustment.get_upper();
+    const value = this._adjustment.get_value();
+    const pageSize = this._adjustment.get_page_size();
+
+    const width = this.get_allocated_width();
+    const height = this.get_allocated_height();
+
+    const [ highWidth, highHeight ] = this._highLayout.get_pixel_size();
+    const [ lowWidth, lowHeight ] = this._lowLayout.get_pixel_size();
+
+    const radius = this._radius;
+    const margin = this._margin;
+
+    const maxScaleHeight = height - highHeight - lowHeight - 2 * radius - 2 * margin;
+
+    const factor = maxScaleHeight / (upper - lower);
+    const scaleY = highHeight + radius + margin + (upper - value - pageSize) * factor;
+    const scaleHeight = pageSize * factor;
+
+    let highY = 0;
+    let lowY = height - lowHeight;
+
+    cr.save();
+
+    if (maxScaleHeight > 0) {
+      this._renderScale(cr, width / 2 - radius, scaleY, radius, scaleHeight);
+
+      highY = scaleY - radius - margin - highHeight;
+      lowY = scaleY + scaleHeight + radius + margin;
+    }
+
+    Gtk.render_layout(this._highContext, cr,
+                      width / 2 - highWidth / 2, highY,
+                      this._highLayout);
+
+    Gtk.render_layout(this._lowContext, cr,
+                      width / 2 - lowWidth / 2, lowY,
+                      this._lowLayout);
+
+    cr.restore();
+
+    return false;
+  }
+
+  _renderScale(cr, x, y, radius, height) {
+    const gradient = this._createGradient(y - radius, y + height + radius);
+    cr.setSource(gradient);
+
+    cr.newSubPath();
+    cr.arc(x + radius, y, radius, Math.PI, 0);
+    cr.arc(x + radius, y + height, radius, 0, Math.PI);
+    cr.closePath();
+    cr.fill();
+  }
+
+  _createGradient(start, end) {
+    const pattern = new Cairo.LinearGradient(0, start, 0, end);
+
+    const highColor = this._highContext.get_color(this._highContext.get_state());
+    pattern.addColorStopRGB(0.0, 246/255, 211/255, 45/255);
+
+    const lowColor = this._lowContext.get_color(this._lowContext.get_state());
+    pattern.addColorStopRGB(1.0, 28/255, 113/255, 216/255);
+
+    return pattern;
+  }
+
+});
diff --git a/src/org.gnome.Weather.src.gresource.xml.in b/src/org.gnome.Weather.src.gresource.xml.in
index 17b7432..9613401 100644
--- a/src/org.gnome.Weather.src.gresource.xml.in
+++ b/src/org.gnome.Weather.src.gresource.xml.in
@@ -4,6 +4,7 @@
     <file>app/city.js</file>
     <file>app/currentLocationController.js</file>
     <file>app/hourlyForecast.js</file>
+    <file>app/thermometer.js</file>
     <file>app/dailyForecast.js</file>
     <file>app/main.js</file>
     <file>app/window.js</file>


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]