Start work on the main panel

This commit is contained in:
Alexander Rosenberg 2023-10-24 04:55:25 -07:00
parent c3cc233132
commit 6cd5280713
Signed by: Zander671
GPG Key ID: 5FD0394ADBD72730
13 changed files with 508 additions and 1259 deletions

View File

@ -65,19 +65,17 @@ template $MainPanel : Box {
vexpand: true; vexpand: true;
ListView alias_list_view { ListView alias_list_view {
factory: SignalListItemFactory alias_item_factory { factory: SignalListItemFactory alias_item_factory {};
}; model: SingleSelection alias_selection_model {};
model: MultiSelection alias_selection_model {
};
} }
}; };
[overlay] [overlay]
Label no_content_label { Label no_content_label {
hexpand: true; hexpand: true;
label: "No more results";
vexpand: true; vexpand: true;
label: "No more results";
visible: false;
styles [ styles [
"loading-label", "loading-label",
@ -133,6 +131,7 @@ template $MainPanel : Box {
[overlay] [overlay]
Box loading_overlay { Box loading_overlay {
orientation: vertical; orientation: vertical;
visible: false;
Spinner { Spinner {
spinning: true; spinning: true;

View File

@ -0,0 +1,253 @@
using Gtk 4.0;
template $SettingsPanel : Box {
Overlay overlay {
hexpand: true;
child: Box content_wrapper {
hexpand: true;
vexpand: true;
Grid content {
hexpand: true;
row-homogeneous: true;
valign: start;
Button profile_image_button {
has-frame: false;
Image profile_image {
halign: center;
icon-name: "user-info-symbolic";
icon-size: large;
margin-end: 5;
valign: center;
vexpand: true;
}
layout {
column: "0";
column-span: "1";
row: "0";
row-span: "2";
}
}
Box {
Button remove_picture_button {
halign: start;
valign: center;
icon-name: "window-close-symbolic";
visible: false;
}
Entry name_label {
editable: false;
has-frame: false;
hexpand: true;
input-hints: no_emoji | no_spellcheck;
text: "Testy McTesterface";
styles [
"login-title-label",
]
}
layout {
column: "1";
column-span: "1";
row: "0";
row-span: "1";
}
}
Box {
Button edit_button {
halign: start;
valign: center;
label: "Edit";
}
Button cancel_edit_button {
halign: start;
valign: center;
visible: false;
label: "Cancel";
}
}
Label stats_label {
halign: center;
hexpand: true;
label: "Stats go here";
layout {
column: "0";
column-span: "3";
row: "2";
row-span: "1";
}
}
Label email_label {
hexpand: true;
label: "mctesterface@test.tld";
xalign: 0.0;
layout {
column: "1";
row: "1";
}
}
Button logout_button {
halign: start;
label: "Logout";
valign: center;
layout {
column: "2";
row: "1";
}
}
Label {
label: "Account Status:";
margin-end: 5;
xalign: 1.0;
layout {
column: "0";
column-span: "1";
row: "3";
row-span: "1";
}
}
Label {
label: "Api Key:";
margin-end: 5;
xalign: 1.0;
layout {
column: "0";
row: "4";
}
}
PasswordEntry api_key_field {
editable: false;
enable-undo: false;
hexpand: true;
show-peek-icon: true;
valign: center;
layout {
column: "1";
row: "4";
}
}
Label status_label {
hexpand: true;
label: "Premium";
xalign: 0.0;
layout {
column: "1";
column-span: "1";
row: "3";
row-span: "1";
}
}
Button api_key_clear_button {
halign: start;
label: "Clear";
valign: center;
clicked => $clear_api_key_clicked() swapped;
layout {
column: "2";
row: "4";
}
}
}
};
[overlay]
Box loading_overlay {
hexpand: true;
orientation: vertical;
visible: false;
Spinner {
spinning: true;
valign: end;
vexpand: true;
styles [
"loading-label",
]
}
Label {
label: "Loading...";
valign: start;
vexpand: true;
styles [
"loading-label",
]
}
styles [
"loading-label",
]
}
[overlay]
Box error_overlay {
hexpand: true;
orientation: vertical;
visible: false;
Box {
halign: center;
valign: end;
Button error_refresh_button {
halign: center;
icon-name: "view-refresh-symbolic";
valign: end;
vexpand: true;
styles [
"loading-label",
]
}
Button error_close_button {
halign: center;
icon-name: "window-close-symbolic";
valign: end;
styles [
"loading-label",
]
}
}
Label error_label {
label: "ERROR";
valign: start;
vexpand: true;
styles [
"error-message-label",
"loading-label",
]
}
}
}
}

View File

@ -1,188 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- Created with Cambalache 0.16.0 -->
<interface>
<!-- interface-name main-window.ui -->
<requires lib="gtk" version="4.12"/>
<template class="SLMainPanel" parent="GtkBox">
<child>
<object class="GtkOverlay" id="overlay">
<child>
<object class="GtkBox" id="content_wrapper">
<property name="orientation">vertical</property>
<property name="vexpand">True</property>
<child>
<object class="GtkBox">
<child>
<object class="GtkSearchEntry" id="search_box">
<property name="hexpand">True</property>
<property name="placeholder-text">Filter by email, description, etc.</property>
</object>
</child>
<child>
<object class="GtkDropDown" id="view_dropdown">
<property name="hexpand">True</property>
<!-- <property name="model"> -->
<!-- <object class="GtkStringList"> -->
<!-- <items> -->
<!-- <item>All aliases</item> -->
<!-- <item>Pinned only</item> -->
<!-- <item>Enabled only</item> -->
<!-- <item>Disabled only</item> -->
<!-- </items> -->
<!-- </object> -->
<!-- </property> -->
<property name="selected">0</property>
</object>
</child>
<child>
<object class="GtkButton" id="search_button">
<property name="label">Search</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkBox">
<child>
<object class="GtkButton" id="new_button">
<property name="label">New</property>
</object>
</child>
<child>
<object class="GtkButton" id="edit_button">
<property name="label">Edit</property>
</object>
</child>
<child>
<object class="GtkButton" id="delete_button">
<property name="label">Delete</property>
</object>
</child>
<child>
<object class="GtkButton" id="prev_page_button">
<property name="halign">end</property>
<property name="hexpand">True</property>
<property name="icon-name">go-previous-symbolic</property>
</object>
</child>
<child>
<object class="GtkEntry" id="page_label">
<property name="has-frame">False</property>
<property name="input-hints">no-emoji | no-spellcheck</property>
<property name="input-purpose">digits</property>
<property name="max-width-chars">0</property>
<property name="text">1</property>
<property name="truncate-multiline">True</property>
<property name="width-chars">5</property>
<property name="xalign">0.5</property>
</object>
</child>
<child>
<object class="GtkButton" id="next_page_button">
<property name="icon-name">go-next-symbolic</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkOverlay" id="scroll_overlay">
<child>
<object class="GtkScrolledWindow">
<property name="vexpand">True</property>
<child>
<object class="GtkListView" id="alias_list_view">
<property name="factory">
<object class="GtkSignalListItemFactory" id="alias_item_factory"/>
</property>
<property name="model">
<object class="GtkMultiSelection" id="alias_selection_model"/>
</property>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</template>
<object class="GtkBox" id="loading_overlay">
<property name="orientation">vertical</property>
<child>
<object class="GtkSpinner">
<property name="spinning">True</property>
<property name="valign">end</property>
<property name="vexpand">True</property>
<style>
<class name="loading-label"/>
</style>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label">Loading...</property>
<property name="valign">start</property>
<property name="vexpand">True</property>
<style>
<class name="loading-label"/>
</style>
</object>
</child>
<style>
<class name="loading-label"/>
</style>
</object>
<object class="GtkLabel" id="no_content_label">
<property name="hexpand">True</property>
<property name="label">No more results</property>
<property name="vexpand">True</property>
<style>
<class name="loading-label"/>
</style>
</object>
<object class="GtkBox" id="error_overlay">
<property name="hexpand">True</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkBox">
<property name="halign">center</property>
<property name="valign">end</property>
<child>
<object class="GtkButton" id="error_refresh_button">
<property name="halign">center</property>
<property name="icon-name">view-refresh-symbolic</property>
<property name="valign">end</property>
<property name="vexpand">True</property>
<style>
<class name="loading-label"/>
</style>
</object>
</child>
<child>
<object class="GtkButton" id="error_close_button">
<property name="halign">center</property>
<property name="icon-name">window-close-symbolic</property>
<property name="valign">end</property>
<style>
<class name="loading-label"/>
</style>
</object>
</child>
</object>
</child>
<child>
<object class="GtkLabel" id="error_label">
<property name="label">ERROR</property>
<property name="valign">start</property>
<property name="vexpand">True</property>
<style>
<class name="error-message-label"/>
<class name="loading-label"/>
</style>
</object>
</child>
</object>
<!-- Custom fragments -->
</interface>

View File

@ -1,263 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- Created with Cambalache 0.16.0 -->
<interface>
<requires lib="gtk" version="4.12"/>
<template class="SLSettingsPanel" parent="GtkBox">
<child>
<object class="GtkOverlay" id="overlay">
<property name="child">content_wrapper</property>
<property name="hexpand">True</property>
<child>
<object class="GtkBox" id="content_wrapper">
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<child>
<object class="GtkGrid" id="content">
<property name="hexpand">True</property>
<property name="row-homogeneous">True</property>
<property name="valign">start</property>
<child>
<object class="GtkButton" id="profile_image_button">
<property name="has-frame">False</property>
<child>
<object class="GtkImage" id="profile_image">
<property name="halign">center</property>
<property name="icon-name">user-info-symbolic</property>
<property name="icon-size">large</property>
<property name="margin-end">5</property>
<property name="valign">center</property>
<property name="vexpand">True</property>
</object>
</child>
<!-- Custom fragments -->
<layout>
<property name="column">0</property>
<property name="column-span">1</property>
<property name="row">0</property>
<property name="row-span">2</property>
</layout>
</object>
</child>
<child>
<object class="GtkBox">
<child>
<object class="GtkButton" id="remove_picture_button">
<property name="icon-name">window-close-symbolic</property>
</object>
</child>
<child>
<object class="GtkEntry" id="name_label">
<property name="editable">False</property>
<property name="has-frame">False</property>
<property name="hexpand">True</property>
<property name="input-hints">no-emoji | no-spellcheck</property>
<property name="text">Testy McTesterface</property>
<style>
<class name="login-title-label"/>
</style>
</object>
</child>
<layout>
<property name="column">1</property>
<property name="column-span">1</property>
<property name="row">0</property>
<property name="row-span">1</property>
</layout>
</object>
</child>
<child>
<object class="GtkBox">
<child>
<object class="GtkButton" id="edit_button">
<property name="halign">start</property>
<property name="label">Edit</property>
<property name="valign">center</property>
</object>
</child>
<child>
<object class="GtkButton" id="cancel_edit_button">
<property name="label">Cancel</property>
</object>
</child>
<layout>
<property name="column">2</property>
<property name="column-span">1</property>
<property name="row">0</property>
<property name="row-span">1</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel" id="stats_label">
<property name="halign">center</property>
<property name="hexpand">True</property>
<property name="label">Stats go here</property>
<layout>
<property name="column">0</property>
<property name="column-span">3</property>
<property name="row">2</property>
<property name="row-span">1</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel" id="email_label">
<property name="hexpand">True</property>
<property name="label">mctesterface@test.tld</property>
<property name="xalign">0.0</property>
<layout>
<property name="column">1</property>
<property name="row">1</property>
</layout>
</object>
</child>
<child>
<object class="GtkButton" id="logout_button">
<property name="halign">start</property>
<property name="label">Logout</property>
<property name="valign">center</property>
<layout>
<property name="column">2</property>
<property name="row">1</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label">Account Status:</property>
<property name="margin-end">5</property>
<property name="xalign">1.0</property>
<layout>
<property name="column">0</property>
<property name="column-span">1</property>
<property name="row">3</property>
<property name="row-span">1</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label">Api Key:</property>
<property name="margin-end">5</property>
<property name="xalign">1.0</property>
<attributes/>
<layout>
<property name="column">0</property>
<property name="row">4</property>
</layout>
</object>
</child>
<child>
<object class="GtkPasswordEntry" id="api_key_field">
<property name="editable">False</property>
<property name="enable-undo">False</property>
<property name="hexpand">True</property>
<property name="show-peek-icon">True</property>
<property name="valign">center</property>
<layout>
<property name="column">1</property>
<property name="row">4</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel" id="status_label">
<property name="hexpand">True</property>
<property name="label">Premium</property>
<property name="xalign">0.0</property>
<layout>
<property name="column">1</property>
<property name="column-span">1</property>
<property name="row">3</property>
<property name="row-span">1</property>
</layout>
</object>
</child>
<child>
<object class="GtkButton" id="api_key_clear_button">
<property name="halign">start</property>
<property name="label">Clear</property>
<property name="valign">center</property>
<layout>
<property name="column">2</property>
<property name="row">4</property>
</layout>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
</template>
<object class="GtkBox" id="loading_overlay">
<property name="hexpand">True</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkSpinner">
<property name="spinning">True</property>
<property name="valign">end</property>
<property name="vexpand">True</property>
<style>
<class name="loading-label"/>
</style>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label">Loading...</property>
<property name="valign">start</property>
<property name="vexpand">True</property>
<style>
<class name="loading-label"/>
</style>
</object>
</child>
<style>
<class name="loading-label"/>
</style>
</object>
<object class="GtkBox" id="error_overlay">
<property name="hexpand">True</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkBox">
<property name="halign">center</property>
<property name="valign">end</property>
<child>
<object class="GtkButton" id="error_refresh_button">
<property name="halign">center</property>
<property name="icon-name">view-refresh-symbolic</property>
<property name="valign">end</property>
<property name="vexpand">True</property>
<style>
<class name="loading-label"/>
</style>
</object>
</child>
<child>
<object class="GtkButton" id="error_close_button">
<property name="halign">center</property>
<property name="icon-name">window-close-symbolic</property>
<property name="valign">end</property>
<style>
<class name="loading-label"/>
</style>
</object>
</child>
</object>
</child>
<child>
<object class="GtkLabel" id="error_label">
<property name="label">ERROR</property>
<property name="valign">start</property>
<property name="vexpand">True</property>
<style>
<class name="error-message-label"/>
<class name="loading-label"/>
</style>
</object>
</child>
</object>
</interface>

View File

@ -1,531 +0,0 @@
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<!DOCTYPE cambalache-project SYSTEM "cambalache-project.dtd">
<cambalache-project version="0.13.1" target_tk="gtk-4.0">
<ui>
(1,1,"main-window.ui","main-panel.ui",None,None,None,None,None,None,""),
(3,18,None,"settings-panel.ui",None,None,None,None,None,None,None),
(4,13,None,"login-window.ui",None,None,None,None,None,None,None),
(5,1,None,"totp-dialog.ui",None,None,None,None,None,None,None)
</ui>
<css>
(1,"global.css",None,1)
</css>
<css_ui>
(1,1),
(1,3),
(1,4),
(1,5)
</css_ui>
<object>
(1,1,"GtkBox","SLMainPanel",None,None,None,None,None,None),
(1,2,"GtkOverlay","overlay",1,None,None,None,None,None),
(1,3,"GtkBox","content_wrapper",2,None,None,None,None,None),
(1,4,"GtkBox",None,3,None,None,None,None,None),
(1,5,"GtkSearchEntry","search_box",4,None,None,None,None,None),
(1,8,"GtkDropDown","view_dropdown",4,None,None,None,1,None),
(1,9,"GtkStringList",None,8,None,None,None,None,None),
(1,10,"GtkButton","search_button",4,None,None,None,2,None),
(1,11,"GtkBox",None,3,None,None,None,1,None),
(1,12,"GtkButton","new_button",11,None,None,None,None,None),
(1,13,"GtkButton","edit_button",11,None,None,None,1,None),
(1,14,"GtkButton","delete_button",11,None,None,None,2,None),
(1,22,"GtkBox","loading_overlay",None,None,None,None,None,None),
(1,23,"GtkSpinner",None,22,None,None,None,None,None),
(1,24,"GtkLabel",None,22,None,None,None,1,None),
(1,28,"GtkButton","prev_page_button",11,None,None,None,3,None),
(1,29,"GtkButton","next_page_button",11,None,None,None,5,None),
(1,30,"GtkEntry","page_label",11,None,None,None,4,None),
(1,31,"GtkOverlay","scroll_overlay",3,None,None,None,2,None),
(1,32,"GtkScrolledWindow",None,31,None,None,None,None,None),
(1,33,"GtkListView","alias_list_view",32,None,None,None,None,None),
(1,34,"GtkSignalListItemFactory","alias_item_factory",33,None,None,None,None,None),
(1,35,"GtkMultiSelection","alias_selection_model",33,None,None,None,None,None),
(1,36,"GtkLabel","no_content_label",None,None,None,None,None,None),
(1,37,"GtkBox","error_overlay",None,None,None,None,None,None),
(1,38,"GtkBox",None,37,None,None,None,None,None),
(1,39,"GtkButton","error_refresh_button",38,None,None,None,None,None),
(1,40,"GtkButton","error_close_button",38,None,None,None,1,None),
(1,41,"GtkLabel","error_label",37,None,None,None,1,None),
(3,18,"GtkBox","SLSettingsPanel",None,None,None,None,None,None),
(3,19,"GtkOverlay","overlay",18,None,None,None,None,None),
(3,30,"GtkBox","loading_overlay",None,None,None,None,None,None),
(3,31,"GtkSpinner",None,30,None,None,None,None,None),
(3,32,"GtkLabel",None,30,None,None,None,1,None),
(3,33,"GtkBox","error_overlay",None,None,None,None,None,None),
(3,35,"GtkLabel","error_label",33,None,None,None,1,None),
(3,45,"GtkBox",None,33,None,None,None,None,None),
(3,46,"GtkButton","error_refresh_button",45,None,None,None,None,None),
(3,47,"GtkButton","error_close_button",45,None,None,None,1,None),
(3,48,"GtkBox","content_wrapper",19,None,None,None,-1,None),
(3,49,"GtkGrid","content",48,None,None,None,None,None),
(3,51,"GtkLabel","stats_label",49,None,None,None,1,None),
(3,52,"GtkLabel","email_label",49,None,None,None,2,None),
(3,53,"GtkButton","logout_button",49,None,None,None,3,None),
(3,55,"GtkLabel",None,49,None,None,None,5,None),
(3,57,"GtkLabel",None,49,None,None,None,7,None),
(3,58,"GtkPasswordEntry","api_key_field",49,None,None,None,8,None),
(3,59,"GtkLabel","status_label",49,None,None,None,9,None),
(3,60,"GtkButton","api_key_clear_button",49,None,None,None,10,None),
(3,63,"GtkButton","profile_image_button",49,None,None,None,None,""),
(3,64,"GtkImage","profile_image",63,None,None,None,None,None),
(3,65,"GtkBox",None,49,None,None,None,None,None),
(3,66,"GtkButton","remove_picture_button",65,None,None,None,None,None),
(3,67,"GtkEntry","name_label",65,None,None,None,1,None),
(3,68,"GtkBox",None,49,None,None,None,None,None),
(3,69,"GtkButton","edit_button",68,None,None,None,None,None),
(3,70,"GtkButton","cancel_edit_button",68,None,None,None,1,None),
(4,11,"GtkPopover","device_entry_popover",None,None,None,None,None,None),
(4,12,"GtkLabel",None,11,None,None,None,None,None),
(4,13,"GtkApplicationWindow","LoginWindow",None,None,None,None,None,None),
(4,14,"GtkBox",None,13,None,None,None,-1,None),
(4,30,"GtkLabel",None,14,None,None,None,1,None),
(4,31,"GtkOverlay","forms_overlay",14,None,None,None,None,None),
(4,32,"GtkBox","forms_wrapper",31,None,None,None,None,None),
(4,33,"GtkGrid",None,32,None,None,None,None,None),
(4,34,"GtkLabel",None,33,None,None,None,None,None),
(4,35,"GtkLabel","error_label",33,None,None,None,1,None),
(4,36,"GtkEntry","device_entry",33,None,None,None,2,None),
(4,37,"GtkLabel",None,33,None,None,None,3,None),
(4,38,"GtkLabel",None,33,None,None,None,4,None),
(4,39,"GtkLabel",None,33,None,None,None,5,None),
(4,40,"GtkEntry","email_entry",33,None,None,None,6,None),
(4,41,"GtkPasswordEntry","password_entry",33,None,None,None,7,None),
(4,42,"GtkButton","login_button",33,None,None,None,8,None),
(4,43,"GtkLabel",None,32,None,None,None,1,None),
(4,44,"GtkBox",None,32,None,None,None,2,None),
(4,45,"GtkLabel",None,44,None,None,None,None,None),
(4,46,"GtkPasswordEntry","api_key_entry",44,None,None,None,1,None),
(4,47,"GtkButton","api_key_button",44,None,None,None,2,None),
(4,48,"GtkBox","loading_overlay",None,None,None,None,None,None),
(4,49,"GtkSpinner",None,48,None,None,None,None,None),
(4,50,"GtkLabel",None,48,None,None,None,1,None),
(5,1,"GtkWindow","SLTotpDialog",None,None,None,None,None,None),
(5,2,"GtkOverlay","overlay",1,None,None,None,-1,None),
(5,11,"GtkBox","loading_overlay",None,None,None,None,None,None),
(5,12,"GtkSpinner",None,11,None,None,None,None,None),
(5,13,"GtkLabel",None,11,None,None,None,1,None),
(5,14,"GtkGrid","form_wrapper",2,None,None,None,None,None),
(5,15,"GtkBox",None,14,None,None,None,None,None),
(5,16,"GtkImage",None,15,None,None,None,None,None),
(5,17,"GtkLabel",None,15,None,None,None,1,None),
(5,19,"GtkButton","accept_button",14,None,None,None,2,None),
(5,20,"GtkButton","cancel_button",14,None,None,None,3,None),
(5,21,"GtkEntry","code_entry",14,None,None,None,4,None),
(5,23,"GtkLabel","error_label",14,None,None,None,4,None)
</object>
<object_property>
(1,3,"GtkOrientable","orientation","vertical",None,None,None,None,None,None,None,None,None),
(1,3,"GtkWidget","vexpand","True",None,None,None,None,None,None,None,None,None),
(1,5,"GtkSearchEntry","placeholder-text","Filter by email, description, etc.",None,None,None,None,None,None,None,None,None),
(1,5,"GtkWidget","hexpand","True",None,None,None,None,None,None,None,None,None),
(1,8,"GtkDropDown","model",None,None,None,None,None,9,None,None,None,None),
(1,8,"GtkDropDown","selected","0",None,None,None,None,None,None,None,None,None),
(1,8,"GtkWidget","hexpand","True",None,None,None,None,None,None,None,None,None),
(1,10,"GtkButton","label","Search",None,None,None,None,None,None,None,None,None),
(1,12,"GtkButton","label","New",None,None,None,None,None,None,None,None,None),
(1,13,"GtkButton","label","Edit",None,None,None,None,None,None,None,None,None),
(1,14,"GtkButton","label","Delete",None,None,None,None,None,None,None,None,None),
(1,22,"GtkOrientable","orientation","vertical",None,None,None,None,None,None,None,None,None),
(1,23,"GtkSpinner","spinning","True",None,None,None,None,None,None,None,None,None),
(1,23,"GtkWidget","valign","end",None,None,None,None,None,None,None,None,None),
(1,23,"GtkWidget","vexpand","True",None,None,None,None,None,None,None,None,None),
(1,24,"GtkLabel","label","Loading...",None,None,None,None,None,None,None,None,None),
(1,24,"GtkWidget","valign","start",None,None,None,None,None,None,None,None,None),
(1,24,"GtkWidget","vexpand","True",None,None,None,None,None,None,None,None,None),
(1,28,"GtkButton","icon-name","go-previous-symbolic",None,None,None,None,None,None,None,None,None),
(1,28,"GtkWidget","halign","end",None,None,None,None,None,None,None,None,None),
(1,28,"GtkWidget","hexpand","True",None,None,None,None,None,None,None,None,None),
(1,29,"GtkButton","icon-name","go-next-symbolic",None,None,None,None,None,None,None,None,None),
(1,30,"GtkEditable","max-width-chars","0",None,None,None,None,None,None,None,None,None),
(1,30,"GtkEditable","text","1",None,None,None,None,None,None,None,None,None),
(1,30,"GtkEditable","width-chars","5",None,None,None,None,None,None,None,None,None),
(1,30,"GtkEditable","xalign","0.5",None,None,None,None,None,None,None,None,None),
(1,30,"GtkEntry","has-frame","False",None,None,None,None,None,None,None,None,None),
(1,30,"GtkEntry","input-hints","no-emoji | no-spellcheck",None,None,None,None,None,None,None,None,None),
(1,30,"GtkEntry","input-purpose","digits",None,None,None,None,None,None,None,None,None),
(1,30,"GtkEntry","truncate-multiline","True",None,None,None,None,None,None,None,None,None),
(1,32,"GtkWidget","vexpand","True",None,None,None,None,None,None,None,None,None),
(1,33,"GtkListView","factory",None,None,None,None,None,34,None,None,None,None),
(1,33,"GtkListView","model",None,None,None,None,None,35,None,None,None,None),
(1,36,"GtkLabel","label","No more results",None,None,None,None,None,None,None,None,None),
(1,36,"GtkWidget","hexpand","True",None,None,None,None,None,None,None,None,None),
(1,36,"GtkWidget","vexpand","True",None,None,None,None,None,None,None,None,None),
(1,37,"GtkOrientable","orientation","vertical",None,None,None,None,None,None,None,None,None),
(1,37,"GtkWidget","hexpand","True",None,None,None,None,None,None,None,None,None),
(1,38,"GtkWidget","halign","center",None,None,None,None,None,None,None,None,None),
(1,38,"GtkWidget","valign","end",None,None,None,None,None,None,None,None,None),
(1,39,"GtkButton","icon-name","view-refresh-symbolic",None,None,None,None,None,None,None,None,None),
(1,39,"GtkWidget","halign","center",None,None,None,None,None,None,None,None,None),
(1,39,"GtkWidget","valign","end",None,None,None,None,None,None,None,None,None),
(1,39,"GtkWidget","vexpand","True",None,None,None,None,None,None,None,None,None),
(1,40,"GtkButton","icon-name","window-close-symbolic",None,None,None,None,None,None,None,None,None),
(1,40,"GtkWidget","halign","center",None,None,None,None,None,None,None,None,None),
(1,40,"GtkWidget","valign","end",None,None,None,None,None,None,None,None,None),
(1,41,"GtkLabel","label","ERROR",None,None,None,None,None,None,None,None,None),
(1,41,"GtkWidget","valign","start",None,None,None,None,None,None,None,None,None),
(1,41,"GtkWidget","vexpand","True",None,None,None,None,None,None,None,None,None),
(3,19,"GtkOverlay","child","48",None,None,None,None,None,None,None,None,None),
(3,19,"GtkWidget","hexpand","True",None,None,None,None,None,None,None,None,None),
(3,30,"GtkOrientable","orientation","vertical",None,None,None,None,None,None,None,None,None),
(3,30,"GtkWidget","hexpand","True",None,None,None,None,None,None,None,None,None),
(3,31,"GtkSpinner","spinning","True",None,None,None,None,None,None,None,None,None),
(3,31,"GtkWidget","valign","end",None,None,None,None,None,None,None,None,None),
(3,31,"GtkWidget","vexpand","True",None,None,None,None,None,None,None,None,None),
(3,32,"GtkLabel","label","Loading...",None,None,None,None,None,None,None,None,None),
(3,32,"GtkWidget","valign","start",None,None,None,None,None,None,None,None,None),
(3,32,"GtkWidget","vexpand","True",None,None,None,None,None,None,None,None,None),
(3,33,"GtkOrientable","orientation","vertical",None,None,None,None,None,None,None,None,None),
(3,33,"GtkWidget","hexpand","True",None,None,None,None,None,None,None,None,None),
(3,35,"GtkLabel","label","ERROR",None,None,None,None,None,None,None,None,None),
(3,35,"GtkWidget","valign","start",None,None,None,None,None,None,None,None,None),
(3,35,"GtkWidget","vexpand","True",None,None,None,None,None,None,None,None,None),
(3,45,"GtkWidget","halign","center",None,None,None,None,None,None,None,None,None),
(3,45,"GtkWidget","valign","end",None,None,None,None,None,None,None,None,None),
(3,46,"GtkButton","icon-name","view-refresh-symbolic",None,None,None,None,None,None,None,None,None),
(3,46,"GtkWidget","halign","center",None,None,None,None,None,None,None,None,None),
(3,46,"GtkWidget","valign","end",None,None,None,None,None,None,None,None,None),
(3,46,"GtkWidget","vexpand","True",None,None,None,None,None,None,None,None,None),
(3,47,"GtkButton","icon-name","window-close-symbolic",None,None,None,None,None,None,None,None,None),
(3,47,"GtkWidget","halign","center",None,None,None,None,None,None,None,None,None),
(3,47,"GtkWidget","valign","end",None,None,None,None,None,None,None,None,None),
(3,48,"GtkWidget","hexpand","True",None,None,None,None,None,None,None,None,None),
(3,48,"GtkWidget","vexpand","True",None,None,None,None,None,None,None,None,None),
(3,49,"GtkGrid","row-homogeneous","True",None,None,None,None,None,None,None,None,None),
(3,49,"GtkWidget","hexpand","True",None,None,None,None,None,None,None,None,None),
(3,49,"GtkWidget","valign","start",None,None,None,None,None,None,None,None,None),
(3,51,"GtkLabel","label","Stats go here",None,None,None,None,None,None,None,None,None),
(3,51,"GtkWidget","halign","center",None,None,None,None,None,None,None,None,None),
(3,51,"GtkWidget","hexpand","True",None,None,None,None,None,None,None,None,None),
(3,52,"GtkLabel","label","mctesterface@test.tld",None,None,None,None,None,None,None,None,None),
(3,52,"GtkLabel","xalign","0.0",None,None,None,None,None,None,None,None,None),
(3,52,"GtkWidget","hexpand","True",None,None,None,None,None,None,None,None,None),
(3,53,"GtkButton","label","Logout",None,None,None,None,None,None,None,None,None),
(3,53,"GtkWidget","halign","start",None,None,None,None,None,None,None,None,None),
(3,53,"GtkWidget","valign","center",None,None,None,None,None,None,None,None,None),
(3,55,"GtkLabel","label","Account Status:",None,None,None,None,None,None,None,None,None),
(3,55,"GtkLabel","xalign","1.0",None,None,None,None,None,None,None,None,None),
(3,55,"GtkWidget","margin-end","5",None,None,None,None,None,None,None,None,None),
(3,57,"GtkLabel","label","Api Key:",None,None,None,None,None,None,None,None,None),
(3,57,"GtkLabel","xalign","1.0",None,None,None,None,None,None,None,None,None),
(3,57,"GtkWidget","margin-end","5",None,None,None,None,None,None,None,None,None),
(3,58,"GtkEditable","editable","False",None,None,None,None,None,None,None,None,None),
(3,58,"GtkEditable","enable-undo","False",None,None,None,None,None,None,None,None,None),
(3,58,"GtkPasswordEntry","show-peek-icon","True",None,None,None,None,None,None,None,None,None),
(3,58,"GtkWidget","hexpand","True",None,None,None,None,None,None,None,None,None),
(3,58,"GtkWidget","valign","center",None,None,None,None,None,None,None,None,None),
(3,59,"GtkLabel","label","Premium",None,None,None,None,None,None,None,None,None),
(3,59,"GtkLabel","xalign","0.0",None,None,None,None,None,None,None,None,None),
(3,59,"GtkWidget","hexpand","True",None,None,None,None,None,None,None,None,None),
(3,60,"GtkButton","label","Clear",None,None,None,None,None,None,None,None,None),
(3,60,"GtkWidget","halign","start",None,None,None,None,None,None,None,None,None),
(3,60,"GtkWidget","valign","center",None,None,None,None,None,None,None,None,None),
(3,63,"GtkButton","has-frame","False",None,None,None,None,None,None,None,None,None),
(3,64,"GtkImage","icon-name","user-info-symbolic",None,None,None,None,None,None,None,None,None),
(3,64,"GtkImage","icon-size","large",None,None,None,None,None,None,None,None,None),
(3,64,"GtkWidget","halign","center",None,None,None,None,None,None,None,None,None),
(3,64,"GtkWidget","margin-end","5",None,None,None,None,None,None,None,None,None),
(3,64,"GtkWidget","valign","center",None,None,None,None,None,None,None,None,None),
(3,64,"GtkWidget","vexpand","True",None,None,None,None,None,None,None,None,None),
(3,66,"GtkButton","icon-name","window-close-symbolic",None,None,None,None,None,None,None,None,None),
(3,67,"GtkEditable","editable","False",None,None,None,None,None,None,None,None,None),
(3,67,"GtkEditable","text","Testy McTesterface",None,None,None,None,None,None,None,None,None),
(3,67,"GtkEntry","has-frame","False",None,None,None,None,None,None,None,None,None),
(3,67,"GtkEntry","input-hints","no-emoji | no-spellcheck",None,None,None,None,None,None,None,None,None),
(3,67,"GtkWidget","hexpand","True",None,None,None,None,None,None,None,None,None),
(3,69,"GtkButton","label","Edit",None,None,None,None,None,None,None,None,None),
(3,69,"GtkWidget","halign","start",None,None,None,None,None,None,None,None,None),
(3,69,"GtkWidget","valign","center",None,None,None,None,None,None,None,None,None),
(3,70,"GtkButton","label","Cancel",None,None,None,None,None,None,None,None,None),
(4,11,"GtkPopover","child",None,None,None,None,None,12,None,None,None,None),
(4,12,"GtkAccessible","accessible-role","tooltip",None,None,None,None,None,None,None,None,None),
(4,12,"GtkLabel","label","This is the human readable name that will show up in the API key section of the SimpleLogin website.",None,None,None,None,None,None,None,None,None),
(4,12,"GtkLabel","max-width-chars","30",None,None,None,None,None,None,None,None,None),
(4,12,"GtkLabel","wrap","True",None,None,None,None,None,None,None,None,None),
(4,13,"GtkWindow","title","SimpleLogin",None,None,None,None,None,None,None,None,None),
(4,14,"GtkOrientable","orientation","vertical",None,None,None,None,None,None,None,None,None),
(4,30,"GtkLabel","label","Don't have an account? You can register &lt;a href=\"https://app.simplelogin.io/auth/register\"&gt;here&lt;/a&gt;.",None,None,None,None,None,None,None,None,None),
(4,30,"GtkLabel","use-markup","True",None,None,None,None,None,None,None,None,None),
(4,30,"GtkLabel","yalign","0.0",None,None,None,None,None,None,None,None,None),
(4,30,"GtkWidget","margin-bottom","10",None,None,None,None,None,None,None,None,None),
(4,30,"GtkWidget","valign","start",None,None,None,None,None,None,None,None,None),
(4,30,"GtkWidget","vexpand","True",None,None,None,None,None,None,None,None,None),
(4,31,"GtkOverlay","child","32",None,None,None,None,None,None,None,None,None),
(4,31,"GtkWidget","valign","end",None,None,None,None,None,None,None,None,None),
(4,31,"GtkWidget","vexpand","True",None,None,None,None,None,None,None,None,None),
(4,32,"GtkWidget","valign","end",None,None,None,None,None,None,None,None,None),
(4,32,"GtkWidget","vexpand","True",None,None,None,None,None,None,None,None,None),
(4,33,"GtkWidget","halign","end",None,None,None,None,None,None,None,None,None),
(4,33,"GtkWidget","hexpand","True",None,None,None,None,None,None,None,None,None),
(4,33,"GtkWidget","margin-bottom","10",None,None,None,None,None,None,None,None,None),
(4,33,"GtkWidget","margin-end","10",None,None,None,None,None,None,None,None,None),
(4,33,"GtkWidget","margin-start","10",None,None,None,None,None,None,None,None,None),
(4,33,"GtkWidget","margin-top","10",None,None,None,None,None,None,None,None,None),
(4,33,"GtkWidget","valign","center",None,None,None,None,None,None,None,None,None),
(4,34,"GtkLabel","label","Login:",None,None,None,None,None,None,None,None,None),
(4,34,"GtkWidget","hexpand","True",None,None,None,None,None,None,None,None,None),
(4,34,"GtkWidget","margin-bottom","5",None,None,None,None,None,None,None,None,None),
(4,34,"GtkWidget","vexpand","True",None,None,None,None,None,None,None,None,None),
(4,35,"GtkLabel","label","ERROR",None,None,None,None,None,None,None,None,None),
(4,35,"GtkWidget","margin-top","5",None,None,None,None,None,None,None,None,None),
(4,36,"GtkEditable","width-chars","18",None,None,None,None,None,None,None,None,None),
(4,36,"GtkEntry","placeholder-text","My Awesome Computer",None,None,None,None,None,None,None,None,None),
(4,36,"GtkEntry","secondary-icon-name","dialog-question-symbolic",None,None,None,None,None,None,None,None,None),
(4,36,"GtkEntry","truncate-multiline","True",None,None,None,None,None,None,None,None,None),
(4,36,"GtkWidget","margin-start","6",None,None,None,None,None,None,None,None,None),
(4,37,"GtkLabel","label","Device:",None,None,None,None,None,None,None,None,None),
(4,37,"GtkLabel","xalign","0.0",None,None,None,None,None,None,None,None,None),
(4,38,"GtkLabel","label","Email:",None,None,None,None,None,None,None,None,None),
(4,38,"GtkLabel","xalign","0.0",None,None,None,None,None,None,None,None,None),
(4,39,"GtkLabel","label","Password:",None,None,None,None,None,None,None,None,None),
(4,39,"GtkLabel","xalign","0.0",None,None,None,None,None,None,None,None,None),
(4,40,"GtkEntry","input-purpose","email",None,None,None,None,None,None,None,None,None),
(4,40,"GtkEntry","truncate-multiline","True",None,None,None,None,None,None,None,None,None),
(4,40,"GtkWidget","hexpand","True",None,None,None,None,None,None,None,None,None),
(4,40,"GtkWidget","margin-start","6",None,None,None,None,None,None,None,None,None),
(4,40,"GtkWidget","receives-default","True",None,None,None,None,None,None,None,None,None),
(4,41,"GtkPasswordEntry","show-peek-icon","True",None,None,None,None,None,None,None,None,None),
(4,41,"GtkWidget","hexpand","True",None,None,None,None,None,None,None,None,None),
(4,41,"GtkWidget","margin-start","6",None,None,None,None,None,None,None,None,None),
(4,42,"GtkButton","label","Login",None,None,None,None,None,None,None,None,None),
(4,42,"GtkWidget","halign","center",None,None,None,None,None,None,None,None,None),
(4,42,"GtkWidget","margin-top","5",None,None,None,None,None,None,None,None,None),
(4,42,"GtkWidget","sensitive","False",None,None,None,None,None,None,None,None,None),
(4,42,"GtkWidget","valign","center",None,None,None,None,None,None,None,None,None),
(4,43,"GtkLabel","justify","center",None,None,None,None,None,None,None,None,None),
(4,43,"GtkLabel","label","- or -",None,None,None,None,None,None,None,None,None),
(4,43,"GtkWidget","halign","center",None,None,None,None,None,None,None,None,None),
(4,44,"GtkOrientable","orientation","vertical",None,None,None,None,None,None,None,None,None),
(4,44,"GtkWidget","halign","start",None,None,None,None,None,None,None,None,None),
(4,44,"GtkWidget","hexpand","True",None,None,None,None,None,None,None,None,None),
(4,44,"GtkWidget","margin-bottom","10",None,None,None,None,None,None,None,None,None),
(4,44,"GtkWidget","margin-end","10",None,None,None,None,None,None,None,None,None),
(4,44,"GtkWidget","margin-start","10",None,None,None,None,None,None,None,None,None),
(4,44,"GtkWidget","margin-top","10",None,None,None,None,None,None,None,None,None),
(4,44,"GtkWidget","valign","center",None,None,None,None,None,None,None,None,None),
(4,45,"GtkLabel","label","Set API Key:",None,None,None,None,None,None,None,None,None),
(4,45,"GtkWidget","hexpand","True",None,None,None,None,None,None,None,None,None),
(4,45,"GtkWidget","margin-bottom","5",None,None,None,None,None,None,None,None,None),
(4,46,"GtkEditable","width-chars","27",None,None,None,None,None,None,None,None,None),
(4,46,"GtkPasswordEntry","show-peek-icon","True",None,None,None,None,None,None,None,None,None),
(4,46,"GtkWidget","hexpand","True",None,None,None,None,None,None,None,None,None),
(4,46,"GtkWidget","margin-end","3",None,None,None,None,None,None,None,None,None),
(4,46,"GtkWidget","margin-start","8",None,None,None,None,None,None,None,None,None),
(4,47,"GtkButton","label","Accept",None,None,None,None,None,None,None,None,None),
(4,47,"GtkWidget","halign","center",None,None,None,None,None,None,None,None,None),
(4,47,"GtkWidget","margin-top","5",None,None,None,None,None,None,None,None,None),
(4,47,"GtkWidget","sensitive","False",None,None,None,None,None,None,None,None,None),
(4,48,"GtkOrientable","orientation","vertical",None,None,None,None,None,None,None,None,None),
(4,49,"GtkSpinner","spinning","True",None,None,None,None,None,None,None,None,None),
(4,49,"GtkWidget","valign","end",None,None,None,None,None,None,None,None,None),
(4,49,"GtkWidget","vexpand","True",None,None,None,None,None,None,None,None,None),
(4,50,"GtkLabel","label","Loading...",None,None,None,None,None,None,None,None,None),
(4,50,"GtkWidget","valign","start",None,None,None,None,None,None,None,None,None),
(4,50,"GtkWidget","vexpand","True",None,None,None,None,None,None,None,None,None),
(5,1,"GtkWindow","modal","True",None,None,None,None,None,None,None,None,None),
(5,1,"GtkWindow","title","TOTP Entry",None,None,None,None,None,None,None,None,None),
(5,2,"GtkOverlay","child","14",None,None,None,None,None,None,None,None,None),
(5,2,"GtkWidget","halign","center",None,None,None,None,None,None,None,None,None),
(5,2,"GtkWidget","hexpand","True",None,None,None,None,None,None,None,None,None),
(5,2,"GtkWidget","valign","center",None,None,None,None,None,None,None,None,None),
(5,2,"GtkWidget","vexpand","True",None,None,None,None,None,None,None,None,None),
(5,11,"GtkOrientable","orientation","vertical",None,None,None,None,None,None,None,None,None),
(5,12,"GtkSpinner","spinning","True",None,None,None,None,None,None,None,None,None),
(5,12,"GtkWidget","valign","end",None,None,None,None,None,None,None,None,None),
(5,12,"GtkWidget","vexpand","True",None,None,None,None,None,None,None,None,None),
(5,13,"GtkLabel","label","Loading...",None,None,None,None,None,None,None,None,None),
(5,13,"GtkWidget","valign","start",None,None,None,None,None,None,None,None,None),
(5,13,"GtkWidget","vexpand","True",None,None,None,None,None,None,None,None,None),
(5,14,"GtkGrid","column-spacing","5",None,None,None,None,None,None,None,None,None),
(5,14,"GtkGrid","row-spacing","5",None,None,None,None,None,None,None,None,None),
(5,14,"GtkWidget","halign","center",None,None,None,None,None,None,None,None,None),
(5,14,"GtkWidget","hexpand","True",None,None,None,None,None,None,None,None,None),
(5,14,"GtkWidget","margin-bottom","10",None,None,None,None,None,None,None,None,None),
(5,14,"GtkWidget","margin-end","10",None,None,None,None,None,None,None,None,None),
(5,14,"GtkWidget","margin-start","10",None,None,None,None,None,None,None,None,None),
(5,14,"GtkWidget","margin-top","10",None,None,None,None,None,None,None,None,None),
(5,14,"GtkWidget","valign","center",None,None,None,None,None,None,None,None,None),
(5,14,"GtkWidget","vexpand","True",None,None,None,None,None,None,None,None,None),
(5,15,"GtkWidget","halign","center",None,None,None,None,None,None,None,None,None),
(5,15,"GtkWidget","hexpand","True",None,None,None,None,None,None,None,None,None),
(5,16,"GtkImage","icon-name","dialog-password-symbolic",None,None,None,None,None,None,None,None,None),
(5,16,"GtkWidget","halign","center",None,None,None,None,None,None,None,None,None),
(5,17,"GtkLabel","label","TOTP",None,None,None,None,None,None,None,None,None),
(5,19,"GtkButton","label","Accept",None,None,None,None,None,None,None,None,None),
(5,20,"GtkButton","label","Cancel",None,None,None,None,None,None,None,None,None),
(5,21,"GtkEntry","placeholder-text","TOTP Code",None,None,None,None,None,None,None,None,None),
(5,23,"GtkLabel","label","ERROR",None,None,None,None,None,None,None,None,None)
</object_property>
<object_layout_property>
(3,49,51,"GtkGridLayoutChild","column","0",None,None,None,None),
(3,49,51,"GtkGridLayoutChild","column-span","3",None,None,None,None),
(3,49,51,"GtkGridLayoutChild","row","2",None,None,None,None),
(3,49,51,"GtkGridLayoutChild","row-span","1",None,None,None,None),
(3,49,52,"GtkGridLayoutChild","column","1",None,None,None,None),
(3,49,52,"GtkGridLayoutChild","row","1",None,None,None,None),
(3,49,53,"GtkGridLayoutChild","column","2",None,None,None,None),
(3,49,53,"GtkGridLayoutChild","row","1",None,None,None,None),
(3,49,55,"GtkGridLayoutChild","column","0",None,None,None,None),
(3,49,55,"GtkGridLayoutChild","column-span","1",None,None,None,None),
(3,49,55,"GtkGridLayoutChild","row","3",None,None,None,None),
(3,49,55,"GtkGridLayoutChild","row-span","1",None,None,None,None),
(3,49,57,"GtkGridLayoutChild","column","0",None,None,None,None),
(3,49,57,"GtkGridLayoutChild","row","4",None,None,None,None),
(3,49,58,"GtkGridLayoutChild","column","1",None,None,None,None),
(3,49,58,"GtkGridLayoutChild","row","4",None,None,None,None),
(3,49,59,"GtkGridLayoutChild","column","1",None,None,None,None),
(3,49,59,"GtkGridLayoutChild","column-span","1",None,None,None,None),
(3,49,59,"GtkGridLayoutChild","row","3",None,None,None,None),
(3,49,59,"GtkGridLayoutChild","row-span","1",None,None,None,None),
(3,49,60,"GtkGridLayoutChild","column","2",None,None,None,None),
(3,49,60,"GtkGridLayoutChild","row","4",None,None,None,None),
(3,49,63,"GtkGridLayoutChild","column","0",None,None,None,None),
(3,49,63,"GtkGridLayoutChild","column-span","1",None,None,None,None),
(3,49,63,"GtkGridLayoutChild","row-span","2",None,None,None,None),
(3,49,65,"GtkGridLayoutChild","column","1",None,None,None,None),
(3,49,65,"GtkGridLayoutChild","column-span","1",None,None,None,None),
(3,49,65,"GtkGridLayoutChild","row","0",None,None,None,None),
(3,49,65,"GtkGridLayoutChild","row-span","1",None,None,None,None),
(3,49,68,"GtkGridLayoutChild","column","2",None,None,None,None),
(3,49,68,"GtkGridLayoutChild","column-span","1",None,None,None,None),
(3,49,68,"GtkGridLayoutChild","row","0",None,None,None,None),
(3,49,68,"GtkGridLayoutChild","row-span","1",None,None,None,None),
(4,33,34,"GtkGridLayoutChild","column","0",None,None,None,None),
(4,33,34,"GtkGridLayoutChild","column-span","2",None,None,None,None),
(4,33,34,"GtkGridLayoutChild","row","0",None,None,None,None),
(4,33,35,"GtkGridLayoutChild","column","0",None,None,None,None),
(4,33,35,"GtkGridLayoutChild","column-span","2",None,None,None,None),
(4,33,35,"GtkGridLayoutChild","row","4",None,None,None,None),
(4,33,36,"GtkGridLayoutChild","column","1",None,None,None,None),
(4,33,36,"GtkGridLayoutChild","row","3",None,None,None,None),
(4,33,37,"GtkGridLayoutChild","column","0",None,None,None,None),
(4,33,37,"GtkGridLayoutChild","row","3",None,None,None,None),
(4,33,38,"GtkGridLayoutChild","column","0",None,None,None,None),
(4,33,38,"GtkGridLayoutChild","row","1",None,None,None,None),
(4,33,39,"GtkGridLayoutChild","column","0",None,None,None,None),
(4,33,39,"GtkGridLayoutChild","row","2",None,None,None,None),
(4,33,40,"GtkGridLayoutChild","column","1",None,None,None,None),
(4,33,40,"GtkGridLayoutChild","row","1",None,None,None,None),
(4,33,41,"GtkGridLayoutChild","column","1",None,None,None,None),
(4,33,41,"GtkGridLayoutChild","row","2",None,None,None,None),
(4,33,42,"GtkGridLayoutChild","column","0",None,None,None,None),
(4,33,42,"GtkGridLayoutChild","column-span","2",None,None,None,None),
(4,33,42,"GtkGridLayoutChild","row","5",None,None,None,None),
(5,14,15,"GtkGridLayoutChild","column","0",None,None,None,None),
(5,14,15,"GtkGridLayoutChild","column-span","2",None,None,None,None),
(5,14,15,"GtkGridLayoutChild","row","0",None,None,None,None),
(5,14,19,"GtkGridLayoutChild","column","1",None,None,None,None),
(5,14,19,"GtkGridLayoutChild","row","3",None,None,None,None),
(5,14,20,"GtkGridLayoutChild","column","0",None,None,None,None),
(5,14,20,"GtkGridLayoutChild","row","3",None,None,None,None),
(5,14,21,"GtkGridLayoutChild","column","0",None,None,None,None),
(5,14,21,"GtkGridLayoutChild","column-span","2",None,None,None,None),
(5,14,21,"GtkGridLayoutChild","row","1",None,None,None,None),
(5,14,23,"GtkGridLayoutChild","column-span","2",None,None,None,None),
(5,14,23,"GtkGridLayoutChild","row","2",None,None,None,None)
</object_layout_property>
<object_data>
(4,34,"GtkWidget",1,1,None,None,None,None,None,None),
(4,34,"GtkWidget",2,2,None,1,None,None,None,None),
(4,35,"GtkWidget",1,1,None,None,None,None,None,None),
(4,35,"GtkWidget",2,2,None,1,None,None,None,None),
(4,40,"GtkWidget",1,1,None,None,None,None,None,None),
(4,33,"GtkWidget",1,1,None,None,None,None,None,None),
(4,33,"GtkWidget",2,2,None,1,None,None,None,None),
(4,45,"GtkWidget",1,1,None,None,None,None,None,None),
(4,45,"GtkWidget",2,2,None,1,None,None,None,None),
(1,9,"GtkStringList",2,5,"Disabled only",1,None,None,None,None),
(1,9,"GtkStringList",2,4,"Enabled only",1,None,None,None,None),
(1,9,"GtkStringList",2,3,"Pinned only",1,None,None,None,None),
(1,9,"GtkStringList",2,2,"All aliases",1,None,None,None,None),
(1,9,"GtkStringList",1,1,None,None,None,None,None,None),
(1,22,"GtkWidget",1,1,None,None,None,None,None,None),
(1,22,"GtkWidget",2,2,None,1,None,None,None,None),
(1,24,"GtkWidget",1,1,None,None,None,None,None,None),
(1,24,"GtkWidget",2,2,None,1,None,None,None,None),
(1,23,"GtkWidget",1,1,None,None,None,None,None,None),
(1,23,"GtkWidget",2,2,None,1,None,None,None,None),
(4,49,"GtkWidget",1,1,None,None,None,None,None,None),
(4,49,"GtkWidget",2,2,None,1,None,None,None,None),
(4,50,"GtkWidget",1,1,None,None,None,None,None,None),
(4,50,"GtkWidget",2,2,None,1,None,None,None,None),
(4,48,"GtkWidget",1,1,None,None,None,None,None,None),
(4,48,"GtkWidget",2,2,None,1,None,None,None,None),
(5,17,"GtkWidget",1,1,None,None,None,None,None,None),
(5,17,"GtkWidget",2,2,None,1,None,None,None,None),
(5,23,"GtkWidget",1,1,None,None,None,None,None,None),
(5,23,"GtkWidget",2,2,None,1,None,None,None,None),
(5,13,"GtkWidget",2,2,None,1,None,None,None,None),
(5,13,"GtkWidget",1,1,None,None,None,None,None,None),
(5,12,"GtkWidget",2,2,None,1,None,None,None,None),
(5,12,"GtkWidget",1,1,None,None,None,None,None,None),
(5,11,"GtkWidget",2,2,None,1,None,None,None,None),
(5,11,"GtkWidget",1,1,None,None,None,None,None,None),
(1,36,"GtkWidget",1,1,None,None,None,None,None,None),
(1,36,"GtkWidget",2,2,None,1,None,None,None,None),
(3,31,"GtkWidget",1,1,None,None,None,None,None,None),
(3,31,"GtkWidget",2,2,None,1,None,None,None,None),
(3,32,"GtkWidget",1,1,None,None,None,None,None,None),
(3,32,"GtkWidget",2,2,None,1,None,None,None,None),
(3,30,"GtkWidget",1,1,None,None,None,None,None,None),
(3,30,"GtkWidget",2,2,None,1,None,None,None,None),
(3,57,"GtkLabel",1,1,None,None,None,None,None,None),
(1,39,"GtkWidget",1,1,None,None,None,None,None,None),
(1,39,"GtkWidget",2,2,None,1,None,None,None,None),
(1,40,"GtkWidget",1,1,None,None,None,None,None,None),
(1,40,"GtkWidget",2,2,None,1,None,None,None,None),
(1,41,"GtkWidget",1,1,None,None,None,None,None,None),
(1,41,"GtkWidget",2,2,None,1,None,None,None,None),
(1,41,"GtkWidget",2,3,None,1,None,None,None,None),
(3,67,"GtkWidget",1,1,None,None,None,None,None,None),
(3,67,"GtkWidget",2,2,None,1,None,None,None,None),
(3,47,"GtkWidget",2,2,None,1,None,None,None,None),
(3,47,"GtkWidget",1,1,None,None,None,None,None,None),
(3,46,"GtkWidget",2,2,None,1,None,None,None,None),
(3,46,"GtkWidget",1,1,None,None,None,None,None,None),
(3,35,"GtkWidget",2,3,None,1,None,None,None,None),
(3,35,"GtkWidget",2,2,None,1,None,None,None,None),
(3,35,"GtkWidget",1,1,None,None,None,None,None,None)
</object_data>
<object_data_arg>
(4,34,"GtkWidget",2,2,"name","login-title-label"),
(4,35,"GtkWidget",2,2,"name","error-message-label"),
(4,33,"GtkWidget",2,2,"name","login-form-warpper"),
(4,45,"GtkWidget",2,2,"name","login-title-label"),
(1,9,"GtkStringList",2,5,"translatable",None),
(1,9,"GtkStringList",2,5,"context",None),
(1,9,"GtkStringList",2,5,"comments",None),
(1,9,"GtkStringList",2,4,"translatable",None),
(1,9,"GtkStringList",2,4,"context",None),
(1,9,"GtkStringList",2,4,"comments",None),
(1,9,"GtkStringList",2,3,"translatable",None),
(1,9,"GtkStringList",2,3,"context",None),
(1,9,"GtkStringList",2,3,"comments",None),
(1,9,"GtkStringList",2,2,"translatable",None),
(1,9,"GtkStringList",2,2,"context",None),
(1,9,"GtkStringList",2,2,"comments",None),
(1,24,"GtkWidget",2,2,"name","loading-label"),
(1,22,"GtkWidget",2,2,"name","loading-label"),
(1,23,"GtkWidget",2,2,"name","loading-label"),
(4,49,"GtkWidget",2,2,"name","loading-label"),
(4,50,"GtkWidget",2,2,"name","loading-label"),
(4,48,"GtkWidget",2,2,"name","loading-label"),
(5,17,"GtkWidget",2,2,"name","login-title-label"),
(5,23,"GtkWidget",2,2,"name","error-message-label"),
(5,13,"GtkWidget",2,2,"name","loading-label"),
(5,12,"GtkWidget",2,2,"name","loading-label"),
(5,11,"GtkWidget",2,2,"name","loading-label"),
(1,36,"GtkWidget",2,2,"name","loading-label"),
(3,31,"GtkWidget",2,2,"name","loading-label"),
(3,32,"GtkWidget",2,2,"name","loading-label"),
(3,30,"GtkWidget",2,2,"name","loading-label"),
(1,39,"GtkWidget",2,2,"name","loading-label"),
(1,40,"GtkWidget",2,2,"name","loading-label"),
(1,41,"GtkWidget",2,2,"name","error-message-label"),
(1,41,"GtkWidget",2,3,"name","loading-label"),
(3,67,"GtkWidget",2,2,"name","login-title-label"),
(3,47,"GtkWidget",2,2,"name","loading-label"),
(3,46,"GtkWidget",2,2,"name","loading-label"),
(3,35,"GtkWidget",2,3,"name","loading-label"),
(3,35,"GtkWidget",2,2,"name","error-message-label")
</object_data_arg>
</cambalache-project>

View File

@ -1,8 +0,0 @@
[wrap-git]
directory = blueprint-compiler
url = https://gitlab.gnome.org/jwestman/blueprint-compiler.git
revision = v0.10.0
depth = 1
[provide]
program_names = blueprint-compiler

View File

@ -1,121 +0,0 @@
using Gtk 4.0;
template $SLTotpDialog : Window {
modal: true;
title: "TOTP Entry";
Overlay overlay {
child: form_wrapper;
halign: center;
hexpand: true;
valign: center;
vexpand: true;
Grid form_wrapper {
column-spacing: 5;
halign: center;
hexpand: true;
margin-bottom: 10;
margin-end: 10;
margin-start: 10;
margin-top: 10;
row-spacing: 5;
valign: center;
vexpand: true;
Box {
halign: center;
hexpand: true;
Image {
halign: center;
icon-name: "dialog-password-symbolic";
}
Label {
label: "TOTP";
styles [
"login-title-label",
]
}
layout {
column: "0";
column-span: "2";
row: "0";
}
}
Button accept_button {
label: "Accept";
layout {
column: "1";
row: "3";
}
}
Button cancel_button {
label: "Cancel";
layout {
column: "0";
row: "3";
}
}
Entry code_entry {
placeholder-text: "TOTP Code";
layout {
column: "0";
column-span: "2";
row: "1";
}
}
Label error_label {
label: "ERROR";
styles [
"error-message-label",
]
layout {
column: "0";
column-span: "2";
row: "2";
}
}
}
}
}
Box loading_overlay {
orientation: vertical;
Spinner {
spinning: true;
valign: end;
vexpand: true;
styles [
"loading-label",
]
}
Label {
label: "Loading...";
valign: start;
vexpand: true;
styles [
"loading-label",
]
}
styles [
"loading-label",
]
}

View File

@ -1,124 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- Created with Cambalache 0.16.0 -->
<interface>
<requires lib="gtk" version="4.12"/>
<template class="SLTotpDialog" parent="GtkWindow">
<property name="modal">True</property>
<property name="title">TOTP Entry</property>
<child>
<object class="GtkOverlay" id="overlay">
<property name="child">form_wrapper</property>
<property name="halign">center</property>
<property name="hexpand">True</property>
<property name="valign">center</property>
<property name="vexpand">True</property>
<child>
<object class="GtkGrid" id="form_wrapper">
<property name="column-spacing">5</property>
<property name="halign">center</property>
<property name="hexpand">True</property>
<property name="margin-bottom">10</property>
<property name="margin-end">10</property>
<property name="margin-start">10</property>
<property name="margin-top">10</property>
<property name="row-spacing">5</property>
<property name="valign">center</property>
<property name="vexpand">True</property>
<child>
<object class="GtkBox">
<property name="halign">center</property>
<property name="hexpand">True</property>
<child>
<object class="GtkImage">
<property name="halign">center</property>
<property name="icon-name">dialog-password-symbolic</property>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label">TOTP</property>
<style>
<class name="login-title-label"/>
</style>
</object>
</child>
<layout>
<property name="column">0</property>
<property name="column-span">2</property>
<property name="row">0</property>
</layout>
</object>
</child>
<child>
<object class="GtkButton" id="accept_button">
<property name="label">Accept</property>
<layout>
<property name="column">1</property>
<property name="row">3</property>
</layout>
</object>
</child>
<child>
<object class="GtkButton" id="cancel_button">
<property name="label">Cancel</property>
<layout>
<property name="column">0</property>
<property name="row">3</property>
</layout>
</object>
</child>
<child>
<object class="GtkEntry" id="code_entry">
<property name="placeholder-text">TOTP Code</property>
<layout>
<property name="column">0</property>
<property name="column-span">2</property>
<property name="row">1</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel" id="error_label">
<property name="label">ERROR</property>
<style>
<class name="error-message-label"/>
</style>
<layout>
<property name="column">0</property>
<property name="column-span">2</property>
<property name="row">2</property>
</layout>
</object>
</child>
</object>
</child>
</object>
</child>
</template>
<object class="GtkBox" id="loading_overlay">
<property name="orientation">vertical</property>
<child>
<object class="GtkSpinner">
<property name="spinning">True</property>
<property name="valign">end</property>
<property name="vexpand">True</property>
<style>
<class name="loading-label"/>
</style>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label">Loading...</property>
<property name="valign">start</property>
<property name="vexpand">True</property>
<style>
<class name="loading-label"/>
</style>
</object>
</child>
<style>
<class name="loading-label"/>
</style>
</object>
</interface>

View File

@ -1,4 +1,7 @@
use crate::login_window::LoginWindow; use crate::login_window::LoginWindow;
use crate::tabbed_window::TabbedWindow;
use crate::main_panel::MainPanel;
use crate::settings_panel::SettingsPanel;
use gtk4 as gtk; use gtk4 as gtk;
use gtk::{glib, gdk, gio, prelude::*, subclass::prelude::*}; use gtk::{glib, gdk, gio, prelude::*, subclass::prelude::*};
@ -46,7 +49,25 @@ mod imp {
} }
fn activate_has_api_key(&self) { fn activate_has_api_key(&self) {
println!("Logged in with API Key: {}", self.api_key().unwrap()); let obj = &self.obj();
let main_panel = MainPanel::new(obj);
let settings_panel = SettingsPanel::new(obj);
let window = TabbedWindow::new(obj,
&[("Aliases", &main_panel.upcast()),
("Settings", &settings_panel.clone().upcast())]);
let window_closure = window.clone();
settings_panel.connect_closure(
"api-key-cleared", false,
closure_local!(@weak-allow-none self as opt_this
=> move |_: SettingsPanel| {
window_closure.close();
if let Some(this) = opt_this {
this.context.borrow_mut().set_api_key(None);
this.set_api_key(None);
this.activate_no_api_key();
}
}));
window.present();
} }
} }
@ -103,3 +124,9 @@ impl Application {
&self.imp().context &self.imp().context
} }
} }
impl Default for Application {
fn default() -> Self {
Self::new(crate::APP_ID)
}
}

View File

@ -2,6 +2,8 @@ mod login_window;
mod totp_window; mod totp_window;
mod application; mod application;
mod main_panel; mod main_panel;
mod settings_panel;
mod tabbed_window;
use gtk4 as gtk; use gtk4 as gtk;
use gtk::prelude::*; use gtk::prelude::*;

View File

@ -1,22 +1,61 @@
use gtk4 as gtk; use gtk4 as gtk;
use gtk::{glib, prelude::*, subclass::prelude::*}; use gtk::{glib, prelude::*, subclass::prelude::*};
use crate::application;
mod imp { mod imp {
use super::*; use super::*;
use glib::subclass::Signal; use std::cell::RefCell;
use once_cell::sync::Lazy;
#[derive(Debug, Default, gtk::CompositeTemplate)] #[derive(Debug, Default, gtk::CompositeTemplate, glib::Properties)]
#[template(file = "data/main_panel.blp")] #[template(file = "data/main_panel.blp")]
#[properties(wrapper_type = super::MainPanel)]
pub struct MainPanel { pub struct MainPanel {
#[template_child] #[template_child]
pub overlay: TemplateChild<gtk::Overlay>, pub overlay: TemplateChild<gtk::Overlay>,
#[template_child]
pub search_box: TemplateChild<gtk::SearchEntry>,
#[template_child]
pub view_dropdown: TemplateChild<gtk::DropDown>,
#[template_child]
pub search_button: TemplateChild<gtk::Button>,
#[template_child]
pub new_button: TemplateChild<gtk::Button>,
#[template_child]
pub edit_button: TemplateChild<gtk::Button>,
#[template_child]
pub delete_button: TemplateChild<gtk::Button>,
#[template_child]
pub prev_page_button: TemplateChild<gtk::Button>,
#[template_child]
pub page_label: TemplateChild<gtk::Entry>,
#[template_child]
pub next_page_button: TemplateChild<gtk::Button>,
#[template_child]
pub scroll_overlay: TemplateChild<gtk::Overlay>,
#[template_child]
pub alias_list_view: TemplateChild<gtk::ListView>,
#[template_child]
pub alias_item_factory: TemplateChild<gtk::SignalListItemFactory>,
#[template_child]
pub alias_selection_model: TemplateChild<gtk::SingleSelection>,
#[template_child]
pub no_content_label: TemplateChild<gtk::Label>,
#[template_child]
pub error_overlay: TemplateChild<gtk::Box>,
#[template_child]
pub error_refresh_button: TemplateChild<gtk::Button>,
#[template_child]
pub error_close_button: TemplateChild<gtk::Button>,
#[template_child]
pub error_label: TemplateChild<gtk::Label>,
#[template_child]
pub loading_overlay: TemplateChild<gtk::Box>,
#[property(get, construct_only)]
pub application: RefCell<application::Application>,
} }
#[gtk::template_callbacks] #[gtk::template_callbacks]
impl MainPanel { impl MainPanel {}
}
#[glib::object_subclass] #[glib::object_subclass]
impl ObjectSubclass for MainPanel { impl ObjectSubclass for MainPanel {
@ -34,21 +73,20 @@ mod imp {
} }
} }
impl ObjectImpl for MainPanel { #[glib::derived_properties]
fn signals() -> &'static [Signal] { impl ObjectImpl for MainPanel {}
static SIGNALS: Lazy<Vec<Signal>> = Lazy::new(||
vec![Signal::builder("logged-out").build(),
Signal::builder("api-key-cleared").build()]);
SIGNALS.as_ref()
}
}
impl BoxImpl for MainPanel {} impl BoxImpl for MainPanel {}
impl WidgetImpl for MainPanel {} impl WidgetImpl for MainPanel {}
} }
glib::wrapper! { glib::wrapper! {
pub struct MainPanel(ObjectSubclass<imp::MainPanel>) pub struct MainPanel(ObjectSubclass<imp::MainPanel>)
@extends gtk::Widget, @implements gtk::Accessible, @extends gtk::Widget, gtk::Box, @implements gtk::Accessible,
gtk::Buildable, gtk::ConstraintTarget, gtk::Orientable; gtk::Buildable, gtk::ConstraintTarget, gtk::Orientable;
}
impl MainPanel {
pub fn new(app: &application::Application) -> Self {
glib::Object::builder().property("application", app).build()
}
} }

View File

@ -0,0 +1,115 @@
use crate::application;
use gtk4 as gtk;
use gtk::{glib, prelude::*, subclass::prelude::*};
mod imp {
use super::*;
use std::cell::RefCell;
use glib::subclass::Signal;
use once_cell::sync::Lazy;
#[derive(Debug, Default, glib::Properties, gtk::CompositeTemplate)]
#[template(file = "data/settings_panel.blp")]
#[properties(wrapper_type = super::SettingsPanel)]
pub struct SettingsPanel {
#[template_child]
pub overlay: TemplateChild<gtk::Overlay>,
#[template_child]
pub content_wrapper: TemplateChild<gtk::Box>,
#[template_child]
pub content: TemplateChild<gtk::Grid>,
#[template_child]
pub profile_image_button: TemplateChild<gtk::Button>,
#[template_child]
pub profile_image: TemplateChild<gtk::Image>,
#[template_child]
pub remove_picture_button: TemplateChild<gtk::Button>,
#[template_child]
pub name_label: TemplateChild<gtk::Entry>,
#[template_child]
pub edit_button: TemplateChild<gtk::Button>,
#[template_child]
pub cancel_edit_button: TemplateChild<gtk::Button>,
#[template_child]
pub stats_label: TemplateChild<gtk::Label>,
#[template_child]
pub email_label: TemplateChild<gtk::Label>,
#[template_child]
pub logout_button: TemplateChild<gtk::Button>,
#[template_child]
pub api_key_field: TemplateChild<gtk::PasswordEntry>,
#[template_child]
pub status_label: TemplateChild<gtk::Label>,
#[template_child]
pub api_key_clear_button: TemplateChild<gtk::Button>,
#[template_child]
pub loading_overlay: TemplateChild<gtk::Box>,
#[template_child]
pub error_overlay: TemplateChild<gtk::Box>,
#[template_child]
pub error_refresh_button: TemplateChild<gtk::Button>,
#[template_child]
pub error_close_button: TemplateChild<gtk::Button>,
#[template_child]
pub error_label: TemplateChild<gtk::Label>,
#[property(get, construct_only)]
pub application: RefCell<application::Application>,
}
#[gtk::template_callbacks]
impl SettingsPanel {
#[template_callback]
fn clear_api_key_clicked(&self, _: &gtk::Button) {
// TODO ensure user is prompted before clear
self.obj().emit_by_name::<()>("api-key-cleared", &[]);
}
}
#[glib::object_subclass]
impl ObjectSubclass for SettingsPanel {
const NAME: &'static str = "SettingsPanel";
type Type = super::SettingsPanel;
type ParentType = gtk::Box;
fn class_init(class: &mut Self::Class) {
class.bind_template();
class.bind_template_callbacks();
}
fn instance_init(obj: &glib::subclass::InitializingObject<Self>) {
obj.init_template();
}
}
#[glib::derived_properties]
impl ObjectImpl for SettingsPanel {
fn signals() -> &'static [Signal] {
static SIGNALS: Lazy<Vec<Signal>> = Lazy::new(||
vec![Signal::builder("logged-out").build(),
Signal::builder("api-key-cleared").build()]);
SIGNALS.as_ref()
}
fn constructed(&self) {
let api_key_opt = self.application.borrow().api_key();
if let Some(api_key) = api_key_opt {
self.api_key_field.set_text(api_key.as_str());
}
}
}
impl WidgetImpl for SettingsPanel {}
impl BoxImpl for SettingsPanel {}
}
glib::wrapper! {
pub struct SettingsPanel(ObjectSubclass<imp::SettingsPanel>)
@extends gtk::Widget, gtk::Box, @implements gtk::Accessible,
gtk::Buildable, gtk::ConstraintTarget, gtk::Orientable;
}
impl SettingsPanel {
pub fn new(app: &application::Application) -> Self {
glib::Object::builder().property("application", app).build()
}
}

View File

@ -0,0 +1,50 @@
use gtk4 as gtk;
use gtk::{glib, gio, prelude::*, subclass::prelude::*};
use crate::application;
mod imp {
use super::*;
#[derive(Default, Debug)]
pub struct TabbedWindow {
pub inner_panel: gtk::Notebook,
}
#[glib::object_subclass]
impl ObjectSubclass for TabbedWindow {
const NAME: &'static str = "TabbedWindow";
type Type = super::TabbedWindow;
type ParentType = gtk::ApplicationWindow;
}
impl ObjectImpl for TabbedWindow {
fn constructed(&self) {
self.parent_constructed();
self.obj().set_child(Some(&self.inner_panel));
}
}
impl WidgetImpl for TabbedWindow {}
impl WindowImpl for TabbedWindow {}
impl ApplicationWindowImpl for TabbedWindow {}
}
glib::wrapper! {
pub struct TabbedWindow(ObjectSubclass<imp::TabbedWindow>)
@extends gtk::Widget, gtk::Window, gtk::ApplicationWindow,
@implements gio::ActionMap, gio::ActionGroup;
}
impl TabbedWindow {
pub fn new(app: &application::Application, tabs: &[(&str, &gtk::Widget)]) -> Self {
let obj: Self = glib::Object::builder().property("application", app).build();
for tab in tabs {
obj.add_tab(tab.0, tab.1);
}
obj.clone()
}
pub fn add_tab<P: glib::IsA<gtk::Widget>>(&self, title: &str, content: &P) {
self.imp().inner_panel.append_page(content, Some(&gtk::Label::new(Some(title))));
}
}