comparison doodle/gtk/canvas.d @ 63:20d6327c4a75

Event progress. Got key press/release working and panning via keyboard.
author daveb
date Thu, 12 Aug 2010 16:35:24 +0930
parents 6c3993f4c3eb
children eb5436b47d13
comparison
equal deleted inserted replaced
62:6c3993f4c3eb 63:20d6327c4a75
10 import doodle.gtk.conversions; 10 import doodle.gtk.conversions;
11 import doodle.tk.misc; 11 import doodle.tk.misc;
12 import doodle.cairo.routines; 12 import doodle.cairo.routines;
13 13
14 import cairo.Surface; 14 import cairo.Surface;
15
16 import std.math;
17 import std.stdio;
18 15
19 import gtk.Widget; 16 import gtk.Widget;
20 import gtk.Toolbar; 17 import gtk.Toolbar;
21 import gtk.Table; 18 import gtk.Table;
22 import gtk.HRuler; 19 import gtk.HRuler;
28 import gtk.Adjustment; 25 import gtk.Adjustment;
29 26
30 import gdk.Drawable; 27 import gdk.Drawable;
31 28
32 import gtkc.gtk; 29 import gtkc.gtk;
30 import gtkc.gtktypes;
31 //import gtkc.gdktypes;
32
33 import std.math;
34 import std.stdio;
35
36 import core.stdc.string : strlen;
33 } 37 }
34 38
35 // x and y run right and up respectively 39 // x and y run right and up respectively
36 // 40 //
37 // Model units are millimetres. 41 // Model units are millimetres.
44 // User operations: 48 // User operations:
45 // pan (middle click and drag) 49 // pan (middle click and drag)
46 // zoom about a point (hold control and move scroll wheel) 50 // zoom about a point (hold control and move scroll wheel)
47 // resize the widget 51 // resize the widget
48 52
49 class Canvas : Table, IViewport { 53 final class Canvas : Table, IViewport {
50 this(in Layer[] layers, IEventHandler eventHandler, IGrid grid, in double ppi) { 54 this(in Layer[] layers, IEventHandler eventHandler, IGrid grid, in double ppi) {
51 super(3, 3, 0); 55 super(3, 3, 0);
52 56
53 _damage = Rectangle.DEFAULT; 57 _damage = Rectangle.DEFAULT;
54 58
83 87
84 _drawingArea = new DrawingArea; 88 _drawingArea = new DrawingArea;
85 _drawingArea.addOnRealize(&onRealize); 89 _drawingArea.addOnRealize(&onRealize);
86 _drawingArea.addOnConfigure(&onConfigure); 90 _drawingArea.addOnConfigure(&onConfigure);
87 _drawingArea.addOnExpose(&onExpose); 91 _drawingArea.addOnExpose(&onExpose);
88 _drawingArea.addOnButtonPress(&onButtonPress); 92 _drawingArea.addOnButtonPress(&onButtonPress); // FIXME merge delegate with next
89 _drawingArea.addOnButtonRelease(&onButtonRelease); 93 _drawingArea.addOnButtonRelease(&onButtonRelease);
90 _drawingArea.addOnKeyPress(&onKeyEvent); 94 _drawingArea.addOnKeyPress(&onKeyPressEvent); // FIXME merge delegate with next
91 _drawingArea.addOnKeyRelease(&onKeyEvent); 95 _drawingArea.addOnKeyRelease(&onKeyReleaseEvent);
92 _drawingArea.addOnMotionNotify(&onMotionNotify); 96 _drawingArea.addOnMotionNotify(&onMotionNotify);
93 _drawingArea.addOnScroll(&onScroll); 97 _drawingArea.addOnScroll(&onScroll);
94 _drawingArea.addOnEnterNotify(&onEnterNotify); 98 _drawingArea.addOnEnterNotify(&onEnterNotify); // FIXME merge delegate with next
95 _drawingArea.addOnLeaveNotify(&onLeaveNotify); 99 _drawingArea.addOnLeaveNotify(&onLeaveNotify);
100
101 _drawingArea.addOnFocusIn(&onFocusIn);
102 _drawingArea.addOnFocusOut(&onFocusOut);
103 _drawingArea.addOnMoveFocus(&onMoveFocus);
104 /*
105 _drawingArea.addOnGrabBroken(&onGrabBroken);
106 */
107 _drawingArea.addOnGrabFocus(&onGrabFocus);
108 _drawingArea.addOnGrabNotify(&onGrabNotify);
109 // addOnPopupMenu
110 // addOnQueryTooltip
111 // addOnSelection*
96 _drawingArea.setEvents(EventMask.EXPOSURE_MASK | 112 _drawingArea.setEvents(EventMask.EXPOSURE_MASK |
97 EventMask.POINTER_MOTION_MASK | 113 EventMask.POINTER_MOTION_MASK |
98 EventMask.POINTER_MOTION_HINT_MASK | 114 EventMask.POINTER_MOTION_HINT_MASK |
99 EventMask.BUTTON_MOTION_MASK | 115 EventMask.BUTTON_MOTION_MASK |
100 EventMask.BUTTON_PRESS_MASK | 116 EventMask.BUTTON_PRESS_MASK |
104 EventMask.ENTER_NOTIFY_MASK | 120 EventMask.ENTER_NOTIFY_MASK |
105 EventMask.LEAVE_NOTIFY_MASK | 121 EventMask.LEAVE_NOTIFY_MASK |
106 EventMask.FOCUS_CHANGE_MASK | 122 EventMask.FOCUS_CHANGE_MASK |
107 EventMask.SCROLL_MASK); 123 EventMask.SCROLL_MASK);
108 124
125 _drawingArea.setCanFocus(true);
126
109 attach(_drawingArea, 127 attach(_drawingArea,
110 1, 2, 128 1, 2,
111 1, 2, 129 1, 2,
112 AttachOptions.FILL | AttachOptions.EXPAND, AttachOptions.FILL | AttachOptions.EXPAND, 130 AttachOptions.FILL | AttachOptions.EXPAND, AttachOptions.FILL | AttachOptions.EXPAND,
113 0, 0); 131 0, 0);
119 _hScrollbar = new HScrollbar(_hAdjustment); 137 _hScrollbar = new HScrollbar(_hAdjustment);
120 _hScrollbar.setInverted(false); 138 _hScrollbar.setInverted(false);
121 attach(_hScrollbar, 139 attach(_hScrollbar,
122 1, 2, 140 1, 2,
123 2, 3, 141 2, 3,
124 AttachOptions.FILL | AttachOptions.EXPAND, AttachOptions.SHRINK, 142 AttachOptions.FILL | AttachOptions.EXPAND,
143 AttachOptions.SHRINK,
125 0, 0); 144 0, 0);
126 145
127 _vAdjustment = new Adjustment(0.0, 0.0, 1.0, 0.2, 0.5, 0.5); 146 _vAdjustment = new Adjustment(0.0, 0.0, 1.0, 0.2, 0.5, 0.5);
128 _vAdjustment.addOnValueChanged(&onValueChanged); 147 _vAdjustment.addOnValueChanged(&onValueChanged);
129 _vScrollbar = new VScrollbar(_vAdjustment); 148 _vScrollbar = new VScrollbar(_vAdjustment);
136 0, 0); 155 0, 0);
137 } 156 }
138 157
139 // IViewport overrides: 158 // IViewport overrides:
140 159
141 override void zoomRelative(in Point pixelDatum, in double factor) { 160 void zoomRelative(in Point pixelDatum, in double factor) {
142 // Work out pixel distance from current centre to datum, 161 // Work out pixel distance from current centre to datum,
143 // Do the zoom, then work out the new centre that keeps the 162 // Do the zoom, then work out the new centre that keeps the
144 // pixel distance the same 163 // pixel distance the same
145 164
146 Point oldModelDatum = pixelToModel(pixelDatum); 165 Point oldModelDatum = pixelToModel(pixelDatum);
147 Vector pixelDistance = modelToPixel(oldModelDatum - _viewCentre); 166 Vector pixelDistance = modelToPixel(oldModelDatum - _viewCentre);
148 _zoom = clampZoom(factor * _zoom); 167 _zoom = clampZoom(factor * _zoom);
149 _viewCentre = oldModelDatum - pixelToModel(pixelDistance); 168 _viewCentre = oldModelDatum - pixelToModel(pixelDistance);
169
170 consolidateBounds;
150 171
151 updateAdjustments; 172 updateAdjustments;
152 updateRulers; 173 updateRulers;
153 _grid.zoomChanged(_zoom); 174 _grid.zoomChanged(_zoom);
154 queueDraw; 175 queueDraw;
155 } 176 }
156 177
157 override void panRelative(in Vector pixelDisplacement) { 178 void panRelative(in Vector pixelDisplacement) {
158 _viewCentre = _viewCentre + pixelToModel(pixelDisplacement); 179 _viewCentre = _viewCentre + pixelToModel(pixelDisplacement);
180
181 consolidateBounds;
159 182
160 updateAdjustments; 183 updateAdjustments;
161 updateRulers; 184 updateRulers;
162 queueDraw; 185 queueDraw;
163 } 186 }
164 187
165 override void setCursor(in Cursor cursor) { 188 void setCursor(in Cursor cursor) {
166 CursorType cursor_type; 189 CursorType cursorType;
167 190
168 switch (cursor) { 191 switch (cursor) {
169 case Cursor.DEFAULT: 192 case Cursor.DEFAULT:
170 cursor_type = CursorType.ARROW; 193 cursorType = CursorType.ARROW;
171 break; 194 break;
172 case Cursor.HAND: 195 case Cursor.HAND:
173 cursor_type = CursorType.HAND1; 196 cursorType = CursorType.HAND1;
174 break; 197 break;
175 case Cursor.CROSSHAIR: 198 case Cursor.CROSSHAIR:
176 cursor_type = CursorType.CROSSHAIR; 199 cursorType = CursorType.CROSSHAIR;
200 break;
201 case Cursor.PENCIL:
202 cursorType = CursorType.PENCIL;
177 break; 203 break;
178 default: 204 default:
179 assert(0); 205 assert(0);
180 } 206 }
181 207
182 _drawingArea.setCursor(new gdk.Cursor.Cursor(cursor_type)); 208 _drawingArea.setCursor(new gdk.Cursor.Cursor(cursorType));
183 } 209 }
184 210
185 override void damageModel(in Rectangle area) { 211 void damageModel(in Rectangle area) {
186 _damage = _damage | modelToPixel(area); 212 _damage = _damage | modelToPixel(area);
187 } 213 }
188 214
189 override void damagePixel(in Rectangle area) { 215 void damagePixel(in Rectangle area) {
190 _damage = _damage | area; 216 _damage = _damage | area;
191 } 217 }
192 218
193 private { 219 private {
194 220
195 void initialise() { 221 void initialiseBounds() {
196 Rectangle layerBounds = Rectangle.DEFAULT; 222 Rectangle layerBounds = Rectangle.DEFAULT;
197 223
198 foreach (ref layer; _layers) { 224 foreach (ref layer; _layers) {
199 layerBounds = layerBounds | layer.bounds; 225 layerBounds = layerBounds | layer.bounds;
200 } 226 }
201 227
202 assert(layerBounds.valid); 228 assert(layerBounds.valid);
203 229
230 // FIXME use a function that grows a rectangle about its centre
231 // and change 2.0 to a class-level constant
204 Rectangle paddedLayerBounds = expand(move(layerBounds, - layerBounds.size), 2.0 * layerBounds.size); 232 Rectangle paddedLayerBounds = expand(move(layerBounds, - layerBounds.size), 2.0 * layerBounds.size);
205 233
206 // 234 //
207 235
208 const double MM_PER_INCH = 25.4;
209 _zoom = 0.25 * _ppi / MM_PER_INCH; 236 _zoom = 0.25 * _ppi / MM_PER_INCH;
210 237
211 _canvasBounds = paddedLayerBounds; 238 _canvasBounds = paddedLayerBounds;
212 _viewCentre = _canvasBounds.centre; 239 _viewCentre = _canvasBounds.centre;
213 240
239 bool onConfigure(GdkEventConfigure * event, Widget widget) { 266 bool onConfigure(GdkEventConfigure * event, Widget widget) {
240 assert(widget is _drawingArea); 267 assert(widget is _drawingArea);
241 268
242 _viewSize = Vector(cast(double)event.width, cast(double)event.height); 269 _viewSize = Vector(cast(double)event.width, cast(double)event.height);
243 270
244 271 if (!_boundsValid) {
245 if (!_hadConfigure) { 272 initialiseBounds;
246 initialise; 273 _boundsValid = true;
247 _hadConfigure = true;
248 } 274 }
249 else { 275 else {
250 consolidateBounds; 276 consolidateBounds;
251 } 277 }
252 278
258 284
259 Drawable dr = _drawingArea.getWindow; 285 Drawable dr = _drawingArea.getWindow;
260 286
261 int width, height; 287 int width, height;
262 dr.getSize(width, height); 288 dr.getSize(width, height);
263 trace("Got expose %dx%d\n", width, height); 289 //trace("Got expose %dx%d\n", width, height);
264 290
265 scope modelCr = new Context(dr); 291 scope modelCr = new Context(dr);
266 scope pixelCr = new Context(dr); 292 scope pixelCr = new Context(dr);
267 293
268 Rectangle pixel_damage = 294 Rectangle pixelDamage =
269 event is null ? 295 event is null ?
270 Rectangle(Point(0.0, 0.0), _viewSize) : 296 Rectangle(Point(0.0, 0.0), _viewSize) :
271 Rectangle(Point(cast(double)event.area.x, _viewSize.y - cast(double)(event.area.y + event.area.height)), 297 Rectangle(Point(cast(double)event.area.x, _viewSize.y - cast(double)(event.area.y + event.area.height)),
272 Vector(cast(double)event.area.width, cast(double)event.area.height)); 298 Vector(cast(double)event.area.width, cast(double)event.area.height));
273 299
274 Rectangle model_damage = pixelToModel(pixel_damage); 300 Rectangle modelDamage = pixelToModel(pixelDamage);
275 301
276 //trace("Pixel damage: %s, model damage: %s", pixel_damage, model_damage); 302 //trace("Pixel damage: %s, model damage: %s", pixelDamage, modelDamage);
277 303
278 modelCr.save; pixelCr.save; { 304 modelCr.save; pixelCr.save; {
279 // Setup model context and clip 305 // Setup model context and clip
280 306
281 GtkAdjustment * h_gtkAdjustment = _hAdjustment.getAdjustmentStruct; 307 GtkAdjustment * hGtkAdjustment = _hAdjustment.getAdjustmentStruct;
282 GtkAdjustment * v_gtkAdjustment = _vAdjustment.getAdjustmentStruct; 308 GtkAdjustment * vGtkAdjustment = _vAdjustment.getAdjustmentStruct;
283 309
284 modelCr.scale(_zoom, -_zoom); 310 modelCr.scale(_zoom, -_zoom);
285 modelCr.translate(-gtk_adjustment_get_value(h_gtkAdjustment), 311 modelCr.translate(-gtk_adjustment_get_value(hGtkAdjustment),
286 -gtk_adjustment_get_value(v_gtkAdjustment) - gtk_adjustment_get_page_size(v_gtkAdjustment)); 312 -gtk_adjustment_get_value(vGtkAdjustment) - gtk_adjustment_get_page_size(vGtkAdjustment));
287 313
288 rectangle(modelCr, model_damage); 314 rectangle(modelCr, modelDamage);
289 modelCr.clip; 315 modelCr.clip;
290 316
291 // Setup pixel context and clip 317 // Setup pixel context and clip
292 318
293 pixelCr.translate(0.0, _viewSize.y); 319 pixelCr.translate(0.0, _viewSize.y);
294 pixelCr.scale(1.0, -1.0); 320 pixelCr.scale(1.0, -1.0);
295 321
296 rectangle(pixelCr, pixel_damage); 322 rectangle(pixelCr, pixelDamage);
297 pixelCr.clip; 323 pixelCr.clip;
298 324
299 // Fill the background 325 // Fill the background
300 326
301 pixelCr.save; { 327 pixelCr.save; {
302 // Make the window light grey 328 // Make the window light grey
303 pixelCr.setSourceRgba(0.9, 0.9, 0.9, 1.0); 329 pixelCr.setSourceRgba(0.9, 0.9, 0.9, 1.0);
304 rectangle(pixelCr, pixel_damage); 330 rectangle(pixelCr, pixelDamage);
305 pixelCr.fill; 331 pixelCr.fill;
306 } pixelCr.restore; 332 } pixelCr.restore;
307 333
308 // Draw each layer 334 // Draw each layer
309 335
310 foreach(ref layer; _layers) { 336 foreach(ref layer; _layers) {
311 modelCr.save; pixelCr.save; { 337 modelCr.save; pixelCr.save; {
312 layer.draw(this, pixel_damage, pixelCr, model_damage, modelCr); 338 layer.draw(this, pixelDamage, pixelCr, modelDamage, modelCr);
313 } pixelCr.restore; modelCr.restore; 339 } pixelCr.restore; modelCr.restore;
314 } 340 }
315 } pixelCr.restore; modelCr.restore; 341 } pixelCr.restore; modelCr.restore;
316 342
317 return true; 343 return true;
318 } 344 }
319 345
320 bool onButtonPress(GdkEventButton * event, Widget widget) { 346 bool onButtonPress(GdkEventButton * event, Widget widget) {
321 assert(widget is _drawingArea); 347 assert(widget is _drawingArea);
322 trace("Got button event\n"); 348 //trace("Got button event\n");
323 349
324 Point pixelPoint = Point(event.x + 0.5, _viewSize.y - (event.y + 0.5)); 350 Point pixelPoint = Point(event.x + 0.5, _viewSize.y - (event.y + 0.5));
325 Point modelPoint = pixelToModel(pixelPoint); 351 Point modelPoint = pixelToModel(pixelPoint);
326 352
327 auto buttonEvent = new ButtonEvent(gtk2tkButtonAction(event.type), 353 auto buttonEvent = new ButtonEvent(gtk2tkButtonAction(event.type),
354 fixDamage; 380 fixDamage;
355 381
356 return true; 382 return true;
357 } 383 }
358 384
359 bool onKeyEvent(GdkEventKey * event, Widget widget) { 385 /*
360 assert(widget is _drawingArea); 386 public struct GdkEventKey {
361 //writefln("Got key event\n"); 387 GdkEventType type;
362 388 GdkWindow *window;
363 //auto key_event = new KeyEvent("", 389 byte sendEvent;
364 // mEventHandle.handle_key(key_event); 390 uint time;
391 uint state;
392 uint keyval;
393 int length;
394 char *string;
395 ushort hardwareKeycode;
396 ubyte group;
397 uint bitfield0;
398 //uint isModifier : 1;
399 }
400 */
401 bool onKeyPressEvent(GdkEventKey * event, Widget widget) {
402 assert(widget is _drawingArea);
403 message("Got key event");
404
405 auto keyEvent = new KeyEvent(event.string[0..strlen(event.string)].idup,
406 event.keyval,
407 gtk2tkMask(event.state));
408 message("Got key press %s", keyEvent);
409 _eventHandler.handleKeyPress(this, keyEvent);
410
411 fixDamage;
412
413 return true;
414 }
415
416 bool onKeyReleaseEvent(GdkEventKey * event, Widget widget) {
417 assert(widget is _drawingArea);
418
419 auto keyEvent = new KeyEvent(event.string[0..strlen(event.string)].idup,
420 event.keyval,
421 gtk2tkMask(event.state));
422 message("Got key release %s", keyEvent);
423 _eventHandler.handleKeyRelease(this, keyEvent);
365 424
366 fixDamage; 425 fixDamage;
367 426
368 return true; 427 return true;
369 } 428 }
375 gtk_widget_event(_vRuler.getWidgetStruct(), cast(GdkEvent *)event); 434 gtk_widget_event(_vRuler.getWidgetStruct(), cast(GdkEvent *)event);
376 435
377 Point pixelPoint = Point(event.x + 0.5, _viewSize.y - (event.y + 0.5)); 436 Point pixelPoint = Point(event.x + 0.5, _viewSize.y - (event.y + 0.5));
378 Point modelPoint = pixelToModel(pixelPoint); 437 Point modelPoint = pixelToModel(pixelPoint);
379 438
380 auto motion_event = new MotionEvent(pixelPoint, 439 auto motionEvent = new MotionEvent(pixelPoint,
381 modelPoint, 440 modelPoint,
382 gtk2tkMask(event.state)); 441 gtk2tkMask(event.state));
383 442
384 _eventHandler.handleMotion(this, motion_event); 443 _eventHandler.handleMotion(this, motionEvent);
385 444
386 fixDamage; 445 fixDamage;
387 446
388 return true; 447 return true;
389 } 448 }
393 //writefln("Got scroll\n"); 452 //writefln("Got scroll\n");
394 453
395 Point pixelPoint = Point(event.x + 0.5, _viewSize.y - (event.y + 0.5)); 454 Point pixelPoint = Point(event.x + 0.5, _viewSize.y - (event.y + 0.5));
396 Point modelPoint = pixelToModel(pixelPoint); 455 Point modelPoint = pixelToModel(pixelPoint);
397 456
398 auto scroll_event = new ScrollEvent(gtk2tkDirection(event.direction), 457 auto scrollEvent = new ScrollEvent(gtk2tkDirection(event.direction),
399 pixelPoint, 458 pixelPoint,
400 modelPoint, 459 modelPoint,
401 gtk2tkMask(event.state)); 460 gtk2tkMask(event.state));
402 461
403 _eventHandler.handleScroll(this, scroll_event); 462 _eventHandler.handleScroll(this, scrollEvent);
404 463
405 fixDamage; 464 fixDamage;
406 465
407 return true; 466 return true;
408 } 467 }
434 } 493 }
435 */ 494 */
436 495
437 bool onEnterNotify(GdkEventCrossing * event, Widget widget) { 496 bool onEnterNotify(GdkEventCrossing * event, Widget widget) {
438 assert(widget is _drawingArea); 497 assert(widget is _drawingArea);
439 //writefln("Enter %d %d %d", cast(int)event.mode, event.focus, event.state); 498 message("Enter %d %d %d", cast(int)event.mode, event.focus, event.state);
440 // TODO 499 // TODO
441 return true; 500 return true;
442 } 501 }
443 502
444 bool onLeaveNotify(GdkEventCrossing * event, Widget widget) { 503 bool onLeaveNotify(GdkEventCrossing * event, Widget widget) {
445 assert(widget is _drawingArea); 504 assert(widget is _drawingArea);
446 //writefln("Leave %d %d %d", cast(int)event.mode, event.focus, event.state); 505 message("Leave %d %d %d", cast(int)event.mode, event.focus, event.state);
447 // TODO 506 // TODO
448 return true; 507 return true;
449 } 508 }
450 509
510 /*
511 public struct GdkEventFocus {
512 GdkEventType type;
513 GdkWindow *window;
514 byte sendEvent;
515 short inn;
516 }
517 */
518 bool onFocusIn(GdkEventFocus * event, Widget widget) {
519 trace("onFocusIn");
520 return true;
521 }
522
523 bool onFocusOut(GdkEventFocus * event, Widget widget) {
524 trace("onFocusOut");
525 return true;
526 }
527
528 void onMoveFocus(GtkDirectionType direction, Widget widget) {
529 trace("onMoveFocus");
530 }
531
532 /*
533 bool onGrabBroken(Event event, Widget widget) {
534 trace("onGrabBroken");
535 return true;
536 }
537 */
538
539 void onGrabFocus(Widget widget) {
540 trace("onGrabFocus");
541 }
542
543 void onGrabNotify(gboolean what, Widget widget){
544 trace("onGrabNotify");
545 }
546
451 void onValueChanged(Adjustment adjustment) { 547 void onValueChanged(Adjustment adjustment) {
452 GtkAdjustment * h_gtkAdjustment = _hAdjustment.getAdjustmentStruct; 548 GtkAdjustment * hGtkAdjustment = _hAdjustment.getAdjustmentStruct;
453 GtkAdjustment * v_gtkAdjustment = _vAdjustment.getAdjustmentStruct; 549 GtkAdjustment * vGtkAdjustment = _vAdjustment.getAdjustmentStruct;
454 550
455 Point viewLeftTop = Point(gtk_adjustment_get_value(h_gtkAdjustment), 551 Point viewLeftTop = Point(gtk_adjustment_get_value(hGtkAdjustment),
456 gtk_adjustment_get_value(v_gtkAdjustment)); 552 gtk_adjustment_get_value(vGtkAdjustment));
457 553
458 Vector modelSize = pixelToModel(_viewSize); 554 Vector modelSize = pixelToModel(_viewSize);
459 555
460 //writefln("%s", viewLeftBottom); 556 //writefln("%s", viewLeftBottom);
461 _viewCentre = viewLeftTop + modelSize / 2.0; 557 _viewCentre = viewLeftTop + modelSize / 2.0;
499 _canvasBounds = Rectangle(minExtents(_canvasBounds.minCorner, viewLeftBottom), 595 _canvasBounds = Rectangle(minExtents(_canvasBounds.minCorner, viewLeftBottom),
500 maxExtents(_canvasBounds.maxCorner, viewRightTop)); 596 maxExtents(_canvasBounds.maxCorner, viewRightTop));
501 597
502 // Update the adjustments 598 // Update the adjustments
503 599
504 GtkAdjustment * h_gtkAdjustment = _hAdjustment.getAdjustmentStruct; 600 GtkAdjustment * hGtkAdjustment = _hAdjustment.getAdjustmentStruct;
505 GtkAdjustment * v_gtkAdjustment = _vAdjustment.getAdjustmentStruct; 601 GtkAdjustment * vGtkAdjustment = _vAdjustment.getAdjustmentStruct;
506 602
507 gtk_adjustment_set_lower(h_gtkAdjustment, _canvasBounds.minCorner.x); 603 gtk_adjustment_set_lower(hGtkAdjustment, _canvasBounds.minCorner.x);
508 gtk_adjustment_set_upper(h_gtkAdjustment, _canvasBounds.maxCorner.x); 604 gtk_adjustment_set_upper(hGtkAdjustment, _canvasBounds.maxCorner.x);
509 gtk_adjustment_set_value(h_gtkAdjustment, viewLeftBottom.x); 605 gtk_adjustment_set_value(hGtkAdjustment, viewLeftBottom.x);
510 gtk_adjustment_set_step_increment(h_gtkAdjustment, _canvasBounds.size.x / 16.0); 606 gtk_adjustment_set_step_increment(hGtkAdjustment, _canvasBounds.size.x / 16.0);
511 gtk_adjustment_set_page_increment(h_gtkAdjustment, _canvasBounds.size.x / 4.0); 607 gtk_adjustment_set_page_increment(hGtkAdjustment, _canvasBounds.size.x / 4.0);
512 gtk_adjustment_set_page_size(h_gtkAdjustment, modelSize.x); 608 gtk_adjustment_set_page_size(hGtkAdjustment, modelSize.x);
513 609
514 gtk_adjustment_set_lower(v_gtkAdjustment, _canvasBounds.minCorner.y); 610 gtk_adjustment_set_lower(vGtkAdjustment, _canvasBounds.minCorner.y);
515 gtk_adjustment_set_upper(v_gtkAdjustment, _canvasBounds.maxCorner.y); 611 gtk_adjustment_set_upper(vGtkAdjustment, _canvasBounds.maxCorner.y);
516 gtk_adjustment_set_value(v_gtkAdjustment, viewLeftBottom.y); 612 gtk_adjustment_set_value(vGtkAdjustment, viewLeftBottom.y);
517 gtk_adjustment_set_step_increment(v_gtkAdjustment, _canvasBounds.size.y / 16.0); 613 gtk_adjustment_set_step_increment(vGtkAdjustment, _canvasBounds.size.y / 16.0);
518 gtk_adjustment_set_page_increment(v_gtkAdjustment, _canvasBounds.size.y / 4.0); 614 gtk_adjustment_set_page_increment(vGtkAdjustment, _canvasBounds.size.y / 4.0);
519 gtk_adjustment_set_page_size(v_gtkAdjustment, modelSize.y); 615 gtk_adjustment_set_page_size(vGtkAdjustment, modelSize.y);
520 616
521 _hAdjustment.changed; 617 _hAdjustment.changed;
522 _hAdjustment.valueChanged; 618 _hAdjustment.valueChanged;
523 _vAdjustment.changed; 619 _vAdjustment.changed;
524 _vAdjustment.valueChanged; 620 _vAdjustment.valueChanged;
525 } 621 }
526 622
527 void fixDamage() { 623 void fixDamage() {
528 if (_damage.valid) { 624 if (_damage.valid) {
529 //writefln("Damage: %s", _damage);
530 int x, y, w, h; 625 int x, y, w, h;
531 _damage.getQuantised(x, y, w, h); 626 _damage.getQuantised(x, y, w, h);
532 _drawingArea.queueDrawArea(x, cast(int)_viewSize.y - (y + h), w, h); 627 _drawingArea.queueDrawArea(x, cast(int)_viewSize.y - (y + h), w, h);
533 _damage = Rectangle.DEFAULT; 628 _damage = Rectangle.DEFAULT;
534 } 629 }
535 else { 630 }
536 //writefln("No damage"); 631
537 } 632 static double clampZoom(in double zoom) { return clamp(zoom, 0.2, 10.0); }
538 }
539
540 double clampZoom(in double zoom) { return clamp(zoom, 0.2, 10.0); }
541 633
542 Point modelToPixel(in Point model) const { 634 Point modelToPixel(in Point model) const {
543 return Point.DEFAULT + _viewSize / 2.0 + _zoom * (model - _viewCentre); 635 return Point.DEFAULT + _viewSize / 2.0 + _zoom * (model - _viewCentre);
544 } 636 }
545 637
564 } 656 }
565 657
566 void onRealize(Widget widget) { 658 void onRealize(Widget widget) {
567 assert(widget is _drawingArea); 659 assert(widget is _drawingArea);
568 //writefln("Got realize\n"); 660 //writefln("Got realize\n");
569 } 661 _drawingArea.grabFocus();
570 662 }
571 bool _hadConfigure; 663
664 bool _boundsValid;
572 Rectangle _damage; // pixels 665 Rectangle _damage; // pixels
573 666
574 // Model units are in millimetres 667 // Model units are in millimetres
575 // Screen units are in pixels 668 // Screen units are in pixels
576 double _zoom; // pixels-per-model-unit 669 double _zoom; // pixels-per-model-unit
589 682
590 Layer[] _layers; 683 Layer[] _layers;
591 IEventHandler _eventHandler; 684 IEventHandler _eventHandler;
592 IGrid _grid; 685 IGrid _grid;
593 double _ppi; 686 double _ppi;
687
688 static immutable MM_PER_INCH = 25.4;
594 } 689 }
595 } 690 }