Mercurial > projects > doodle
view doodle/gtk/canvas.d @ 75:3cac0ee8ca35
Ok, things are seem on-track again
author | "David Bryant <bagnose@gmail.com>" |
---|---|
date | Sun, 15 Aug 2010 01:14:26 +0930 |
parents | c03ed75c0f8e |
children | 15ca7d5cd1ed |
line wrap: on
line source
module doodle.gtk.canvas; public { import doodle.dia.icanvas; import doodle.gtk.events; } private { import doodle.core.misc; import doodle.core.logging; import doodle.tk.pixel_model; import doodle.tk.cairo; import doodle.dia.layer_stack; import doodle.gtk.conversions; import cairo.Surface; 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; } // // TODO // Pass out a scope Drawing reference to the layers for drawing on instead of cairo context. // Create a class called LayerStack so we don't have to provide that non-gtk behaviour here. // final class Canvas : Table, private IViewport { this(in Layer[] layers, IEventHandler eventHandler, IGrid grid, in double pixelsPerMillimetre) { super(3, 3, 0); _damageScreen = Rectangle.DEFAULT; _layerStack = new LayerStack(layers); _eventHandler = eventHandler; _grid = grid; _pixelsPerMillimetre = pixelsPerMillimetre; // 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); // FIXME merge delegate with next _drawingArea.addOnButtonRelease(&onButtonRelease); _drawingArea.addOnKeyPress(&onKeyPressEvent); // FIXME merge delegate with next _drawingArea.addOnKeyRelease(&onKeyReleaseEvent); _drawingArea.addOnMotionNotify(&onMotionNotify); _drawingArea.addOnScroll(&onScroll); _drawingArea.addOnEnterNotify(&onEnterNotify); // FIXME merge delegate with next _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); } protected { // XXX the compiler complains about unimplemented methods if this is private // IViewport overrides: void zoomRelative(in Point screenDatum, in double factor) { _pixelModel.zoomRelative(factor, screenDatum); consolidateBounds; updateAdjustments; updateRulers; _grid.zoomChanged(_pixelModel.zoom); queueDraw; } void panRelative(in Vector screenDisplacement) { _pixelModel.panRelativePixel(screenDisplacement); consolidateBounds; updateAdjustments; updateRulers; queueDraw; } void setCursor(in Cursor cursor) { CursorType cursorType; switch (cursor) { case Cursor.DEFAULT: cursorType = CursorType.ARROW; break; case Cursor.HAND: cursorType = CursorType.HAND1; break; case Cursor.CROSSHAIR: cursorType = CursorType.CROSSHAIR; break; case Cursor.PENCIL: cursorType = CursorType.PENCIL; break; default: assert(0); } _drawingArea.setCursor(new gdk.Cursor.Cursor(cursorType)); } void damageModel(in Rectangle area) { _damageScreen = _damageScreen | _pixelModel.modelToPixel(area); } void damagePixel(in Rectangle area) { _damageScreen = _damageScreen | area; } } private { void initialiseBounds(in Rectangle viewBounds) { Rectangle lb = _layerStack.bounds; // FIXME use a function that grows a rectangle about its centre // and change 2.0 to a class-level constant Rectangle paddedLayerBounds = expand(move(lb, - lb.size), 2.0 * lb.size); _pixelModel = new PixelModel(0.25 * _pixelsPerMillimetre, paddedLayerBounds, viewBounds); _grid.zoomChanged(_pixelModel.zoom); updateAdjustments; updateRulers; } void consolidateBounds() { Rectangle lb = _layerStack.bounds; // FIXME likewise as above Rectangle paddedLayerBounds = expand(move(lb, - lb.size), 2.0 * lb.size); _pixelModel.consolidateCanvasBounds(paddedLayerBounds); updateAdjustments; updateRulers; } bool onConfigure(GdkEventConfigure * event, Widget widget) { assert(widget is _drawingArea); Rectangle viewBounds = Rectangle(Point(0.0, 0.0), Vector(cast(double)event.width, cast(double)event.height)); if (_pixelModel is null) { initialiseBounds(viewBounds); } else { consolidateBounds; } return true; } bool onExpose(GdkEventExpose * event, Widget widget) { assert(widget is _drawingArea); Drawable dr = _drawingArea.getWindow; int width, height; dr.getSize(width, height); //trace("Got expose %dx%d\n", width, height); scope modelCr = new Context(dr); scope pixelCr = new Context(dr); Rectangle pixelDamage = event is null ? _pixelModel.viewBounds : // FIXME next line sucks Rectangle(_pixelModel.viewBounds.position + Vector(cast(double)event.area.x, _pixelModel.viewBounds.h - cast(double)(event.area.y + event.area.height)), Vector(cast(double)event.area.width, cast(double)event.area.height)); Rectangle modelDamage = _pixelModel.pixelToModel(pixelDamage); //trace("Pixel damage: %s, model damage: %s", pixelDamage, modelDamage); modelCr.save; pixelCr.save; { { // Setup model context and clip modelCr.translate(0.0, _pixelModel.viewBounds.h); modelCr.scale(_pixelModel.zoom, -_pixelModel.zoom); immutable Point viewLeftBottom = _pixelModel.pixelToModel(Point(0.0, 0.0)); modelCr.translate(-viewLeftBottom.x, -viewLeftBottom.y); rectangle(modelCr, modelDamage); modelCr.clip; } { // Setup pixel context and clip pixelCr.translate(0.0, _pixelModel.viewBounds.h); pixelCr.scale(1.0, -1.0); rectangle(pixelCr, pixelDamage); pixelCr.clip; } pixelCr.save; { // Fill the background with light grey pixelCr.setSourceRgba(0.9, 0.9, 0.9, 1.0); rectangle(pixelCr, pixelDamage); pixelCr.fill; } pixelCr.restore; _layerStack.draw(pixelDamage, pixelCr, modelDamage, modelCr); } pixelCr.restore; modelCr.restore; return true; } bool onButtonPress(GdkEventButton * event, Widget widget) { assert(widget is _drawingArea); auto buttonEvent = makeButtonEvent(event, _pixelModel); _eventHandler.handleButtonPress(this, buttonEvent); fixDamage; return true; } bool onButtonRelease(GdkEventButton * event, Widget widget) { assert(widget is _drawingArea); auto buttonEvent = makeButtonEvent(event, _pixelModel); _eventHandler.handleButtonRelease(this, buttonEvent); fixDamage; return true; } bool onKeyPressEvent(GdkEventKey * event, Widget widget) { assert(widget is _drawingArea); auto keyEvent = new KeyEvent(event.string[0..strlen(event.string)].idup, event.keyval, gtk2tkMask(event.state)); message("Got key press %s", keyEvent); _eventHandler.handleKeyPress(this, keyEvent); fixDamage; return true; } bool onKeyReleaseEvent(GdkEventKey * event, Widget widget) { assert(widget is _drawingArea); _eventHandler.handleKeyRelease(this, makeKeyEvent(event, _pixelModel)); 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, _pixelModel)); fixDamage; return true; } bool onScroll(GdkEventScroll * event, Widget widget) { assert(widget is _drawingArea); _eventHandler.handleScroll(this, makeScrollEvent(event, _pixelModel)); fixDamage; return true; } bool onEnterNotify(GdkEventCrossing * event, Widget widget) { assert(widget is _drawingArea); _eventHandler.handleEnter(this, makeCrossingEvent(event, _pixelModel)); fixDamage; return true; } bool onLeaveNotify(GdkEventCrossing * event, Widget widget) { assert(widget is _drawingArea); _eventHandler.handleLeave(this, makeCrossingEvent(event, _pixelModel)); 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"); } void onAdjustmentValueChanged(Adjustment adjustment) { GtkAdjustment * hGtkAdjustment = _hAdjustment.getAdjustmentStruct; GtkAdjustment * vGtkAdjustment = _vAdjustment.getAdjustmentStruct; Point oldViewLeftBottom = _pixelModel.pixelToModel(Point(0.0, 0.0)); Point newViewLeftBottom = Point(gtk_adjustment_get_value(hGtkAdjustment), gtk_adjustment_get_value(vGtkAdjustment)); _pixelModel.panRelativeModel(newViewLeftBottom - oldViewLeftBottom); updateRulers; queueDraw; } void updateRulers() { immutable Point viewLeftBottom = _pixelModel.pixelToModel(Point(0.0, 0.0)); immutable Point viewRightTop = _pixelModel.pixelToModel(_pixelModel.viewBounds.corner1); // Define these just to obtain the position // below and we can preserve it double lower, upper, position, maxSize; _hRuler.getRange(lower, upper, position, maxSize); _hRuler.setRange(viewLeftBottom.x, viewRightTop.x, position, _pixelModel.zoom * 50.0); _vRuler.getRange(lower, upper, position, maxSize); _vRuler.setRange(viewRightTop.y, viewLeftBottom.y, position, _pixelModel.zoom * 50.0); } void updateAdjustments() { immutable Point viewLeftBottom = _pixelModel.pixelToModel(Point(0.0, 0.0)); immutable Point viewRightTop = _pixelModel.pixelToModel(_pixelModel.viewBounds.corner1); // Adjust the canvas size if necessary FIXME is this required?? _pixelModel.canvasAccommodate(Rectangle(viewLeftBottom, viewRightTop)); // FIXME Rectangle modelSize = _pixelModel.pixelToModel(_pixelModel.viewBounds); // Update the adjustments GtkAdjustment * hGtkAdjustment = _hAdjustment.getAdjustmentStruct; GtkAdjustment * vGtkAdjustment = _vAdjustment.getAdjustmentStruct; gtk_adjustment_set_lower(hGtkAdjustment, _pixelModel.canvasBounds.x0); gtk_adjustment_set_upper(hGtkAdjustment, _pixelModel.canvasBounds.x1); gtk_adjustment_set_value(hGtkAdjustment, viewLeftBottom.x); gtk_adjustment_set_step_increment(hGtkAdjustment, _pixelModel.canvasBounds.w / 16.0); gtk_adjustment_set_page_increment(hGtkAdjustment, _pixelModel.canvasBounds.w / 4.0); gtk_adjustment_set_page_size(hGtkAdjustment, modelSize.w); gtk_adjustment_set_lower(vGtkAdjustment, _pixelModel.canvasBounds.y0); gtk_adjustment_set_upper(vGtkAdjustment, _pixelModel.canvasBounds.y1); gtk_adjustment_set_value(vGtkAdjustment, viewLeftBottom.y); gtk_adjustment_set_step_increment(vGtkAdjustment, _pixelModel.canvasBounds.h / 16.0); gtk_adjustment_set_page_increment(vGtkAdjustment, _pixelModel.canvasBounds.h / 4.0); gtk_adjustment_set_page_size(vGtkAdjustment, modelSize.h); _hAdjustment.changed; _hAdjustment.valueChanged; _vAdjustment.changed; _vAdjustment.valueChanged; } void fixDamage() { if (_damageScreen.valid) { int x, y, w, h; _damageScreen.getQuantised(x, y, w, h); _drawingArea.queueDrawArea(x, cast(int)_pixelModel.viewBounds.h - (y + h), w, h); _damageScreen = Rectangle.DEFAULT; } } void onRealize(Widget widget) { assert(widget is _drawingArea); //writefln("Got realize\n"); _drawingArea.grabFocus(); } LayerStack _layerStack; IEventHandler _eventHandler; IGrid _grid; double _pixelsPerMillimetre; // Child widgets: HRuler _hRuler; VRuler _vRuler; DrawingArea _drawingArea; Adjustment _hAdjustment; HScrollbar _hScrollbar; Adjustment _vAdjustment; VScrollbar _vScrollbar; Rectangle _damageScreen; // in pixels PixelModel _pixelModel; } }