Added resources.
This commit is contained in:
parent
11080210c1
commit
710379c19f
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<gresources>
|
||||
<gresource prefix="/ui">
|
||||
<file>todo-ui.glade</file>
|
||||
</gresource>
|
||||
</gresources>
|
95
source/app.d
95
source/app.d
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,92 @@
|
|||
module model;
|
||||
|
||||
struct ToDoItem {
|
||||
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);
|
||||
}
|
||||
}
|
105
source/view.d
105
source/view.d
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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>
|
Loading…
Reference in New Issue