comparison gtk/canvas.d @ 22:17c2df87b459

Package refactoring.
author "David Bryant <bagnose@gmail.com>"
date Wed, 15 Jul 2009 23:31:29 +0930
parents canvas.d@d6e7a5a6f008
children a24c13bb9c98
comparison
equal deleted inserted replaced
21:993ac3a183c8 22:17c2df87b459
1 module gtk.canvas;
2
3 public {
4 import dia.icanvas;
5 import tk.geometry;
6 import tk.events;
7 }
8
9 private {
10 import tk.gtk_support;
11 import tk.misc;
12
13 import cairo.Surface;
14 import cairo_support;
15
16 import std.math;
17 import std.stdio;
18
19 import gtk.Widget;
20 import gtk.Toolbar;
21 import gtk.Table;
22 import gtk.HRuler;
23 import gtk.VRuler;
24 import gtk.Range;
25 import gtk.HScrollbar;
26 import gtk.VScrollbar;
27 import gtk.DrawingArea;
28 import gtk.Adjustment;
29
30 import gdk.Drawable;
31
32 import gtkc.gtk;
33 }
34
35 // x and y run right and up respectively
36
37 class Canvas : Table, Viewport {
38 this(in Layer[] layers, EventHandler event_handler, in double ppi) {
39 super(3, 3, 0);
40
41 mDamage = Rectangle.DEFAULT;
42
43 mLayers = layers.dup;
44 mEventHandler = event_handler;
45 mPPI = ppi;
46
47 /*
48 writefln("Layer bounds: %s", layer_bounds);
49 writefln("Canvas bounds: %s", mCanvasBounds);
50 writefln("View centre: %s", mViewCentre);
51 */
52
53 // Create our child widgets and register callbacks
54
55 mHRuler = new HRuler;
56 attach(mHRuler,
57 1, 2,
58 0, 1,
59 AttachOptions.FILL | AttachOptions.EXPAND, AttachOptions.SHRINK,
60 0, 0);
61 mHRuler.setMetric(MetricType.PIXELS);
62
63 mVRuler = new VRuler;
64 attach(mVRuler,
65 0, 1,
66 1, 2,
67 AttachOptions.SHRINK, AttachOptions.FILL | AttachOptions.EXPAND,
68 0, 0);
69 mVRuler.setMetric(MetricType.PIXELS);
70
71 mDrawingArea = new DrawingArea;
72 mDrawingArea.addOnRealize(&onRealize);
73 mDrawingArea.addOnConfigure(&onConfigure);
74 mDrawingArea.addOnExpose(&onExpose);
75 mDrawingArea.addOnButtonPress(&onButtonPress);
76 mDrawingArea.addOnButtonRelease(&onButtonRelease);
77 mDrawingArea.addOnKeyPress(&onKeyEvent);
78 mDrawingArea.addOnKeyRelease(&onKeyEvent);
79 mDrawingArea.addOnMotionNotify(&onMotionNotify);
80 mDrawingArea.addOnScroll(&onScroll);
81 mDrawingArea.addOnEnterNotify(&onEnterNotify);
82 mDrawingArea.addOnLeaveNotify(&onLeaveNotify);
83 mDrawingArea.setEvents(EventMask.EXPOSURE_MASK |
84 EventMask.POINTER_MOTION_MASK |
85 EventMask.POINTER_MOTION_HINT_MASK |
86 EventMask.BUTTON_MOTION_MASK |
87 EventMask.BUTTON_PRESS_MASK |
88 EventMask.BUTTON_RELEASE_MASK |
89 EventMask.KEY_PRESS_MASK |
90 EventMask.KEY_RELEASE_MASK |
91 EventMask.ENTER_NOTIFY_MASK |
92 EventMask.LEAVE_NOTIFY_MASK |
93 EventMask.FOCUS_CHANGE_MASK |
94 EventMask.SCROLL_MASK);
95
96 attach(mDrawingArea,
97 1, 2,
98 1, 2,
99 AttachOptions.FILL | AttachOptions.EXPAND, AttachOptions.FILL | AttachOptions.EXPAND,
100 0, 0);
101
102 // value, lower, upper, step-inc, page-inc, page-size
103 // Give the adjustments dummy values until we receive a configure
104 mHAdjustment = new Adjustment(0.0, 0.0, 1.0, 0.2, 0.5, 0.5);
105 mHAdjustment.addOnValueChanged(&onValueChanged);
106 mHScrollbar = new HScrollbar(mHAdjustment);
107 mHScrollbar.setInverted(false);
108 attach(mHScrollbar,
109 1, 2,
110 2, 3,
111 AttachOptions.FILL | AttachOptions.EXPAND, AttachOptions.SHRINK,
112 0, 0);
113
114 mVAdjustment = new Adjustment(0.0, 0.0, 1.0, 0.2, 0.5, 0.5);
115 mVAdjustment.addOnValueChanged(&onValueChanged);
116 mVScrollbar = new VScrollbar(mVAdjustment);
117 mVScrollbar.setInverted(true);
118 attach(mVScrollbar,
119 2, 3,
120 1, 2,
121 AttachOptions.SHRINK,
122 AttachOptions.FILL | AttachOptions.EXPAND,
123 0, 0);
124 }
125
126 override void zoom_relative(Point pixel_datum, double factor) {
127 // Work out pixel distance from current centre to datum,
128 // Do the zoom, then work out the new centre that keeps the
129 // pixel distance the same
130
131 Point old_model_datum = pixel_to_model(pixel_datum);
132 Vector pixel_distance = model_to_pixel(old_model_datum - mViewCentre);
133 mZoom = clamp_zoom(factor * mZoom);
134 mViewCentre = old_model_datum - pixel_to_model(pixel_distance);
135
136 update_adjustments;
137 update_rulers;
138 queueDraw;
139 }
140
141 override void pan_relative(Vector pixel_displacement) {
142 mViewCentre = mViewCentre + pixel_to_model(pixel_displacement);
143
144 update_adjustments;
145 update_rulers;
146 queueDraw;
147 }
148
149 override void damage_model(Rectangle area) {
150 mDamage = mDamage | model_to_pixel(area);
151 }
152
153 override void damage_pixel(Rectangle area) {
154 mDamage = mDamage | area;
155 }
156
157 override double zoom() const {
158 return mZoom;
159 }
160
161 override Point model_to_pixel(Point model) const {
162 return Point.DEFAULT + mViewSize / 2.0 + mZoom * (model - mViewCentre);
163 }
164
165 override Point pixel_to_model(Point pixel) const {
166 return mViewCentre + (pixel - mViewSize / 2.0 - Point.DEFAULT) / mZoom;
167 }
168
169 override Vector model_to_pixel(Vector model) const {
170 return mZoom * model;
171 }
172
173 override Vector pixel_to_model(Vector pixel) const {
174 return pixel / mZoom;
175 }
176
177 override double model_to_pixel(double model) const {
178 return mZoom * model;
179 }
180
181 override double pixel_to_model(double pixel) const {
182 return pixel / mZoom;
183 }
184
185 override Rectangle model_to_pixel(Rectangle model) const {
186 return Rectangle(model_to_pixel(model.position), model_to_pixel(model.size));
187 }
188
189 override Rectangle pixel_to_model(Rectangle model) const {
190 return Rectangle(pixel_to_model(model.position), pixel_to_model(model.size));
191 }
192
193 private {
194
195 void onRealize(Widget widget) {
196 assert(widget is mDrawingArea);
197 //writefln("Got realize\n");
198 }
199
200 bool onConfigure(GdkEventConfigure * event, Widget widget) {
201 assert(widget is mDrawingArea);
202
203 if (!mHadConfigure) {
204 const double MM_PER_INCH = 25.4;
205 mZoom = 0.25 * mPPI / MM_PER_INCH;
206
207 // Take the union of the bounds of each layer to
208 // determine the canvas size
209
210 Rectangle layer_bounds = Rectangle.DEFAULT;
211
212 foreach (ref layer; mLayers) {
213 layer_bounds = layer_bounds | layer.bounds;
214 }
215
216 assert(layer_bounds.valid);
217
218 mCanvasBounds = layer_bounds.moved(-layer_bounds.size).expanded(2.0 * layer_bounds.size);
219 mViewCentre = mCanvasBounds.centre;
220
221 mHadConfigure = true;
222 }
223
224 mViewSize = Vector(cast(double)event.width, cast(double)event.height);
225 update_adjustments;
226 update_rulers;
227
228 //writefln("Canvas bounds: %s", mCanvasBounds);
229 //writefln("View centre: %s", mViewCentre);
230
231 return true;
232 }
233
234 bool onExpose(GdkEventExpose * event, Widget widget) {
235 assert(widget is mDrawingArea);
236
237 Drawable dr = mDrawingArea.getWindow;
238
239 int width, height;
240 dr.getSize(width, height);
241 //writefln("Got expose %dx%d\n", width, height);
242
243 scope model_cr = new Context(dr);
244 scope pixel_cr = new Context(dr);
245
246 Rectangle pixel_damage =
247 event is null ?
248 Rectangle(Point(0.0, 0.0), mViewSize) :
249 Rectangle(Point(cast(double)event.area.x, mViewSize.y - cast(double)(event.area.y + event.area.height)),
250 Vector(cast(double)event.area.width, cast(double)event.area.height));
251
252 Rectangle model_damage = pixel_to_model(pixel_damage);
253
254 //writefln("Pixel damage: %s, model damage: %s", pixel_damage, model_damage);
255
256 model_cr.save; pixel_cr.save; {
257 // Setup model context and clip
258
259 GtkAdjustment * h_gtkAdjustment = mHAdjustment.getAdjustmentStruct;
260 GtkAdjustment * v_gtkAdjustment = mVAdjustment.getAdjustmentStruct;
261
262 model_cr.scale(mZoom, -mZoom);
263 model_cr.translate(-gtk_adjustment_get_value(h_gtkAdjustment),
264 -gtk_adjustment_get_value(v_gtkAdjustment) - gtk_adjustment_get_page_size(v_gtkAdjustment));
265
266 rectangle(model_cr, model_damage);
267 model_cr.clip;
268
269 // Setup pixel context and clip
270
271 pixel_cr.translate(0.0, mViewSize.y);
272 pixel_cr.scale(1.0, -1.0);
273
274 rectangle(pixel_cr, pixel_damage);
275 pixel_cr.clip;
276
277 // Fill the background
278
279 pixel_cr.save; {
280 // Make the window light grey
281 pixel_cr.setSourceRgba(0.6, 0.6, 0.6, 1.0);
282 rectangle(pixel_cr, pixel_damage);
283 pixel_cr.fill;
284 } pixel_cr.restore;
285
286 // Draw each layer
287
288 foreach(ref layer; mLayers) {
289 model_cr.save; pixel_cr.save; {
290 layer.draw(this, pixel_damage, pixel_cr, model_damage, model_cr);
291 } pixel_cr.restore; model_cr.restore;
292 }
293 } pixel_cr.restore; model_cr.restore;
294
295 return true;
296 }
297
298 bool onButtonPress(GdkEventButton * event, Widget widget) {
299 assert(widget is mDrawingArea);
300 //writefln("Got button event\n");
301
302 Point pixel_point = Point(event.x + 0.5, mViewSize.y - (event.y + 0.5));
303 Point model_point = pixel_to_model(pixel_point);
304
305 auto button_event = new ButtonEvent(gtk2tk_button_action(event.type),
306 gtk2tk_button_name(event.button),
307 pixel_point,
308 model_point,
309 gtk2tk_mask(event.state));
310
311 mEventHandler.handle_button_press(this, button_event);
312
313 process_damage;
314
315 return true;
316 }
317
318 bool onButtonRelease(GdkEventButton * event, Widget widget) {
319 assert(widget is mDrawingArea);
320 //writefln("Got button event\n");
321
322 Point pixel_point = Point(event.x + 0.5, mViewSize.y - (event.y + 0.5));
323 Point model_point = pixel_to_model(pixel_point);
324
325 auto button_event = new ButtonEvent(gtk2tk_button_action(event.type),
326 gtk2tk_button_name(event.button),
327 pixel_point,
328 model_point,
329 gtk2tk_mask(event.state));
330
331 mEventHandler.handle_button_release(this, button_event);
332
333 process_damage;
334
335 return true;
336 }
337
338 bool onKeyEvent(GdkEventKey * event, Widget widget) {
339 assert(widget is mDrawingArea);
340 //writefln("Got key event\n");
341
342 //auto key_event = new KeyEvent("",
343 // mEventHandle.handle_key(key_event);
344
345 process_damage;
346
347 return true;
348 }
349
350 bool onMotionNotify(GdkEventMotion * event, Widget widget) {
351 assert(widget is mDrawingArea);
352 //writefln("Got motion notify\n");
353 gtk_widget_event(mHRuler.getWidgetStruct(), cast(GdkEvent *)event);
354 gtk_widget_event(mVRuler.getWidgetStruct(), cast(GdkEvent *)event);
355
356 Point pixel_point = Point(event.x + 0.5, mViewSize.y - (event.y + 0.5));
357 Point model_point = pixel_to_model(pixel_point);
358
359 auto motion_event = new MotionEvent(pixel_point,
360 model_point,
361 gtk2tk_mask(event.state));
362
363 mEventHandler.handle_motion(this, motion_event);
364
365 process_damage;
366
367 return true;
368 }
369
370 bool onScroll(GdkEventScroll * event, Widget widget) {
371 assert(widget is mDrawingArea);
372 //writefln("Got scroll\n");
373
374 Point pixel_point = Point(event.x + 0.5, mViewSize.y - (event.y + 0.5));
375 Point model_point = pixel_to_model(pixel_point);
376
377 auto scroll_event = new ScrollEvent(gtk2tk_direction(event.direction),
378 pixel_point,
379 model_point,
380 gtk2tk_mask(event.state));
381
382 mEventHandler.handle_scroll(this, scroll_event);
383
384 process_damage;
385
386 return true;
387 }
388
389 /*
390 public enum GdkCrossingMode {
391 NORMAL,
392 GRAB,
393 UNGRAB,
394 GTK_GRAB,
395 GTK_UNGRAB,
396 STATE_CHANGED
397 }
398
399 public struct GdkEventCrossing {
400 GdkEventType type;
401 GdkWindow *window;
402 byte sendEvent;
403 GdkWindow *subwindow;
404 uint time;
405 double x;
406 double y;
407 double xRoot;
408 double yRoot;
409 GdkCrossingMode mode;
410 GdkNotifyType detail;
411 int focus;
412 uint state;
413 }
414 */
415
416 bool onEnterNotify(GdkEventCrossing * event, Widget widget) {
417 assert(widget is mDrawingArea);
418 writefln("Enter %d %d %d", cast(int)event.mode, event.focus, event.state);
419 return true;
420 }
421
422 bool onLeaveNotify(GdkEventCrossing * event, Widget widget) {
423 assert(widget is mDrawingArea);
424 writefln("Leave %d %d %d", cast(int)event.mode, event.focus, event.state);
425 return true;
426 }
427
428 void onValueChanged(Adjustment adjustment) {
429 GtkAdjustment * h_gtkAdjustment = mHAdjustment.getAdjustmentStruct;
430 GtkAdjustment * v_gtkAdjustment = mVAdjustment.getAdjustmentStruct;
431
432 Point view_left_top = Point(gtk_adjustment_get_value(h_gtkAdjustment),
433 gtk_adjustment_get_value(v_gtkAdjustment));
434
435 Vector model_size = pixel_to_model(mViewSize);
436
437 //writefln("%s", view_left_bottom);
438 mViewCentre = view_left_top + model_size / 2.0;
439 //writefln("onValueChanged mViewCentre: %s", mViewCentre);
440
441 update_rulers;
442
443 queueDraw;
444 }
445
446 void update_rulers() {
447 Vector model_size = pixel_to_model(mViewSize);
448
449 Point view_left_bottom = mViewCentre - model_size / 2.0;
450 Point view_right_top = mViewCentre + model_size / 2.0;
451
452 // Define these just to obtain the position
453 // below and we can preserve it
454 double lower, upper, position, max_size;
455
456
457 mHRuler.getRange(lower, upper, position, max_size);
458 mHRuler.setRange(view_left_bottom.x,
459 view_right_top.x,
460 position,
461 mZoom * 50.0);
462
463 mVRuler.getRange(lower, upper, position, max_size);
464 mVRuler.setRange(view_right_top.y,
465 view_left_bottom.y,
466 position,
467 mZoom * 50.0);
468 }
469
470 void update_adjustments() {
471 Vector model_size = pixel_to_model(mViewSize);
472
473 Point view_left_bottom = mViewCentre - model_size / 2.0;
474 Point view_right_top = mViewCentre + model_size / 2.0;
475
476 // Adjust the canvas size if necessary
477 mCanvasBounds = Rectangle(min_extents(mCanvasBounds.min_corner, view_left_bottom),
478 max_extents(mCanvasBounds.max_corner, view_right_top));
479
480 // Update the adjustments
481
482 GtkAdjustment * h_gtkAdjustment = mHAdjustment.getAdjustmentStruct;
483 GtkAdjustment * v_gtkAdjustment = mVAdjustment.getAdjustmentStruct;
484
485 gtk_adjustment_set_lower(h_gtkAdjustment, mCanvasBounds.min_corner.x);
486 gtk_adjustment_set_upper(h_gtkAdjustment, mCanvasBounds.max_corner.x);
487 gtk_adjustment_set_value(h_gtkAdjustment, view_left_bottom.x);
488 gtk_adjustment_set_step_increment(h_gtkAdjustment, mCanvasBounds.size.x / 16.0);
489 gtk_adjustment_set_page_increment(h_gtkAdjustment, mCanvasBounds.size.x / 4.0);
490 gtk_adjustment_set_page_size(h_gtkAdjustment, model_size.x);
491
492 gtk_adjustment_set_lower(v_gtkAdjustment, mCanvasBounds.min_corner.y);
493 gtk_adjustment_set_upper(v_gtkAdjustment, mCanvasBounds.max_corner.y);
494 gtk_adjustment_set_value(v_gtkAdjustment, view_left_bottom.y);
495 gtk_adjustment_set_step_increment(v_gtkAdjustment, mCanvasBounds.size.y / 16.0);
496 gtk_adjustment_set_page_increment(v_gtkAdjustment, mCanvasBounds.size.y / 4.0);
497 gtk_adjustment_set_page_size(v_gtkAdjustment, model_size.y);
498
499 mHAdjustment.changed;
500 mHAdjustment.valueChanged;
501 mVAdjustment.changed;
502 mVAdjustment.valueChanged;
503 }
504
505 void process_damage() {
506 if (mDamage.valid) {
507 //writefln("Damage: %s", mDamage);
508 int x, y, w, h;
509 mDamage.get_quantised(x, y, w, h);
510 //writefln("Quantised damage: %d %d %d %d", x, y, w, h);
511 y = cast(int)mViewSize.y - (y + h);
512 //writefln("Flipped Quantised damage: %d %d %d %d", x, y, w, h);
513 mDrawingArea.queueDrawArea(x, y, w, h);
514 //mDrawingArea.queueDraw();
515 mDamage = Rectangle.DEFAULT;
516 }
517 else {
518 //writefln("No damage");
519 }
520 }
521
522 double clamp_zoom(double zoom) { return clamp(zoom, 0.2, 10.0); }
523
524 bool mHadConfigure;
525 Rectangle mDamage; // pixels
526
527 // Model units are in millimetres
528 // Screen units are in pixels
529 double mZoom; // pixels-per-model-unit (mm)
530 Vector mViewSize; // pixel: size of view window in pixels
531 Point mViewCentre; // model: where in the model is the centre of our view
532 Rectangle mCanvasBounds; // model:
533
534 // Child widgets:
535 HRuler mHRuler;
536 VRuler mVRuler;
537 DrawingArea mDrawingArea;
538 Adjustment mHAdjustment;
539 HScrollbar mHScrollbar;
540 Adjustment mVAdjustment;
541 VScrollbar mVScrollbar;
542
543 // Layers:
544 Layer[] mLayers;
545 EventHandler mEventHandler;
546 double mPPI;
547 }
548 }