timezone: rework to allow searching

This commit is contained in:
Peter Eisenmann
2022-10-28 14:13:19 +02:00
parent 8946ed9f15
commit 19e073fcb8
4 changed files with 139 additions and 101 deletions

View File

@@ -1,5 +1,6 @@
using Gtk 4.0;
using Adw 1;
using Gio 2.0;
template TimezonePage : Box {
orientation: vertical;
@@ -14,37 +15,52 @@ template TimezonePage : Box {
styles ["heading"]
}
ScrolledWindow {
hexpand: true;
propagate-natural-height: true;
styles ["embedded"]
child: Stack list_stack {
vhomogeneous: false;
transition-type: crossfade;
ListBox {
selection-mode: none;
styles ["boxed-list"]
ListBoxRow {
child: SearchEntry search_entry {
hexpand: true;
placeholder-text: _("Filter");
receives-default: true;
styles ["nested-list"]
};
}
}
StackPage {
name: "continents";
child: ListBox continents {
row-activated => timezone_selected();
Stack stack {
transition-type: crossfade;
vhomogeneous: false;
StackPage {
name: "list";
child:
ScrolledWindow {
hexpand: true;
propagate-natural-height: true;
styles ["embedded"]
child: ListBox list {
row-activated => row_selected();
styles ["boxed-list", "bottom-spacing"]
};
}
};
}
StackPage {
name: "countries";
child: ListBox countries {
row-activated => timezone_selected();
styles ["boxed-list", "bottom-spacing"]
};
}
StackPage {
name: "subzones";
child: ListBox subzones {
row-activated => timezone_selected();
styles ["boxed-list", "bottom-spacing"]
};
}
};
StackPage {
name: "none";
child: Adw.StatusPage {
title: _("No Results");
description: _("Try a different search");
};
}
}
}
Gio.ListStore list_model {}
CustomFilter custom_filter {}
FilterListModel filter_list_model {
filter: custom_filter;
model: list_model;
}

View File

@@ -28,6 +28,7 @@ os_installer_sources = [
'provider/language_provider.py',
'provider/locale_provider.py',
'provider/software_provider.py',
'provider/timezone_provider.py',
'ui/confirm_quit_popup.py',
'ui/page.py',
'ui/pages/confirm.py',

View File

@@ -0,0 +1,61 @@
# SPDX-License-Identifier: GPL-3.0-or-later
from time import time
from gi.repository import GnomeDesktop, GObject, GWeather
class Timezone(GObject.GObject):
__gtype_name__ = __qualname__
def __init__(self, name):
super().__init__()
self.name: str = name
self.lower_case_name: str = name.lower()
self.locations: set = set()
def _get_location_children(location):
# this code is un-pythonesque because libgweather decided to simplify their API too much
first_child = location.next_child(None)
if not first_child:
return []
children = [first_child]
while child := location.next_child(children[-1]):
children.append(child)
return children
def _add_all_locations_to_timezone(timezone, location):
for child in _get_location_children(location):
timezone.locations.add(child.get_name().lower())
_add_all_locations_to_timezone(timezone, child)
def _recurse_location(location, timezones):
for child in _get_location_children(location):
if child.has_timezone():
timezone_id = child.get_timezone().get_identifier()
if not timezone_id in timezones:
print(f'Developer hint: Unknown timezone {timezone_id} {child.get_name()}')
continue
_add_all_locations_to_timezone(timezones[timezone_id], child)
else:
_recurse_location(child, timezones)
### public methods ###
def get_current_timezone():
timezone = GnomeDesktop.WallClock().get_timezone()
return timezone.get_identifier()
def get_timezones():
timezones = {timezone.get_identifier(): Timezone(timezone.get_identifier())
for timezone in GWeather.Location().get_world().get_timezones()}
for child in _get_location_children(GWeather.Location().get_world()):
if not child.has_timezone(): # skips UTC and Etc/GMT+12
_recurse_location(child, timezones)
return sorted(timezones.values(), key=lambda t: t.name)

View File

@@ -1,104 +1,64 @@
# SPDX-License-Identifier: GPL-3.0-or-later
from gi.repository import Gio, Gtk, GWeather
from gi.repository import Gtk
from .global_state import global_state
from .page import Page
from .system_calls import set_system_timezone
from .timezone_provider import get_timezones
from .widgets import reset_model, ProgressRow
def get_location_children(location):
# this code is un-pythonesque because libgweather decided to simplify their API too much
children = [location.next_child(None)]
while child := location.next_child(children[-1]):
children.append(child)
return children
def create_location_row(location):
return ProgressRow(location.get_name(), location)
@Gtk.Template(resource_path='/com/github/p3732/os-installer/ui/pages/timezone.ui')
class TimezonePage(Gtk.Box, Page):
__gtype_name__ = __qualname__
image = 'globe-symbolic'
list_stack = Gtk.Template.Child()
continents = Gtk.Template.Child()
continents_loaded = False
countries = Gtk.Template.Child()
subzones = Gtk.Template.Child()
search_entry = Gtk.Template.Child()
custom_filter = Gtk.Template.Child()
filter_list_model = Gtk.Template.Child()
continents_model = Gio.ListStore()
countries_model = Gio.ListStore()
subzones_model = Gio.ListStore()
stack = Gtk.Template.Child()
list = Gtk.Template.Child()
list_loaded = False
list_model = Gtk.Template.Child()
def __init__(self, **kwargs):
Gtk.Box.__init__(self, **kwargs)
self.countries.bind_model(self.countries_model, create_location_row)
self.continents.bind_model(self.continents_model, create_location_row)
self.subzones.bind_model(self.subzones_model, create_location_row)
self.search_entry.connect("search-changed", self._filter)
def _load_countries(self, continent):
countries = get_location_children(continent)
reset_model(self.countries_model, countries)
self.list.bind_model(
self.filter_list_model, lambda l: ProgressRow(l.name))
self.list_stack.set_visible_child_name('countries')
def _filter(self, *args):
self.search_text = self.search_entry.get_text().lower()
self.custom_filter.set_filter_func(self._timezone_filter)
def _load_subzones(self, country):
subzones = []
for subzone in get_location_children(country):
if subzone.get_timezone():
subzones.append(subzone)
reset_model(self.subzones_model, subzones)
if self.filter_list_model.get_n_items() > 0:
self.stack.set_visible_child_name('list')
else:
self.stack.set_visible_child_name('none')
self.list_stack.set_visible_child_name('subzones')
def _set_timezone(self, timezone):
self.can_navigate_backward = False
set_system_timezone(timezone)
global_state.advance(self)
def _timezone_filter(self, timezone):
if self.search_text in timezone.lower_case_name:
return True
for location in timezone.locations:
if self.search_text in location:
return True
return False
### callbacks ###
@Gtk.Template.Callback('timezone_selected')
def _timezone_selected(self, list_box, row):
location = row.info
if (timezone := location.get_timezone_str()):
self._set_timezone(timezone)
elif list_box == self.subzones:
print(f'Subzone {location.get_name()} does not have any'
' timezone attached to it! Falling back to UTC.')
self._set_timezone('UTC')
elif list_box == self.continents:
self._load_countries(location)
self.can_navigate_backward = True
elif list_box == self.countries:
self._load_subzones(location)
self.can_navigate_backward = True
@Gtk.Template.Callback('row_selected')
def _row_selected(self, list_box, row):
set_system_timezone(row.get_label())
global_state.advance(self)
### public methods ###
def load(self):
if not self.continents_loaded:
self.continents_loaded = True
continents = []
for continent in get_location_children(GWeather.Location.get_world()):
if not continent.get_timezone(): # skip dummy locations
continents.append(continent)
reset_model(self.continents_model, continents)
self.list_stack.set_visible_child_name('continents')
if not self.list_loaded:
self.list_loaded = True
reset_model(self.list_model, get_timezones())
return "load_next"
def navigate_backward(self):
match self.list_stack.get_visible_child_name():
case 'countries':
self.list_stack.set_visible_child_name('continents')
self.can_navigate_backward = False
case 'subzones':
self.list_stack.set_visible_child_name('countries')