[gnome-clocks] Redesign the Add World Clock dialog
- From: Zander Brown <zbrown src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-clocks] Redesign the Add World Clock dialog
- Date: Sat, 11 Jul 2020 15:26:36 +0000 (UTC)
commit a912edfb7be9cc91239777aa9fcaaad9823bf023
Author: matyas5 <hronekmatyas gmail com>
Date: Tue Jul 7 21:44:11 2020 +0200
Redesign the Add World Clock dialog
Fixes #71
data/gnome-clocks.gresource.xml | 1 +
data/ui/world-location-dialog-row.ui | 108 ++++++++++++++++++++++++
data/ui/world-location-dialog.ui | 154 ++++++++++++++++++++++++++---------
po/POTFILES.in | 1 +
src/meson.build | 1 +
src/utils.vala | 31 +++++++
src/world-face.vala | 8 +-
src/world-location-dialog-row.vala | 77 ++++++++++++++++++
src/world-location-dialog.vala | 139 ++++++++++++++++++++++++++-----
src/world-row.vala | 28 +------
10 files changed, 461 insertions(+), 87 deletions(-)
---
diff --git a/data/gnome-clocks.gresource.xml b/data/gnome-clocks.gresource.xml
index 9b73df8..1538975 100644
--- a/data/gnome-clocks.gresource.xml
+++ b/data/gnome-clocks.gresource.xml
@@ -18,6 +18,7 @@
<file preprocess="xml-stripblanks">ui/window.ui</file>
<file preprocess="xml-stripblanks">ui/world-face.ui</file>
<file preprocess="xml-stripblanks">ui/world-location-dialog.ui</file>
+ <file preprocess="xml-stripblanks">ui/world-location-dialog-row.ui</file>
<file preprocess="xml-stripblanks">ui/world-row.ui</file>
<file preprocess="xml-stripblanks">ui/world-standalone.ui</file>
</gresource>
diff --git a/data/ui/world-location-dialog-row.ui b/data/ui/world-location-dialog-row.ui
new file mode 100644
index 0000000..757a30c
--- /dev/null
+++ b/data/ui/world-location-dialog-row.ui
@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.36.0 -->
+<interface>
+ <requires lib="gtk+" version="3.20"/>
+ <template class="ClocksWorldAddClockRow" parent="GtkListBoxRow">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="selectable">False</property>
+ <child>
+ <object class="GtkGrid">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">start</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="hexpand">True</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="desc">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label">label</property>
+ <property name="ellipsize">end</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <style>
+ <class name="dim-label"/>
+ <class name="clock-desc"/>
+ </style>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">10</property>
+ <child>
+ <object class="GtkLabel" id="name_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label">label</property>
+ <property name="ellipsize">end</property>
+ <property name="xalign">0</property>
+ <property name="yalign">1</property>
+ <style>
+ <class name="clock-title"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="country_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">center</property>
+ <property name="label">label</property>
+ <style>
+ <class name="caption-heading"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkImage" id="checkmark">
+ <property name="can_focus">False</property>
+ <property name="icon_name">object-select-symbolic</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">0</property>
+ <property name="height">2</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ <style>
+ <class name="clock"/>
+ </style>
+ </template>
+</interface>
diff --git a/data/ui/world-location-dialog.ui b/data/ui/world-location-dialog.ui
index 3c69861..5f0e869 100644
--- a/data/ui/world-location-dialog.ui
+++ b/data/ui/world-location-dialog.ui
@@ -1,11 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.36.0 -->
<interface>
- <!-- interface-requires libgweather 3.0 -->
- <!-- interface-requires gtk+ 3.12 -->
+ <requires lib="gtk+" version="3.22"/>
+ <requires lib="libhandy" version="0.0"/>
<template class="ClocksWorldLocationDialog" parent="GtkDialog">
<property name="can_focus">False</property>
+ <property name="title" translatable="yes">Add World Clock</property>
<property name="modal">True</property>
<property name="window_position">center-on-parent</property>
+ <property name="default_width">400</property>
+ <property name="default_height">540</property>
<property name="destroy_with_parent">True</property>
<property name="type_hint">dialog</property>
<property name="gravity">center</property>
@@ -32,7 +36,7 @@
</packing>
</child>
<child>
- <object class="GtkButton" id="button2">
+ <object class="GtkButton" id="button_add">
<property name="label" translatable="yes">_Add</property>
<property name="visible">True</property>
<property name="sensitive">False</property>
@@ -58,48 +62,124 @@
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">2</property>
- <property name="valign">center</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox">
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
<child>
- <object class="GtkGrid" id="location_dialog_content">
+ <object class="GtkScrolledWindow">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="border_width">6</property>
- <property name="row_spacing">6</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="height_request">250</property>
<child>
- <object class="GtkLabel" id="label2">
+ <object class="HdyClamp">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="xalign">0</property>
- <property name="label" translatable="yes">Search for a city:</property>
- </object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">0</property>
- <property name="width">1</property>
- <property name="height">1</property>
- </packing>
- </child>
- <child>
- <object class="GWeatherLocationEntry" id="location_entry">
- <property name="show-named-timezones">True</property>
- <property name="visible">True</property>
- <property name="hexpand">True</property>
- <property name="can_focus">True</property>
- <property name="activates_default">True</property>
- <signal name="activate" handler="location_changed" object="ClocksWorldLocationDialog"
swapped="no"/>
- <signal name="changed" handler="location_changed" object="ClocksWorldLocationDialog"
swapped="no"/>
- <signal name="icon-release" handler="icon_released" object="ClocksWorldLocationDialog"
swapped="no"/>
+ <property name="width_request">300</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkSearchEntry" id="location_entry">
+ <property name="visible">True</property>
+ <property name="margin_top">18</property>
+ <property name="margin_start">50</property>
+ <property name="margin_end">50</property>
+ <property name="can_focus">True</property>
+ <signal name="search-changed" handler="on_search_changed" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkStack" id="stack">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="transition_duration">100</property>
+ <property name="transition_type">crossfade</property>
+ <child>
+ <object class="GtkBox" id="empty_search_box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">center</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="pixel_size">128</property>
+ <property name="icon_name">edit-find-symbolic</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Search for a city</property>
+ <style>
+ <class name="dim-label"/>
+ <class name="title-4"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBox" id="listbox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">start</property>
+ <property name="selection_mode">none</property>
+ <style>
+ <class name="clocks-list"/>
+ <class name="preferences"/>
+ </style>
+ <signal name="row-activated" handler="item_activated" swapped="no"/>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
</object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">1</property>
- <property name="width">1</property>
- <property name="height">1</property>
- </packing>
</child>
</object>
<packing>
- <property name="expand">False</property>
+ <property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
@@ -108,7 +188,7 @@
</child>
<action-widgets>
<action-widget response="0">button1</action-widget>
- <action-widget response="1">button2</action-widget>
+ <action-widget response="1">button_add</action-widget>
</action-widgets>
</template>
</interface>
diff --git a/po/POTFILES.in b/po/POTFILES.in
index b78700c..ba47283 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -18,6 +18,7 @@ data/ui/timer-setup.ui
data/ui/window.ui
data/ui/world-face.ui
data/ui/world-location-dialog.ui
+data/ui/world-location-dialog-row.ui
data/ui/world-standalone.ui
src/alarm-face.vala
src/alarm-item.vala
diff --git a/src/meson.build b/src/meson.build
index 0c5b2d2..3b33ab9 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -29,6 +29,7 @@ clocks_vala_sources = [
'world-face.vala',
'world-item.vala',
'world-location-dialog.vala',
+ 'world-location-dialog-row.vala',
'world-row.vala',
'world-shell-world-clocks.vala',
'world-standalone.vala',
diff --git a/src/utils.vala b/src/utils.vala
index 79c2e20..eebad84 100644
--- a/src/utils.vala
+++ b/src/utils.vala
@@ -60,6 +60,37 @@ public void time_to_hms (double t, out int h, out int m, out int s, out double r
remainder = t - s;
}
+public string get_time_difference_message (double offset) {
+ var diff = (double) offset / (double) TimeSpan.HOUR;
+ var diff_string = "%.0f".printf (diff.abs ());
+
+ if (diff != Math.round (diff)) {
+ if (diff * 2 != Math.round (diff * 2)) {
+ diff_string = "%.2f".printf (diff.abs ());
+ } else {
+ diff_string = "%.1f".printf (diff.abs ());
+ }
+ }
+
+ // Translators: The time is the same as the local time
+ var message = _("Current timezone");
+
+ if (diff > 0) {
+ // Translators: The (possibly fractical) number hours in the past
+ // (relative to local) the clock/location is
+ message = ngettext ("%s hour earlier",
+ "%s hours earlier",
+ ((int) diff).abs ()).printf (diff_string);
+ } else if (diff < 0) {
+ // Translators: The (possibly fractical) number hours in the
+ // future (relative to local) the clock/location is
+ message = ngettext ("%s hour later",
+ "%s hours later",
+ ((int) diff).abs ()).printf (diff_string);
+ }
+ return message;
+}
+
// TODO: For now we are wrapping Gnome's clock, but we should probably
// implement our own class, maybe using gnome-datetime-source
// Especially if we want to try to use CLOCK_REALTIME_ALARM
diff --git a/src/world-face.vala b/src/world-face.vala
index c84f822..30f8cba 100644
--- a/src/world-face.vala
+++ b/src/world-face.vala
@@ -148,11 +148,13 @@ public class Face : Gtk.Stack, Clocks.Clock {
public void activate_new () {
var dialog = new LocationDialog ((Gtk.Window) get_toplevel (), this);
- dialog.response.connect ((dialog, response) => {
+ dialog.response.connect ((_, response) => {
if (response == 1) {
- var location = ((LocationDialog) dialog).get_location ();
- add_location_item ((Item) location);
+ var location = dialog.get_selected_location ();
+ if (location != null)
+ add_location ((GWeather.Location) location);
}
+
dialog.destroy ();
});
dialog.show ();
diff --git a/src/world-location-dialog-row.vala b/src/world-location-dialog-row.vala
new file mode 100644
index 0000000..0a4cd2d
--- /dev/null
+++ b/src/world-location-dialog-row.vala
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2020 Matyáš Hronek <saytamkenorh seznam cz>
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+namespace Clocks {
+namespace World {
+
+[GtkTemplate (ui = "/org/gnome/clocks/ui/world-location-dialog-row.ui")]
+private class AddClockRow : Gtk.ListBoxRow {
+ public ClockLocation data { get; construct set; }
+
+ [GtkChild]
+ private Gtk.Label name_label;
+ [GtkChild]
+ private Gtk.Label country_label;
+ [GtkChild]
+ private Gtk.Label desc;
+ [GtkChild]
+ private Gtk.Widget checkmark;
+
+ public AddClockRow (ClockLocation data) {
+ Object (data: data);
+
+ name_label.label = data.location.get_name ();
+
+ var country_name = data.location.get_country_name ();
+ if (country_name != null) {
+ country_label.label = (string) country_name;
+ }
+
+ var wallclock = Utils.WallClock.get_default ();
+ var local_time = wallclock.date_time;
+ var weather_time_zone = data.location.get_timezone ();
+ if (weather_time_zone != null) {
+ var time_zone = new TimeZone (((GWeather.Timezone) weather_time_zone).get_tzid ());
+ var date_time = local_time.to_timezone (time_zone);
+ var local_offset = local_time.get_utc_offset () - date_time.get_utc_offset ();
+ var time_diff_message = Utils.get_time_difference_message (local_offset);
+ var time_zone_name = ((GWeather.Timezone) weather_time_zone).get_name ();
+
+ if ((string?) time_zone_name != null) {
+ desc.label = "%s • %s".printf (time_zone_name, time_diff_message);
+ } else {
+ desc.label = "%s".printf (time_diff_message);
+ }
+
+
+ } else {
+ desc.hide ();
+ }
+
+ sensitive = !data.selected;
+
+ data.notify["selected"].connect (on_selected_changed);
+ }
+
+ private void on_selected_changed () {
+ checkmark.visible = data.selected;
+ }
+}
+
+} // namespace World
+} // namespace Clocks
diff --git a/src/world-location-dialog.vala b/src/world-location-dialog.vala
index 44bdeef..63907ca 100644
--- a/src/world-location-dialog.vala
+++ b/src/world-location-dialog.vala
@@ -19,49 +19,148 @@
namespace Clocks {
namespace World {
+private class ClockLocation : Object {
+ public GWeather.Location location { get; construct set; }
+ public bool selected { get; set; }
+
+ public ClockLocation (GWeather.Location location, bool selected) {
+ Object (location: location, selected: selected);
+ }
+}
+
[GtkTemplate (ui = "/org/gnome/clocks/ui/world-location-dialog.ui")]
private class LocationDialog : Gtk.Dialog {
[GtkChild]
- private GWeather.LocationEntry location_entry;
+ private Gtk.Stack stack;
+ [GtkChild]
+ private Gtk.Box empty_search_box;
+ [GtkChild]
+ private Gtk.SearchEntry location_entry;
+ [GtkChild]
+ private Gtk.ListBox listbox;
+ [GtkChild]
+ private Gtk.Button button_add;
+
private Face world;
+ private ListStore locations;
+ private AddClockRow? _selected_row = null;
+ private AddClockRow? selected_row {
+ get {
+ return _selected_row;
+ } set {
+ _selected_row = value;
+ button_add.sensitive = _selected_row != null;
+ }
+ }
+
+ private const int RESULT_COUNT_LIMIT = 12;
public LocationDialog (Gtk.Window parent, Face world_face) {
Object (transient_for: parent, use_header_bar: 1);
+ key_press_event.connect ((event) => {
+ if (event.keyval == Gdk.Key.Tab)
+ return Gdk.EVENT_PROPAGATE;
+ return location_entry.handle_event (event);
+ });
+
world = world_face;
+
+ locations = new ListStore (typeof (ClockLocation));
+ listbox.bind_model (locations, (data) => {
+ return new AddClockRow ((ClockLocation) data);
+ });
+ }
+
+ public GWeather.Location? get_selected_location () {
+ if (selected_row == null)
+ return null;
+ return ((AddClockRow) selected_row).data.location;
}
[GtkCallback]
- private void icon_released () {
- if (location_entry.secondary_icon_name == "edit-clear-symbolic") {
- location_entry.set_text ("");
+ private void item_activated (Gtk.ListBoxRow listbox_row) {
+ var row = (AddClockRow) listbox_row;
+
+ if (selected_row != null && selected_row != row) {
+ ((AddClockRow) selected_row).data.selected = false;
+ }
+
+ row.data.selected = !row.data.selected;
+ if (row.data.selected) {
+ selected_row = row;
+ } else {
+ selected_row = null;
}
}
[GtkCallback]
- private void location_changed () {
- GWeather.Location? l = null;
- GWeather.Timezone? t = null;
+ private void on_search_changed () {
+ selected_row = null;
- if (location_entry.get_text () != "") {
- l = location_entry.get_location ();
+ // Remove old results
+ locations.remove_all ();
- if (l != null && !world.location_exists ((GWeather.Location) l)) {
- t = ((GWeather.Location) l).get_timezone ();
+ if (location_entry.text == "") {
+ stack.visible_child = empty_search_box;
+ return;
+ }
- if (t == null) {
- warning ("Timezone not defined for %s. This is a bug in libgweather database",
- (string) ((GWeather.Location) l).get_city_name ());
- }
- }
+ string search = location_entry.text.normalize ().casefold ();
+ var world_location = GWeather.Location.get_world ();
+ if (world_location == null) {
+ return;
}
- set_response_sensitive (1, l != null && t != null);
+ query_locations ((GWeather.Location) world_location, search);
+
+ if (locations.get_n_items () == 0) {
+ stack.visible_child = empty_search_box;
+ return;
+ }
+ locations.sort ((a, b) => {
+ var name_a = ((ClockLocation) a).location.get_sort_name ();
+ var name_b = ((ClockLocation) b).location.get_sort_name ();
+ return strcmp (name_a, name_b);
+ });
+
+ stack.visible_child = listbox;
}
- public Item? get_location () {
- var location = location_entry.get_location ();
- return location != null ? (Item?) new Item ((GWeather.Location) location) : null;
+ private void query_locations (GWeather.Location location, string search) {
+ if (locations.get_n_items () >= RESULT_COUNT_LIMIT) return;
+
+ if (location.get_level () == GWeather.LocationLevel.CITY) {
+ var contains_name = location.get_sort_name ().contains (search);
+
+ var country_name = location.get_country_name ();
+ if (country_name != null) {
+ country_name = ((string) country_name).normalize ().casefold ();
+ }
+ var contains_country_name = country_name != null && ((string) country_name).contains (search);
+
+ string? timezone_name = null;
+ var timezone = location.get_timezone ();
+ if (timezone != null) {
+ timezone_name = ((GWeather.Timezone) timezone).get_name ();
+ if (timezone_name != null) {
+ timezone_name = ((string) timezone_name).normalize ().casefold ();
+ }
+ }
+ var contains_timezone_name = timezone_name != null && ((string) timezone_name).contains (search);
+
+ if (contains_name || contains_country_name || contains_timezone_name) {
+ bool selected = world.location_exists (location);
+ locations.append (new ClockLocation (location, selected));
+ }
+ return;
+ }
+ foreach (var child in location.get_children ()) {
+ query_locations (child, search);
+ if (locations.get_n_items () >= RESULT_COUNT_LIMIT) {
+ return;
+ }
+ }
}
}
diff --git a/src/world-row.vala b/src/world-row.vala
index a8ceb45..c0ed307 100644
--- a/src/world-row.vala
+++ b/src/world-row.vala
@@ -56,33 +56,7 @@ private class Row : Gtk.ListBoxRow {
ctx.remove_class ("day");
ctx.add_class (location.state_class);
- var diff = ((double) location.local_offset / (double) TimeSpan.HOUR);
- var diff_string = "%.0f".printf (diff.abs ());
-
- if (diff != Math.round (diff)) {
- if (diff * 2 != Math.round (diff * 2)) {
- diff_string = "%.2f".printf (diff.abs ());
- } else {
- diff_string = "%.1f".printf (diff.abs ());
- }
- }
-
- // Translators: The time is the same as the local time
- var message = _("Current timezone");
-
- if (diff > 0) {
- // Translators: The (possibly fractical) number hours in the past
- // (relative to local) the clock/location is
- message = ngettext ("%s hour earlier",
- "%s hours earlier",
- ((int) diff).abs ()).printf (diff_string);
- } else if (diff < 0) {
- // Translators: The (possibly fractical) number hours in the
- // future (relative to local) the clock/location is
- message = ngettext ("%s hour later",
- "%s hours later",
- ((int) diff).abs ()).printf (diff_string);
- }
+ var message = Utils.get_time_difference_message ((double) location.local_offset);
if (location.day_label != null && location.day_label != "") {
desc.label = "%s • %s".printf ((string) location.day_label, message);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]