main window: start migration to Gtk.Template

This commit is contained in:
Bilal Elmoussaoui 2019-02-11 01:35:55 +01:00
parent 26ab204c51
commit 077b2afb61
10 changed files with 280 additions and 134 deletions

View file

@ -15,6 +15,7 @@
<file compressed="true" preprocess="xml-stripblanks" alias="account_add.ui">ui/account_add.ui</file>
<file compressed="true" preprocess="xml-stripblanks" alias="account_edit.ui">ui/account_edit.ui</file>
<file compressed="true" preprocess="xml-stripblanks" alias="account_row.ui">ui/account_row.ui</file>
<file compressed="true" preprocess="xml-stripblanks" alias="window.ui">window.ui</file>
<!-- Default pre-shipped icons -->
<file alias="amazon.svg">icons/hicolor/48x48/apps/amazon.svg</file>

View file

@ -7,11 +7,17 @@ gnome.compile_resources(
gresource_bundle: true,
install_dir: join_paths(get_option('datadir'), meson.project_name()),
install: true,
dependencies: configure_file(
dependencies: [configure_file(
input: 'ui/about_dialog.ui.in',
output: 'about_dialog.ui',
configuration: about_dialog_conf
),
configure_file(
input: 'ui/window.ui.in',
output: 'window.ui',
configuration: about_dialog_conf
)
]
)
# Install gschema

View file

@ -10,6 +10,7 @@
<template class="AddAccountWindow" parent="GtkWindow">
<property name="can_focus">False</property>
<property name="type">popup</property>
<property name="resizable">False</property>
<property name="window_position">center-on-parent</property>
<property name="default_width">350</property>
<property name="default_height">500</property>

View file

@ -5,6 +5,7 @@
<template class="EditAccountWindow" parent="GtkWindow">
<property name="can_focus">False</property>
<property name="type">popup</property>
<property name="resizable">False</property>
<property name="window_position">center-on-parent</property>
<property name="default_width">350</property>
<property name="default_height">500</property>

206
data/ui/window.ui.in Normal file
View file

@ -0,0 +1,206 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.0 -->
<interface>
<requires lib="gtk+" version="3.22"/>
<object class="GtkImage" id="add_image">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">list-add-symbolic</property>
</object>
<object class="GtkImage" id="primary_menu_image">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">open-menu-symbolic</property>
</object>
<object class="GtkImage" id="search_image">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">system-search-symbolic</property>
</object>
<template class="Window" parent="GtkApplicationWindow">
<property name="width_request">350</property>
<property name="height_request">500</property>
<property name="can_focus">False</property>
<property name="default_width">350</property>
<property name="default_height">500</property>
<child>
<object class="GtkStack" id="main_stack">
<property name="width_request">350</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkBox" id="empty_accounts_box">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkImage" id="empty_accounts_list_img">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="icon_name">dialog-information-symbolic</property>
<property name="icon_size">6</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="padding">6</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="halign">center</property>
<property name="valign">center</property>
<property name="label" translatable="yes">There are no accounts yet…</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="padding">6</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="name">empty_state</property>
</packing>
</child>
<child>
<object class="GtkBox" id="accounts_box">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkSearchBar" id="search_bar">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkSearchEntry" id="search_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="primary_icon_name">edit-find-symbolic</property>
<property name="primary_icon_activatable">False</property>
<property name="primary_icon_sensitive">False</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkRevealer" id="notification">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkOverlay">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkLabel" id="notification_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<style>
<class name="app-notification"/>
</style>
</object>
<packing>
<property name="index">-1</property>
</packing>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow" id="accounts_scrolled_window">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkViewport" id="accounts_viewport">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<child>
<placeholder/>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="name">normal_state</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
<child type="titlebar">
<object class="GtkHeaderBar" id="headerbar">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="title" translatable="yes">Authenticator</property>
<property name="show_close_button">True</property>
<child>
<object class="GtkButton" id="add_btn">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="action_name">win.add-account</property>
<property name="image">add_image</property>
<signal name="clicked" handler="add_btn_clicked" swapped="no"/>
</object>
</child>
<child>
<object class="GtkButton" id="primary_menu_btn">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="image">primary_menu_image</property>
<signal name="clicked" handler="primary_menu_btn_clicked" swapped="no"/>
</object>
<packing>
<property name="pack_type">end</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkToggleButton" id="search_btn">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="image">search_image</property>
<signal name="toggled" handler="search_btn_toggled" swapped="no"/>
</object>
<packing>
<property name="pack_type">end</property>
<property name="position">2</property>
</packing>
</child>
</object>
</child>
</template>
</interface>

View file

@ -145,7 +145,6 @@ class Application(Gtk.Application):
window = Window.get_default()
window.set_application(self)
window.set_menu(self._menu)
window.connect("delete-event", lambda x, y: self.__on_quit())
if Application.IS_DEVEL:
window.get_style_context().add_class("devel")

View file

@ -17,5 +17,5 @@
along with Authenticator. If not, see <http://www.gnu.org/licenses/>.
"""
from .add import AddAccountWindow
from .list import AccountsWidget, AccountsList, EmptyAccountsList
from .list import AccountsWidget, AccountsList
from .row import AccountRow

View file

@ -206,38 +206,3 @@ class AccountsList(Gtk.ListBox, GObject.GObject):
row = AccountRow(account)
self.add(row)
class EmptyAccountsList(Gtk.Box):
"""
Empty accounts list widget.
"""
# Default instance
instance = None
def __init__(self):
Gtk.Box.__init__(self, orientation=Gtk.Orientation.VERTICAL)
self._build_widgets()
@staticmethod
def get_default():
if not EmptyAccountsList.instance:
EmptyAccountsList.instance = EmptyAccountsList()
return EmptyAccountsList.instance
def _build_widgets(self):
"""
Build EmptyAccountList widget.
"""
self.set_border_width(36)
self.set_valign(Gtk.Align.CENTER)
self.set_halign(Gtk.Align.CENTER)
# Image
g_icon = Gio.ThemedIcon(name="dialog-information-symbolic.symbolic")
img = Gtk.Image.new_from_gicon(g_icon, Gtk.IconSize.DIALOG)
# Label
label = Gtk.Label(label=_("There are no accounts yet…"))
self.pack_start(img, False, False, 6)
self.pack_start(label, False, False, 6)

View file

@ -21,14 +21,20 @@ from gettext import gettext as _
from gi import require_version
require_version('Gd', '1.0')
require_version("Gtk", "3.0")
from gi.repository import Gd, Gtk, GObject, Gio
from gi.repository import Gd, Gtk, GObject, Gio, GLib
from ..models import Logger, Settings, Database, AccountsManager
from .headerbar import HeaderBar, HeaderBarState
from .accounts import AccountsWidget, AddAccountWindow, EmptyAccountsList
from .accounts import AccountsWidget, AddAccountWindow
from .search_bar import SearchBar
from . import LoginWidget
class WindowState:
NORMAL = 0
LOCKED = 1
EMPTY = 2
@Gtk.Template(resource_path='/com/github/bilelmoussaoui/Authenticator/window.ui')
class Window(Gtk.ApplicationWindow, GObject.GObject):
"""Main Window object."""
__gsignals__ = {
@ -37,22 +43,41 @@ class Window(Gtk.ApplicationWindow, GObject.GObject):
'unlocked': (GObject.SignalFlags.RUN_LAST, None, ())
}
__gtype_name__ = 'Window'
# Default Window instance
instance = None
is_empty = GObject.Property(type=bool, default=False)
state = GObject.Property(type=int, default=0)
headerbar = Gtk.Template.Child()
add_btn = Gtk.Template.Child()
search_btn = Gtk.Template.Child()
main_stack = Gtk.Template.Child()
search_bar = Gtk.Template.Child()
notification = Gtk.Template.Child()
notification_label = Gtk.Template.Child()
accounts_viewport = Gtk.Template.Child()
def __init__(self):
Gtk.ApplicationWindow.__init__(self, type=Gtk.WindowType.TOPLEVEL)
self.set_icon_name("@APP_ID@")
self.resize(350, 500)
super(Window, self).__init__()
self.init_template('Window')
self.connect("locked", self.__on_locked)
self.connect("unlocked", self.__on_unlocked)
self.key_press_signal = None
self.restore_state()
# Start the Account Manager
AccountsManager.get_default()
self._build_widgets()
self.show_all()
self.__init_widgets()
@staticmethod
def get_default():
@ -66,10 +91,6 @@ class Window(Gtk.ApplicationWindow, GObject.GObject):
AccountsManager.get_default().kill()
self.destroy()
def set_menu(self, gio_menu):
"""Set Headerbar popover menu."""
HeaderBar.get_default().generate_popover_menu(gio_menu)
def add_account(self, *_):
if not self.get_application().is_locked:
add_window = AddAccountWindow()
@ -78,43 +99,42 @@ class Window(Gtk.ApplicationWindow, GObject.GObject):
add_window.present()
def update_view(self, *_):
header_bar = HeaderBar.get_default()
count = Database.get_default().count
self.set_property("is-empty", count == 0)
if not self.is_empty:
child_name = "accounts-list"
header_bar.set_state(HeaderBarState.NORMAL)
self.main_stack.set_visible_child_name("normal_state")
child_name = "normal_state"
self.props.state = WindowState.NORMAL
else:
header_bar.set_state(HeaderBarState.EMPTY)
child_name = "empty-accounts-list"
child = self.main_stack.get_child_by_name(child_name)
child.show_all()
self.main_stack.set_visible_child(child)
@staticmethod
def toggle_select(*_):
"""
Toggle select mode
"""
header_bar = HeaderBar.get_default()
if header_bar.state == HeaderBarState.NORMAL:
header_bar.set_state(HeaderBarState.SELECT)
elif header_bar.state != HeaderBarState.LOCKED:
header_bar.set_state(HeaderBarState.NORMAL)
self.main_stack.set_visible_child_name("empty_state")
child_name = "empty_state"
self.props.state = WindowState.EMPTY
def toggle_search(self, *_):
"""
Switch the state of the search mode
Switches the state of the search mode if:
- The application is not locked
- There are at least one account in the database
return: None
"""
if not (self.get_application().is_locked or self.is_empty):
toggled = not self.search_bar.get_property("search_mode_enabled")
self.search_bar.set_property("search_mode_enabled", toggled)
def save_state(self):
"""Save window position & size."""
"""
Save window position and maximized state.
"""
settings = Settings.get_default()
settings.window_position = self.get_position()
settings.window_maximized = self.is_maximized()
def restore_state(self):
"""Restore the window's state."""
"""
Restore the window's state.
"""
settings = Settings.get_default()
# Restore the window position
position_x, position_y = settings.window_position
@ -129,85 +149,40 @@ class Window(Gtk.ApplicationWindow, GObject.GObject):
if settings.window_maximized:
self.maximize()
def _build_widgets(self):
def __init_widgets(self):
"""Build main window widgets."""
# Actions
# Register Actions
self.__add_action("add-account", self.add_account)
self.__add_action("toggle-searchbar", self.toggle_search)
# HeaderBar
header_bar = HeaderBar.get_default()
header_bar.add_btn.set_action_name("win.add-account")
self.set_titlebar(header_bar)
# Main Container
self.main_container = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
# Main Stack
self.main_stack = Gtk.Stack()
# Accounts List
account_list_cntr = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
# Set up accounts Widget
accounts_widget = AccountsWidget.get_default()
accounts_widget.connect("changed", self.update_view)
self.accounts_viewport.add(accounts_widget)
# Search Bar
self.search_bar = SearchBar()
self.search_bar.search_button = header_bar.search_btn
self.search_bar.search_list = accounts_widget.accounts_lists
account_list_cntr.pack_start(self.search_bar, False, False, 0)
account_list_cntr.pack_start(accounts_widget, True, True, 0)
self.main_stack.add_named(account_list_cntr,
"accounts-list")
# Empty accounts list
self.main_stack.add_named(EmptyAccountsList.get_default(),
"empty-accounts-list")
# Login Widget
login_widget = LoginWidget.get_default()
login_widget.login_btn.connect("clicked", self.__on_unlock)
self.main_stack.add_named(login_widget, "login")
self.main_container.pack_start(self.main_stack, True, True, 0)
self.add(self.main_container)
self.update_view()
def _on_account_delete(self, *_):
Window.toggle_select()
self.update_view()
def __on_delete_clicked(self, *__):
notification = Gd.Notification()
accounts_widget = AccountsWidget.get_default()
# notification.connect("dismissed", accounts_widget.delete_selected)
notification.set_timeout(5)
container = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
self.notification_label.set_text(_("An account or more were removed."))
self.notification.set_reveal_child(True)
GLib.timeout_add_seconds(5,
lambda _: self.notification.set_reveal_child(False), None)
notification_lbl = Gtk.Label()
notification_lbl.set_text(_("An account or more were removed."))
container.pack_start(notification_lbl, False, False, 3)
undo_btn = Gtk.Button()
undo_btn.set_label(_("Undo"))
undo_btn.connect("clicked", lambda widget: notification.hide())
container.pack_end(undo_btn, False, False, 3)
notification.add(container)
accounts_widget.add(notification)
accounts_widget.reorder_child(notification, 1)
accounts_widget.show_all()
def __on_locked(self, *_):
if self.key_press_signal:
self.disconnect(self.key_press_signal)
HeaderBar.get_default().set_state(HeaderBarState.LOCKED)
child = self.main_stack.get_child_by_name("login")
child.show_all()
self.main_stack.set_visible_child(child)
self.props.state = WindowState.LOCKED
self.main_stack.set_visible_child_name("login")
def __on_unlocked(self, *_):
self.update_view()

View file

@ -23,14 +23,6 @@ configure_file(
'Authenticator/models')
)
configure_file(
input: 'Authenticator/widgets/window.py.in',
output: 'window.py',
configuration: conf,
install_dir: join_paths(python.sysconfig_path('purelib'),
'Authenticator/widgets')
)
configure_file(
input: 'Authenticator/utils.py.in',
output: 'utils.py',