kapstok / NHL-DePa2 (public) (License: Unspecified) (since 2018-11-09) (hash sha1)
Design Patterns school project; a GUI using multiple Design Patterns.
List of commits:
Subject Hash Author Date (UTC)
Initial commit. 38c8818349679937ae25dde38949202ec45ea7b2 Jan Allersma 2018-09-11 20:21:57
Commit 38c8818349679937ae25dde38949202ec45ea7b2 - Initial commit.
Author: Jan Allersma
Author date (UTC): 2018-09-11 20:21
Committer name: Jan Allersma
Committer date (UTC): 2018-09-11 20:21
Parent(s):
Signing key:
Tree: 50aa9180267c0b7505f1b500f0e13868092fa160
File Lines added Lines deleted
.gitignore 20 0
README.md 14 0
dub.json 12 0
dub.selections.json 6 0
example.sav 11 0
source/app.d 42 0
source/commands/command.d 7 0
source/commands/shapeCommands.d 73 0
source/drawing/brush.d 8 0
source/drawing/shapeOptions.d 57 0
source/entities/canvas.d 143 0
source/entities/entity.d 54 0
source/entities/group.d 21 0
source/entities/shape.d 298 0
source/file.d 74 0
source/globals.d 8 0
source/history.d 31 0
source/menubar.d 155 0
File .gitignore added (mode: 100644) (index 0000000..f5374fc)
1 .dub
2 docs.json
3 __dummy.html
4 docs/
5 dpainter.so
6 dpainter.dylib
7 dpainter.dll
8 dpainter.a
9 dpainter.lib
10 dpainter-test-*
11 *.exe
12 *.o
13 *.obj
14 *.lst
15 dpainter
16 test.sav
17 source/test.sav
18 dpainter.dlangidews
19 dpainter.dlangidews.wssettings
20 dpainter.settings
File README.md added (mode: 100644) (index 0000000..0be689b)
1 # NHL-DPainter
2 This repo is a school project for Design Patterns.
3
4 use 'dub' to compile and run the code.
5
6 Each iteration of the application can be found under 'Releases'.
7
8 # TODO:
9
10 * How could 'isRemoved' not showing up everywhere in the code?
11
12 * Refractor 'shape.d'.
13
14 * Refractor 'shapeCommands.d'.
File dub.json added (mode: 100644) (index 0000000..d998cf4)
1 {
2 "name": "dpainter",
3 "authors": [
4 "Jan Allersma"
5 ],
6 "description": "A minimal D application.",
7 "copyright": "Copyright © 2017, Jan Allersma",
8 "license": "proprietary",
9 "dependencies": {
10 "gtk-d": "~>3.6.6"
11 }
12 }
File dub.selections.json added (mode: 100644) (index 0000000..979754a)
1 {
2 "fileVersion": 1,
3 "versions": {
4 "gtk-d": "3.6.6"
5 }
6 }
File example.sav added (mode: 100644) (index 0000000..a56a457)
1 group 2
2 ornament top "rondje"
3 ellipse 100 100 20 50
4 group 3
5 rectangle 10 20 100 100
6 ornament top "group"
7 ornament top "ellipses"
8 group 2
9 ellipse 50 150 20 50
10 ellipse 70 150 20 50
11 rectangle 100 100 10 10
File source/app.d added (mode: 100644) (index 0000000..7140f21)
1 import gio.Application : GioApplication = Application;
2 import gtk.Application;
3 import gtk.ApplicationWindow;
4 import gtk.Box;
5
6 import dp.canvas;
7 import dp.menubar;
8
9 import Global = dp.globals;
10
11 class win : ApplicationWindow
12 {
13 Canvas canvas;
14
15 this(Application application)
16 {
17 super(application);
18 setTitle("DPainter");
19 setBorderWidth(10);
20
21 Box container = new Box(Orientation.VERTICAL, 15);
22
23 Box menubar = new Menubar();
24 container.add(menubar);
25
26 canvas = new Canvas(500, 500);
27 container.add(canvas.area);
28 Global.canvas = &canvas;
29
30 add(container);
31 showAll();
32 }
33 }
34
35 int main(string[] args)
36 {
37 auto application = new Application ( "org.gtkd.demo.helloworld", GApplicationFlags.FLAGS_NONE );
38 application.addOnActivate(delegate void(GioApplication app) {
39 new win(application);
40 });
41 return application.run(args);
42 }
File source/commands/command.d added (mode: 100644) (index 0000000..baacb6b)
1 module dp.command.cmd;
2
3 // Should be an interface. Made it a class to prevent the compiler from complaining.
4 abstract class Command {
5 public abstract void execute();
6 public abstract void undo();
7 }
File source/commands/shapeCommands.d added (mode: 100644) (index 0000000..f0bb7e2)
1 module dp.command.shape;
2
3 import dp.command.cmd;
4 import Global = dp.globals;
5 import File = dp.file;
6
7 import dp.shape;
8
9 import std.stdio; // For debug.
10
11 public class CreateCmd : Command {
12 //size_t shapePtr;
13
14 this() {
15 shapePtr = Global.canvas.childrenAmount;
16 Global.canvas.addShape();
17 File.Append(Global.canvas.getChild(shapePtr).toString());
18 }
19
20 public override void execute() {
21 //Global.canvas.getChild(shapePtr).isRemoved = false;
22 Global.canvas.repaint();
23 }
24
25 public override void undo() {
26 //Global.canvas.removeChild(shapePtr);
27 Global.canvas.repaint();
28 }
29 }
30
31 public class MoveCmd : Command {
32 // size_t shapePtr;
33 // bool executed;
34
35 this () {
36 //executed = false;
37 execute();
38 }
39
40 public override void execute() {
41 //if(!executed) {
42 //double[2] tmp = Global.canvas.getShape(shapePtr).lastPosition;
43
44 //executed = true;
45
46 //Global.canvas.getShape(shapePtr).lastPosition = Global.canvas.getShape(shapePtr).position;
47 //Global.canvas.getShape(shapePtr).position = tmp;
48 Global.canvas.getShape(shapePtr).remove();
49
50 Global.canvas.getShape(shapePtr).revive(
51 12,//Global.canvas.getShape(shapePtr).position[0],
52 177//Global.canvas.getShape(shapePtr).position[1]
53 );
54 //}
55 }
56
57 public override void undo() {
58 //if(executed) {
59 //double[2] tmp = Global.canvas.getShape(shapePtr).position;
60
61 //executed = false;
62
63 //Global.canvas.getShape(shapePtr).position = Global.canvas.getShape(shapePtr).lastPosition;
64 //Global.canvas.getShape(shapePtr).lastPosition = tmp;
65 Global.canvas.getShape(shapePtr).remove();
66
67 //Global.canvas.getShape(shapePtr).revive(
68 //Global.canvas.getShape(shapePtr).position[0],
69 //Global.canvas.getShape(shapePtr).position[1]
70 //);
71 //}
72 }
73 }
File source/drawing/brush.d added (mode: 100644) (index 0000000..8d9cd40)
1 module dp.brush;
2
3 string Shape = null;
4 int Size = 125;
5 double Red = 0.5;
6 double Green = 0.9;
7 double Blue = 0.5;
8 double Alpha = 0.8;
File source/drawing/shapeOptions.d added (mode: 100644) (index 0000000..bd1d840)
1 module dp.shapeOptions;
2
3 import gtk.Menu, gtk.MenuItem;
4 import gtk.Widget;
5 import gdk.Event;
6
7 import dp.shape;
8 import Global = dp.globals;
9
10 import std.stdio; // For debugging.
11
12 protected Shape s;
13
14 public class ShapeOptions : Menu {
15 this() {
16 super();
17 this.append(new DeleteShape());
18 this.append(new MoveShape());
19 }
20
21 // Will look like: sOptions.OfShape(s); Should be differently named.
22 public void OfShape (Shape shape) { // Shows options of param shape.
23 s = shape;
24 this.showAll();
25 this.popup(0, 0);
26 }
27 }
28
29 protected class DeleteShape : MenuItem {
30 this() {
31 super("Delete");
32 addOnButtonRelease(&relCallback);
33 }
34
35 private bool relCallback (Event event, Widget widget) {
36 s.remove();
37 Global.canvas.repaint();
38 writeln("Deleted shape.");
39 return false; // Hide ShapeOptions when button is released.
40 }
41 }
42
43 protected class MoveShape : MenuItem {
44 this() {
45 super("Move shape");
46 addOnButtonRelease(&relCallback);
47 }
48
49 private bool relCallback (Event event, Widget widget) {
50 writeln("Moved shape");
51 s.remove();
52 //s.queueRevival = true;
53 Global.canvas.repaint();
54 s.select();
55 return false; // Hide ShapeOptions when button is released.
56 }
57 }
File source/entities/canvas.d added (mode: 100644) (index 0000000..fd0ffc5)
1 module dp.canvas;
2
3 import gtk.Widget, gtk.DrawingArea;
4 import gdk.Event;
5 import cairo.Context;
6 import cairo.ImageSurface;
7
8 import std.stdio; // For debug
9
10 import dp.entity;
11
12 import dp.shape;
13 import dp.shapeOptions;
14
15 import dp.history;
16 import dp.command.shape;
17
18 /+
19 Reference:
20 https://github.com/gtkd-developers/GtkD/blob/42ef854f7cd975519926900fe326e220410c028a/demos/gtkD/TestWindow/TestDrawingArea.d
21 +/
22
23 public class Canvas : Entity {
24
25 DrawingArea area;
26 ImageSurface surface;
27 int mouseX;
28 int mouseY;
29 Shape[] shapes;
30 ShapeOptions shapeOptions;
31
32 this (int width, int height) {
33 super();
34 area = new DrawingArea(width, height);
35 shapeOptions = new ShapeOptions();
36 area.addOnButtonPress(&clickCallback);
37 area.addOnScroll(&scrollCallback);
38 surface = ImageSurface.create(CairoFormat.ARGB32, width, height);
39 area.addOnDraw(&drawCallback);
40 shapes.length = 0;
41 clearCanvas();
42 }
43
44 @property
45 public override size_t childrenAmount() {
46 return shapes.length;
47 }
48
49 public override Entity[] getChildren() {
50 return cast(Entity[]) shapes;
51 }
52
53 public Shape getShape(ulong n) {
54 return shapes[n];
55 }
56
57 public void addChild(ulong n) {
58 getChild(n).isRemoved = false;
59 }
60
61 public void removeChild(ulong n) {
62 getChild(n).isRemoved = true;
63 }
64
65 private bool clickCallback(Event event, Widget widget) {
66 // Get mouse position relative to widget..
67 area.getPointer(mouseX, mouseY);
68
69 if(Global.Brush.Shape is null) { // Brush imported from dp.shape.
70 for(int i = cast(int)shapes.length - 1; i >= 0; i -= 1) {
71 /+
72 Look FIFO wether you clicked on a Shape.
73 FIFO is required to make sure the Shape
74 most at front is selected first.
75
76 if boolean expression below is true, the
77 shape destroys itself and this function
78 stops looking for more shapes that
79 could have been clicked.
80 +/
81 if(shapes[i].CheckBounds(mouseX,mouseY)) {
82 if(event.button.button == 3) { // if right click...
83 shapeOptions.OfShape(shapes[i]);
84 }
85 break;
86 }
87 }
88 } else {
89 History.addCommand(new CreateCmd()); // Add the creation of the shape to the undo-redo history.
90 }
91 return true;
92 }
93
94 private bool drawCallback(Scoped!Context context, Widget widget) {
95 context.setSourceSurface(surface, 0, 0);
96 context.paint();
97 writeln(treeToString(0));//Debug
98 return true;
99 }
100
101 private bool scrollCallback (Event event, Widget widget) {
102 // Get mouse position relative to widget..
103 area.getPointer(mouseX, mouseY);
104
105 GdkScrollDirection dir;
106 event.getScrollDirection(dir);
107
108 /+
109 Look FIFO wether you clicked on a Shape.
110 FIFO is required to make sure the Shape
111 most at front is selected first.
112 +/
113 for(int i = cast(int)shapes.length - 1; i >= 0; i -= 1)
114 if(shapes[i].CheckBounds(mouseX, mouseY, dir))
115 break;
116
117 return true;
118 }
119
120 private void clearCanvas() {
121 Context c = Context.create(surface);
122 c.setSourceRgba(1,1,1,1);
123 c.paint();
124 }
125
126 public void repaint() {
127 clearCanvas();
128 for(int i = 0; i < shapes.length; i++)
129 shapes[i].applyToSource();
130 area.queueDraw();
131 }
132
133 public Shape addShape() {
134 size_t i = shapes.length;
135
136 shapes.length++;
137 shapes[i] = new Shape(Context.create(surface), mouseX, mouseY);
138
139 area.queueDraw();
140
141 return shapes[i];
142 }
143 }
File source/entities/entity.d added (mode: 100644) (index 0000000..6d889c6)
1 module dp.entity;
2
3 import std.stdio;
4
5 abstract class Entity {
6 protected Entity[] children;
7 public bool isRemoved = false;
8
9 this() {}
10
11 @property
12 public string type() {
13 return "entity";
14 }
15
16 @property
17 public size_t childrenAmount() {
18 return children.length;
19 }
20 /+
21 public Entity getChild() { // Debug
22 writeln(children[0]);
23 return children[0];
24 }
25 +/
26 public Entity getChild(ulong n) {
27 return getChildren()[n];
28 }
29
30 public Entity[] getChildren() {
31 return children;
32 }
33
34 public void addChild(Entity child) {
35 children.length++;
36 children[children.length - 1] = child;
37 }
38 /+
39 public void removeChild(int n) {
40 getChild(n).isRemoved = true;
41 }
42 +/
43 public string treeToString(int depth = 1) { // Laagste depth = 1.
44 string result = this.toString() ~ "\n";
45
46 for(ulong i = 0; i < childrenAmount; i++) {
47 for(int tabs = 0; tabs < depth; tabs++)
48 result ~= "\t";
49
50 result ~= getChildren[i].treeToString(++depth) ~ "\n";
51 }
52 return result;
53 }
54 }
File source/entities/group.d added (mode: 100644) (index 0000000..f03caad)
1 module dp.group;
2
3 import Global = dp.globals;
4 import std.conv;
5
6 class Group : Global.Entity {
7 private int index;
8
9 this(int index) {
10 this.index = index;
11 }
12
13 @property
14 public override string type() {
15 return "group";
16 }
17
18 public override string toString() {
19 return text(type, " ", index);
20 }
21 }
File source/entities/shape.d added (mode: 100644) (index 0000000..49c986d)
1 module dp.shape;
2
3 public import Global = dp.globals;
4
5 import cairo.Context;
6 import cairo.ImageSurface;
7
8 import std.stdio; // For debug
9 import std.conv;
10
11 import dp.history;
12 import dp.command.shape;
13
14 class Shape : Global.Entity {
15 private Context c;
16 private bool revivable;
17
18 /+
19 bounds[0][0] = minX
20 bounds[1][0] = maxX
21 bounds[0][1] = minY
22 bounds[1][1] = maxY
23 +/
24 private double[2][2] bounds;
25 private double[2] lastPos;
26
27 private string shapeType;
28
29
30 this(Context context, int x, int y) {
31 isRemoved = revivable = false;
32 c = context;
33 draw(x,y);
34 }
35
36 @property
37 public double[2] position() {
38 return bounds[0];
39 }
40
41 @property
42 public double[2] lastPosition() {
43 return lastPos;
44 }
45
46 //TMP?
47 @property
48 public double[2] lastPosition(double[2] newPos) {
49 return lastPos = newPos;
50 }
51
52 @property
53 public double[2] position(double[2] newPos) {
54 double sX = sizeX;
55 double sY = sizeY;
56
57 bounds[1][0] = newPos[0] + sX;
58 bounds[1][1] = newPos[1] + sY;
59
60 return bounds[0] = newPos;
61 }
62
63 @property
64 public double sizeX () {
65 return bounds[1][0] - bounds[0][0];
66 }
67
68 @property
69 public double sizeY () {
70 return bounds[1][1] - bounds[0][1];
71 }
72
73 @property
74 public override string type () {
75 return shapeType;
76 }
77
78 /+
79 Wait for revival of shape until first left-click.
80 Just to prevent instant movement from
81 dp.shapeOptions.MoveShape.
82 +/
83 @property
84 public bool queueRevival (bool revive) {
85 return revivable = revive;
86 }
87
88 public override string toString() {
89 return text(type, " ", bounds[0][0], " ", bounds[0][1], " ", sizeX, " ", sizeY);
90 }
91
92 public void select () {
93 c.setSourceRgba(1-Global.Brush.Red, 1-Global.Brush.Green, 1-Global.Brush.Blue, 1);
94 c.stroke();
95 c.setSourceRgba(Global.Brush.Red, Global.Brush.Green, Global.Brush.Blue, Global.Brush.Alpha);
96 }
97
98 public void remove () {
99 writeln("Shape removed. Please repaint canvas to see changes.");
100 isRemoved = true;
101 revivable = false;
102 }
103
104 public void revive (int x, int y) {
105 writeln("Im alive! Redraw me!");
106 isRemoved = false;
107 c.newPath();
108
109 final switch (type) {
110 case "rectangle":
111 c.rectangle(x, y, sizeX, sizeY);
112 c.fillPreserve();
113
114 /+
115 Recalculate the bounds, since they changed due to
116 moving the shape.
117
118 Since sizeX and sizeY change to different values
119 during recalculation of bounds, due to sizeX and
120 sizeY are based off the bounds, sizeX and sizeY
121 have to be captured before recalculation.
122 +/
123 double sX = sizeX;
124 double sY = sizeY;
125 bounds[0][0] = x;
126 bounds[1][0] = x + sX;
127 bounds[0][1] = y;
128 bounds[1][1] = y + sY;
129
130 break;
131 case "ellipse":
132 /+
133 3.141 = Pi.
134 sizeY is in diameters. Parameter arc() requires radius.
135 +/
136 c.arc(x * 2 + sizeX / 2, y + sizeY / 2, sizeY / 2 , 0 , 2 * 3.141);
137 c.fillPreserve();
138
139 /+
140 No need to scale c again. That has been preserved
141 from the first time it was drawn.
142
143 Recalculate the bounds, since they changed due to
144 moving the shape.
145
146 Since sizeX and sizeY change to different values
147 during recalculation of bounds, due to sizeX and
148 sizeY are based off the bounds, sizeX and sizeY
149 have to be captured before recalculation.
150 +/
151 double sX = sizeX;
152 double sY = sizeY;
153 bounds[0][0] = x;
154 bounds[1][0] = x + sX;
155 bounds[0][1] = y;
156 bounds[1][1] = y + sY;
157
158 break;
159 case "circle":
160 /+
161 3.141 = Pi.
162 sizeY is in diameters. Parameter arc() requires radius.
163 +/
164 c.arc(x + sizeX / 2, y + sizeY / 2, sizeY / 2 , 0 , 2 * 3.141);
165 c.fillPreserve();
166
167 /+
168 Recalculate the bounds, since they changed due to
169 moving the shape.
170
171 Since sizeX and sizeY change to different values
172 during recalculation of bounds, due to sizeX and
173 sizeY are based off the bounds, sizeX and sizeY
174 have to be captured before recalculation.
175 +/
176 double sX = sizeX;
177 double sY = sizeY;
178 bounds[0][0] = x;
179 bounds[1][0] = x + sX;
180 bounds[0][1] = y;
181 bounds[1][1] = y + sY;
182
183 break;
184
185 }
186 applyToSource();
187 Global.canvas.repaint();
188 }
189
190 /+private+/public void revive(double x, double y) { revive(cast(int)x, cast(int)y); }
191
192 private void draw(int x, int y) {
193 if(!isRemoved) {
194 shapeType = Global.Brush.Shape;
195 final switch (Global.Brush.Shape) {
196 case "rectangle":
197 c.setSourceRgba(Global.Brush.Red, Global.Brush.Green, Global.Brush.Blue, Global.Brush.Alpha);
198 c.rectangle(x - 125 / 2, y - 125 / 2, 125, 125);
199 c.fillPreserve();
200
201 bounds[0][0] = x - 125 / 2; // minX
202 bounds[1][0] = x + 125 / 2; // maxX
203 bounds[0][1] = y - 125 / 2; // minY
204 bounds[1][1] = y + 125 / 2; // maxY
205
206 break;
207 case "ellipse":
208 c.setSourceRgba(Global.Brush.Red, Global.Brush.Green, Global.Brush.Blue, Global.Brush.Alpha);
209 c.scale(0.5, 1);
210 c.arc(x*2,y,50,0,2*3.141); // 3.141 = Pi
211 c.fillPreserve();
212
213 bounds[0][0] = x - 50 / 2; // minX
214 bounds[1][0] = x + 50 / 2; // maxX
215 bounds[0][1] = y - 50; // minY
216 bounds[1][1] = y + 50; // maxY
217
218 break;
219 case "circle":
220 c.setSourceRgba(Global.Brush.Red, Global.Brush.Green, Global.Brush.Blue, Global.Brush.Alpha);
221 c.arc(x,y,50,0,2*3.141); // 3.141 = Pi
222 c.fillPreserve();
223
224 bounds[0][0] = x - 50; // minX
225 bounds[1][0] = x + 50; // maxX
226 bounds[0][1] = y - 50; // minY
227 bounds[1][1] = y + 50; // maxY
228
229 break;
230 }
231 }
232 Global.Brush.Shape = null;
233 }
234
235 public bool CheckBounds (int x, int y) {
236 if(!isRemoved) {
237 write(bounds[0][0]);write(" <- ");write(x);write(" -> ");writeln(bounds[1][0]);
238 write(bounds[0][1]);write(" <- ");write(y);write(" -> ");writeln(bounds[1][1]);
239
240 if(
241 x > bounds[0][0] && // minX
242 x < bounds[1][0] && // maxX
243 y > bounds[0][1] && // minY
244 y < bounds[1][1] // maxY
245 ) {
246 writeln("Clicked me!");
247 return true;
248 }
249 }
250 else if(revivable) {
251 lastPos = [x - sizeX / 2, y - sizeY / 2];
252 History.addCommand(new MoveCmd());
253 }
254
255 return false;
256 }
257
258 // This will be used when the user scrolls on the canvas.
259 public bool CheckBounds (int x, int y, int growContext) {
260 if(!isRemoved) {
261 //write(bounds[0][0]);write(" <- ");write(x);write(" -> ");writeln(bounds[1][0]);
262 //write(bounds[0][1]);write(" <- ");write(y);write(" -> ");writeln(bounds[1][1]);
263
264 if(
265 x > bounds[0][0] && // minX
266 x < bounds[1][0] && // maxX
267 y > bounds[0][1] && // minY
268 y < bounds[1][1] // maxY
269 ) {
270 if(growContext == 0) {
271 bounds[0][0] -= 5;
272 bounds[1][0] += 5;
273 bounds[0][1] -= 5;
274 bounds[1][1] += 5;
275 } else {
276 bounds[0][0] += 5;
277 bounds[1][0] -= 5;
278 bounds[0][1] += 5;
279 bounds[1][1] -= 5;
280 }
281
282 revive(
283 (bounds[0][0] + bounds[1][0]) / 2,
284 (bounds[0][1] + bounds[1][1]) / 2
285 );
286
287 return true;
288 }
289
290 }
291 return false;
292 }
293
294 public void applyToSource() {
295 if(!isRemoved)
296 c.fillPreserve();
297 }
298 }
File source/file.d added (mode: 100644) (index 0000000..69461ee)
1 module dp.file;
2
3 import std.stdio;
4 import std.file;
5
6 protected string[] content;
7 protected size_t contentPtr = 0;
8 protected string filename = "";
9 protected string[] tmpContent;
10
11 public void New() {
12 content.length = contentPtr = 0;
13 }
14
15 public void Open(string name) {
16 filename = name;
17 Append(readText(filename));
18 }
19
20 public void Append(string text) {
21 content.length++;
22 content[contentPtr++] = text;
23 }
24
25 public void RemoveLastRecord() {
26 content.length--;
27 }
28
29 public void Undo() {
30 if(contentPtr > 0) {
31 if(contentPtr > tmpContent.length)
32 tmpContent.length = contentPtr--;
33
34 write("contentPtr = ");writeln(contentPtr); // Debug
35 tmpContent[contentPtr] = content[contentPtr];
36 content[contentPtr--] = "";
37 }
38 else if(contentPtr > content.length)
39 contentPtr = 0;
40 /+
41 contentPtr is unsigned. Mocht het toch voorkomen for some
42 reason de ptr onder nul komt, undo() dat herstelt.
43 +/
44 }
45
46 public void Redo() {
47 if(content.length > contentPtr + 1) {
48 contentPtr++; // Don't increment in if statement; the pointer would otherwise also be incremented is the expression = false.
49 write("contentPtr = ");writeln(contentPtr); // Debug
50 content[contentPtr] = tmpContent[contentPtr];
51 tmpContent[contentPtr] = "";
52 }
53 }
54
55 public void Save() {
56 Save(filename);
57 }
58
59 public void Save(string name) {
60 string file = "";
61
62 write("contentPtr = ");writeln(contentPtr); // Debug
63 for(ulong i = 0; i < content.length-1; i++)
64 file ~= content[i] ~ "\n";
65
66 writeln("ping"); // Debug
67 file ~= content[content.length-1];
68 file.length--;
69 File f = File(name, "w");
70 f.writeln(file);
71 f.close();
72
73 content.length = contentPtr = tmpContent.length = 0;
74 }
File source/globals.d added (mode: 100644) (index 0000000..d66b5dc)
1 module dp.globals;
2
3 //public import dp.canvas;
4 public import dp.history;
5 public import dp.entity;
6 public import Brush = dp.brush;
7
8 public dp.canvas.Canvas* canvas;
File source/history.d added (mode: 100644) (index 0000000..9555e0a)
1 module dp.history;
2
3 import dp.command.cmd;
4
5 import File = dp.file;
6
7 // The Invoker in the command pattern.
8 static class History {
9 private static Command[] commands;
10 private static int cmdPtr = 0;
11
12 public static void addCommand(Command cmd) {
13 commands.length++;
14 commands[cmdPtr] = cmd;
15 cmdPtr++;
16 }
17
18 public static void undo() {
19 if(cmdPtr > 0) {
20 commands[--cmdPtr].undo();
21 File.Undo();
22 }
23 }
24
25 public static void redo() {
26 if(commands.length > cmdPtr) {
27 commands[cmdPtr++].execute();
28 File.Redo();
29 }
30 }
31 }
File source/menubar.d added (mode: 100644) (index 0000000..6a01cb0)
1 module dp.menubar;
2
3 import gtk.Box;
4 import gtk.MenuBar, gtk.Menu, gtk.MenuItem;
5 import gtk.Widget;
6 import gdk.Event;
7
8 import Global = dp.globals;
9 import File = dp.file;
10 import Brush = dp.brush;
11
12 import dp.group;
13
14 public class Menubar : Box {
15 this() {
16 super(Orientation.VERTICAL, 10);
17
18 MenuBar menubar = new MenuBar();
19
20 menubar.append(new FileMenu());
21 menubar.append(new EditMenu());
22 menubar.append(new ShapeMenu());
23 //menubar.append(new GroupMenu());
24
25 this.packStart(menubar, false, false, 0);
26 }
27 }
28
29 protected class FileMenu : MenuItem {
30 Menu fileMenu;
31
32 MenuItem newFile;
33 MenuItem saveFile;
34 MenuItem saveAsFile;
35 MenuItem openFile;
36
37 this () {
38 super("File");
39 fileMenu = new Menu();
40
41 newFile = new MenuItem("New");
42 newFile.addOnButtonPress(&newFCallback);
43 fileMenu.append(newFile);
44
45 saveFile = new MenuItem("Save");
46 saveFile.addOnButtonPress(&saveFCallback);
47 fileMenu.append(saveFile);
48
49 saveAsFile = new MenuItem("Save as");
50 saveAsFile.addOnButtonPress(&saveAsFCallback);
51 fileMenu.append(saveAsFile);
52
53 openFile = new MenuItem("Open");
54 openFile.addOnButtonPress(&openFCallback);
55 fileMenu.append(openFile);
56
57 setSubmenu(fileMenu);
58 }
59
60 private bool newFCallback(Event event, Widget widget) {
61 File.New();
62 return true;
63 }
64
65 private bool saveFCallback(Event event, Widget widget) {
66 File.Save("./" ~ "test.sav");
67 return true;
68 }
69
70 private bool saveAsFCallback(Event event, Widget widget) {
71 // File.Save() na een soort file choose dialog.
72 return true;
73 }
74
75 private bool openFCallback(Event event, Widget widget) {
76 File.Open("./" ~ "test.sav");
77 return true;
78 }
79 }
80
81 protected class EditMenu : MenuItem {
82 Menu editMenu;
83
84 MenuItem undo;
85 MenuItem redo;
86
87 this () {
88 super("Edit");
89 editMenu = new Menu();
90
91 undo = new MenuItem("Undo");
92 undo.addOnButtonPress(&undoCallback);
93 editMenu.append(undo);
94
95 redo = new MenuItem("Redo");
96 redo.addOnButtonPress(&redoCallback);
97 editMenu.append(redo);
98
99 setSubmenu(editMenu);
100 }
101
102 private bool undoCallback(Event event, Widget widget) {
103 Global.History.undo();
104 return true;
105 }
106
107 private bool redoCallback(Event event, Widget widget) {
108 Global.History.redo();
109 return true;
110 }
111 }
112
113 protected class ShapeMenu : MenuItem {
114 // Hetgeen wat geactiveerd wordt als je op 'New shape' klikt.
115 Menu shapeMenu;
116
117 // De items die zich in shapeMenu bevinden.
118 MenuItem rectangle;
119 MenuItem ellipse;
120 MenuItem circle;
121
122 this () {
123 super("Shapes");
124 shapeMenu = new Menu();
125
126 rectangle = new MenuItem("New rectangle");
127 rectangle.addOnButtonPress(&rectCallback);
128 shapeMenu.append(rectangle);
129
130 ellipse = new MenuItem("New ellipse");
131 ellipse.addOnButtonPress(&ellipseCallback);
132 shapeMenu.append(ellipse);
133
134 circle = new MenuItem("New circle");
135 circle.addOnButtonPress(&circleCallback);
136 shapeMenu.append(circle);
137
138 setSubmenu(shapeMenu);
139 }
140
141 private bool rectCallback(Event event, Widget widget) {
142 Brush.Shape = "rectangle";
143 return true;
144 }
145
146 private bool ellipseCallback(Event event, Widget widget) {
147 Brush.Shape = "ellipse";
148 return true;
149 }
150
151 private bool circleCallback(Event event, Widget widget) {
152 Brush.Shape = "circle";
153 return true;
154 }
155 }
Hints:
Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://rocketgit.com/user/kapstok/NHL-DePa2

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@ssh.rocketgit.com/user/kapstok/NHL-DePa2

Clone this repository using git:
git clone git://git.rocketgit.com/user/kapstok/NHL-DePa2

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a merge request:
... clone the repository ...
... make some changes and some commits ...
git push origin main