[gnome-shell/wip/fmuellner/calendar-refresh: 2/12] dateMenu: Add Weather section



commit acd50d619d7ba28cfa51d34231c539a2e98235bd
Author: Florian Müllner <fmuellner gnome org>
Date:   Fri Feb 24 13:15:39 2017 +0100

    dateMenu: Add Weather section
    
    Similar to the Clocks integration we've had in the date+time drop-down for
    a while, the designs have called for a similar section that integrates
    GNOME weather as well. Use the WeatherClient added in the previous commit
    to implement that section and add it to the popover.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=754031

 data/theme/gnome-shell-high-contrast.css |    8 ++
 data/theme/gnome-shell.css               |    8 ++
 js/ui/dateMenu.js                        |  131 ++++++++++++++++++++++++++++++
 3 files changed, 147 insertions(+), 0 deletions(-)
---
diff --git a/data/theme/gnome-shell-high-contrast.css b/data/theme/gnome-shell-high-contrast.css
index be92d53..33c95cc 100644
--- a/data/theme/gnome-shell-high-contrast.css
+++ b/data/theme/gnome-shell-high-contrast.css
@@ -736,6 +736,7 @@ StScrollBar {
 
 .datemenu-today-button,
 .world-clocks-button,
+.weather-button,
 .message-list-section-title {
   border-radius: 4px;
   padding: .4em; }
@@ -749,11 +750,14 @@ StScrollBar {
 .datemenu-today-button:hover, .datemenu-today-button:focus,
 .world-clocks-button:hover,
 .world-clocks-button:focus,
+.weather-button:hover,
+.weather-button:focus,
 .message-list-section-title:hover,
 .message-list-section-title:focus {
   background-color: #0d0d0d; }
 .datemenu-today-button:active,
 .world-clocks-button:active,
+.weather-button:active,
 .message-list-section-title:active {
   color: white;
   background-color: #215d9c; }
@@ -762,6 +766,7 @@ StScrollBar {
   font-size: 1.5em; }
 
 .world-clocks-header,
+.weather-header,
 .message-list-section-title {
   color: #999999;
   font-weight: bold; }
@@ -769,6 +774,9 @@ StScrollBar {
 .world-clocks-grid {
   spacing-rows: 0.4em; }
 
+.weather-box {
+  spacing: 0.4em; }
+
 .calendar-month-label {
   color: #f2f2f2;
   font-weight: bold;
diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css
index f6d2f67..88ec11b 100644
--- a/data/theme/gnome-shell.css
+++ b/data/theme/gnome-shell.css
@@ -736,6 +736,7 @@ StScrollBar {
 
 .datemenu-today-button,
 .world-clocks-button,
+.weather-button,
 .message-list-section-title {
   border-radius: 4px;
   padding: .4em; }
@@ -749,11 +750,14 @@ StScrollBar {
 .datemenu-today-button:hover, .datemenu-today-button:focus,
 .world-clocks-button:hover,
 .world-clocks-button:focus,
+.weather-button:hover,
+.weather-button:focus,
 .message-list-section-title:hover,
 .message-list-section-title:focus {
   background-color: #454c4c; }
 .datemenu-today-button:active,
 .world-clocks-button:active,
+.weather-button:active,
 .message-list-section-title:active {
   color: white;
   background-color: #215d9c; }
@@ -762,6 +766,7 @@ StScrollBar {
   font-size: 1.5em; }
 
 .world-clocks-header,
+.weather-header,
 .message-list-section-title {
   color: #8e8e80;
   font-weight: bold; }
@@ -769,6 +774,9 @@ StScrollBar {
 .world-clocks-grid {
   spacing-rows: 0.4em; }
 
+.weather-box {
+  spacing: 0.4em; }
+
 .calendar-month-label {
   color: #e2e2df;
   font-weight: bold;
diff --git a/js/ui/dateMenu.js b/js/ui/dateMenu.js
index 9e49e2d..5380672 100644
--- a/js/ui/dateMenu.js
+++ b/js/ui/dateMenu.js
@@ -8,6 +8,7 @@ const Gtk = imports.gi.Gtk;
 const GWeather = imports.gi.GWeather;
 const Lang = imports.lang;
 const Mainloop = imports.mainloop;
+const Pango = imports.gi.Pango;
 const Cairo = imports.cairo;
 const Clutter = imports.gi.Clutter;
 const Shell = imports.gi.Shell;
@@ -20,6 +21,7 @@ const Main = imports.ui.main;
 const PanelMenu = imports.ui.panelMenu;
 const PopupMenu = imports.ui.popupMenu;
 const Calendar = imports.ui.calendar;
+const Weather = imports.misc.weather;
 
 function _isToday(date) {
     let now = new Date();
@@ -192,6 +194,133 @@ const WorldClocksSection = new Lang.Class({
     }
 });
 
+const WeatherSection = new Lang.Class({
+    Name: 'WeatherSection',
+
+    _init: function() {
+        this._weatherClient = new Weather.WeatherClient();
+
+        this.actor = new St.Button({ style_class: 'weather-button',
+                                     x_fill: true,
+                                     can_focus: true });
+        this.actor.connect('clicked', () => {
+            this._weatherClient.activateApp();
+
+            Main.overview.hide();
+            Main.panel.closeCalendar();
+        });
+        this.actor.connect('notify::mapped', () => {
+            if (this.actor.mapped)
+                this._weatherClient.update();
+        });
+
+        let box = new St.BoxLayout({ style_class: 'weather-box',
+                                      vertical: true });
+
+        this.actor.child = box;
+
+        box.add_child(new St.Label({ style_class: 'weather-header',
+                                     x_align: Clutter.ActorAlign.START,
+                                     text: _("Weather") }));
+
+        this._conditionsLabel = new St.Label({ style_class: 'weather-conditions',
+                                               x_align: Clutter.ActorAlign.START });
+        this._conditionsLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
+        this._conditionsLabel.clutter_text.line_wrap = true;
+        box.add_child(this._conditionsLabel);
+
+        this._weatherClient.connect('changed', Lang.bind(this, this._sync));
+        this._sync();
+    },
+
+    _getSummary: function(info) {
+        let summary = info.get_conditions();
+        if (summary == '-')
+            return info.get_sky();
+        return summary;
+    },
+
+    _sameSummary: function(info1, info2) {
+        let [ok1, phenom1, qualifier1] = info1.get_value_conditions();
+        let [ok2, phenom2, qualifier2] = info2.get_value_conditions();
+        if (ok1 || ok2)
+            return ok1 == ok2 && phenom1 == phenom2 && qualifier1 == qualifier2;
+
+        let [, sky1] = info1.get_value_sky();
+        let [, sky2] = info2.get_value_sky();
+        return sky1 == sky2;
+    },
+
+    _getSummaryText: function() {
+        let info = this._weatherClient.info;
+        let forecasts = info.get_forecast_list();
+        if (forecasts.length == 0) // No forecasts, just current conditions
+            return '%s.'.format(this._getSummary(info));
+
+        let current = info;
+        let summaries = [this._getSummary(info)];
+        for (let i = 0; i < forecasts.length; i++) {
+            let [ok, timestamp] = forecasts[i].get_value_update();
+            if (!_isToday(new Date(timestamp * 1000)))
+                continue; // Ignore forecasts from other days
+
+            if (this._sameSummary(current, forecasts[i]))
+                continue; // Ignore consecutive runs of equal summaries
+
+            current = forecasts[i];
+            if (summaries.push(this._getSummary(current)) == 3)
+                break; // Use a maximum of three summaries
+        }
+
+        let fmt;
+        switch(summaries.length) {
+            /* Translators: %s is a weather condition like "Clear sky"; see
+               libgweather for the possible condition strings. If at all
+               possible, the sentence should match the grammatical case etc. of
+               the inserted conditions. */
+            case 1: fmt = _("%s all day."); break;
+
+            /* Translators: %s is a weather condition like "Clear sky"; see
+               libgweather for the possible condition strings. If at all
+               possible, the sentence should match the grammatical case etc. of
+               the inserted conditions. */
+            case 2: fmt = _("%s, then %s later."); break;
+
+            /* Translators: %s is a weather condition like "Clear sky"; see
+               libgweather for the possible condition strings. If at all
+               possible, the sentence should match the grammatical case etc. of
+               the inserted conditions. */
+            case 3: fmt = _("%s, then %s, followed by %s later."); break;
+        }
+        return String.prototype.format.apply(fmt, summaries);
+    },
+
+    _getLabelText: function() {
+        if (this._weatherClient.loading)
+            return _("Loading…");
+
+        let info = this._weatherClient.info;
+        if (info.is_valid())
+            return this._getSummaryText() + ' ' +
+                   /* Translators: %s is a temperature with unit, e.g. "23℃" */
+                   _("Feels like %s.").format(info.get_apparent());
+
+        if (info.network_error())
+            return _("Go online for weather information");
+
+        return _("Weather information is currently unavailable");
+    },
+
+    _sync: function() {
+        this.actor.visible = this._weatherClient.available;
+
+        if (!this.actor.visible)
+            return;
+
+        this._conditionsLabel.text = this._getLabelText();
+    }
+});
+
 const MessagesIndicator = new Lang.Class({
     Name: 'MessagesIndicator',
 
@@ -394,6 +523,8 @@ const DateMenuButton = new Lang.Class({
         this._clocksItem = new WorldClocksSection();
         displaysBox.add(this._clocksItem.actor, { x_fill: true });
 
+        this._weatherItem = new WeatherSection();
+        displaysBox.add(this._weatherItem.actor, { x_fill: true });
 
         // Done with hbox for calendar and event list
 


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