accounts: bind add dialog

This commit is contained in:
Bilal Elmoussaoui 2020-11-08 14:16:34 +01:00
parent 2ba7e0f54d
commit 5c9dc36e1b
19 changed files with 200 additions and 340 deletions

View file

@ -5,7 +5,6 @@
<!-- Accounts -->
<file compressed="true" preprocess="xml-stripblanks" alias="add_account.ui">resources/ui/add_account.ui</file>
<file compressed="true" preprocess="xml-stripblanks" alias="accounts_list.ui">resources/ui/accounts_list.ui</file>
<!-- Providers -->
<file compressed="true" preprocess="xml-stripblanks" alias="provider_row.ui">resources/ui/provider_row.ui</file>

View file

@ -1,3 +1,24 @@
.providers-list {
background-color: @theme_base_bg;
background-color: @theme_base_bg;
margin-top: 20px;
}
.provider-row {
margin-top: 12px;
margin-bottom: 12px;
}
.provider-row:hover {
background-color: @theme_base_bg;
}
.provider-row:active {
background: @theme_base_bg;
box-shadow: none;
}
.provider-row .title-2 {
margin-bottom: 8px;
}
.account-row {
padding: 12px;
}

View file

@ -1,33 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<object class="GtkPopoverMenu" id="more_actions_popover">
<child>
<object class="GtkBox">
<property name="valign">center</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkModelButton" id="edit_btn">
<property name="receives_default">False</property>
<property name="margin-left">6</property>
<property name="margin-right">6</property>
<property name="margin-top">6</property>
<property name="margin-bottom">6</property>
<property name="text" translatable="yes">Edit</property>
</object>
</child>
<child>
<object class="GtkModelButton" id="delete_btn">
<property name="receives_default">False</property>
<property name="margin-left">6</property>
<property name="margin-right">6</property>
<property name="margin-top">6</property>
<property name="margin-bottom">6</property>
<property name="text" translatable="yes">Delete</property>
</object>
</child>
</object>
</child>
</object>
<menu id="account-menu">
<item>
<attribute name="label">Edit</attribute>
<attribute name="action">account.edit</attribute>
</item>
<item>
<attribute name="label">Delete</attribute>
<attribute name="action">account.delete</attribute>
</item>
</menu>
<object class="GtkListBoxRow" id="account_row">
<property name="can_focus">True</property>
<property name="valign">center</property>
@ -35,46 +17,10 @@
<child>
<object class="GtkBox">
<child>
<object class="GtkMenuButton" id="more_actions_btn">
<property name="receives_default">False</property>
<object class="GtkLabel" id="username_label">
<property name="halign">start</property>
<property name="valign">center</property>
<property name="popover">more_actions_popover</property>
<child>
<object class="GtkImage" id="more_actions_img">
<property name="icon-name">view-more-symbolic</property>
</object>
</child>
<style>
<class name="image-button"/>
<class name="flat"/>
</style>
</object>
</child>
<child>
<object class="GtkButton" id="copy_btn">
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Copy PIN to clipboard</property>
<property name="valign">center</property>
<property name="relief">none</property>
<child>
<object class="GtkStack" id="copy_btn_stack">
<property name="halign">center</property>
<property name="valign">center</property>
<child>
<object class="GtkImage" id="copy_img">
<property name="icon-name">edit-copy-symbolic</property>
</object>
</child>
<child>
<object class="GtkImage" id="ok_image">
<property name="icon-name">emblem-ok-symbolic</property>
</object>
</child>
</object>
</child>
<style>
<class name="flat"/>
</style>
<property name="ellipsize">end</property>
</object>
</child>
<child>
@ -87,10 +33,46 @@
</object>
</child>
<child>
<object class="GtkLabel" id="username_label">
<property name="halign">start</property>
<property name="valign">center</property>
<property name="ellipsize">end</property>
<object class="GtkBox">
<property name="hexpand">True</property>
<property name="halign">end</property>
<child>
<object class="GtkButton" id="copy_btn">
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Copy PIN to clipboard</property>
<property name="valign">center</property>
<child>
<object class="GtkStack" id="copy_btn_stack">
<property name="halign">center</property>
<property name="valign">center</property>
<child>
<object class="GtkImage" id="copy_img">
<property name="icon-name">edit-copy-symbolic</property>
</object>
</child>
<child>
<object class="GtkImage" id="ok_image">
<property name="icon-name">emblem-ok-symbolic</property>
</object>
</child>
</object>
</child>
<style>
<class name="image-button" />
</style>
</object>
</child>
<child>
<object class="GtkMenuButton">
<property name="receives_default">False</property>
<property name="valign">center</property>
<property name="menu-model">account-menu</property>
<property name="icon-name">view-more-symbolic</property>
</object>
</child>
<style>
<class name="linked" />
</style>
</object>
</child>
</object>

View file

@ -1,32 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<object class="GtkBox" id="accounts_list">
<property name="orientation">vertical</property>
<child>
<object class="GtkBox">
<child>
<object class="GtkImage">
<property name="pixel_size">96</property>
<property name="icon-name">image-missing-symbolic</property>
</object>
</child>
<child>
<object class="GtkLabel" id="provider_name">
<property name="label" translatable="yes">Some random provider</property>
<style>
<class name="provider-name"/>
</style>
</object>
</child>
</object>
</child>
<child>
<object class="GtkListBox" id="listbox">
<property name="selection_mode">none</property>
<style>
<class name="frame"/>
</style>
</object>
</child>
</object>
</interface>

View file

@ -16,6 +16,14 @@
</style>
</object>
</child>
<child>
<object class="GtkListBox" id="accounts_list">
<property name="vexpand">True</property>
<style>
<class name="content" />
</style>
</object>
</child>
</object>
</child>
<style>

View file

@ -7,10 +7,14 @@
<child>
<object class="GtkScrolledWindow">
<property name="vexpand">True</property>
<property name="min-content-width">340</property>
<child>
<object class="HdyClamp">
<child>
<object class="GtkListBox" id="providers_list">
<property name="selection-mode">none</property>
<property name="margin-start">6</property>
<property name="margin-end">6</property>
<child type="placeholder">
<object class="GtkBox">
<property name="halign">center</property>

View file

@ -54,6 +54,16 @@
<child>
<object class="GtkBox" id="container">
<property name="vexpand">True</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkSearchBar" id="search_bar">
<child>
<object class="GtkSearchEntry" id="search_entry">
</object>
</child>
</object>
</child>
</object>
</child>
</object>

View file

@ -1,5 +1,5 @@
use crate::config;
use crate::models::Account;
use crate::models::{Account, Provider, ProvidersModel};
use crate::widgets::{AddAccountDialog, View, Window};
use gio::prelude::*;
use glib::subclass;
@ -7,18 +7,19 @@ use glib::subclass::prelude::*;
use glib::translate::*;
use gtk::prelude::*;
use gtk::subclass::prelude::{ApplicationImpl, ApplicationImplExt, GtkApplicationImpl};
use std::cell::RefCell;
use std::env;
use std::{cell::RefCell, rc::Rc};
use glib::{Receiver, Sender};
pub enum Action {
ViewAccounts,
AccountCreated(Account),
AccountCreated(Account, Provider),
OpenAddAccountDialog,
}
pub struct ApplicationPrivate {
window: RefCell<Option<Window>>,
model: Rc<ProvidersModel>,
sender: Sender<Action>,
receiver: RefCell<Option<Receiver<Action>>>,
}
@ -34,11 +35,13 @@ impl ObjectSubclass for ApplicationPrivate {
fn new() -> Self {
let (sender, r) = glib::MainContext::channel(glib::PRIORITY_DEFAULT);
let receiver = RefCell::new(Some(r));
let model = Rc::new(ProvidersModel::new());
Self {
window: RefCell::new(None),
sender,
receiver,
model,
}
}
}
@ -134,7 +137,7 @@ impl Application {
fn create_window(&self) -> Window {
let self_ = ApplicationPrivate::from_instance(self);
let window = Window::new(self_.sender.clone(), &self.clone());
let window = Window::new(self_.model.clone(), self_.sender.clone(), &self.clone());
window
}
@ -143,12 +146,13 @@ impl Application {
let self_ = ApplicationPrivate::from_instance(self);
match action {
Action::OpenAddAccountDialog => {
let dialog = AddAccountDialog::new(self_.sender.clone());
let dialog = AddAccountDialog::new(self_.model.clone(), self_.sender.clone());
//dialog.widget.set_transient_for(Some(&self_.window));
dialog.widget.show();
}
Action::ViewAccounts => (), //self.set_view(View::Accounts),
Action::AccountCreated(account) => {
Action::AccountCreated(account, provider) => {
self_.model.add_account(&account, &provider);
println!("{:#?}", account);
}
};

View file

@ -44,14 +44,12 @@ sources = files(
'helpers/mod.rs',
'helpers/qrcode.rs',
'models/account.rs',
'models/accounts.rs',
'models/database.rs',
'models/favicon.rs',
'models/mod.rs',
'models/provider.rs',
'models/providers.rs',
'widgets/accounts/add.rs',
'widgets/accounts/list.rs',
'widgets/accounts/mod.rs',
'widgets/providers/list.rs',
'widgets/providers/mod.rs',

View file

@ -1,109 +0,0 @@
use super::account::Account;
use super::provider::Provider;
use gio::prelude::*;
use std::cell::RefCell;
#[derive(Clone, PartialEq)]
pub enum SortOrder {
Asc,
Desc,
}
#[derive(Clone, PartialEq)]
pub enum SortBy {
Name,
Date,
}
impl From<&str> for SortBy {
fn from(sortby: &str) -> Self {
match sortby {
"name" => SortBy::Name,
"date" => SortBy::Date,
_ => SortBy::Name,
}
}
}
pub struct AccountsModel {
pub model: gio::ListStore,
sort_order: RefCell<SortOrder>,
sort_by: RefCell<SortBy>,
provider: Provider,
}
impl AccountsModel {
pub fn from_provider(provider: &Provider) -> Self {
let gio_model = gio::ListStore::new(Account::static_type());
let model = Self {
model: gio_model,
sort_order: RefCell::new(SortOrder::Desc),
sort_by: RefCell::new(SortBy::Name),
provider: provider.clone(),
};
model.init();
model
}
fn init(&self) {
// fill in the accounts from the database
/*let accounts = database::get_accounts_by_provider(self.provider.clone()).unwrap();
for account in accounts.into_iter() {
self.add_account(&account);
}*/
}
fn add_account(&self, account: &Account) {
let sort_by = self.sort_by.clone();
let sort_order = self.sort_order.clone();
self.model.insert_sorted(account, move |a, b| {
Self::accounts_cmp(a, b, sort_by.borrow().clone(), sort_order.borrow().clone())
});
}
pub fn get_count(&self) -> u32 {
self.model.get_n_items()
}
pub fn set_sorting(&self, sort_by: Option<SortBy>, sort_order: Option<SortOrder>) {
let sort_by = match sort_by {
Some(sort_by) => {
self.sort_by.replace(sort_by.clone());
sort_by
}
None => self.sort_by.borrow().clone(),
};
let sort_order = match sort_order {
Some(sort_order) => {
self.sort_order.replace(sort_order.clone());
sort_order
}
None => self.sort_order.borrow().clone(),
};
self.model
.sort(move |a, b| Self::accounts_cmp(a, b, sort_by.clone(), sort_order.clone()));
}
fn accounts_cmp(
a: &glib::Object,
b: &glib::Object,
sort_by: SortBy,
sort_order: SortOrder,
) -> std::cmp::Ordering {
let mut account_a: &Account = a.downcast_ref::<Account>().unwrap();
let mut account_b: &Account = b.downcast_ref::<Account>().unwrap();
if sort_order == SortOrder::Desc {
let tmp = account_a;
account_a = account_b;
account_b = tmp;
}
/*match sort_by {
SortBy::Name => account_a.get_title().cmp(&account_b.get_title()),
SortBy::Date => account_a.get_created_at().cmp(&account_b.get_created_at()),
}*/
account_a.name().cmp(&account_b.name())
}
}

View file

@ -1,5 +1,4 @@
mod account;
mod accounts;
mod algorithm;
pub mod database;
mod favicon;
@ -7,7 +6,6 @@ mod provider;
mod providers;
pub use self::account::Account;
pub use self::accounts::AccountsModel;
pub use self::algorithm::Algorithm;
pub use self::favicon::{FaviconError, FaviconScrapper};
pub use self::provider::Provider;

View file

@ -5,6 +5,7 @@ use crate::schema::providers;
use anyhow::Result;
use diesel::QueryDsl;
use diesel::RunQueryDsl;
use gio::prelude::*;
use gio::FileExt;
use glib::subclass;
use glib::subclass::prelude::*;
@ -47,7 +48,7 @@ pub struct ProviderPriv {
pub website: RefCell<Option<String>>,
pub help_url: RefCell<Option<String>>,
pub image_uri: RefCell<Option<String>>,
pub accounts: RefCell<Vec<Account>>,
pub accounts: gio::ListStore,
}
static PROPERTIES: [subclass::Property; 8] = [
@ -58,17 +59,11 @@ static PROPERTIES: [subclass::Property; 8] = [
glib::ParamSpec::string(name, "name", "Name", None, glib::ParamFlags::READWRITE)
}),
subclass::Property("accounts", |name| {
glib::ParamSpec::value_array(
glib::ParamSpec::object(
name,
"accounts",
"accounts",
&glib::ParamSpec::object(
"account",
"account",
"account",
Account::static_type(),
glib::ParamFlags::READWRITE,
),
gio::ListModel::static_type(),
glib::ParamFlags::READWRITE,
)
}),
@ -142,7 +137,7 @@ impl ObjectSubclass for ProviderPriv {
image_uri: RefCell::new(None),
algorithm: RefCell::new(Algorithm::OTP.to_string()),
period: Cell::new(30),
accounts: RefCell::new(Vec::new()),
accounts: gio::ListStore::new(Account::static_type()),
}
}
}
@ -272,7 +267,7 @@ impl Provider {
.map(From::from)
.map(|p: Provider| {
let accounts = Account::load(&p).unwrap();
p.set_accounts(accounts);
accounts.iter().for_each(|a| p.add_account(a));
p
})
.collect::<Vec<Provider>>();
@ -375,13 +370,20 @@ impl Provider {
pub fn has_accounts(&self) -> bool {
let priv_ = ProviderPriv::from_instance(self);
!priv_.accounts.borrow().is_empty()
priv_.accounts.get_n_items() != 0
}
fn set_accounts(&self, accounts: Vec<Account>) {
pub fn add_account(&self, account: &Account) {
let priv_ = ProviderPriv::from_instance(self);
priv_.accounts.borrow_mut().clone_from(&accounts);
priv_.accounts.append(account);
}
pub fn accounts(&self) -> &gio::ListStore {
let priv_ = ProviderPriv::from_instance(self);
&priv_.accounts
}
fn remove_account(&self, account: &Account) {}
}
impl From<DiProvider> for Provider {

View file

@ -1,3 +1,4 @@
use super::account::Account;
use super::provider::Provider;
use gio::prelude::*;
use glib::StaticType;
@ -53,10 +54,26 @@ impl ProvidersModel {
}
pub fn add_provider(&self, provider: &Provider) {
//let accounts_model = AccountsModel::from_provider(&provider);
self.model.append(provider);
}
pub fn add_account(&self, account: &Account, provider: &Provider) {
let mut found = false;
for pos in 0..self.count() {
let obj = self.model.get_object(pos).unwrap();
let p = obj.downcast_ref::<Provider>().unwrap();
if p.id() == provider.id() {
found = true;
p.add_account(account);
break;
}
}
if !found {
provider.add_account(account);
self.add_provider(provider);
}
}
pub fn count(&self) -> u32 {
self.model.get_n_items()
}

View file

@ -29,7 +29,7 @@ pub struct AddAccountDialog {
}
impl AddAccountDialog {
pub fn new(global_sender: Sender<Action>) -> Rc<Self> {
pub fn new(model: Rc<ProvidersModel>, global_sender: Sender<Action>) -> Rc<Self> {
let builder = gtk::Builder::from_resource("/com/belmoussaoui/Authenticator/add_account.ui");
get_widget!(builder, libhandy::Window, add_dialog);
@ -44,7 +44,7 @@ impl AddAccountDialog {
sender,
receiver,
actions,
model: Rc::new(ProvidersModel::new()),
model,
selected_provider: Rc::new(RefCell::new(None)),
});
@ -71,7 +71,7 @@ impl AddAccountDialog {
token_entry.connect_changed(validate_entries);
}
fn scan_qr(&self) {
fn scan_qr(&self) -> Result<()> {
qrcode::screenshot_area(
clone!(@strong self.builder as builder, @strong self.model as model,
@strong self.sender as sender => move |screenshot| {
@ -86,8 +86,8 @@ impl AddAccountDialog {
}
}
}),
)
.unwrap();
)?;
Ok(())
}
fn save(&self) -> Result<()> {
@ -123,7 +123,10 @@ impl AddAccountDialog {
.unwrap();
let account = Account::create(&username, &token, provider.id())?;
send!(self.global_sender, Action::AccountCreated(account));
send!(
self.global_sender,
Action::AccountCreated(account, provider)
);
Ok(())
}
@ -235,9 +238,13 @@ impl AddAccountDialog {
}
AddAccountAction::SetProvider(p) => self.set_provider(p),
AddAccountAction::Save => {
self.save().unwrap();
if self.save().is_ok() {
self.widget.close();
}
}
AddAccountAction::ScanQR => {
self.scan_qr();
}
AddAccountAction::ScanQR => self.scan_qr(),
};
glib::Continue(true)
}

View file

@ -1,68 +0,0 @@
use gtk::prelude::*;
use glib::Sender;
use crate::application::Action;
use crate::models::{Account, AccountsModel, Provider};
use crate::widgets::accounts::AccountRow;
pub struct AccountsList<'a> {
pub widget: gtk::Box,
builder: gtk::Builder,
sender: Sender<Action>,
model: &'a AccountsModel,
provider: &'a Provider,
}
/*
ProvidersList -> Vec<AccountsList>
*/
impl<'a> AccountsList<'a> {
pub fn new(model: &'a AccountsModel, provider: &'a Provider, sender: Sender<Action>) -> Self {
let builder =
gtk::Builder::from_resource("/com/belmoussaoui/Authenticator/accounts_list.ui");
get_widget!(builder, gtk::Box, accounts_list);
let accounts = Self {
widget: accounts_list,
builder,
sender,
model,
provider,
};
accounts.init();
accounts
}
fn init(&self) {
get_widget!(self.builder, gtk::Label, provider_name);
provider_name.set_text(&self.provider.name());
get_widget!(self.builder, gtk::ListBox, listbox);
listbox.bind_model(
Some(&self.model.model),
Some(Box::new(
clone!(@strong self.sender as sender => move |account: &glib::Object| {
let account: &Account = account
.downcast_ref::<Account>()
.unwrap();
let row = AccountRow::new(account, sender.clone());
/*row.set_on_click_callback(move |_, _| {
// sender.send(Action::LoadChapter(chapter.clone())).unwrap();
Inhibit(false)
});*/
row.widget.upcast::<gtk::Widget>()
}),
)),
);
listbox.set_header_func(Some(Box::new(
move |row1: &gtk::ListBoxRow, row2: Option<&gtk::ListBoxRow>| {
if row2.is_some() {
let separator = gtk::Separator::new(gtk::Orientation::Horizontal);
row1.set_header(Some(&separator));
}
},
)));
}
}

View file

@ -1,7 +1,5 @@
mod add;
mod list;
mod row;
pub use self::add::AddAccountDialog;
pub use self::list::AccountsList;
pub use self::row::AccountRow;

View file

@ -2,6 +2,6 @@ mod accounts;
mod providers;
mod window;
pub use self::accounts::{AccountsList, AddAccountDialog};
pub use self::accounts::AddAccountDialog;
pub use self::providers::ProvidersList;
pub use self::window::{View, Window};

View file

@ -1,9 +1,9 @@
use crate::application::Action;
use crate::models::{Account, Provider, ProvidersModel};
use crate::widgets::accounts::AccountRow;
use glib::Sender;
use gtk::prelude::*;
use crate::application::Action;
use crate::models::{Provider, ProvidersModel};
pub struct ProvidersList {
pub widget: gtk::Box,
builder: gtk::Builder,
@ -36,11 +36,13 @@ impl ProvidersList {
providers_list.bind_model(
Some(&providers_model),
Some(Box::new(move |obj| {
let provider = obj.downcast_ref::<Provider>().unwrap();
let row = ProviderRow::new(provider);
row.widget.upcast::<gtk::Widget>()
})),
Some(Box::new(
clone!(@strong self.sender as sender => move |obj| {
let provider = obj.downcast_ref::<Provider>().unwrap();
let row = ProviderRow::new(provider, sender.clone());
row.widget.upcast::<gtk::Widget>()
}),
)),
);
}
}
@ -49,16 +51,18 @@ pub struct ProviderRow<'a> {
pub widget: gtk::ListBoxRow,
provider: &'a Provider,
builder: gtk::Builder,
sender: Sender<Action>,
}
impl<'a> ProviderRow<'a> {
pub fn new(provider: &'a Provider) -> Self {
pub fn new(provider: &'a Provider, sender: Sender<Action>) -> Self {
let builder =
gtk::Builder::from_resource("/com/belmoussaoui/Authenticator/provider_row.ui");
get_widget!(builder, gtk::ListBoxRow, provider_row);
let row = Self {
widget: provider_row,
builder,
sender,
provider,
};
row.init();
@ -72,5 +76,23 @@ impl<'a> ProviderRow<'a> {
.bind_property("name", &name, "label")
.flags(glib::BindingFlags::DEFAULT | glib::BindingFlags::SYNC_CREATE)
.build();
get_widget!(self.builder, gtk::ListBox, accounts_list);
accounts_list.bind_model(
Some(self.provider.accounts()),
Some(Box::new(
clone!(@strong self.sender as sender => move |account: &glib::Object| {
let account: &Account = account
.downcast_ref::<Account>()
.unwrap();
let row = AccountRow::new(account, sender.clone());
/*row.set_on_click_callback(move |_, _| {
// sender.send(Action::LoadChapter(chapter.clone())).unwrap();
Inhibit(false)
});*/
row.widget.upcast::<gtk::Widget>()
}),
)),
);
}
}

View file

@ -11,6 +11,7 @@ use glib::{signal::Inhibit, Sender};
use gtk::prelude::*;
use gtk::subclass::prelude::{WidgetImpl, WindowImpl};
use libhandy::prelude::*;
use std::rc::Rc;
#[derive(PartialEq, Debug)]
pub enum View {
@ -34,7 +35,6 @@ impl ObjectSubclass for WindowPrivate {
fn new() -> Self {
let builder = gtk::Builder::from_resource("/com/belmoussaoui/Authenticator/window.ui");
let settings = gio::Settings::new(APP_ID);
Self { builder, settings }
}
}
@ -61,7 +61,7 @@ glib_wrapper! {
}
impl Window {
pub fn new(sender: Sender<Action>, app: &Application) -> Self {
pub fn new(model: Rc<ProvidersModel>, sender: Sender<Action>, app: &Application) -> Self {
let window = glib::Object::new(Window::static_type(), &[("application", app)])
.unwrap()
.downcast::<Window>()
@ -71,7 +71,7 @@ impl Window {
if PROFILE == "Devel" {
window.get_style_context().add_class("devel");
}
window.init(sender.clone());
window.init(model, sender.clone());
window.setup_actions(sender.clone());
window.set_view(View::Accounts); // Start by default in an empty state
window
@ -91,7 +91,7 @@ impl Window {
}
}
fn init(&self, sender: Sender<Action>) {
fn init(&self, model: Rc<ProvidersModel>, sender: Sender<Action>) {
let self_ = WindowPrivate::from_instance(self);
// load latest window state
window_state::load(&self, &self_.settings);
@ -105,19 +105,18 @@ impl Window {
get_widget!(builder, gtk::ShortcutsWindow, shortcuts);
self.set_help_overlay(Some(&shortcuts));
let providers_model = ProvidersModel::new();
get_widget!(self_.builder, libhandy::Leaflet, deck);
let providers_list = ProvidersList::new(&providers_model, sender.clone());
let providers_list = ProvidersList::new(&model, sender.clone());
get_widget!(self_.builder, gtk::Box, container);
container.append(&providers_list.widget);
/*get_widget!(self.builder, gtk::Box, providers_container);
providers_container.append(&providers_list.widget);
if providers_list.model.borrow().get_count() != 0 {
send!(self.sender, Action::ViewAccounts);
}*/
get_widget!(self_.builder, gtk::SearchBar, search_bar);
get_widget!(self_.builder, gtk::ToggleButton, search_btn);
search_btn
.bind_property("active", &search_bar, "search-mode-enabled")
.flags(glib::BindingFlags::BIDIRECTIONAL | glib::BindingFlags::SYNC_CREATE)
.build();
get_widget!(self_.builder, libhandy::Leaflet, deck);
libhandy::ApplicationWindowExt::set_child(self, Some(&deck));
}