root/trunk/qd/test24.d

Revision 560, 8.0 kB (checked in by FeepingCreature, 3 years ago)
  • da STUFF
  • Image rotation and flipping
  • Refcount surfaces
Line 
1 module test24;
2
3 import qd, SDL_image;
4 import tools.log;
5
6 interface Draggable {
7   void pickUp(pt);
8   void drag(pt);
9   void drop(pt);
10   void setDragDg(void delegate(ref proc));
11 }
12
13 interface Drawable {
14   void draw();
15 }
16
17 interface DropTarget {
18   bool accept(Draggable droppings, ref pt where);
19 }
20
21 struct DragDropFuns(DragDropDesc) {
22   static {
23     bool delegate(pt)[Draggable] drags;
24     Draggable selected;
25    
26     void draw() {
27       if (auto draw = cast(Drawable) selected)
28         draw.draw();
29     }
30     void checkEvents() {
31       if (selected) { // currently dragging
32         if (DragDropDesc.stopDrag()) {
33           selected.drop(mouse.pos);
34           selected = null;
35         } else if (mouse.moved) {
36           selected.drag(mouse.pos);
37         }
38       } else { // not currently dragging and not currently clicking
39         if (DragDropDesc.startDrag()) {
40           Draggable target;
41           foreach (drag, dg; drags) {
42             if (dg(mouse.pos)) {
43               target = drag;
44               break;
45             }
46           }
47           if (!target) return;
48           drags.remove(target);
49           selected = target;
50           selected.pickUp(mouse.pos);
51         }
52       }
53     }
54   }
55 }
56
57 struct DropTargets {
58   static {
59     bool delegate(pt)[DropTarget] targets;
60    
61     void opIndexAssign(bool delegate(pt) dg, DropTarget dt) { targets[dt] = dg; }
62     bool checkDrop(Draggable dr, ref pt p) {
63       foreach (target, dg; targets) {
64         if (dg(p) && target.accept(dr, p)) return true;
65       }
66       return false;
67     }
68   }
69 }
70
71 struct ClickDragRelease {
72   static {
73     bool startDrag() { return mouse.clicked(Button.Left); }
74     bool stopDrag() { return mouse.released(Button.Left); }
75   }
76 }
77
78 struct ClickMoveClick {
79   static {
80     bool startDrag() { return mouse.clicked(Button.Left); }
81     bool stopDrag() { return mouse.clicked(Button.Left); }
82   }
83 }
84
85 struct HybridDrag {
86   static {
87     pt startPt; bool dragging;
88     bool startDrag() {
89       if (mouse.clicked(Button.Left)) {
90         startPt = mouse.pos;
91         return true;
92       }
93       return false;
94     }
95     bool stopDrag() {
96       auto
97         distpt = mouse.pos - startPt,
98         distval = abs(distpt.x) + abs(distpt.y);
99       if (distval > 16 && mouse.released(Button.Left)) return true;
100       return mouse.clicked(Button.Left);
101     }
102   }
103 }
104
105 // alias DragDropFuns!(ClickDragRelease) DragDrop;
106 // alias DragDropFuns!(ClickMoveClick) DragDrop;
107 alias DragDropFuns!(HybridDrag) DragDrop;
108
109 template castCall(T, string MEMBER) {
110   void castCall(S)(S[] input) {
111     foreach (value; input)
112       if (auto asTee = cast(T) value)
113         mixin("asTee."~MEMBER~"; ");
114   }
115 }
116
117 class Grid : Drawable, DropTarget {
118   Area area;
119   int w, h;
120   Draggable[] field;
121   int draggingOver = -1;
122   this(int w, int h) {
123     this.w = w; this.h = h;
124     // snap from outside
125     area = display.select(32-16, 32-16, 32*w - 1, 32*h - 1);
126     DropTargets[this] = area /apply/ (Area a, pt p) {
127       return !!(p in a);
128     };
129     field = new Draggable[w * h];
130   }
131   override { // Drawable
132     void draw() {
133       for (int x = 0; x < w; x ++) {
134         for (int y = 0; y < h; y ++) {
135           auto sx = x * 32 + 32, sy = y * 32 + 32;
136           auto id = y * w + x;
137           if (id == draggingOver) line(sx, sy, sx + 32, sy + 32,
138             Box=Black, Fill=Black~White~White);
139           else line(sx, sy, sx + 32, sy + 32, Box=Black);
140         }
141       }
142       castCall!(Drawable, "draw()")(field);
143     }
144   }
145   pt snap(pt p) {
146     pt pp;
147     pp.x = (p.x + 16) & ~31;
148     pp.y = (p.y + 16) & ~31;
149     return pp;
150   }
151   int toID(pt p) {
152     p.x = (p.x - area.tl.x) / 32;
153     p.y = (p.y - area.tl.y) / 32;
154     return p.y * w + p.x;
155   }
156   bool accept(Draggable dropping, ref pt p) {
157     auto pp = snap(p), id = toID(pp);
158     // we already know we're in the area
159     if (field[id]) return false;
160     p = pp;
161     field[id] = dropping;
162     dropping.setDragDg((ref proc dg) {
163       dg = stuple(this, id) /apply/ (Grid That, int id) {
164         That.field[id] = null;
165       };
166     });
167     return true;
168   }
169 }
170
171 template DefaultDrag(alias SIZE) {
172   pt pos, dragStart, dragCursorStart;
173   proc onDrag;
174   void setDragArea() {
175     DragDrop.drags[this] = display.select(pos.x, pos.y, SIZE.x, SIZE.y)
176       /apply/ (Area a, pt p) { return !!(p in a); };
177   }
178   override {
179     void setDragDg(void delegate(ref proc) dg) { dg(onDrag); }
180     void pickUp(pt p) {
181       dragStart = pos;
182       dragCursorStart = p;
183       onDrag();
184     }
185     void drag(pt p) {
186       pos = dragStart + (p - dragCursorStart);
187     }
188     void drop(pt p) {
189       pos = dragStart + (p - dragCursorStart);
190       if (!DropTargets.checkDrop(this, pos)) {
191         // Time to die, Mr. Anderson.
192         // delete this;
193         return;
194       }
195       setDragArea;
196     }
197   }
198 }
199
200 class Icon : Image, Draggable, Drawable {
201   mixin DefaultDrag!(dimensions);
202   this(void[] data, pt pos) {
203     super(data);
204     this.pos = pos;
205     setDragArea;
206   }
207   override void draw() { display.select(pos.x, pos.y).blit(this); }
208 }
209
210 class GroupIcon : Draggable, Drawable {
211   Area area;
212   pt pos, dragStart, dragCursorStart;
213   proc onDrag;
214   void setDragArea() {
215     DragDrop.drags[this] = display.select(pos.x, pos.y, 32, 32)
216       /apply/ (Area a, pt p) { return !!(p in a); };
217   }
218   bool follower, dropFailed;
219   override {
220     void setDragDg(void delegate(ref proc) dg) { dg(onDrag); }
221     void pickUp(pt p) {
222       dragStart = pos;
223       dragCursorStart = p;
224       onDrag();
225       if (follower) return;
226       foreach (bro; brothers) {
227         bro.follower = true;
228         bro.pickUp(p + (bro.pos - pos));
229         DragDrop.drags.remove(bro);
230       }
231     }
232     void drag(pt p) {
233       auto oldpos = pos;
234       pos = dragStart + (p - dragCursorStart);
235       if (follower) return;
236       foreach (bro; brothers)
237         bro.drag(p + (bro.pos - oldpos));
238     }
239     void drop(pt p) {
240       auto oldpos = pos;
241       pos = dragStart + (p - dragCursorStart);
242       if (!DropTargets.checkDrop(this, pos)) {
243         dropFailed = true;
244       }
245       if (follower) return;
246       bool oneFailed = dropFailed;
247       foreach (bro; brothers) {
248         bro.drop(p + (bro.pos - oldpos));
249         if (bro.dropFailed) oneFailed = true;
250       }
251       if (oneFailed) { // IF ONE OF US FAILS, ALL OF US FAIL
252         foreach (bro; brothers) {
253           if (!bro.dropFailed) {
254             bro.onDrag(); // drag body into the grave
255             delete bro;
256           }
257         }
258         if (!dropFailed) onDrag(); // drag ourselves loose.
259         delete this;
260       } else {
261         foreach (bro; brothers) {
262           bro.setDragArea;
263           bro.follower = false;
264         }
265         setDragArea;
266       }
267     }
268     void draw() {
269       display.select(pos.x, pos.y).blit(area);
270       if (follower) return;
271       foreach (bro; brothers)
272         if (bro.follower)
273           bro.draw();
274     }
275   }
276   GroupIcon[] brothers;
277   void linkUp(GroupIcon[] family) {
278     foreach (gi; family)
279       if (gi !is this)
280         brothers ~= gi;
281   }
282   this(Area area, pt pos) {
283     this.area = area;
284     this.pos = pos;
285     setDragArea;
286   }
287 }
288
289 GroupIcon[] mkGroup(Area area, pt p) {
290   auto w = area.width / 32, h = area.height / 32;
291   GroupIcon[] res;
292   for (int x = 0; x < w; ++x)
293     for (int y = 0; y < h; ++y)
294       res ~= new GroupIcon(area.select(x * 32, y * 32, 32, 32), p + pt(x * 32, y * 32));
295   foreach (gi; res)
296     gi.linkUp(res);
297   return res;
298 }
299
300 void main() {
301   screen(640, 480);
302   GroupIcon delegate(pt) genPizza; GroupIcon[] delegate() genPizzaz;
303   GroupIcon[] pizzaz;
304   void servePizza() {
305     pizzaz = genPizzaz();
306   }
307   void setServe(ref proc dg) { dg = &servePizza; };
308   auto pipes = new Image(import("straightpipe.png"));
309   genPizzaz = {
310     auto res = mkGroup(pipes, pt(312, 32));
311     foreach (icon; res)
312       icon.setDragDg(&setServe);
313     return res;
314   };
315   servePizza;
316   auto grid = new Grid(7, 7);
317   while (true) {
318     cls(White);
319     grid.draw;
320     foreach (pizza; pizzaz)
321       pizza.draw;
322     DragDrop.draw;
323     flip;
324     events;
325     DragDrop.checkEvents;
326     if (DragDrop.selected && key.pressed(SDLKey.LCtrl)) {
327       logln("Rotate pic!");
328       DragDrop.selected = null;
329       auto npipes = pipes.rot_right();
330      
331     }
332   }
333 }
Note: See TracBrowser for help on using the browser.