Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Timezone dropdown #106

Merged
merged 26 commits into from
Aug 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
- name: Install Dependencies
run: |
apt update
apt install -y libgranite-7-dev libswitchboard-3-dev libadwaita-1-dev meson valac
apt install -y libadwaita-1-dev libgranite-7-dev libical-dev libswitchboard-3-dev meson valac
- name: Build
env:
DESTDIR: out
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
# Date & Time Settings
[![Translation status](https://l10n.elementary.io/widgets/switchboard/-/datetime/svg-badge.svg)](https://l10n.elementary.io/engage/switchboard/?utm_source=widget)
[![Translation status](https://l10n.elementary.io/widgets/switchboard/-/switchboard-plug-datetime/svg-badge.svg)](https://l10n.elementary.io/engage/switchboard/?utm_source=widget)

![screenshot](data/screenshot.png?raw=true)

## Building and Installation

You'll need the following dependencies:

* libgranite-7-dev
* libadwaita-1-dev >= 1.4.0
* libgranite-7-dev
* libical-dev
* libswitchboard-3-dev
* meson
* valac
Expand Down
19 changes: 14 additions & 5 deletions data/datetime.metainfo.xml.in
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,19 @@
<update_contact>contact_at_elementary.io</update_contact>

<releases>
<release version="8.1.0" date="2024-08-06" urgency="medium">
<description>
<p>Minor updates</p>
<ul>
<li>Updated translations</li>
</ul>
</description>
<issues>
<issue url="https://github.com/elementary/switchboard-plug-datetime/issues/38">"Time Zone" popover is shown only the left portion in the first click</issue>
<issue url="https://github.com/elementary/switchboard-plug-datetime/issues/119">Incorrect TimeZoneGrid city view scroll position</issue>
</issues>
</release>

<release version="8.0.0" date="2024-07-26" urgency="medium">
<description>
<p>Minor updates</p>
Expand Down Expand Up @@ -69,10 +82,6 @@
</description>
</release>

<release version="2.1.7" date="2020-04-01" urgency="medium">
<description>
<p>Updated translations</p>
</description>
</release>
<release version="2.1.7" date="2020-04-01" urgency="medium" />
</releases>
</component>
Binary file modified data/screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ libdir = join_paths(prefix, get_option('libdir'))

add_project_arguments(
'-DGETTEXT_PACKAGE="@0@"'.format(gettext_name),
'-DLIBICAL_GLIB_UNSTABLE_API=1',
language:'c'
)

Expand Down
2 changes: 0 additions & 2 deletions src/MainView.vala
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,8 @@ public class DateTime.MainView : Switchboard.SettingsPage {
};

time_zone_picker = new DateTime.TimeZoneGrid () {
// hexpand = true,
margin_start = 6
};
time_zone_picker.add_css_class ("frame");
time_zone_picker.set_parent (manual_time_zone_radio);

var date_time_header = new Granite.HeaderLabel (_("Date & Time"));
Expand Down
256 changes: 127 additions & 129 deletions src/Widgets/TimeZoneGrid.vala
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*-
* Copyright (c) 2014 elementary, Inc. (https://elementary.io)
* Copyright 2014-2024 elementary, Inc. (https://elementary.io)
*
* 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
Expand All @@ -20,155 +20,153 @@
public class DateTime.TimeZoneGrid : Gtk.Box {
public signal void request_timezone_change (string tz);

private const string AFRICA = "Africa";
private const string AMERICA = "America";
private const string ANTARTICA = "Antarctica";
private const string ASIA = "Asia";
private const string ATLANTIC = "Atlantic";
private const string AUSTRALIA = "Australia";
private const string EUROPE = "Europe";
private const string INDIAN = "Indian";
private const string PACIFIC = "Pacific";
Gtk.TreeView continent_view;
Gtk.ListStore continent_list_store;
Gtk.TreeView city_view;
Gtk.ListStore city_list_store;
string old_selection;
string current_tz;
bool setting_cities = false;
// Enum representation of "clock-format" key in org.gnome.desktop.interface GSettings
enum ClockFormat {
24_H,
12_H
}

private Gtk.DropDown dropdown;
private ListStore timezone_list;
private string time_format;

private string _time_zone;
public string time_zone {
get {
return _time_zone;
}
set {
set_timezone (value);
for (int i = 0; i < timezone_list.get_n_items (); i++) {
if (((ICal.Timezone) timezone_list.get_item (i)).get_display_name () == value) {
dropdown.selected = i;
break;
}
}

_time_zone = value;
}
}

public TimeZoneGrid () {
continent_list_store = new Gtk.ListStore (2, typeof (string), typeof (string));
continent_list_store.set_default_sort_func ((model, a, b) => {
Value value_a;
Value value_b;
model.get_value (a, 0, out value_a);
model.get_value (b, 0, out value_b);
return value_a.get_string ().collate (value_b.get_string ());
});
~TimeZoneGrid () {
ICal.Timezone.free_builtin_timezones ();
}

continent_list_store.set_sort_column_id (Gtk.TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, Gtk.SortType.ASCENDING);
Gtk.TreeIter iter;
continent_list_store.append (out iter);
continent_list_store.set (iter, 0, _("Africa"), 1, AFRICA);
continent_list_store.append (out iter);
continent_list_store.set (iter, 0, _("America"), 1, AMERICA);
continent_list_store.append (out iter);
continent_list_store.set (iter, 0, _("Antarctica"), 1, ANTARTICA);
continent_list_store.append (out iter);
continent_list_store.set (iter, 0, _("Asia"), 1, ASIA);
continent_list_store.append (out iter);
continent_list_store.set (iter, 0, _("Atlantic"), 1, ATLANTIC);
continent_list_store.append (out iter);
continent_list_store.set (iter, 0, _("Australia"), 1, AUSTRALIA);
continent_list_store.append (out iter);
continent_list_store.set (iter, 0, _("Europe"), 1, EUROPE);
continent_list_store.append (out iter);
continent_list_store.set (iter, 0, _("Indian"), 1, INDIAN);
continent_list_store.append (out iter);
continent_list_store.set (iter, 0, _("Pacific"), 1, PACIFIC);

continent_view = new Gtk.TreeView.with_model (continent_list_store) {
headers_visible = false
};
continent_view.add_css_class ("sidebar");
continent_view.get_selection ().mode = Gtk.SelectionMode.BROWSE;
construct {
time_format = Granite.DateTime.get_default_time_format (
new Settings ("org.gnome.desktop.interface").get_enum ("clock-format") == ClockFormat.12_H,
false
);

var cellrenderer = new Gtk.CellRendererText () {
xpad = 12
};
continent_view.insert_column_with_attributes (-1, null, cellrenderer, "text", 0);
continent_view.get_selection ().changed.connect (() => {
Gtk.TreeIter activated_iter;
if (continent_view.get_selection ().get_selected (null, out activated_iter)) {
Value value;
continent_list_store.get_value (activated_iter, 1, out value);
if (old_selection != value.get_string ()) {
change_city_from_continent (value.get_string ());
old_selection = value.get_string ();
}
}
});
timezone_list = new ListStore (typeof (ICal.Timezone));

city_list_store = new Gtk.ListStore (2, typeof (string), typeof (string));
city_list_store.set_default_sort_func ((model, a, b) => {
Value value_a;
Value value_b;
model.get_value (a, 0, out value_a);
model.get_value (b, 0, out value_b);
return value_a.get_string ().collate (value_b.get_string ());
});
var timezone_array = ICal.Timezone.get_builtin_timezones ();
ryonakano marked this conversation as resolved.
Show resolved Hide resolved
for (int i = 0; i < timezone_array.size (); i++) {
timezone_list.insert_sorted (timezone_array.timezone_element_at (i), (a, b) => {
var a_name = ((ICal.Timezone) a).get_display_name ();
var b_name = ((ICal.Timezone) b).get_display_name ();

city_list_store.set_sort_column_id (Gtk.TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, Gtk.SortType.ASCENDING);
city_view = new Gtk.TreeView.with_model (city_list_store) {
headers_visible = false,
hexpand = true
};
return a_name.collate (b_name);
});
}

var city_cellrenderer = new Gtk.CellRendererText () {
ellipsize_set = true,
width_chars = 50,
wrap_mode = Pango.WrapMode.WORD_CHAR,
ellipsize = Pango.EllipsizeMode.END
var expression = new Gtk.CClosureExpression (
typeof (string),
null,
{},
(Callback) get_timezone_name,
null,
null
);

var list_factory = new Gtk.SignalListItemFactory ();
list_factory.setup.connect (setup_factory);
list_factory.bind.connect (bind_factory);

dropdown = new Gtk.DropDown (timezone_list, null) {
enable_search = true,
expression = expression,
factory = list_factory,
hexpand = true,
search_match_mode = SUBSTRING
};
city_view.insert_column_with_attributes (-1, null, city_cellrenderer, "text", 0);
city_view.get_selection ().changed.connect (() => {
if (setting_cities == true)
return;

Gtk.TreeIter activated_iter;
if (city_view.get_selection ().get_selected (null, out activated_iter)) {
Value value;
city_list_store.get_value (activated_iter, 1, out value);
request_timezone_change (value.get_string ());
current_tz = value.get_string ();
}

append (dropdown);

dropdown.notify["selected"].connect (() => {
var timezone = (ICal.Timezone) dropdown.get_selected_item ();
request_timezone_change (timezone.get_display_name ());
});
}

static string get_timezone_name (ICal.Timezone timezone) {
return Parser.format_city (_(timezone.get_display_name ()));
}

var city_scrolled = new Gtk.ScrolledWindow () {
child = city_view,
hscrollbar_policy = Gtk.PolicyType.NEVER,
vscrollbar_policy = Gtk.PolicyType.AUTOMATIC
private void setup_factory (Object object) {
var title = new Gtk.Label ("");

var time = new Gtk.Label ("") {
halign = END,
hexpand = true,
use_markup = true
};
time.add_css_class (Granite.STYLE_CLASS_DIM_LABEL);

var box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 6);
box.append (title);
box.append (time);

append (continent_view);
append (city_scrolled);
var list_item = object as Gtk.ListItem;
list_item.set_data ("title", title);
list_item.set_data ("time", time);
list_item.set_child (box);
}

public void set_timezone (string tz) {
current_tz = tz;
var values = tz.split ("/", 3);
continent_list_store.@foreach ((model, path, iter) => {
Value value;
model.get_value (iter, 1, out value);
if (values[0] == value.get_string ()) {
continent_view.get_selection ().select_iter (iter);
return true;
}
private void bind_factory (Object object) {
var list_item = object as Gtk.ListItem;

return false;
});
var ical_timezone = (ICal.Timezone) list_item.get_item ();
var localized_tz = _(ical_timezone.get_display_name ());

var title = list_item.get_data<Gtk.Label> ("title");
try {
var glib_timezone = new TimeZone.identifier (ical_timezone.get_display_name ());
ryonakano marked this conversation as resolved.
Show resolved Hide resolved

var datetime = new GLib.DateTime.now (glib_timezone);

var offset = glib_timezone.get_offset (
glib_timezone.find_interval (UNIVERSAL, datetime.to_unix ())
);

// TRANSLATORS: The first "%s" represents the timezone name
// and the second "%s" represents the offset from UTC.
// e.g. "America, Santiago (UTC - 4:00)"
title.label = _("%s (%s)").printf (
Parser.format_city (localized_tz),
seconds_to_utc_offset (offset)
);

var time = list_item.get_data<Gtk.Label> ("time");
time.label = "<span font-features='tnum'>%s</span>".printf (datetime.format (time_format));
} catch (Error e) {
warning (e.message);
title.label = Parser.format_city (localized_tz);
}
}

private void change_city_from_continent (string continent) {
setting_cities = true;
city_list_store.clear ();
Parser.get_default ().get_timezones_from_continent (continent).foreach ((key, value) => {
Gtk.TreeIter iter;
city_list_store.append (out iter);
city_list_store.set (iter, 0, key, 1, value);
if (current_tz == value) {
city_view.get_selection ().select_iter (iter);
city_view.scroll_to_cell (city_list_store.get_path (iter), null, true, 0, 0);
}
});
private string seconds_to_utc_offset (int seconds) {
if (seconds == 0) {
return _("UTC");
}

var hours = seconds / 3600;
var minutes = seconds % 3600 / 60;

if (hours > 0) {
return _("UTC +%i:%02i").printf (hours, minutes);
}

setting_cities = false;
// Make sure we use typographical minus
return _("UTC −%i:%02i").printf (hours.abs (), minutes.abs ());
}
}
1 change: 1 addition & 0 deletions src/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ shared_module(
dependency('granite-7'),
dependency('gtk4'),
dependency('libadwaita-1', version: '>=1.4.0'),
dependency('libical-glib'),
meson.get_compiler('vala').find_library('posix'),
switchboard_dep,
meson.get_compiler('c').find_library('m', required : false)
Expand Down
Loading