Mercurial > projects > doodle
view doodle/gtk/cairo_canvas.d @ 100:a274d16ab6ce
struct initialisers
author | David Bryant <bagnose@gmail.com> |
---|---|
date | Mon, 18 Oct 2010 18:10:02 +1030 |
parents | a98116479793 |
children | 345fb56d89fc |
line wrap: on
line source
module doodle.gtk.cairo_canvas; public { import doodle.dia.icanvas; import doodle.gtk.events; } private { import doodle.core.logging; import doodle.tk.screen_model; import doodle.dia.layer_stack; import doodle.gtk.cairo_renderer; import cairo.Surface; import cairo.Context; import gtk.Widget; import gtk.Toolbar; import gtk.Table; import gtk.HRuler; import gtk.VRuler; import gtk.Range; import gtk.HScrollbar; import gtk.VScrollbar; import gtk.DrawingArea; import gtk.Adjustment; import gdk.Drawable; import gtkc.gtk; import gtkc.gtktypes; //import gtkc.gdktypes; import std.math; import std.stdio; } final class CairoCanvas : Table, private IViewport { this(in Layer[] layers, IEventHandler eventHandler, IGrid grid, in double pixelsPerMillimetre) { super(3, 3, 0); _eventHandler = eventHandler; _grid = grid; _pixelsPerMillimetre = pixelsPerMillimetre; _layerStack = new LayerStack(layers); // Create our child widgets and register callbacks _hRuler = new HRuler; attach(_hRuler, 1, 2, 0, 1, AttachOptions.FILL | AttachOptions.EXPAND, AttachOptions.SHRINK, 0, 0); _hRuler.setMetric(MetricType.PIXELS); _vRuler = new VRuler; attach(_vRuler, 0, 1, 1, 2, AttachOptions.SHRINK, AttachOptions.FILL | AttachOptions.EXPAND, 0, 0); _vRuler.setMetric(MetricType.PIXELS); _drawingArea = new DrawingArea; _drawingArea.addOnRealize(&onRealize); _drawingArea.addOnConfigure(&onConfigure); _drawingArea.addOnExpose(&onExpose); _drawingArea.addOnButtonPress(&onButtonPress); _drawingArea.addOnButtonRelease(&onButtonRelease); _drawingArea.addOnKeyPress(&onKeyPressEvent); _drawingArea.addOnKeyRelease(&onKeyReleaseEvent); _drawingArea.addOnMotionNotify(&onMotionNotify); _drawingArea.addOnScroll(&onScroll); _drawingArea.addOnEnterNotify(&onEnterNotify); _drawingArea.addOnLeaveNotify(&onLeaveNotify); _drawingArea.addOnFocusIn(&onFocusIn); _drawingArea.addOnFocusOut(&onFocusOut); _drawingArea.addOnMoveFocus(&onMoveFocus); _drawingArea.addOnGrabBroken(&onGrabBroken); _drawingArea.addOnGrabFocus(&onGrabFocus); _drawingArea.addOnGrabNotify(&onGrabNotify); // addOnPopupMenu // addOnQueryTooltip // addOnSelection* _drawingArea.setEvents(EventMask.EXPOSURE_MASK | EventMask.POINTER_MOTION_MASK | EventMask.POINTER_MOTION_HINT_MASK | EventMask.BUTTON_MOTION_MASK | EventMask.BUTTON_PRESS_MASK | EventMask.BUTTON_RELEASE_MASK | EventMask.KEY_PRESS_MASK | EventMask.KEY_RELEASE_MASK | EventMask.ENTER_NOTIFY_MASK | EventMask.LEAVE_NOTIFY_MASK | EventMask.FOCUS_CHANGE_MASK | EventMask.SCROLL_MASK); _drawingArea.setCanFocus(true); attach(_drawingArea, 1, 2, 1, 2, AttachOptions.FILL | AttachOptions.EXPAND, AttachOptions.FILL | AttachOptions.EXPAND, 0, 0); // value, lower, upper, step-inc, page-inc, page-size // Give the adjustments dummy values until we receive a configure _hAdjustment = new Adjustment(0.0, 0.0, 1.0, 0.2, 0.5, 0.5); _hAdjustment.addOnValueChanged(&onAdjustmentValueChanged); _hScrollbar = new HScrollbar(_hAdjustment); _hScrollbar.setInverted(false); attach(_hScrollbar, 1, 2, 2, 3, AttachOptions.FILL | AttachOptions.EXPAND, AttachOptions.SHRINK, 0, 0); _vAdjustment = new Adjustment(0.0, 0.0, 1.0, 0.2, 0.5, 0.5); _vAdjustment.addOnValueChanged(&onAdjustmentValueChanged); _vScrollbar = new VScrollbar(_vAdjustment); _vScrollbar.setInverted(true); attach(_vScrollbar, 2, 3, 1, 2, AttachOptions.SHRINK, AttachOptions.FILL | AttachOptions.EXPAND, 0, 0); _cursors = [ Cursor.DEFAULT : CursorType.ARROW, Cursor.HAND : CursorType.HAND1, Cursor.CROSSHAIR : CursorType.CROSSHAIR, Cursor.PENCIL : CursorType.PENCIL ]; } protected { // XXX the compiler complains about unimplemented methods if this is private // IViewport overrides: void zoomRelative(in double factor, in Point screenDatum) { _screenModel.zoomRelative(factor, screenDatum); consolidateBounds; updateAdjustments; updateRulers; _grid.zoomChanged(_screenModel.zoom); queueDraw; } void panRelative(in Vector screenDisplacement) { _screenModel.panRelativeScreen(screenDisplacement); consolidateBounds; updateAdjustments; updateRulers; queueDraw; } void setCursor(in Cursor cursor) { _drawingArea.setCursor(new gdk.Cursor.Cursor(_cursors[cursor])); } void damageModel(in Rectangle area) { _damageScreen = _damageScreen | _screenModel.modelToScreen(area); } void damageScreen(in Rectangle area) { _damageScreen = _damageScreen | area; } } private { void consolidateBounds() { Rectangle layerBounds = _layerStack.bounds; Rectangle paddedLayerBounds = growCentre(layerBounds, 2 * layerBounds.size); _screenModel.consolidateCanvasBounds(paddedLayerBounds); updateAdjustments; updateRulers; } bool onConfigure(GdkEventConfigure * event, Widget widget) { assert(widget is _drawingArea); auto viewBoundsScreen = Rectangle(Point(0.0, 0.0), Vector(cast(double)event.width, cast(double)event.height)); if (_screenModel is null) { Rectangle layerBounds = _layerStack.bounds; Rectangle paddedLayerBounds = growCentre(layerBounds, 2 * layerBounds.size); _screenModel = new ScreenModel(0.25 * _pixelsPerMillimetre, paddedLayerBounds, viewBoundsScreen); _grid.zoomChanged(_screenModel.zoom); updateAdjustments; updateRulers; } else { _screenModel.setViewBoundsScreen(viewBoundsScreen); consolidateBounds; } return true; } bool onExpose(GdkEventExpose * event, Widget widget) { assert(widget is _drawingArea); auto dr = _drawingArea.getWindow; scope modelCr = new Context(dr); // Causing a memory leak! scope screenCr = new Context(dr); // Causing a memory leak! Rectangle screenDamage = event is null ? _screenModel.viewBoundsScreen : Rectangle(_screenModel.viewBoundsScreen.position + Vector(cast(double)event.area.x, _screenModel.viewBoundsScreen.h - cast(double)(event.area.y + event.area.height)), Vector(cast(double)event.area.width, cast(double)event.area.height)); Rectangle modelDamage = _screenModel.screenToModel(screenDamage); modelCr.save; screenCr.save; { { // Setup model context and clip modelCr.translate(0.0, _screenModel.viewBoundsScreen.h); modelCr.scale(_screenModel.zoom, -_screenModel.zoom); immutable Point viewLeftBottom = _screenModel.screenToModel(Point(0.0, 0.0)); modelCr.translate(-viewLeftBottom.x, -viewLeftBottom.y); modelCr.rectangle(modelDamage.x0, modelDamage.y0, modelDamage.w, modelDamage.h); modelCr.clip; } { // Setup screen context and clip screenCr.translate(0.0, _screenModel.viewBoundsScreen.h); screenCr.scale(1.0, -1.0); screenCr.rectangle(screenDamage.x0, screenDamage.y0, screenDamage.w, screenDamage.h); screenCr.clip; } screenCr.save; { // Fill the background with light grey screenCr.setSourceRgba(0.9, 0.9, 0.9, 1.0); screenCr.rectangle(screenDamage.x0, screenDamage.y0, screenDamage.w, screenDamage.h); screenCr.fill; } screenCr.restore; _layerStack.draw(screenDamage, new CairoRenderer(screenCr), modelDamage, new CairoRenderer(modelCr), _screenModel); } screenCr.restore; modelCr.restore; return true; } bool onButtonPress(GdkEventButton * event, Widget widget) { assert(widget is _drawingArea); _eventHandler.handleButtonPress(this, makeButtonEvent(event, _screenModel)); fixDamage; return true; } bool onButtonRelease(GdkEventButton * event, Widget widget) { assert(widget is _drawingArea); _eventHandler.handleButtonRelease(this, makeButtonEvent(event, _screenModel)); fixDamage; return true; } bool onKeyPressEvent(GdkEventKey * event, Widget widget) { assert(widget is _drawingArea); _eventHandler.handleKeyPress(this, makeKeyEvent(event, _screenModel)); fixDamage; return true; } bool onKeyReleaseEvent(GdkEventKey * event, Widget widget) { assert(widget is _drawingArea); _eventHandler.handleKeyRelease(this, makeKeyEvent(event, _screenModel)); fixDamage; return true; } bool onMotionNotify(GdkEventMotion * event, Widget widget) { assert(widget is _drawingArea); // Pass the events on to the rulers so that they update gtk_widget_event(_hRuler.getWidgetStruct(), cast(GdkEvent *)event); gtk_widget_event(_vRuler.getWidgetStruct(), cast(GdkEvent *)event); _eventHandler.handleMotion(this, makeMotionEvent(event, _screenModel)); fixDamage; return true; } bool onScroll(GdkEventScroll * event, Widget widget) { assert(widget is _drawingArea); _eventHandler.handleScroll(this, makeScrollEvent(event, _screenModel)); fixDamage; return true; } bool onEnterNotify(GdkEventCrossing * event, Widget widget) { assert(widget is _drawingArea); _eventHandler.handleEnter(this, makeCrossingEvent(event, _screenModel)); fixDamage; return true; } bool onLeaveNotify(GdkEventCrossing * event, Widget widget) { assert(widget is _drawingArea); _eventHandler.handleLeave(this, makeCrossingEvent(event, _screenModel)); fixDamage; return true; } bool onFocusIn(GdkEventFocus * event, Widget widget) { trace("onFocusIn"); return true; } bool onFocusOut(GdkEventFocus * event, Widget widget) { trace("onFocusOut"); return true; } void onMoveFocus(GtkDirectionType direction, Widget widget) { trace("onMoveFocus"); } bool onGrabBroken(gdk.Event.Event event, Widget widget) { trace("onGrabBroken"); return true; } void onGrabFocus(Widget widget) { //trace("onGrabFocus"); } void onGrabNotify(gboolean what, Widget widget) { trace("onGrabNotify: %s", what); } void onAdjustmentValueChanged(Adjustment adjustment) { GtkAdjustment * hGtkAdjustment = _hAdjustment.getAdjustmentStruct; GtkAdjustment * vGtkAdjustment = _vAdjustment.getAdjustmentStruct; Point oldViewLeftBottom = _screenModel.screenToModel(Point(0.0, 0.0)); Point newViewLeftBottom = Point(gtk_adjustment_get_value(hGtkAdjustment), gtk_adjustment_get_value(vGtkAdjustment)); _screenModel.panRelativeModel(newViewLeftBottom - oldViewLeftBottom); updateRulers; queueDraw; } void updateRulers() { immutable Point viewLeftBottom = _screenModel.screenToModel(_screenModel.viewBoundsScreen.corner0); immutable Point viewRightTop = _screenModel.screenToModel(_screenModel.viewBoundsScreen.corner1); // Define these just to obtain the position // below so we can preserve it double lower, upper, position, maxSize; _hRuler.getRange(lower, upper, position, maxSize); _hRuler.setRange(viewLeftBottom.x, viewRightTop.x, position, _screenModel.zoom * 50.0); _vRuler.getRange(lower, upper, position, maxSize); _vRuler.setRange(viewRightTop.y, viewLeftBottom.y, position, _screenModel.zoom * 50.0); } void updateAdjustments() { immutable Point viewLeftBottom = _screenModel.screenToModel(Point(0.0, 0.0)); immutable Point viewRightTop = _screenModel.screenToModel(_screenModel.viewBoundsScreen.corner1); // Adjust the canvas size if necessary _screenModel.canvasAccommodate(Rectangle(viewLeftBottom, viewRightTop)); Rectangle viewBoundsModel = _screenModel.viewBoundsModel; // Update the adjustments _hAdjustment.configure(viewLeftBottom.x, _screenModel.canvasBoundsModel.x0, _screenModel.canvasBoundsModel.x1, _screenModel.canvasBoundsModel.w / 16.0, _screenModel.canvasBoundsModel.w / 4.0, _screenModel.viewBoundsModel.w); _vAdjustment.configure(viewLeftBottom.y, _screenModel.canvasBoundsModel.y0, _screenModel.canvasBoundsModel.y1, _screenModel.canvasBoundsModel.h / 16.0, _screenModel.canvasBoundsModel.h / 4.0, _screenModel.viewBoundsModel.h); } void fixDamage() { if (_damageScreen.valid) { int x, y, w, h; _damageScreen.getQuantised(x, y, w, h); _drawingArea.queueDrawArea(x, cast(int)_screenModel.viewBoundsScreen.h - (y + h), w, h); _damageScreen = Rectangle(); } } void onRealize(Widget widget) { assert(widget is _drawingArea); _drawingArea.grabFocus(); } IEventHandler _eventHandler; IGrid _grid; double _pixelsPerMillimetre; LayerStack _layerStack; // Child widgets: HRuler _hRuler; VRuler _vRuler; DrawingArea _drawingArea; Adjustment _hAdjustment; HScrollbar _hScrollbar; Adjustment _vAdjustment; VScrollbar _vScrollbar; Rectangle _damageScreen; ScreenModel _screenModel; immutable CursorType[Cursor] _cursors; } }