Mercurial > projects > doodle
annotate doodle/gtk/canvas.d @ 62:6c3993f4c3eb
Checkpoint
author | daveb |
---|---|
date | Thu, 12 Aug 2010 11:48:55 +0930 |
parents | c63719604adb |
children | 20d6327c4a75 |
rev | line source |
---|---|
28
1754cb773d41
Part-way through getting to compile with configure/builder.
Graham St Jack <graham.stjack@internode.on.net>
parents:
27
diff
changeset
|
1 module doodle.gtk.canvas; |
0 | 2 |
16
9e63308b749c
* Fix up public/private includes
David Bryant <daveb@acres.com.au>
parents:
15
diff
changeset
|
3 public { |
28
1754cb773d41
Part-way through getting to compile with configure/builder.
Graham St Jack <graham.stjack@internode.on.net>
parents:
27
diff
changeset
|
4 import doodle.dia.icanvas; |
1754cb773d41
Part-way through getting to compile with configure/builder.
Graham St Jack <graham.stjack@internode.on.net>
parents:
27
diff
changeset
|
5 import doodle.tk.events; |
16
9e63308b749c
* Fix up public/private includes
David Bryant <daveb@acres.com.au>
parents:
15
diff
changeset
|
6 } |
0 | 7 |
16
9e63308b749c
* Fix up public/private includes
David Bryant <daveb@acres.com.au>
parents:
15
diff
changeset
|
8 private { |
40 | 9 import doodle.core.logging; |
28
1754cb773d41
Part-way through getting to compile with configure/builder.
Graham St Jack <graham.stjack@internode.on.net>
parents:
27
diff
changeset
|
10 import doodle.gtk.conversions; |
1754cb773d41
Part-way through getting to compile with configure/builder.
Graham St Jack <graham.stjack@internode.on.net>
parents:
27
diff
changeset
|
11 import doodle.tk.misc; |
1754cb773d41
Part-way through getting to compile with configure/builder.
Graham St Jack <graham.stjack@internode.on.net>
parents:
27
diff
changeset
|
12 import doodle.cairo.routines; |
0 | 13 |
16
9e63308b749c
* Fix up public/private includes
David Bryant <daveb@acres.com.au>
parents:
15
diff
changeset
|
14 import cairo.Surface; |
9e63308b749c
* Fix up public/private includes
David Bryant <daveb@acres.com.au>
parents:
15
diff
changeset
|
15 |
9e63308b749c
* Fix up public/private includes
David Bryant <daveb@acres.com.au>
parents:
15
diff
changeset
|
16 import std.math; |
9e63308b749c
* Fix up public/private includes
David Bryant <daveb@acres.com.au>
parents:
15
diff
changeset
|
17 import std.stdio; |
0 | 18 |
16
9e63308b749c
* Fix up public/private includes
David Bryant <daveb@acres.com.au>
parents:
15
diff
changeset
|
19 import gtk.Widget; |
9e63308b749c
* Fix up public/private includes
David Bryant <daveb@acres.com.au>
parents:
15
diff
changeset
|
20 import gtk.Toolbar; |
9e63308b749c
* Fix up public/private includes
David Bryant <daveb@acres.com.au>
parents:
15
diff
changeset
|
21 import gtk.Table; |
9e63308b749c
* Fix up public/private includes
David Bryant <daveb@acres.com.au>
parents:
15
diff
changeset
|
22 import gtk.HRuler; |
9e63308b749c
* Fix up public/private includes
David Bryant <daveb@acres.com.au>
parents:
15
diff
changeset
|
23 import gtk.VRuler; |
9e63308b749c
* Fix up public/private includes
David Bryant <daveb@acres.com.au>
parents:
15
diff
changeset
|
24 import gtk.Range; |
9e63308b749c
* Fix up public/private includes
David Bryant <daveb@acres.com.au>
parents:
15
diff
changeset
|
25 import gtk.HScrollbar; |
9e63308b749c
* Fix up public/private includes
David Bryant <daveb@acres.com.au>
parents:
15
diff
changeset
|
26 import gtk.VScrollbar; |
9e63308b749c
* Fix up public/private includes
David Bryant <daveb@acres.com.au>
parents:
15
diff
changeset
|
27 import gtk.DrawingArea; |
9e63308b749c
* Fix up public/private includes
David Bryant <daveb@acres.com.au>
parents:
15
diff
changeset
|
28 import gtk.Adjustment; |
0 | 29 |
16
9e63308b749c
* Fix up public/private includes
David Bryant <daveb@acres.com.au>
parents:
15
diff
changeset
|
30 import gdk.Drawable; |
0 | 31 |
16
9e63308b749c
* Fix up public/private includes
David Bryant <daveb@acres.com.au>
parents:
15
diff
changeset
|
32 import gtkc.gtk; |
9e63308b749c
* Fix up public/private includes
David Bryant <daveb@acres.com.au>
parents:
15
diff
changeset
|
33 } |
0 | 34 |
35 // x and y run right and up respectively | |
41 | 36 // |
37 // Model units are millimetres. | |
38 // | |
48 | 39 // _zoom -> pixels-per-model-unit |
40 // _viewSize -> size of view window in pixels | |
41 // _viewCentre -> location in model corresponding to centre of view | |
42 // _canvasBounds -> size of the virtual canvas in model coordinates | |
41 | 43 // |
44 // User operations: | |
45 // pan (middle click and drag) | |
46 // zoom about a point (hold control and move scroll wheel) | |
47 // resize the widget | |
0 | 48 |
57 | 49 class Canvas : Table, IViewport { |
50 this(in Layer[] layers, IEventHandler eventHandler, IGrid grid, in double ppi) { | |
0 | 51 super(3, 3, 0); |
52 | |
48 | 53 _damage = Rectangle.DEFAULT; |
17 | 54 |
48 | 55 _layers = layers.dup; |
56 _eventHandler = eventHandler; | |
57 _grid = grid; | |
58 _ppi = ppi; | |
0 | 59 |
16
9e63308b749c
* Fix up public/private includes
David Bryant <daveb@acres.com.au>
parents:
15
diff
changeset
|
60 /* |
62 | 61 trace("Layer bounds: %s", layerBounds); |
62 trace("Canvas bounds: %s", _canvasBounds); | |
63 trace("View centre: %s", _viewCentre); | |
48 | 64 */ |
0 | 65 |
13 | 66 // Create our child widgets and register callbacks |
67 | |
48 | 68 _hRuler = new HRuler; |
69 attach(_hRuler, | |
0 | 70 1, 2, |
71 0, 1, | |
72 AttachOptions.FILL | AttachOptions.EXPAND, AttachOptions.SHRINK, | |
73 0, 0); | |
48 | 74 _hRuler.setMetric(MetricType.PIXELS); |
0 | 75 |
48 | 76 _vRuler = new VRuler; |
77 attach(_vRuler, | |
0 | 78 0, 1, |
79 1, 2, | |
80 AttachOptions.SHRINK, AttachOptions.FILL | AttachOptions.EXPAND, | |
81 0, 0); | |
48 | 82 _vRuler.setMetric(MetricType.PIXELS); |
0 | 83 |
48 | 84 _drawingArea = new DrawingArea; |
85 _drawingArea.addOnRealize(&onRealize); | |
86 _drawingArea.addOnConfigure(&onConfigure); | |
87 _drawingArea.addOnExpose(&onExpose); | |
88 _drawingArea.addOnButtonPress(&onButtonPress); | |
89 _drawingArea.addOnButtonRelease(&onButtonRelease); | |
90 _drawingArea.addOnKeyPress(&onKeyEvent); | |
91 _drawingArea.addOnKeyRelease(&onKeyEvent); | |
92 _drawingArea.addOnMotionNotify(&onMotionNotify); | |
93 _drawingArea.addOnScroll(&onScroll); | |
94 _drawingArea.addOnEnterNotify(&onEnterNotify); | |
95 _drawingArea.addOnLeaveNotify(&onLeaveNotify); | |
96 _drawingArea.setEvents(EventMask.EXPOSURE_MASK | | |
20 | 97 EventMask.POINTER_MOTION_MASK | |
98 EventMask.POINTER_MOTION_HINT_MASK | | |
99 EventMask.BUTTON_MOTION_MASK | | |
100 EventMask.BUTTON_PRESS_MASK | | |
101 EventMask.BUTTON_RELEASE_MASK | | |
102 EventMask.KEY_PRESS_MASK | | |
103 EventMask.KEY_RELEASE_MASK | | |
104 EventMask.ENTER_NOTIFY_MASK | | |
105 EventMask.LEAVE_NOTIFY_MASK | | |
106 EventMask.FOCUS_CHANGE_MASK | | |
107 EventMask.SCROLL_MASK); | |
108 | |
48 | 109 attach(_drawingArea, |
0 | 110 1, 2, |
111 1, 2, | |
112 AttachOptions.FILL | AttachOptions.EXPAND, AttachOptions.FILL | AttachOptions.EXPAND, | |
113 0, 0); | |
114 | |
115 // value, lower, upper, step-inc, page-inc, page-size | |
116 // Give the adjustments dummy values until we receive a configure | |
48 | 117 _hAdjustment = new Adjustment(0.0, 0.0, 1.0, 0.2, 0.5, 0.5); |
118 _hAdjustment.addOnValueChanged(&onValueChanged); | |
119 _hScrollbar = new HScrollbar(_hAdjustment); | |
120 _hScrollbar.setInverted(false); | |
121 attach(_hScrollbar, | |
0 | 122 1, 2, |
123 2, 3, | |
124 AttachOptions.FILL | AttachOptions.EXPAND, AttachOptions.SHRINK, | |
125 0, 0); | |
126 | |
48 | 127 _vAdjustment = new Adjustment(0.0, 0.0, 1.0, 0.2, 0.5, 0.5); |
128 _vAdjustment.addOnValueChanged(&onValueChanged); | |
129 _vScrollbar = new VScrollbar(_vAdjustment); | |
130 _vScrollbar.setInverted(true); | |
131 attach(_vScrollbar, | |
0 | 132 2, 3, |
133 1, 2, | |
134 AttachOptions.SHRINK, | |
135 AttachOptions.FILL | AttachOptions.EXPAND, | |
136 0, 0); | |
137 } | |
138 | |
62 | 139 // IViewport overrides: |
140 | |
48 | 141 override void zoomRelative(in Point pixelDatum, in double factor) { |
0 | 142 // Work out pixel distance from current centre to datum, |
143 // Do the zoom, then work out the new centre that keeps the | |
144 // pixel distance the same | |
145 | |
48 | 146 Point oldModelDatum = pixelToModel(pixelDatum); |
147 Vector pixelDistance = modelToPixel(oldModelDatum - _viewCentre); | |
148 _zoom = clampZoom(factor * _zoom); | |
149 _viewCentre = oldModelDatum - pixelToModel(pixelDistance); | |
0 | 150 |
48 | 151 updateAdjustments; |
152 updateRulers; | |
153 _grid.zoomChanged(_zoom); | |
13 | 154 queueDraw; |
0 | 155 } |
156 | |
48 | 157 override void panRelative(in Vector pixelDisplacement) { |
158 _viewCentre = _viewCentre + pixelToModel(pixelDisplacement); | |
0 | 159 |
48 | 160 updateAdjustments; |
161 updateRulers; | |
13 | 162 queueDraw; |
163 } | |
164 | |
48 | 165 override void setCursor(in Cursor cursor) { |
27 | 166 CursorType cursor_type; |
167 | |
168 switch (cursor) { | |
169 case Cursor.DEFAULT: | |
170 cursor_type = CursorType.ARROW; | |
171 break; | |
172 case Cursor.HAND: | |
173 cursor_type = CursorType.HAND1; | |
174 break; | |
175 case Cursor.CROSSHAIR: | |
176 cursor_type = CursorType.CROSSHAIR; | |
177 break; | |
40 | 178 default: |
179 assert(0); | |
27 | 180 } |
181 | |
48 | 182 _drawingArea.setCursor(new gdk.Cursor.Cursor(cursor_type)); |
27 | 183 } |
184 | |
48 | 185 override void damageModel(in Rectangle area) { |
186 _damage = _damage | modelToPixel(area); | |
17 | 187 } |
188 | |
48 | 189 override void damagePixel(in Rectangle area) { |
190 _damage = _damage | area; | |
18 | 191 } |
192 | |
0 | 193 private { |
194 | |
62 | 195 void initialise() { |
48 | 196 Rectangle layerBounds = Rectangle.DEFAULT; |
41 | 197 |
48 | 198 foreach (ref layer; _layers) { |
199 layerBounds = layerBounds | layer.bounds; | |
41 | 200 } |
201 | |
48 | 202 assert(layerBounds.valid); |
41 | 203 |
48 | 204 Rectangle paddedLayerBounds = expand(move(layerBounds, - layerBounds.size), 2.0 * layerBounds.size); |
41 | 205 |
62 | 206 // |
207 | |
208 const double MM_PER_INCH = 25.4; | |
209 _zoom = 0.25 * _ppi / MM_PER_INCH; | |
210 | |
211 _canvasBounds = paddedLayerBounds; | |
212 _viewCentre = _canvasBounds.centre; | |
213 | |
214 _grid.zoomChanged(_zoom); | |
215 | |
216 updateAdjustments; | |
217 updateRulers; | |
218 } | |
219 | |
220 void consolidateBounds() { | |
221 Rectangle layerBounds = Rectangle.DEFAULT; | |
222 | |
223 foreach (ref layer; _layers) { | |
224 layerBounds = layerBounds | layer.bounds; | |
225 } | |
16
9e63308b749c
* Fix up public/private includes
David Bryant <daveb@acres.com.au>
parents:
15
diff
changeset
|
226 |
62 | 227 assert(layerBounds.valid); |
228 | |
229 Rectangle paddedLayerBounds = expand(move(layerBounds, - layerBounds.size), 2.0 * layerBounds.size); | |
230 | |
231 Vector z = _viewSize / _zoom; | |
232 Rectangle r = Rectangle(_viewCentre - z / 2.0, z); | |
233 _canvasBounds = r | paddedLayerBounds; | |
16
9e63308b749c
* Fix up public/private includes
David Bryant <daveb@acres.com.au>
parents:
15
diff
changeset
|
234 |
62 | 235 updateAdjustments; |
236 updateRulers; | |
237 } | |
35 | 238 |
62 | 239 bool onConfigure(GdkEventConfigure * event, Widget widget) { |
240 assert(widget is _drawingArea); | |
241 | |
242 _viewSize = Vector(cast(double)event.width, cast(double)event.height); | |
243 | |
244 | |
245 if (!_hadConfigure) { | |
246 initialise; | |
48 | 247 _hadConfigure = true; |
16
9e63308b749c
* Fix up public/private includes
David Bryant <daveb@acres.com.au>
parents:
15
diff
changeset
|
248 } |
41 | 249 else { |
62 | 250 consolidateBounds; |
41 | 251 } |
16
9e63308b749c
* Fix up public/private includes
David Bryant <daveb@acres.com.au>
parents:
15
diff
changeset
|
252 |
2
d6f44347373d
* Switched over to geometry done with structs instead of classes.
David Bryant <daveb@acres.com.au>
parents:
0
diff
changeset
|
253 return true; |
0 | 254 } |
255 | |
48 | 256 bool onExpose(GdkEventExpose * event, Widget widget) { |
257 assert(widget is _drawingArea); | |
0 | 258 |
48 | 259 Drawable dr = _drawingArea.getWindow; |
0 | 260 |
261 int width, height; | |
262 dr.getSize(width, height); | |
62 | 263 trace("Got expose %dx%d\n", width, height); |
0 | 264 |
48 | 265 scope modelCr = new Context(dr); |
266 scope pixelCr = new Context(dr); | |
12 | 267 |
19 | 268 Rectangle pixel_damage = |
11 | 269 event is null ? |
48 | 270 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)), | |
11 | 272 Vector(cast(double)event.area.width, cast(double)event.area.height)); |
0 | 273 |
48 | 274 Rectangle model_damage = pixelToModel(pixel_damage); |
19 | 275 |
62 | 276 //trace("Pixel damage: %s, model damage: %s", pixel_damage, model_damage); |
19 | 277 |
48 | 278 modelCr.save; pixelCr.save; { |
19 | 279 // Setup model context and clip |
12 | 280 |
48 | 281 GtkAdjustment * h_gtkAdjustment = _hAdjustment.getAdjustmentStruct; |
282 GtkAdjustment * v_gtkAdjustment = _vAdjustment.getAdjustmentStruct; | |
12 | 283 |
48 | 284 modelCr.scale(_zoom, -_zoom); |
285 modelCr.translate(-gtk_adjustment_get_value(h_gtkAdjustment), | |
286 -gtk_adjustment_get_value(v_gtkAdjustment) - gtk_adjustment_get_page_size(v_gtkAdjustment)); | |
0 | 287 |
48 | 288 rectangle(modelCr, model_damage); |
289 modelCr.clip; | |
19 | 290 |
291 // Setup pixel context and clip | |
12 | 292 |
48 | 293 pixelCr.translate(0.0, _viewSize.y); |
294 pixelCr.scale(1.0, -1.0); | |
19 | 295 |
48 | 296 rectangle(pixelCr, pixel_damage); |
297 pixelCr.clip; | |
12 | 298 |
299 // Fill the background | |
300 | |
48 | 301 pixelCr.save; { |
11 | 302 // Make the window light grey |
48 | 303 pixelCr.setSourceRgba(0.9, 0.9, 0.9, 1.0); |
304 rectangle(pixelCr, pixel_damage); | |
305 pixelCr.fill; | |
306 } pixelCr.restore; | |
12 | 307 |
308 // Draw each layer | |
0 | 309 |
48 | 310 foreach(ref layer; _layers) { |
311 modelCr.save; pixelCr.save; { | |
312 layer.draw(this, pixel_damage, pixelCr, model_damage, modelCr); | |
313 } pixelCr.restore; modelCr.restore; | |
11 | 314 } |
48 | 315 } pixelCr.restore; modelCr.restore; |
0 | 316 |
2
d6f44347373d
* Switched over to geometry done with structs instead of classes.
David Bryant <daveb@acres.com.au>
parents:
0
diff
changeset
|
317 return true; |
0 | 318 } |
319 | |
48 | 320 bool onButtonPress(GdkEventButton * event, Widget widget) { |
321 assert(widget is _drawingArea); | |
62 | 322 trace("Got button event\n"); |
0 | 323 |
48 | 324 Point pixelPoint = Point(event.x + 0.5, _viewSize.y - (event.y + 0.5)); |
325 Point modelPoint = pixelToModel(pixelPoint); | |
0 | 326 |
48 | 327 auto buttonEvent = new ButtonEvent(gtk2tkButtonAction(event.type), |
328 gtk2tkButtonName(event.button), | |
329 pixelPoint, | |
330 modelPoint, | |
331 gtk2tkMask(event.state)); | |
0 | 332 |
48 | 333 _eventHandler.handleButtonPress(this, buttonEvent); |
5 | 334 |
48 | 335 fixDamage; |
17 | 336 |
5 | 337 return true; |
338 } | |
339 | |
48 | 340 bool onButtonRelease(GdkEventButton * event, Widget widget) { |
341 assert(widget is _drawingArea); | |
5 | 342 |
48 | 343 Point pixelPoint = Point(event.x + 0.5, _viewSize.y - (event.y + 0.5)); |
344 Point modelPoint = pixelToModel(pixelPoint); | |
5 | 345 |
48 | 346 auto buttonEvent = new ButtonEvent(gtk2tkButtonAction(event.type), |
347 gtk2tkButtonName(event.button), | |
348 pixelPoint, | |
349 modelPoint, | |
350 gtk2tkMask(event.state)); | |
5 | 351 |
48 | 352 _eventHandler.handleButtonRelease(this, buttonEvent); |
0 | 353 |
48 | 354 fixDamage; |
17 | 355 |
2
d6f44347373d
* Switched over to geometry done with structs instead of classes.
David Bryant <daveb@acres.com.au>
parents:
0
diff
changeset
|
356 return true; |
0 | 357 } |
358 | |
48 | 359 bool onKeyEvent(GdkEventKey * event, Widget widget) { |
360 assert(widget is _drawingArea); | |
0 | 361 //writefln("Got key event\n"); |
362 | |
363 //auto key_event = new KeyEvent("", | |
364 // mEventHandle.handle_key(key_event); | |
365 | |
48 | 366 fixDamage; |
17 | 367 |
2
d6f44347373d
* Switched over to geometry done with structs instead of classes.
David Bryant <daveb@acres.com.au>
parents:
0
diff
changeset
|
368 return true; |
0 | 369 } |
370 | |
48 | 371 bool onMotionNotify(GdkEventMotion * event, Widget widget) { |
372 assert(widget is _drawingArea); | |
0 | 373 //writefln("Got motion notify\n"); |
48 | 374 gtk_widget_event(_hRuler.getWidgetStruct(), cast(GdkEvent *)event); |
375 gtk_widget_event(_vRuler.getWidgetStruct(), cast(GdkEvent *)event); | |
0 | 376 |
48 | 377 Point pixelPoint = Point(event.x + 0.5, _viewSize.y - (event.y + 0.5)); |
378 Point modelPoint = pixelToModel(pixelPoint); | |
0 | 379 |
48 | 380 auto motion_event = new MotionEvent(pixelPoint, |
381 modelPoint, | |
382 gtk2tkMask(event.state)); | |
0 | 383 |
48 | 384 _eventHandler.handleMotion(this, motion_event); |
0 | 385 |
48 | 386 fixDamage; |
17 | 387 |
2
d6f44347373d
* Switched over to geometry done with structs instead of classes.
David Bryant <daveb@acres.com.au>
parents:
0
diff
changeset
|
388 return true; |
0 | 389 } |
390 | |
48 | 391 bool onScroll(GdkEventScroll * event, Widget widget) { |
392 assert(widget is _drawingArea); | |
0 | 393 //writefln("Got scroll\n"); |
394 | |
48 | 395 Point pixelPoint = Point(event.x + 0.5, _viewSize.y - (event.y + 0.5)); |
396 Point modelPoint = pixelToModel(pixelPoint); | |
0 | 397 |
48 | 398 auto scroll_event = new ScrollEvent(gtk2tkDirection(event.direction), |
399 pixelPoint, | |
400 modelPoint, | |
401 gtk2tkMask(event.state)); | |
0 | 402 |
48 | 403 _eventHandler.handleScroll(this, scroll_event); |
0 | 404 |
48 | 405 fixDamage; |
17 | 406 |
2
d6f44347373d
* Switched over to geometry done with structs instead of classes.
David Bryant <daveb@acres.com.au>
parents:
0
diff
changeset
|
407 return true; |
0 | 408 } |
409 | |
20 | 410 /* |
48 | 411 public enum GdkCrossingMode { |
412 NORMAL, | |
413 GRAB, | |
414 UNGRAB, | |
415 GTK_GRAB, | |
416 GTK_UNGRAB, | |
417 STATE_CHANGED | |
418 } | |
20 | 419 |
48 | 420 public struct GdkEventCrossing { |
421 GdkEventType type; | |
422 GdkWindow *window; | |
423 byte sendEvent; | |
424 GdkWindow *subwindow; | |
425 uint time; | |
426 double x; | |
427 double y; | |
428 double xRoot; | |
429 double yRoot; | |
430 GdkCrossingMode mode; | |
431 GdkNotifyType detail; | |
432 int focus; | |
433 uint state; | |
434 } | |
435 */ | |
20 | 436 |
48 | 437 bool onEnterNotify(GdkEventCrossing * event, Widget widget) { |
438 assert(widget is _drawingArea); | |
27 | 439 //writefln("Enter %d %d %d", cast(int)event.mode, event.focus, event.state); |
440 // TODO | |
20 | 441 return true; |
442 } | |
443 | |
48 | 444 bool onLeaveNotify(GdkEventCrossing * event, Widget widget) { |
445 assert(widget is _drawingArea); | |
27 | 446 //writefln("Leave %d %d %d", cast(int)event.mode, event.focus, event.state); |
447 // TODO | |
20 | 448 return true; |
449 } | |
450 | |
0 | 451 void onValueChanged(Adjustment adjustment) { |
48 | 452 GtkAdjustment * h_gtkAdjustment = _hAdjustment.getAdjustmentStruct; |
453 GtkAdjustment * v_gtkAdjustment = _vAdjustment.getAdjustmentStruct; | |
0 | 454 |
48 | 455 Point viewLeftTop = Point(gtk_adjustment_get_value(h_gtkAdjustment), |
456 gtk_adjustment_get_value(v_gtkAdjustment)); | |
16
9e63308b749c
* Fix up public/private includes
David Bryant <daveb@acres.com.au>
parents:
15
diff
changeset
|
457 |
48 | 458 Vector modelSize = pixelToModel(_viewSize); |
16
9e63308b749c
* Fix up public/private includes
David Bryant <daveb@acres.com.au>
parents:
15
diff
changeset
|
459 |
48 | 460 //writefln("%s", viewLeftBottom); |
461 _viewCentre = viewLeftTop + modelSize / 2.0; | |
462 //writefln("onValueChanged _viewCentre: %s", _viewCentre); | |
0 | 463 |
48 | 464 updateRulers; |
0 | 465 |
13 | 466 queueDraw; |
0 | 467 } |
468 | |
48 | 469 void updateRulers() { |
58
c63719604adb
Beginnings of creating a rectangle...
"David Bryant <bagnose@gmail.com>"
parents:
57
diff
changeset
|
470 immutable Vector modelSize = pixelToModel(_viewSize); |
0 | 471 |
58
c63719604adb
Beginnings of creating a rectangle...
"David Bryant <bagnose@gmail.com>"
parents:
57
diff
changeset
|
472 immutable Point viewLeftBottom = _viewCentre - modelSize / 2.0; |
c63719604adb
Beginnings of creating a rectangle...
"David Bryant <bagnose@gmail.com>"
parents:
57
diff
changeset
|
473 immutable Point viewRightTop = _viewCentre + modelSize / 2.0; |
0 | 474 |
16
9e63308b749c
* Fix up public/private includes
David Bryant <daveb@acres.com.au>
parents:
15
diff
changeset
|
475 // Define these just to obtain the position |
9e63308b749c
* Fix up public/private includes
David Bryant <daveb@acres.com.au>
parents:
15
diff
changeset
|
476 // below and we can preserve it |
48 | 477 double lower, upper, position, maxSize; |
12 | 478 |
48 | 479 _hRuler.getRange(lower, upper, position, maxSize); |
480 _hRuler.setRange(viewLeftBottom.x, | |
481 viewRightTop.x, | |
12 | 482 position, |
48 | 483 _zoom * 50.0); |
12 | 484 |
48 | 485 _vRuler.getRange(lower, upper, position, maxSize); |
486 _vRuler.setRange(viewRightTop.y, | |
487 viewLeftBottom.y, | |
14 | 488 position, |
48 | 489 _zoom * 50.0); |
0 | 490 } |
491 | |
48 | 492 void updateAdjustments() { |
58
c63719604adb
Beginnings of creating a rectangle...
"David Bryant <bagnose@gmail.com>"
parents:
57
diff
changeset
|
493 immutable Vector modelSize = pixelToModel(_viewSize); |
0 | 494 |
58
c63719604adb
Beginnings of creating a rectangle...
"David Bryant <bagnose@gmail.com>"
parents:
57
diff
changeset
|
495 immutable Point viewLeftBottom = _viewCentre - modelSize / 2.0; |
c63719604adb
Beginnings of creating a rectangle...
"David Bryant <bagnose@gmail.com>"
parents:
57
diff
changeset
|
496 immutable Point viewRightTop = _viewCentre + modelSize / 2.0; |
0 | 497 |
498 // Adjust the canvas size if necessary | |
48 | 499 _canvasBounds = Rectangle(minExtents(_canvasBounds.minCorner, viewLeftBottom), |
500 maxExtents(_canvasBounds.maxCorner, viewRightTop)); | |
0 | 501 |
502 // Update the adjustments | |
503 | |
48 | 504 GtkAdjustment * h_gtkAdjustment = _hAdjustment.getAdjustmentStruct; |
505 GtkAdjustment * v_gtkAdjustment = _vAdjustment.getAdjustmentStruct; | |
0 | 506 |
48 | 507 gtk_adjustment_set_lower(h_gtkAdjustment, _canvasBounds.minCorner.x); |
508 gtk_adjustment_set_upper(h_gtkAdjustment, _canvasBounds.maxCorner.x); | |
509 gtk_adjustment_set_value(h_gtkAdjustment, viewLeftBottom.x); | |
510 gtk_adjustment_set_step_increment(h_gtkAdjustment, _canvasBounds.size.x / 16.0); | |
511 gtk_adjustment_set_page_increment(h_gtkAdjustment, _canvasBounds.size.x / 4.0); | |
512 gtk_adjustment_set_page_size(h_gtkAdjustment, modelSize.x); | |
0 | 513 |
48 | 514 gtk_adjustment_set_lower(v_gtkAdjustment, _canvasBounds.minCorner.y); |
515 gtk_adjustment_set_upper(v_gtkAdjustment, _canvasBounds.maxCorner.y); | |
516 gtk_adjustment_set_value(v_gtkAdjustment, viewLeftBottom.y); | |
517 gtk_adjustment_set_step_increment(v_gtkAdjustment, _canvasBounds.size.y / 16.0); | |
518 gtk_adjustment_set_page_increment(v_gtkAdjustment, _canvasBounds.size.y / 4.0); | |
519 gtk_adjustment_set_page_size(v_gtkAdjustment, modelSize.y); | |
0 | 520 |
48 | 521 _hAdjustment.changed; |
522 _hAdjustment.valueChanged; | |
523 _vAdjustment.changed; | |
524 _vAdjustment.valueChanged; | |
0 | 525 } |
526 | |
48 | 527 void fixDamage() { |
528 if (_damage.valid) { | |
529 //writefln("Damage: %s", _damage); | |
17 | 530 int x, y, w, h; |
48 | 531 _damage.getQuantised(x, y, w, h); |
532 _drawingArea.queueDrawArea(x, cast(int)_viewSize.y - (y + h), w, h); | |
533 _damage = Rectangle.DEFAULT; | |
17 | 534 } |
535 else { | |
18 | 536 //writefln("No damage"); |
17 | 537 } |
538 } | |
539 | |
48 | 540 double clampZoom(in double zoom) { return clamp(zoom, 0.2, 10.0); } |
16
9e63308b749c
* Fix up public/private includes
David Bryant <daveb@acres.com.au>
parents:
15
diff
changeset
|
541 |
48 | 542 Point modelToPixel(in Point model) const { |
543 return Point.DEFAULT + _viewSize / 2.0 + _zoom * (model - _viewCentre); | |
27 | 544 } |
545 | |
48 | 546 Point pixelToModel(in Point pixel) const { |
547 return _viewCentre + (pixel - _viewSize / 2.0 - Point.DEFAULT) / _zoom; | |
27 | 548 } |
549 | |
48 | 550 Vector modelToPixel(in Vector model) const { |
551 return _zoom * model; | |
27 | 552 } |
553 | |
48 | 554 Vector pixelToModel(in Vector pixel) const { |
555 return pixel / _zoom; | |
27 | 556 } |
557 | |
48 | 558 Rectangle modelToPixel(in Rectangle model) const { |
559 return Rectangle(modelToPixel(model.position), modelToPixel(model.size)); | |
27 | 560 } |
561 | |
48 | 562 Rectangle pixelToModel(in Rectangle model) const { |
563 return Rectangle(pixelToModel(model.position), pixelToModel(model.size)); | |
27 | 564 } |
565 | |
48 | 566 void onRealize(Widget widget) { |
567 assert(widget is _drawingArea); | |
27 | 568 //writefln("Got realize\n"); |
569 } | |
570 | |
48 | 571 bool _hadConfigure; |
572 Rectangle _damage; // pixels | |
0 | 573 |
574 // Model units are in millimetres | |
575 // Screen units are in pixels | |
48 | 576 double _zoom; // pixels-per-model-unit |
577 Vector _viewSize; // pixel: size of view window in pixels | |
578 Point _viewCentre; // model: where in the model is the centre of our view | |
579 Rectangle _canvasBounds; // model: | |
0 | 580 |
10 | 581 // Child widgets: |
48 | 582 HRuler _hRuler; |
583 VRuler _vRuler; | |
584 DrawingArea _drawingArea; | |
585 Adjustment _hAdjustment; | |
586 HScrollbar _hScrollbar; | |
587 Adjustment _vAdjustment; | |
588 VScrollbar _vScrollbar; | |
11 | 589 |
48 | 590 Layer[] _layers; |
57 | 591 IEventHandler _eventHandler; |
592 IGrid _grid; | |
48 | 593 double _ppi; |
0 | 594 } |
595 } |