diff --git a/dub.json b/dub.json
index 614ab67..6aa22ea 100644
--- a/dub.json
+++ b/dub.json
@@ -4,7 +4,8 @@
],
"copyright": "Copyright © 2022, Andrew Lalis",
"dependencies": {
- "gtk-d": "~>3.10.0"
+ "gtk-d": "~>3.10.0",
+ "dsh": "~>1.6.1"
},
"stringImportPaths": [
"resources"
diff --git a/dub.selections.json b/dub.selections.json
index fd7260a..65cd0e3 100644
--- a/dub.selections.json
+++ b/dub.selections.json
@@ -1,6 +1,7 @@
{
"fileVersion": 1,
"versions": {
+ "dsh": "1.6.1",
"gtk-d": "3.10.0"
}
}
diff --git a/resources/todo-ui.glade b/resources/todo-ui.glade
index 3dc8f12..656e54a 100644
--- a/resources/todo-ui.glade
+++ b/resources/todo-ui.glade
@@ -107,6 +107,7 @@
False
True
True
+
diff --git a/source/app.d b/source/app.d
index a6baf3a..d5c9f75 100644
--- a/source/app.d
+++ b/source/app.d
@@ -2,6 +2,9 @@ import model;
import std.stdio;
import std.functional : toDelegate;
+import dsh : getHomeDir;
+import std.path;
+import std.file;
import gtk.MainWindow;
import gtk.Main;
@@ -63,6 +66,7 @@ void main(string[] args) {
builder.addCallbackSymbol("onSaveAsMenuActivated", &onSaveAsMenuActivated);
builder.addCallbackSymbol("onOpenMenuActivated", &onOpenMenuActivated);
builder.addCallbackSymbol("onQuitMenuActivated", &onWindowDestroy);
+ builder.addCallbackSymbol("onAboutMenuActivated", &onAboutMenuActivated);
builder.connectSignals(null);
taskList = cast(ListBox) builder.getObject("taskList");
@@ -71,24 +75,53 @@ void main(string[] args) {
taskEntry = cast(Entry) builder.getObject("addTaskEntry");
todoModel = new ToDoModel();
- todoModel.addListener(ModelUpdateListener.of(toDelegate(&itemsUpdated)));
+ string lastOpenPath = buildPath(getHomeDir(), ".config/todo-d/last-open.txt");
+ if (exists(lastOpenPath)) {
+ import std.string : strip;
+ string lastOpenFile = readText(lastOpenPath).strip;
+ todoModel.openFromJson(lastOpenFile);
+ }
window = cast(ApplicationWindow) builder.getObject("window");
+ auto listener = new UIModelUpdateListener(window);
+ todoModel.addListener(listener);
+
+ // Trigger UI updates once before rendering the window.
+ todoModel.notifyListeners();
+ listener.fileUpdated(todoModel.getOpenFilename());
+
window.showAll();
Main.run();
}
-void itemsUpdated(ToDoItem[] items) {
- taskList.removeAll();
- foreach (item; items) {
- auto widget = new ToDoItemWidget(item, todoModel);
- auto row = new ListBoxRow();
- row.setSelectable(true);
- row.setActivatable(false);
- row.add(widget);
- taskList.add(row);
+class UIModelUpdateListener : ModelUpdateListener {
+ private ApplicationWindow window;
+
+ this(ApplicationWindow window) {
+ this.window = window;
+ }
+
+ void itemsUpdated(ToDoItem[] items) {
+ taskList.removeAll();
+ foreach (item; items) {
+ auto widget = new ToDoItemWidget(item, todoModel);
+ auto row = new ListBoxRow();
+ row.setSelectable(true);
+ row.setActivatable(false);
+ row.add(widget);
+ taskList.add(row);
+ }
+ taskList.showAll();
+ }
+
+ void fileUpdated(string filename) {
+ if (filename is null) {
+ window.setTitle("todo-d");
+ } else {
+ window.setTitle("todo-d - " ~ filename);
+ }
+ window.showAll();
}
- taskList.showAll();
}
bool taskListKeyPressed(GdkEventKey* event, Widget w) {
@@ -146,6 +179,12 @@ extern (C) void onWindowDestroy() {
Main.quit();
if (todoModel.getOpenFilename() !is null) {
todoModel.saveToJson(todoModel.getOpenFilename());
+ string configPath = buildPath(getHomeDir(), ".config/todo-d/");
+ if (!exists(configPath)) {
+ mkdirRecurse(configPath);
+ }
+ string lastOpenFile = buildPath(configPath, "last-open.txt");
+ std.file.write(lastOpenFile, todoModel.getOpenFilename());
}
}
@@ -191,3 +230,15 @@ extern (C) void onOpenMenuActivated() {
}
dialog.close();
}
+
+extern (C) void onAboutMenuActivated() {
+ import gtk.AboutDialog;
+ AboutDialog dialog = new AboutDialog();
+ dialog.setProgramName("Todo-D");
+ dialog.setLicenseType(GtkLicense.MIT_X11);
+ dialog.setAuthors(["Andrew Lalis"]);
+ dialog.setComments("A simple To-Do app written in D using GTK.");
+ dialog.setWebsite("https://github.com/andrewlalis/todo-d");
+ dialog.run();
+ dialog.destroy();
+}
diff --git a/source/model.d b/source/model.d
index d6b8534..2f87316 100644
--- a/source/model.d
+++ b/source/model.d
@@ -13,14 +13,7 @@ class ToDoItem {
interface ModelUpdateListener {
void itemsUpdated(ToDoItem[] items);
-
- static ModelUpdateListener of(void delegate(ToDoItem[]) dg) {
- return new class ModelUpdateListener {
- void itemsUpdated(ToDoItem[] items) {
- dg(items);
- }
- };
- }
+ void fileUpdated(string filename);
}
class ToDoModel {
@@ -108,7 +101,10 @@ class ToDoModel {
);
items ~= item;
}
- openFilename = filename;
+ if (openFilename != filename) {
+ openFilename = filename;
+ foreach (l; listeners) l.fileUpdated(filename);
+ }
sort!((a, b) => a.priority < b.priority)(items);
normalizePrio();
notifyListeners();
@@ -128,12 +124,16 @@ class ToDoModel {
}
j["items"] = JSONValue(itemObjs);
write(filename, toJSON(j, true));
- openFilename = filename;
+ if (openFilename != filename) {
+ openFilename = filename;
+ foreach (l; listeners) l.fileUpdated(filename);
+ }
}
void clear() {
items = [];
openFilename = null;
+ foreach (l; listeners) l.fileUpdated(openFilename);
notifyListeners();
}