starting account crud

main
Steve Ayerhart 2022-01-05 15:58:29 -05:00
parent 520e3fa609
commit 1b3203cc9f
No known key found for this signature in database
GPG Key ID: 5C815FDF3A00B8BA
20 changed files with 407 additions and 90 deletions

View File

@ -7,6 +7,12 @@
<file compressed="true" preprocess="xml-stripblanks" alias="MainAccountListView.ui">ui/MainAccountListView.ui</file>
<file compressed="true" preprocess="xml-stripblanks" alias="MainAccountListRow.ui">ui/MainAccountListRow.ui</file>
<file compressed="true" preprocess="xml-stripblanks" alias="ContentView.ui">ui/ContentView.ui</file>
<file compressed="true" preprocess="xml-stripblanks" alias="EditAccountsModal.ui">ui/EditAccountsModal.ui</file>
<file compressed="true" preprocess="xml-stripblanks" alias="EditAcountListView.ui">ui/EditAccountListView.ui</file>
<file compressed="true" preprocess="xml-stripblanks" alias="EditAccountRow.ui">ui/EditAccountRow.ui</file>
<file compressed="true" preprocess="xml-stripblanks" alias="EditAccountRowContent.ui">ui/EditAccountRowContent.ui</file>
</gresource>
</gresources>

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="ReclaimEditAccountListView" parent="ReclaimView">
<signal name="account_removal_requested" handler="on_account_removal_requested" />
<child>
<object class="GtkScrolledWindow">
<property name="hscrollbar-policy">never</property>
<property name="width-request">250</property>
<property name="vexpand">1</property>
<child>
<object class="GtkListView" id="lv">
<property name="factory">
<object class="GtkBuilderListItemFactory">
<property name="resource">/reclaim/EditAccountRow.ui</property>
</object>
</property>
<property name="model">
<object class="GtkNoSelection" id="selection_model">
<binding name="model">
<lookup name="accounts">ReclaimEditAccountListView</lookup>
</binding>
</object>
</property>
</object>
</child>
</object>
</child>
</template>
</interface>

12
data/ui/EditAccountRow.ui Normal file
View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="GtkListItem">
<property name="child">
<object class="ReclaimEditAccountRowContent">
<binding name="account">
<lookup name="item">GtkListItem</lookup>
</binding>
</object>
</property>
</template>
</interface>

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="ReclaimEditAccountRowContent" parent="AdwBin">
<property name="child">
<object class="GtkBox">
<property name="orientation">horizontal</property>
<property name="valign">center</property>
<property name="spacing">12</property>
<child type="prefix">
<object class="GtkLabel" id="account_label">
<property name="valign">center</property>
<property name="hexpand">1</property>
</object>
</child>
<child type="suffix">
<object class="GtkButton" id="delete_button">
<property name="icon-name">window-close-symbolic</property>
<property name="valign">center</property>
<signal name="clicked" handler="on_delete_button_clicked" />
<style>
<class name="flat" />
<class name="circular" />
</style>
</object>
</child>
</object>
</property>
</template>
</interface>

View File

@ -0,0 +1,76 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk" version="4.0"/>
<requires lib="libadwaita" version="1.0"/>
<template class="ReclaimEditAccountsModal" parent="AdwWindow">
<property name="title" translatable="yes">Manage Accounts</property>
<property name="modal">1</property>
<property name="default-width">360</property>
<property name="default-height">480</property>
<property name="default-widget">account_add_button</property>
<child>
<object class="GtkShortcutController">
<property name="scope">managed</property>
<child>
<object class="GtkShortcut">
<property name="trigger">Escape</property>
<property name="action">action(window.close)</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkBox">
<property name="orientation">vertical</property>
<child>
<object class="AdwHeaderBar">
<style>
<class name="flat"/>
</style>
</object>
</child>
<child>
<object class="GtkBox">
<property name="orientation">vertical</property>
<child>
<object class="GtkBox">
<property name="margin-top">12</property>
<property name="spacing">12</property>
<property name="margin-start">12</property>
<property name="margin-end">12</property>
<property name="margin-bottom">6</property>
<child>
<object class="GtkEntry" id="account_name_entry">
<property name="hexpand">1</property>
<property name="activates-default">1</property>
<property name="placeholder-text" translatable="yes">Account Name</property>
</object>
</child>
<child>
<object class="GtkButton" id="account_add_button">
<property name="sensitive">0</property>
<signal name="clicked" handler="on_new_account_requested" />
<property name="child">
<object class="AdwButtonContent">
<property name="icon-name">list-add-symbolic</property>
<property name="label" translatable="yes">Add</property>
</object>
</property>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkScrolledWindow">
<property name="margin-bottom">12</property>
<property name="vexpand">1</property>
<child>
</child>
</object>
</child>
</object>
</child>
</template>
</interface>

View File

@ -25,7 +25,7 @@
<property name="hexpand">0</property>
<property name="orientation">vertical</property>
<child>
<object class="AdwHeaderBar" id="nbtitlebar">
<object class="AdwHeaderBar" id="accounts_title_bar">
<property name="show-end-title-buttons" bind-source="leaf" bind-property="folded" bind-flags="sync-create"/>
<property name="valign">center</property>
<property name="hexpand">0</property>
@ -53,6 +53,7 @@
</child>
<child>
<object class="GtkButton" id="en_button">
<property name="action-name">win.action_edit_accounts</property>
<property name="icon-name">x-office-spreadsheet-symbolic</property>
<property name="tooltip-text" translatable="yes">Manage Accounts…</property>
</object>

View File

@ -1,4 +1,5 @@
public class Reclaim.Application : Adw.Application {
namespace Reclaim {
public class Application : Adw.Application {
public static int main (string[] args) {
var app = new Reclaim.Application ();
@ -27,14 +28,14 @@ public class Reclaim.Application : Adw.Application {
base.startup ();
// typeof (AccountListView).ensure ();
// typeof (AccountListView).ensure ();
var account_repository = new SqliteAccountRepository ();
var accounts_view_model = new AccountsViewModel (account_repository);
// var account_repository = new AccountRespository ();
// var account_view_model = new AccountViewModel (account_repository);
//
typeof (ContentView).ensure ();
new MainWindow (this);
new MainWindow (this, accounts_view_model);
}
protected override void shutdown () {
@ -47,4 +48,5 @@ public class Reclaim.Application : Adw.Application {
return 0;
}
}
}

View File

@ -1,6 +1,6 @@
namespace Reclaim {
public class Account : Object {
public int id { get; set; }
public string id { get; set; default = Uuid.string_random (); }
public string name { get; set; }
}
}

View File

@ -3,16 +3,19 @@ source_files = files(
'utils/ObservableList.vala',
'repositories/AccountRepository.vala',
'core/Account.vala',
'repositories/AccountRepository.vala',
'repositories/IAccountRepository.vala',
'repositories/SqliteAccountRepository.vala',
'ui/View.vala',
'ui/EditAccountsModal.vala',
'ui/EditAccountListView.vala',
'ui/EditAccountRowContent.vala',
'ui/ContentView.vala',
'ui/AccountViewModel.vala',
'ui/AccountsViewModel.vala',
'ui/MainAccountListView.vala',
'ui/MainWindow.vala'
)

View File

@ -1,4 +0,0 @@
namespace Reclaim {
public class AccountRepository : Object {
}
}

View File

@ -0,0 +1,14 @@
using Gee;
using GLib;
namespace Reclaim {
public interface IAccountRepository : Object {
public abstract signal void opened ();
public abstract ArrayList<Account> read_all ();
public abstract Account read (string id);
public abstract void create (Account account);
public abstract void update (Account account);
public abstract void delete (string id);
}
}

View File

@ -0,0 +1,52 @@
namespace Reclaim {
public class SqliteAccountRepository : IAccountRepository, GLib.Object {
private Sqlite.Database database;
public SqliteAccountRepository () {
var path = get_database_path ();
var status = Sqlite.Database.open (path, out database);
if (status != Sqlite.OK) {
stderr.printf (
"Can't open database: %d: %s\n",
database.errcode (),
database.errmsg ());
(GLib.Application.get_default ()).quit ();
}
}
public Gee.ArrayList<Account> read_all() {
const string query = "SELECT * FROM Account;";
var accounts = new Gee.ArrayList<Account> ();
accounts.add(new Account());
return accounts;
}
public Account read(string id) {
return new Account ();
}
public void create(Account account) {
}
public void update(Account account) {
}
public void delete (string id) {
}
private string get_database_path () {
// TODO: make configurable
string path = Path.build_path (
Path.DIR_SEPARATOR_S,
Environment.get_user_data_dir (),
Config.APP_ID,
Config.PROJECT_NAME + ".db");
return path;
}
}
}

View File

@ -1,10 +0,0 @@
namespace Reclaim {
public class AccountViewModel : Object {
public ObservableList<Account> accounts { get; default = new ObservableList<Account> (); }
public AccountRepository? repository { get; construct; }
public AccountViewModel (AccountRepository repository) {
Object(repository: repository);
}
}
}

View File

@ -0,0 +1,26 @@
namespace Reclaim {
public class AccountsViewModel : Object {
public ObservableList<Account> accounts { get; default = new ObservableList<Account> (); }
public IAccountRepository? repository { get; construct; }
public AccountsViewModel (IAccountRepository repository) {
Object(repository: repository);
}
construct {
populate_accounts ();
}
public void create_new_account (Account account) {
repository.create (account);
accounts.add (account);
}
public void populate_accounts () {
var accounts = repository.read_all ();
this.accounts.add_all (accounts);
}
}
}

View File

@ -0,0 +1,14 @@
namespace Reclaim {
[GtkTemplate (ui = "/reclaim/EditAcountListView.ui")]
public class EditAccountListView : View {
public ObservableList<Account>? accounts { get; set; }
public AccountsViewModel? accounts_view_model { get; set; }
public signal void new_account_requested (Account account);
public signal void account_removal_requested (Account account);
[GtkCallback]
public void on_account_removal_requested (Account account) {
}
}
}

View File

@ -0,0 +1,8 @@
[GtkTemplate (ui = "/reclaim/EditAccountListRowContent.ui")]
namespace Reclaim {
public class EditAccountListRowContent : Adw.Bin {
public signal void clicked ();
public AccountsViewModel? accounts {get; set;}
}
}

View File

@ -0,0 +1,36 @@
namespace Reclaim {
[GtkTemplate (ui = "/reclaim/EditAccountsModal.ui")]
public class EditAccountsModal : Adw.Window {
public unowned MainWindow main_window = null;
public AccountsViewModel accounts_view_model { get; set; }
[GtkChild]
public unowned Gtk.Entry account_name_entry;
[GtkChild]
public unowned Gtk.Button account_add_button;
public EditAccountsModal (MainWindow main_window, AccountsViewModel accounts_view_model) {
Object (
accounts_view_model: accounts_view_model
);
this.main_window = main_window;
set_modal (true);
set_transient_for (main_window);
account_name_entry.notify["text"].connect (() => {
account_add_button.sensitive = account_name_entry.get_text () != "";
});
}
[GtkCallback]
void on_new_account_requested () {
var account = new Account ();
account.name = account_name_entry.text;
accounts_view_model.create_new_account (account);
account_name_entry.text = "";
}
}
}

View File

@ -1,5 +1,7 @@
namespace Reclaim {
[GtkTemplate (ui = "/reclaim/MainAccountListView.ui")]
public class MainAccountListView : View {
public ObservableList<Account>? accounts { get; set; }
public AccountsViewModel? accounts_view_model { get; set; }
}
}

View File

@ -4,12 +4,33 @@ namespace Reclaim {
public Reclaim.Application app { get; construct; }
public MainWindow? main_window { get; set; }
public Adw.Leaflet? leaflet { get; set; }
public AccountsViewModel accounts_view_model {get; construct; }
public MainWindow (Reclaim.Application app) {
public SimpleActionGroup actions { get; construct; }
public MainWindow (Reclaim.Application app, AccountsViewModel accounts_view_model) {
GLib.Object (
application: app,
accounts_view_model: accounts_view_model,
title: "Reclaim"
);
}
construct {
actions = new SimpleActionGroup ();
actions.add_action_entries(
{
{"action_edit_accounts", action_edit_accounts}
},
this);
insert_action_group ("win", actions);
}
public void action_edit_accounts () {
var edit_accounts_modal = new EditAccountsModal (this, accounts_view_model);
edit_accounts_modal.show ();
}
}
}

View File

@ -1,36 +1,36 @@
namespace Reclaim {
public class ObservableList<T> : Object, ListModel {
List<T> data = new List<T> ();
Gee.ArrayList<T> data = new Gee.ArrayList<T> ();
public void add (T item) {
var position = data.length ();
var position = data.size;
data.append (item);
data.add (item);
items_changed (position, 0, 1);
}
public void add_all (List<T> items) {
var position = data.length ();
public void add_all (Gee.ArrayList<T> items) {
var position = data.size;
foreach (var item in items)
data.append (item);
data.add (item);
items_changed (position, 0, items.length ());
items_changed (position, 0, items.size);
}
public new T @get (int index) {
return data.get (index);
}
public void remove_all () {
var current_size = data.length ();
data = new List<T> ();
var current_size = data.size;
data = new Gee.ArrayList<T> ();
items_changed (0, current_size, 0);
}
public new T @get (uint index) {
return data.nth_data (index);
}
public bool remove (T item) {
var position = data.index (item);
var position = data.index_of (item);
if (position == -1)
return false;
@ -42,7 +42,7 @@ namespace Reclaim {
}
Object? get_item (uint position) {
return this[position] as Object;
return get ((int)position) as Object;
}
Type get_item_type () {
@ -50,7 +50,7 @@ namespace Reclaim {
}
uint get_n_items () {
return data.length ();
return data.size;
}
}
}