[gnome-shell/wip/fmuellner/weather-clocks-refresh: 46/48] dateMenu: Use graphical weather forecasts
- From: Florian Müllner <fmuellner src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-shell/wip/fmuellner/weather-clocks-refresh: 46/48] dateMenu: Use graphical weather forecasts
- Date: Tue, 5 Feb 2019 11:36:54 +0000 (UTC)
commit 5dedb97fcc1e1bf430b4d494275e03e5ef5dfa3d
Author: Florian Müllner <fmuellner gnome org>
Date: Thu Dec 13 14:45:22 2018 +0100
dateMenu: Use graphical weather forecasts
While the current textual forecast is non-intrusive, it may be too
much so, making it less effective to spot the current conditions
at a glance.
Refresh the section to use a more conventional graphical representation,
similar to the one used by gnome-weather itself.
https://gitlab.gnome.org/GNOME/gnome-shell/issues/262
data/theme/gnome-shell-sass/_common.scss | 16 +++-
js/ui/dateMenu.js | 147 +++++++++++++++----------------
2 files changed, 87 insertions(+), 76 deletions(-)
---
diff --git a/data/theme/gnome-shell-sass/_common.scss b/data/theme/gnome-shell-sass/_common.scss
index b7d052bcc..801792fa3 100644
--- a/data/theme/gnome-shell-sass/_common.scss
+++ b/data/theme/gnome-shell-sass/_common.scss
@@ -922,7 +922,8 @@ StScrollBar {
font-weight: bold;
}
- .world-clocks-grid {
+ .world-clocks-grid,
+ .weather-grid {
spacing-rows: 0.4em;
}
@@ -930,6 +931,19 @@ StScrollBar {
spacing: 0.4em;
}
+ .weather-grid {
+ spacing-columns: 0.8em;
+ }
+
+ .weather-forecast-icon {
+ icon-size: 2.18em;
+ }
+
+ .weather-forecast-time {
+ color: darken($fg_color,40%);
+ font-size: 0.8em;
+ }
+
.calendar-month-label {
color: darken($fg_color,5%);
font-weight: bold;
diff --git a/js/ui/dateMenu.js b/js/ui/dateMenu.js
index a04efb2f3..c87df8de2 100644
--- a/js/ui/dateMenu.js
+++ b/js/ui/dateMenu.js
@@ -22,6 +22,8 @@ const Calendar = imports.ui.calendar;
const Weather = imports.misc.weather;
const System = imports.system;
+const MAX_FORECASTS = 5;
+
function _isToday(date) {
let now = new Date();
return now.getYear() == date.getYear() &&
@@ -220,106 +222,101 @@ var WeatherSection = class WeatherSection {
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);
+ let layout = new Clutter.GridLayout({ orientation: Clutter.Orientation.VERTICAL });
+ this._forecastGrid = new St.Widget({ style_class: 'weather-grid',
+ layout_manager: layout });
+ layout.hookup_style(this._forecastGrid);
+ box.add_child(this._forecastGrid);
this._weatherClient.connect('changed', this._sync.bind(this));
this._sync();
}
- _getSummary(info, capitalize=false) {
- let options = capitalize ? GWeather.FormatOptions.SENTENCE_CAPITALIZATION
- : GWeather.FormatOptions.NO_CAPITALIZATION;
-
- let [ok, phenomenon, qualifier] = info.get_value_conditions();
- if (ok)
- return new GWeather.Conditions({ significant: true,
- phenomenon,
- qualifier }).to_string_full(options);
-
- let [, sky] = info.get_value_sky();
- return GWeather.Sky.to_string_full(sky, options);
- }
-
- _sameSummary(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() {
+ _getInfos() {
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, true));
let current = info;
let infos = [info];
for (let i = 0; i < forecasts.length; i++) {
let [ok, timestamp] = forecasts[i].get_value_update();
- if (!_isToday(new Date(timestamp * 1000)))
+ let datetime = new Date(timestamp * 1000);
+ if (!_isToday(datetime))
continue; // Ignore forecasts from other days
- if (this._sameSummary(current, forecasts[i]))
- continue; // Ignore consecutive runs of equal summaries
+ [ok, timestamp] = current.get_value_update();
+ let currenttime = new Date(timestamp * 1000);
+ if (currenttime.getHours() == datetime.getHours())
+ continue; // Enforce a minimum interval of 1h
current = forecasts[i];
- if (infos.push(current) == 3)
- break; // Use a maximum of three summaries
+ if (infos.push(current) == MAX_FORECASTS)
+ break; // Use a maximum of five forecasts
}
+ return infos;
+ }
- let fmt;
- switch(infos.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;
- }
- let summaries = infos.map((info, i) => {
- let capitalize = i == 0 && fmt.startsWith('%s');
- return this._getSummary(info, capitalize);
+ _addForecasts() {
+ let layout = this._forecastGrid.layout_manager;
+
+ let infos = this._getInfos();
+ if (this._forecastGrid.text_direction == Clutter.TextDirection.RTL)
+ infos.reverse();
+
+ let col = 0;
+ infos.forEach(fc => {
+ let [ok, timestamp] = fc.get_value_update();
+ let timeStr = Util.formatTime(new Date(timestamp * 1000), {
+ timeOnly: true
+ });
+
+ let icon = new St.Icon({ style_class: 'weather-forecast-icon',
+ icon_name: fc.get_symbolic_icon_name(),
+ x_align: Clutter.ActorAlign.CENTER,
+ x_expand: true });
+ let temp = new St.Label({ style_class: 'weather-forecast-temp',
+ text: fc.get_temp_summary(),
+ x_align: Clutter.ActorAlign.CENTER });
+ let time = new St.Label({ style_class: 'weather-forecast-time',
+ text: timeStr,
+ x_align: Clutter.ActorAlign.CENTER });
+
+ layout.attach(icon, col, 0, 1, 1);
+ layout.attach(temp, col, 1, 1, 1);
+ layout.attach(time, col, 2, 1, 1);
+ col++;
});
- return String.prototype.format.apply(fmt, summaries);
}
- _getLabelText() {
- if (!this._weatherClient.hasLocation)
- return _("Select a location…");
+ _setStatusLabel(text) {
+ let layout = this._forecastGrid.layout_manager;
+ let label = new St.Label({ text });
+ layout.attach(label, 0, 0, 1, 1);
+ }
+
+ _updateForecasts() {
+ this._forecastGrid.destroy_all_children();
+
+ if (!this._weatherClient.hasLocation) {
+ this._setStatusLabel(_("Select a location…"));
+ return;
+ }
- if (this._weatherClient.loading)
- return _("Loading…");
+ if (this._weatherClient.loading) {
+ this._setStatusLabel(_("Loading…"));
+ return;
+ }
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.is_valid()) {
+ this._addForecasts();
+ return;
+ }
if (info.network_error())
- return _("Go online for weather information");
-
- return _("Weather information is currently unavailable");
+ this._setStatusLabel(_("Go online for weather information"));
+ else
+ this._setStatusLabel(_("Weather information is currently unavailable"));
}
_sync() {
@@ -328,7 +325,7 @@ var WeatherSection = class WeatherSection {
if (!this.actor.visible)
return;
- this._conditionsLabel.text = this._getLabelText();
+ this._updateForecasts();
}
};
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]