timezone: rework to allow searching
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
|
||||
61
src/provider/timezone_provider.py
Normal file
61
src/provider/timezone_provider.py
Normal 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)
|
||||
@@ -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')
|
||||
|
||||
Reference in New Issue
Block a user