Added resources.

This commit is contained in:
Andrew Lalis 2022-04-28 22:24:47 +02:00
parent 11080210c1
commit 710379c19f
5 changed files with 418 additions and 114 deletions

6
gresource.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<gresources>
<gresource prefix="/ui">
<file>todo-ui.glade</file>
</gresource>
</gresources>

View File

@ -1,16 +1,99 @@
import std.stdio;
import model;
import view : setupUI;
import std.stdio;
import gtk.MainWindow;
import gtk.Main;
import gtk.Builder;
import gtk.Application;
import gtk.ApplicationWindow;
import gtk.Entry;
import gtk.Box;
import gtk.ListBox;
import gtk.ListBoxRow;
import gtk.Label;
import gtk.CheckButton;
import gtk.ToggleButton;
import gtk.Button;
class ToDoItemWidget : Box {
this(ToDoItem item, ToDoModel todoModel) {
super(GtkOrientation.HORIZONTAL, 5);
CheckButton button = new CheckButton();
button.setActive(item.checked);
button.addOnToggled(delegate(ToggleButton b) {
item.checked = b.getActive();
});
Label label = new Label(item.text);
label.setLineWrap(true);
label.setHalign(GtkAlign.START);
label.setValign(GtkAlign.CENTER);
this.packStart(button, false, false, 0);
Button removeButton = new Button(StockID.REMOVE);
removeButton.addOnClicked(delegate(Button b) {
todoModel.removeItem(item.priority);
});
this.packEnd(removeButton, false, false, 0);
if (todoModel.canIncrement(item)) {
Button upButton = new Button(StockID.GO_UP, delegate(Button b) {
todoModel.incrementPriority(item);
});
this.packEnd(upButton, false, false, 0);
}
if (todoModel.canDecrement(item)) {
Button downButton = new Button(StockID.GO_DOWN, delegate(Button b) {
todoModel.decrementPriority(item);
});
this.packEnd(downButton, false, false, 0);
}
this.packEnd(label, true, true, 0);
}
}
Entry taskEntry;
ListBox taskList;
ToDoModel todoModel;
void main(string[] args) {
Main.init(args);
MainWindow window = new MainWindow("To-Do");
window.setDefaultSize(300, 500);
setupUI(window);
Builder builder = new Builder("todo-ui.glade");
builder.addCallbackSymbol("onAddTask", &addTask);
builder.addCallbackSymbol("onWindowDestroy", &onWindowDestroy);
builder.addCallbackSymbol("onQuitMenuActivated", &onWindowDestroy);
builder.connectSignals(null);
taskList = cast(ListBox) builder.getObject("taskList");
taskEntry = cast(Entry) builder.getObject("addTaskEntry");
todoModel = new ToDoModel();
import std.functional : toDelegate;
todoModel.addListener(ModelUpdateListener.of(toDelegate(&itemsUpdated)));
ApplicationWindow window = cast(ApplicationWindow) builder.getObject("window");
window.showAll();
Main.run();
}
void itemsUpdated(ToDoItem[] items) {
taskList.removeAll();
foreach (item; items) {
auto widget = new ToDoItemWidget(item, todoModel);
auto row = new ListBoxRow();
row.add(widget);
taskList.add(row);
}
taskList.showAll();
}
extern (C) void addTask() {
todoModel.addItem(taskEntry.getText());
taskEntry.setText("");
}
extern (C) void onWindowDestroy() {
Main.quit();
}

View File

@ -1,6 +1,92 @@
module model;
struct ToDoItem {
string text;
int priority;
class ToDoItem {
string text;
int priority;
bool checked;
this(string text, int priority, bool checked) {
this.text = text;
this.priority = priority;
this.checked = checked;
}
}
interface ModelUpdateListener {
void itemsUpdated(ToDoItem[] items);
static ModelUpdateListener of(void delegate(ToDoItem[]) dg) {
return new class ModelUpdateListener {
void itemsUpdated(ToDoItem[] items) {
dg(items);
}
};
}
}
class ToDoModel {
private ToDoItem[] items;
private ModelUpdateListener[] listeners;
void addListener(ModelUpdateListener listener) {
listeners ~= listener;
}
void addItem(string text) {
addItem(new ToDoItem(text, 1_000_000, false));
}
void addItem(ToDoItem item) {
items ~= item;
normalizePrio();
notifyListeners();
}
void removeItem(int prio) {
import std.algorithm;
size_t idx = prio - 1; // Assume prio is normalized to start at 1.
items = items.remove(idx);
normalizePrio();
notifyListeners();
}
bool canIncrement(ToDoItem item) {
return item.priority > 1;
}
void incrementPriority(ToDoItem item) {
size_t idx = item.priority - 1;
if (idx == 0) return;
ToDoItem higher = items[idx - 1];
item.priority -= 1;
higher.priority += 1;
items[idx - 1] = item;
items[idx] = higher;
notifyListeners();
}
bool canDecrement(ToDoItem item) {
return item.priority < items.length;
}
void decrementPriority(ToDoItem item) {
size_t idx = item.priority - 1;
if (idx + 1 >= items.length) return;
ToDoItem lower = items[idx + 1];
item.priority += 1;
lower.priority -= 1;
items[idx + 1] = item;
items[idx] = lower;
notifyListeners();
}
private void normalizePrio() {
int prio = 1;
foreach (item; items) {
item.priority = prio++;
}
}
private void notifyListeners() {
foreach (l; listeners) l.itemsUpdated(this.items);
}
}

View File

@ -1,105 +0,0 @@
module view;
import model;
import gtk.Box;
import gtk.ListBox;
import gtk.ListBoxRow;
import gtk.Label;
import gtk.CheckButton;
import gtk.Window;
import gtk.ScrolledWindow;
import gtk.Entry;
import gtk.Button;
// Yes, I use global items for my application.
ListBox todoList;
Entry todoEntry;
ToDoItem[] items = [];
void setupUI(Window w) {
todoList = new ListBox();
todoEntry = new Entry();
auto vbox = new Box(GtkOrientation.VERTICAL, 5);
todoList.setSelectionMode(GtkSelectionMode.NONE);
vbox.packStart(new ScrolledWindow(todoList), true, true, 5);
auto addBox = new Box(GtkOrientation.HORIZONTAL, 5);
addBox.packStart(todoEntry, true, true, 0);
Button addButton = new Button("Add");
addButton.addOnClicked(delegate(Button b) {addItem();});
addBox.packEnd(addButton, false, false, 0);
vbox.packEnd(addBox, false, false, 0);
w.add(vbox);
}
void addItem() {
import std.algorithm;
import std.string;
string text = todoEntry.getText().strip;
if (text.length == 0) return;
int maxPrio = -1_000_000;
foreach (item; items) {
maxPrio = max(maxPrio, item.priority);
}
ToDoItem newItem = ToDoItem(text, maxPrio + 1);
items ~= newItem;
todoEntry.setText("");
normalizePriorities();
refreshList();
}
void normalizePriorities() {
int prio = 1;
foreach (item; items) {
item.priority = prio++;
}
}
void removeItem(int prio) {
import std.algorithm;
import std.array;
ToDoItem[] newItems = items.filter!(item => item.priority != prio).array;
items = newItems;
normalizePriorities();
refreshList();
}
void refreshList() {
todoList.removeAll();
foreach (item; items) {
auto widget = new ToDoItemWidget(item);
auto row = new ListBoxRow();
row.add(widget);
todoList.add(row);
}
todoList.showAll();
}
class ToDoItemWidget : Box {
private ToDoItem item;
this(ToDoItem item) {
super(GtkOrientation.HORIZONTAL, 5);
this.item = item;
CheckButton button = new CheckButton();
Label label = new Label(item.text);
label.setLineWrap(true);
label.setHalign(GtkAlign.START);
label.setValign(GtkAlign.CENTER);
this.packStart(button, false, false, 0);
Button removeButton = new Button("Remove");
removeButton.addOnClicked(delegate(Button b) {
removeItem(item.priority);
});
this.packEnd(removeButton, false, false, 0);
this.packEnd(label, true, true, 0);
}
}

234
todo-ui.glade Normal file
View File

@ -0,0 +1,234 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.2 -->
<interface>
<requires lib="gtk+" version="3.10"/>
<object class="GtkApplicationWindow" id="window">
<property name="can_focus">False</property>
<property name="window_position">center</property>
<signal name="destroy" handler="onWindowDestroy" swapped="no"/>
<child type="titlebar">
<placeholder/>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkMenuBar">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_File</property>
<property name="use_underline">True</property>
<child type="submenu">
<object class="GtkMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkImageMenuItem" id="newMenuItem">
<property name="label">gtk-new</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</object>
</child>
<child>
<object class="GtkImageMenuItem" id="openMenuItem">
<property name="label">gtk-open</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</object>
</child>
<child>
<object class="GtkImageMenuItem">
<property name="label">gtk-save</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</object>
</child>
<child>
<object class="GtkImageMenuItem">
<property name="label">gtk-save-as</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
</child>
<child>
<object class="GtkImageMenuItem" id="quitMenuItem">
<property name="label">gtk-quit</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
<signal name="activate" handler="onQuitMenuActivated" swapped="no"/>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_Edit</property>
<property name="use_underline">True</property>
<child type="submenu">
<object class="GtkMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkImageMenuItem">
<property name="label">gtk-cut</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</object>
</child>
<child>
<object class="GtkImageMenuItem">
<property name="label">gtk-copy</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</object>
</child>
<child>
<object class="GtkImageMenuItem">
<property name="label">gtk-paste</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</object>
</child>
<child>
<object class="GtkImageMenuItem">
<property name="label">gtk-delete</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_Help</property>
<property name="use_underline">True</property>
<child type="submenu">
<object class="GtkMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkImageMenuItem">
<property name="label">gtk-about</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkViewport">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkListBox" id="taskList">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="selection_mode">none</property>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkEntry" id="addTaskEntry">
<property name="visible">True</property>
<property name="can_focus">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="addTaskButton">
<property name="label">gtk-add</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
<property name="always_show_image">True</property>
<signal name="clicked" handler="onAddTask" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
</child>
</object>
</interface>