Subject | Hash | Author | Date (UTC) |
---|---|---|---|
Add resize option. | 961e464090918d83690ff66838f2fc96c6ad213a | Jan Allersma | 2018-09-13 18:29:44 |
Make `MoveCmd` Command-oriented. | 2970a8d862285752551be2464422375ca958357f | Jan Allersma | 2018-09-12 15:33:17 |
Change of strategy: Make project command-oriented. | ce40dffb1675661922411eeb07f148f61a176c22 | Jan Allersma | 2018-09-12 11:35:45 |
Initial commit. | 38c8818349679937ae25dde38949202ec45ea7b2 | Jan Allersma | 2018-09-11 20:21:57 |
File | Lines added | Lines deleted |
---|---|---|
README.md | 6 | 2 |
source/app.d | 6 | 33 |
source/commands/shapeCommands.d | 25 | 3 |
source/drawing/shapeOptions.d | 15 | 1 |
source/entities/canvas.d | 0 | 20 |
source/entities/shape.d | 76 | 83 |
source/windows/mainWindow.d | 5 | 15 |
source/windows/resizeDialog.d | 63 | 0 |
File README.md changed (mode: 100644) (index 17074fb..9917291) | |||
... | ... | use 'dub' to compile and run the code. | |
7 | 7 | ||
8 | 8 | * CheckBounds werkt retrograde (laatst toegevoegde item eerst checken) en stopt na een collision. | * CheckBounds werkt retrograde (laatst toegevoegde item eerst checken) en stopt na een collision. |
9 | 9 | ||
10 | * [DLang issue](https://forum.dlang.org/post/emixkjutxnnrplaziwkj@forum.dlang.org) | ||
10 | # TODO | ||
11 | 11 | ||
12 | * Moet nog checken op ongebruikte functies. | ||
12 | * Undo en redo werkt nog niet helemaal lekker. Waarschijnlijk als gevolg van het niet gebruiken van pointers in de 'shapes' array. | ||
13 | |||
14 | * File IO op undo redo afstemmen. | ||
15 | |||
16 | * Commands door een enkele klasse laten uitvoeren (?) |
File source/app.d changed (mode: 100644) (index 7140f21..a68c161) | |||
1 | 1 | import gio.Application : GioApplication = Application; | import gio.Application : GioApplication = Application; |
2 | 2 | import gtk.Application; | import gtk.Application; |
3 | import gtk.ApplicationWindow; | ||
4 | import gtk.Box; | ||
5 | 3 | ||
6 | import dp.canvas; | ||
7 | import dp.menubar; | ||
4 | import dp.win.main; | ||
8 | 5 | ||
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 | { | ||
6 | int main(string[] args) { | ||
37 | 7 | auto application = new Application ( "org.gtkd.demo.helloworld", GApplicationFlags.FLAGS_NONE ); | auto application = new Application ( "org.gtkd.demo.helloworld", GApplicationFlags.FLAGS_NONE ); |
8 | |||
38 | 9 | application.addOnActivate(delegate void(GioApplication app) { | application.addOnActivate(delegate void(GioApplication app) { |
39 | new win(application); | ||
10 | Win win = new Win(application); | ||
11 | //new ResizeWin(application); | ||
40 | 12 | }); | }); |
13 | |||
41 | 14 | return application.run(args); | return application.run(args); |
42 | 15 | } | } |
File source/commands/shapeCommands.d changed (mode: 100644) (index 06b10d2..1ca5444) | |||
... | ... | public class MoveCmd : Command { | |
32 | 32 | Shape original; | Shape original; |
33 | 33 | Shape newShape; | Shape newShape; |
34 | 34 | ||
35 | this (Shape original) { | ||
35 | this(Shape original) { | ||
36 | 36 | this.original = original; | this.original = original; |
37 | newShape = new Shape(); | ||
38 | Global.Brush.Shape = original.type; | ||
37 | newShape = original.clone(); | ||
39 | 38 | Global.canvas.addShape(newShape); | Global.canvas.addShape(newShape); |
40 | 39 | execute(); | execute(); |
41 | 40 | } | } |
... | ... | public class MoveCmd : Command { | |
50 | 49 | newShape.visible = newShape.enabled = false; | newShape.visible = newShape.enabled = false; |
51 | 50 | } | } |
52 | 51 | } | } |
52 | |||
53 | public class ResizeCmd : Command { | ||
54 | Shape original; | ||
55 | Shape newShape; | ||
56 | |||
57 | this(Shape original, float newSize) { | ||
58 | this.original = original; | ||
59 | newShape = original.clone(); | ||
60 | Global.canvas.addShape(newShape); | ||
61 | newShape.resize(newSize); | ||
62 | execute(); | ||
63 | } | ||
64 | |||
65 | public override void execute() { | ||
66 | original.visible = original.enabled = false; | ||
67 | newShape.visible = newShape.enabled = true; | ||
68 | } | ||
69 | |||
70 | public override void undo() { | ||
71 | original.visible = original.enabled = true; | ||
72 | newShape.visible = newShape.enabled = false; | ||
73 | } | ||
74 | } |
File source/drawing/shapeOptions.d changed (mode: 100644) (index 657ec24..e59df47) | |||
... | ... | import gtk.Widget; | |
5 | 5 | import gdk.Event; | import gdk.Event; |
6 | 6 | ||
7 | 7 | import dp.shape; | import dp.shape; |
8 | import dp.win.resize; | ||
8 | 9 | import Global = dp.globals; | import Global = dp.globals; |
9 | 10 | ||
10 | 11 | import std.stdio; // For debugging. | import std.stdio; // For debugging. |
11 | 12 | ||
12 | 13 | protected Shape s; | protected Shape s; |
14 | protected ResizeDialog rd; | ||
13 | 15 | ||
14 | 16 | public class ShapeOptions : Menu { | public class ShapeOptions : Menu { |
15 | 17 | this() { | this() { |
16 | 18 | super(); | super(); |
17 | 19 | this.append(new DeleteShape()); | this.append(new DeleteShape()); |
18 | 20 | this.append(new MoveShape()); | this.append(new MoveShape()); |
21 | this.append(new ResizeShape()); | ||
19 | 22 | } | } |
20 | 23 | ||
21 | 24 | // Will look like: sOptions.OfShape(s); Should be differently named. | // Will look like: sOptions.OfShape(s); Should be differently named. |
... | ... | protected class MoveShape : MenuItem { | |
50 | 53 | writeln("Moved shape"); | writeln("Moved shape"); |
51 | 54 | s.select(); | s.select(); |
52 | 55 | s.enabled = false; | s.enabled = false; |
53 | //s.queueRevival = true; | ||
54 | 56 | Global.canvas.repaint(); | Global.canvas.repaint(); |
55 | 57 | // Create ghost shape. | // Create ghost shape. |
56 | 58 | return false; // Hide ShapeOptions when button is released. | return false; // Hide ShapeOptions when button is released. |
57 | 59 | } | } |
58 | 60 | } | } |
61 | |||
62 | protected class ResizeShape : MenuItem { | ||
63 | this() { | ||
64 | super("Resize shape"); | ||
65 | addOnButtonRelease(&relCallback); | ||
66 | } | ||
67 | |||
68 | private bool relCallback (Event event, Widget widget) { | ||
69 | rd = new ResizeDialog(s); | ||
70 | return false; // Hide ShapeOptions when button is released. | ||
71 | } | ||
72 | } |
File source/entities/canvas.d changed (mode: 100644) (index ac3c76c..f4b246d) | |||
... | ... | public class Canvas : Entity { | |
34 | 34 | area = new DrawingArea(width, height); | area = new DrawingArea(width, height); |
35 | 35 | shapeOptions = new ShapeOptions(); | shapeOptions = new ShapeOptions(); |
36 | 36 | area.addOnButtonPress(&clickCallback); | area.addOnButtonPress(&clickCallback); |
37 | area.addOnScroll(&scrollCallback); | ||
38 | 37 | surface = ImageSurface.create(CairoFormat.ARGB32, width, height); | surface = ImageSurface.create(CairoFormat.ARGB32, width, height); |
39 | 38 | area.addOnDraw(&drawCallback); | area.addOnDraw(&drawCallback); |
40 | 39 | shapes.length = 0; | shapes.length = 0; |
... | ... | public class Canvas : Entity { | |
98 | 97 | return true; | return true; |
99 | 98 | } | } |
100 | 99 | ||
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 | 100 | private void clearCanvas() { | private void clearCanvas() { |
121 | 101 | Context c = Context.create(surface); | Context c = Context.create(surface); |
122 | 102 | c.setSourceRgba(1,1,1,1); | c.setSourceRgba(1,1,1,1); |
File source/entities/shape.d changed (mode: 100644) (index bb2a901..cde8389) | |||
... | ... | import cairo.Context; | |
6 | 6 | import cairo.ImageSurface; | import cairo.ImageSurface; |
7 | 7 | ||
8 | 8 | import std.stdio; // For debug | import std.stdio; // For debug |
9 | import std.conv; // Redundant? | ||
9 | import std.conv; | ||
10 | 10 | ||
11 | 11 | import dp.history; | import dp.history; |
12 | 12 | import dp.command.shape; | import dp.command.shape; |
13 | 13 | ||
14 | 14 | class Shape : Global.Entity { | class Shape : Global.Entity { |
15 | private Context c; | ||
16 | public bool visible; // Should be encapsulated. | ||
17 | private string shapeType; | ||
18 | |||
19 | 15 | /+ | /+ |
20 | 16 | bounds[0][0] = minX | bounds[0][0] = minX |
21 | 17 | bounds[1][0] = maxX | bounds[1][0] = maxX |
22 | 18 | bounds[0][1] = minY | bounds[0][1] = minY |
23 | 19 | bounds[1][1] = maxY | bounds[1][1] = maxY |
24 | 20 | +/ | +/ |
21 | |||
22 | private Context c; | ||
23 | public bool visible; // Should be encapsulated. | ||
24 | private string shapeType; | ||
25 | 25 | private double[2][2] bounds; | private double[2][2] bounds; |
26 | private double size = 0; | ||
26 | 27 | ||
27 | 28 | this() { | this() { |
28 | 29 | enabled = visible = true; | enabled = visible = true; |
29 | 30 | } | } |
30 | 31 | ||
31 | @property | ||
32 | public double[2] position() { | ||
33 | return bounds[0]; | ||
34 | } | ||
35 | |||
36 | @property | ||
37 | public double[2] position(double[2] newPos) { | ||
38 | double sX = sizeX; | ||
39 | double sY = sizeY; | ||
40 | |||
41 | bounds[1][0] = newPos[0] + sX; | ||
42 | bounds[1][1] = newPos[1] + sY; | ||
32 | this(double[2][2] bounds, double size, string shapeType) { | ||
33 | enabled = visible = true; | ||
34 | this.bounds = bounds; | ||
35 | this.size = size; | ||
36 | this.shapeType = shapeType; | ||
43 | 37 | ||
44 | return bounds[0] = newPos; | ||
38 | writeln(this.bounds);writeln(this.size);writeln(this.type); | ||
45 | 39 | } | } |
46 | 40 | ||
47 | @property | ||
48 | public double sizeX () { | ||
49 | return bounds[1][0] - bounds[0][0]; | ||
50 | } | ||
41 | public void resize(float factor) { | ||
42 | bounds[1][1] += factor / 2; | ||
43 | bounds[1][0] += factor / 2; | ||
44 | bounds[0][1] -= factor / 2; | ||
45 | bounds[0][0] -= factor / 2; | ||
51 | 46 | ||
52 | @property | ||
53 | public double sizeY () { | ||
54 | return bounds[1][1] - bounds[0][1]; | ||
47 | size += factor; | ||
48 | draw(); | ||
55 | 49 | } | } |
56 | 50 | ||
57 | 51 | @property | @property |
58 | public override string type () { | ||
52 | public override string type() { | ||
59 | 53 | return shapeType; | return shapeType; |
60 | 54 | } | } |
61 | 55 | ||
62 | 56 | public override string toString() { | public override string toString() { |
63 | return text(type, " ", bounds[0][0], " ", bounds[0][1], " ", sizeX, " ", sizeY); | ||
57 | return text(type, " ", bounds[0][0], " ", bounds[0][1], " ", size, " ", size); | ||
58 | } | ||
59 | |||
60 | public Shape clone() { | ||
61 | return new Shape(bounds, size, type); | ||
64 | 62 | } | } |
65 | 63 | ||
66 | 64 | public void move(int x, int y, Context context=null) { | public void move(int x, int y, Context context=null) { |
... | ... | class Shape : Global.Entity { | |
70 | 68 | draw(x,y); | draw(x,y); |
71 | 69 | } | } |
72 | 70 | ||
73 | public void select () { | ||
71 | public void select() { | ||
74 | 72 | c.setSourceRgba(1-Global.Brush.Red, 1-Global.Brush.Green, 1-Global.Brush.Blue, 1); | c.setSourceRgba(1-Global.Brush.Red, 1-Global.Brush.Green, 1-Global.Brush.Blue, 1); |
75 | 73 | c.stroke(); | c.stroke(); |
76 | 74 | c.setSourceRgba(Global.Brush.Red, Global.Brush.Green, Global.Brush.Blue, Global.Brush.Alpha); | c.setSourceRgba(Global.Brush.Red, Global.Brush.Green, Global.Brush.Blue, Global.Brush.Alpha); |
77 | 75 | } | } |
78 | 76 | ||
79 | public void remove () { | ||
77 | public void remove() { | ||
80 | 78 | visible = enabled = false; | visible = enabled = false; |
81 | 79 | } | } |
82 | 80 | ||
81 | private void initSize() { | ||
82 | shapeType = Global.Brush.Shape; | ||
83 | Global.Brush.Shape = null; | ||
84 | |||
85 | final switch (type) { | ||
86 | case "rectangle": | ||
87 | size = 125; | ||
88 | break; | ||
89 | case "ellipse": | ||
90 | case "circle": | ||
91 | size = 50; | ||
92 | break; | ||
93 | } | ||
94 | } | ||
95 | |||
83 | 96 | private void draw(int x, int y) { | private void draw(int x, int y) { |
97 | if(size == 0) initSize(); | ||
98 | |||
99 | final switch (type) { | ||
100 | case "rectangle": | ||
101 | bounds[0][0] = x - size / 2; // minX | ||
102 | bounds[1][0] = x + size / 2; // maxX | ||
103 | bounds[0][1] = y - size / 2; // minY | ||
104 | bounds[1][1] = y + size / 2; // maxY | ||
105 | break; | ||
106 | case "ellipse": | ||
107 | bounds[0][0] = x - size / 2; // minX | ||
108 | bounds[1][0] = x + size / 2; // maxX | ||
109 | bounds[0][1] = y - size; // minY | ||
110 | bounds[1][1] = y + size; // maxY | ||
111 | break; | ||
112 | case "circle": | ||
113 | bounds[0][0] = x - size; // minX | ||
114 | bounds[1][0] = x + size; // maxX | ||
115 | bounds[0][1] = y - size; // minY | ||
116 | bounds[1][1] = y + size; // maxY | ||
117 | break; | ||
118 | } | ||
119 | draw(); | ||
120 | } | ||
121 | |||
122 | private void draw() { | ||
84 | 123 | if(visible) { | if(visible) { |
85 | shapeType = Global.Brush.Shape; | ||
86 | final switch (Global.Brush.Shape) { | ||
124 | final switch (type) { | ||
87 | 125 | case "rectangle": | case "rectangle": |
88 | 126 | c.setSourceRgba(Global.Brush.Red, Global.Brush.Green, Global.Brush.Blue, Global.Brush.Alpha); | c.setSourceRgba(Global.Brush.Red, Global.Brush.Green, Global.Brush.Blue, Global.Brush.Alpha); |
89 | c.rectangle(x - 125 / 2, y - 125 / 2, 125, 125); | ||
90 | c.fillPreserve(); | ||
91 | |||
92 | bounds[0][0] = x - 125 / 2; // minX | ||
93 | bounds[1][0] = x + 125 / 2; // maxX | ||
94 | bounds[0][1] = y - 125 / 2; // minY | ||
95 | bounds[1][1] = y + 125 / 2; // maxY | ||
96 | |||
127 | c.rectangle(bounds[0][0], bounds[0][1], size, size); | ||
128 | applyToSource(); | ||
97 | 129 | break; | break; |
98 | 130 | case "ellipse": | case "ellipse": |
99 | 131 | c.setSourceRgba(Global.Brush.Red, Global.Brush.Green, Global.Brush.Blue, Global.Brush.Alpha); | c.setSourceRgba(Global.Brush.Red, Global.Brush.Green, Global.Brush.Blue, Global.Brush.Alpha); |
100 | 132 | c.scale(0.5, 1); | c.scale(0.5, 1); |
101 | c.arc(x*2,y,50,0,2*3.141); // 3.141 = Pi | ||
102 | c.fillPreserve(); | ||
103 | |||
104 | bounds[0][0] = x - 50 / 2; // minX | ||
105 | bounds[1][0] = x + 50 / 2; // maxX | ||
106 | bounds[0][1] = y - 50; // minY | ||
107 | bounds[1][1] = y + 50; // maxY | ||
108 | |||
133 | c.arc((bounds[0][0]+size/2)*2 ,bounds[0][1]+size ,size,0,2*3.141); // 3.141 = Pi | ||
134 | applyToSource(); | ||
109 | 135 | break; | break; |
110 | 136 | case "circle": | case "circle": |
111 | 137 | c.setSourceRgba(Global.Brush.Red, Global.Brush.Green, Global.Brush.Blue, Global.Brush.Alpha); | c.setSourceRgba(Global.Brush.Red, Global.Brush.Green, Global.Brush.Blue, Global.Brush.Alpha); |
112 | c.arc(x,y,50,0,2*3.141); // 3.141 = Pi | ||
113 | c.fillPreserve(); | ||
114 | |||
115 | bounds[0][0] = x - 50; // minX | ||
116 | bounds[1][0] = x + 50; // maxX | ||
117 | bounds[0][1] = y - 50; // minY | ||
118 | bounds[1][1] = y + 50; // maxY | ||
119 | |||
138 | c.arc(bounds[0][0]+size, bounds[0][1]+size, size,0,2*3.141); // 3.141 = Pi | ||
139 | applyToSource(); | ||
120 | 140 | break; | break; |
121 | 141 | } | } |
122 | 142 | } | } |
123 | Global.Brush.Shape = null; | ||
124 | 143 | } | } |
125 | 144 | ||
126 | public bool CheckBounds (int x, int y) { | ||
145 | public bool CheckBounds(int x, int y) { | ||
127 | 146 | if(enabled) { | if(enabled) { |
128 | 147 | writeln(shapeType ~ ":"); | writeln(shapeType ~ ":"); |
129 | 148 | write(bounds[0][0]);write(" <- ");write(x);write(" -> ");writeln(bounds[1][0]); | write(bounds[0][0]);write(" <- ");write(x);write(" -> ");writeln(bounds[1][0]); |
... | ... | class Shape : Global.Entity { | |
145 | 164 | return false; | return false; |
146 | 165 | } | } |
147 | 166 | ||
148 | // This will be used when the user scrolls on the canvas. | ||
149 | public bool CheckBounds (int x, int y, int growContext) { | ||
150 | if(enabled) { | ||
151 | if( | ||
152 | x > bounds[0][0] && // minX | ||
153 | x < bounds[1][0] && // maxX | ||
154 | y > bounds[0][1] && // minY | ||
155 | y < bounds[1][1] // maxY | ||
156 | ) { | ||
157 | if(growContext == 0) { | ||
158 | bounds[0][0] -= 5; | ||
159 | bounds[1][0] += 5; | ||
160 | bounds[0][1] -= 5; | ||
161 | bounds[1][1] += 5; | ||
162 | } else { | ||
163 | bounds[0][0] += 5; | ||
164 | bounds[1][0] -= 5; | ||
165 | bounds[0][1] += 5; | ||
166 | bounds[1][1] -= 5; | ||
167 | } | ||
168 | return true; | ||
169 | } | ||
170 | } | ||
171 | return false; | ||
172 | } | ||
173 | |||
174 | 167 | public void applyToSource() { | public void applyToSource() { |
175 | 168 | if(visible) | if(visible) |
176 | 169 | c.fillPreserve(); | c.fillPreserve(); |
File source/windows/mainWindow.d copied from file source/app.d (similarity 55%) (mode: 100644) (index 7140f21..10e234b) | |||
1 | import gio.Application : GioApplication = Application; | ||
1 | module dp.win.main; | ||
2 | |||
2 | 3 | import gtk.Application; | import gtk.Application; |
3 | 4 | import gtk.ApplicationWindow; | import gtk.ApplicationWindow; |
4 | 5 | import gtk.Box; | import gtk.Box; |
... | ... | import dp.menubar; | |
8 | 9 | ||
9 | 10 | import Global = dp.globals; | import Global = dp.globals; |
10 | 11 | ||
11 | class win : ApplicationWindow | ||
12 | { | ||
12 | class Win : ApplicationWindow { | ||
13 | 13 | Canvas canvas; | Canvas canvas; |
14 | 14 | ||
15 | this(Application application) | ||
16 | { | ||
15 | this(Application application) { | ||
17 | 16 | super(application); | super(application); |
18 | 17 | setTitle("DPainter"); | setTitle("DPainter"); |
19 | 18 | setBorderWidth(10); | setBorderWidth(10); |
20 | 19 | ||
21 | 20 | Box container = new Box(Orientation.VERTICAL, 15); | Box container = new Box(Orientation.VERTICAL, 15); |
22 | |||
21 | |||
23 | 22 | Box menubar = new Menubar(); | Box menubar = new Menubar(); |
24 | 23 | container.add(menubar); | container.add(menubar); |
25 | 24 | ||
... | ... | class win : ApplicationWindow | |
31 | 30 | showAll(); | showAll(); |
32 | 31 | } | } |
33 | 32 | } | } |
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/windows/resizeDialog.d added (mode: 100644) (index 0000000..dc961a4) | |||
1 | module dp.win.resize; | ||
2 | |||
3 | import gtk.Dialog; | ||
4 | import gtk.MessageDialog; | ||
5 | import gtk.Label; | ||
6 | import gtk.Button; | ||
7 | import gtk.Entry; | ||
8 | |||
9 | import dp.shape; | ||
10 | import dp.history; | ||
11 | import dp.command.shape; | ||
12 | |||
13 | public import Global = dp.globals; | ||
14 | |||
15 | import std.stdio; // Debug | ||
16 | |||
17 | import std.string, std.conv; | ||
18 | |||
19 | class ResizeDialog : Dialog { | ||
20 | Shape shape; | ||
21 | Entry factorEntry; | ||
22 | |||
23 | this(Shape shape) { | ||
24 | super(); | ||
25 | setTitle("Resize shape"); | ||
26 | this.shape = shape; | ||
27 | |||
28 | factorEntry = new Entry(); | ||
29 | factorEntry.setPlaceholderText("factor"); | ||
30 | |||
31 | Button confirmBtn = new Button("Resize!", &clickCallback); | ||
32 | |||
33 | // Content area, containing non-interactable GTK objects. | ||
34 | getContentArea().add(new Label("By what factor should the shape be resized?")); | ||
35 | getContentArea().setBorderWidth(20); | ||
36 | |||
37 | // Action area, containing interactable GTK objects. | ||
38 | getActionArea().add(factorEntry); | ||
39 | getActionArea().add(confirmBtn); | ||
40 | |||
41 | showAll(); | ||
42 | } | ||
43 | |||
44 | void clickCallback (Button button) { | ||
45 | float factor = 1.3; | ||
46 | string input = factorEntry.getText(); | ||
47 | |||
48 | if(isNumeric(input)) { | ||
49 | factor = parse!float(input); | ||
50 | History.addCommand(new ResizeCmd(shape, factor)); | ||
51 | this.close(); | ||
52 | } | ||
53 | else { | ||
54 | MessageDialog msgd = new MessageDialog ( | ||
55 | this, DialogFlags.MODAL, MessageType.ERROR, ButtonsType.OK, | ||
56 | "'" ~ input ~ "' is an invalid factor. Please use numbers only.", | ||
57 | null | ||
58 | ); | ||
59 | scope(exit) msgd.destroy(); | ||
60 | msgd.run(); | ||
61 | } | ||
62 | } | ||
63 | } |