diff 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
line wrap: on
line diff
--- a/doodle/gtk/canvas.d	Thu Aug 12 11:48:55 2010 +0930
+++ b/doodle/gtk/canvas.d	Thu Aug 12 16:35:24 2010 +0930
@@ -13,9 +13,6 @@
 
     import cairo.Surface;
 
-    import std.math;
-    import std.stdio;
-
     import gtk.Widget;
     import gtk.Toolbar;
     import gtk.Table;
@@ -30,6 +27,13 @@
     import gdk.Drawable;
 
     import gtkc.gtk;
+    import gtkc.gtktypes;
+    //import gtkc.gdktypes;
+
+    import std.math;
+    import std.stdio;
+
+    import core.stdc.string : strlen;
 }
 
 // x and y run right and up respectively
@@ -46,7 +50,7 @@
 //   zoom about a point (hold control and move scroll wheel)
 //   resize the widget
 
-class Canvas : Table, IViewport {
+final class Canvas : Table, IViewport {
     this(in Layer[] layers, IEventHandler eventHandler, IGrid grid, in double ppi) {
         super(3, 3, 0);
 
@@ -85,14 +89,26 @@
         _drawingArea.addOnRealize(&onRealize);
         _drawingArea.addOnConfigure(&onConfigure);
         _drawingArea.addOnExpose(&onExpose);
-        _drawingArea.addOnButtonPress(&onButtonPress);
+        _drawingArea.addOnButtonPress(&onButtonPress);              // FIXME merge delegate with next
         _drawingArea.addOnButtonRelease(&onButtonRelease);
-        _drawingArea.addOnKeyPress(&onKeyEvent);
-        _drawingArea.addOnKeyRelease(&onKeyEvent);
+        _drawingArea.addOnKeyPress(&onKeyPressEvent);               // FIXME merge delegate with next
+        _drawingArea.addOnKeyRelease(&onKeyReleaseEvent);
         _drawingArea.addOnMotionNotify(&onMotionNotify);
         _drawingArea.addOnScroll(&onScroll);
-        _drawingArea.addOnEnterNotify(&onEnterNotify);
+        _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 |
@@ -106,6 +122,8 @@
                                EventMask.FOCUS_CHANGE_MASK |
                                EventMask.SCROLL_MASK);
 
+        _drawingArea.setCanFocus(true);
+
         attach(_drawingArea,
                1, 2,
                1, 2, 
@@ -121,7 +139,8 @@
         attach(_hScrollbar,
                1, 2,
                2, 3,
-               AttachOptions.FILL | AttachOptions.EXPAND, AttachOptions.SHRINK,
+               AttachOptions.FILL | AttachOptions.EXPAND,
+               AttachOptions.SHRINK,
                0, 0);
 
         _vAdjustment = new Adjustment(0.0, 0.0, 1.0, 0.2, 0.5, 0.5);
@@ -138,7 +157,7 @@
 
     // IViewport overrides:
 
-    override void zoomRelative(in Point pixelDatum, in double factor) {
+    void zoomRelative(in Point pixelDatum, in double factor) {
         // Work out pixel distance from current centre to datum,
         // Do the zoom, then work out the new centre that keeps the
         // pixel distance the same
@@ -148,51 +167,58 @@
         _zoom = clampZoom(factor * _zoom);
         _viewCentre = oldModelDatum - pixelToModel(pixelDistance);
 
+        consolidateBounds;
+
         updateAdjustments;
         updateRulers;
         _grid.zoomChanged(_zoom);
         queueDraw;
     }
 
-    override void panRelative(in Vector pixelDisplacement) {
+    void panRelative(in Vector pixelDisplacement) {
         _viewCentre = _viewCentre + pixelToModel(pixelDisplacement);
 
+        consolidateBounds;
+
         updateAdjustments;
         updateRulers;
         queueDraw;
     }
 
-    override void setCursor(in Cursor cursor) {
-        CursorType cursor_type;
+    void setCursor(in Cursor cursor) {
+        CursorType cursorType;
 
         switch (cursor) {
         case Cursor.DEFAULT:
-            cursor_type = CursorType.ARROW;
+            cursorType = CursorType.ARROW;
             break;
         case Cursor.HAND:
-            cursor_type = CursorType.HAND1;
+            cursorType = CursorType.HAND1;
             break;
         case Cursor.CROSSHAIR:
-            cursor_type = CursorType.CROSSHAIR;
+            cursorType = CursorType.CROSSHAIR;
+            break;
+        case Cursor.PENCIL:
+            cursorType = CursorType.PENCIL;
             break;
         default:
             assert(0);
         }
 
-        _drawingArea.setCursor(new gdk.Cursor.Cursor(cursor_type));
+        _drawingArea.setCursor(new gdk.Cursor.Cursor(cursorType));
     }
 
-    override void damageModel(in Rectangle area) {
+    void damageModel(in Rectangle area) {
         _damage = _damage | modelToPixel(area);
     }
 
-    override void damagePixel(in Rectangle area) {
+    void damagePixel(in Rectangle area) {
         _damage = _damage | area;
     }
 
     private {
 
-        void initialise() {
+        void initialiseBounds() {
             Rectangle layerBounds = Rectangle.DEFAULT;
 
             foreach (ref layer; _layers) {
@@ -201,11 +227,12 @@
 
             assert(layerBounds.valid);
 
+            // FIXME use a function that grows a rectangle about its centre
+            // and change 2.0 to a class-level constant
             Rectangle paddedLayerBounds = expand(move(layerBounds, - layerBounds.size), 2.0 * layerBounds.size);
 
             //
 
-            const double MM_PER_INCH = 25.4;
             _zoom = 0.25 * _ppi / MM_PER_INCH;
 
             _canvasBounds = paddedLayerBounds;
@@ -241,10 +268,9 @@
 
             _viewSize = Vector(cast(double)event.width, cast(double)event.height);
 
-
-            if (!_hadConfigure) {
-                initialise;
-                _hadConfigure = true;
+            if (!_boundsValid) {
+                initialiseBounds;
+                _boundsValid = true;
             }
             else {
                 consolidateBounds;
@@ -260,32 +286,32 @@
 
             int width, height;
             dr.getSize(width, height);
-            trace("Got expose %dx%d\n", width, height);
+            //trace("Got expose %dx%d\n", width, height);
 
             scope modelCr = new Context(dr);
             scope pixelCr = new Context(dr);
 
-            Rectangle pixel_damage =
+            Rectangle pixelDamage =
                 event is null ?
                 Rectangle(Point(0.0, 0.0), _viewSize) :
                 Rectangle(Point(cast(double)event.area.x, _viewSize.y - cast(double)(event.area.y + event.area.height)),
                           Vector(cast(double)event.area.width, cast(double)event.area.height));
 
-            Rectangle model_damage = pixelToModel(pixel_damage);
+            Rectangle modelDamage = pixelToModel(pixelDamage);
 
-            //trace("Pixel damage: %s, model damage: %s", pixel_damage, model_damage);
+            //trace("Pixel damage: %s, model damage: %s", pixelDamage, modelDamage);
 
             modelCr.save; pixelCr.save; {
                 // Setup model context and clip
 
-                GtkAdjustment * h_gtkAdjustment = _hAdjustment.getAdjustmentStruct;
-                GtkAdjustment * v_gtkAdjustment = _vAdjustment.getAdjustmentStruct;
+                GtkAdjustment * hGtkAdjustment = _hAdjustment.getAdjustmentStruct;
+                GtkAdjustment * vGtkAdjustment = _vAdjustment.getAdjustmentStruct;
 
                 modelCr.scale(_zoom, -_zoom);
-                modelCr.translate(-gtk_adjustment_get_value(h_gtkAdjustment),
-                                  -gtk_adjustment_get_value(v_gtkAdjustment) - gtk_adjustment_get_page_size(v_gtkAdjustment));
+                modelCr.translate(-gtk_adjustment_get_value(hGtkAdjustment),
+                                  -gtk_adjustment_get_value(vGtkAdjustment) - gtk_adjustment_get_page_size(vGtkAdjustment));
 
-                rectangle(modelCr, model_damage);
+                rectangle(modelCr, modelDamage);
                 modelCr.clip;
 
                 // Setup pixel context and clip
@@ -293,7 +319,7 @@
                 pixelCr.translate(0.0, _viewSize.y);
                 pixelCr.scale(1.0, -1.0);
 
-                rectangle(pixelCr, pixel_damage);
+                rectangle(pixelCr, pixelDamage);
                 pixelCr.clip;
 
                 // Fill the background
@@ -301,7 +327,7 @@
                 pixelCr.save; {
                     // Make the window light grey
                     pixelCr.setSourceRgba(0.9, 0.9, 0.9, 1.0);
-                    rectangle(pixelCr, pixel_damage);
+                    rectangle(pixelCr, pixelDamage);
                     pixelCr.fill;
                 } pixelCr.restore;
 
@@ -309,7 +335,7 @@
 
                 foreach(ref layer; _layers) {
                     modelCr.save; pixelCr.save; {
-                        layer.draw(this, pixel_damage, pixelCr, model_damage, modelCr);
+                        layer.draw(this, pixelDamage, pixelCr, modelDamage, modelCr);
                     } pixelCr.restore; modelCr.restore;
                 }
             } pixelCr.restore; modelCr.restore;
@@ -319,7 +345,7 @@
 
         bool onButtonPress(GdkEventButton * event, Widget widget) {
             assert(widget is _drawingArea);
-            trace("Got button event\n");
+            //trace("Got button event\n");
 
             Point pixelPoint = Point(event.x + 0.5, _viewSize.y - (event.y + 0.5));
             Point modelPoint = pixelToModel(pixelPoint);
@@ -356,12 +382,45 @@
             return true;
         }
 
-        bool onKeyEvent(GdkEventKey * event, Widget widget) {
+        /*
+           public struct GdkEventKey {
+           GdkEventType type;
+           GdkWindow *window;
+           byte sendEvent;
+           uint time;
+           uint state;
+           uint keyval;
+           int length;
+           char *string;
+           ushort hardwareKeycode;
+           ubyte group;
+           uint bitfield0;
+        //uint isModifier : 1;
+        }
+         */
+        bool onKeyPressEvent(GdkEventKey * event, Widget widget) {
             assert(widget is _drawingArea);
-            //writefln("Got key event\n");
+            message("Got key event");
+
+            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;
 
-            //auto key_event = new KeyEvent("",
-            // mEventHandle.handle_key(key_event);
+            return true;
+        }
+
+        bool onKeyReleaseEvent(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 release %s", keyEvent);
+            _eventHandler.handleKeyRelease(this, keyEvent);
 
             fixDamage;
 
@@ -377,11 +436,11 @@
             Point pixelPoint = Point(event.x + 0.5, _viewSize.y - (event.y + 0.5));
             Point modelPoint = pixelToModel(pixelPoint);
 
-            auto motion_event = new MotionEvent(pixelPoint,
+            auto motionEvent = new MotionEvent(pixelPoint,
                                                 modelPoint,
                                                 gtk2tkMask(event.state));
 
-            _eventHandler.handleMotion(this, motion_event);
+            _eventHandler.handleMotion(this, motionEvent);
 
             fixDamage;
 
@@ -395,12 +454,12 @@
             Point pixelPoint = Point(event.x + 0.5, _viewSize.y - (event.y + 0.5));
             Point modelPoint = pixelToModel(pixelPoint);
 
-            auto scroll_event = new ScrollEvent(gtk2tkDirection(event.direction),
+            auto scrollEvent = new ScrollEvent(gtk2tkDirection(event.direction),
                                                 pixelPoint,
                                                 modelPoint,
                                                 gtk2tkMask(event.state));
 
-            _eventHandler.handleScroll(this, scroll_event);
+            _eventHandler.handleScroll(this, scrollEvent);
 
             fixDamage;
 
@@ -436,24 +495,61 @@
 
         bool onEnterNotify(GdkEventCrossing * event, Widget widget) {
             assert(widget is _drawingArea);
-            //writefln("Enter %d %d %d", cast(int)event.mode, event.focus, event.state);
+            message("Enter %d %d %d", cast(int)event.mode, event.focus, event.state);
             // TODO
             return true;
         }
 
         bool onLeaveNotify(GdkEventCrossing * event, Widget widget) {
             assert(widget is _drawingArea);
-            //writefln("Leave %d %d %d", cast(int)event.mode, event.focus, event.state);
+            message("Leave %d %d %d", cast(int)event.mode, event.focus, event.state);
             // TODO
             return true;
         }
 
+        /*
+           public struct GdkEventFocus {
+           GdkEventType type;
+           GdkWindow *window;
+           byte sendEvent;
+           short inn;
+           }
+         */
+        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(Event event, Widget widget) {
+            trace("onGrabBroken");
+            return true;
+        }
+        */
+
+        void onGrabFocus(Widget widget) {
+            trace("onGrabFocus");
+        }
+
+        void onGrabNotify(gboolean what, Widget widget){
+            trace("onGrabNotify");
+        }
+
         void onValueChanged(Adjustment adjustment) {
-            GtkAdjustment * h_gtkAdjustment = _hAdjustment.getAdjustmentStruct;
-            GtkAdjustment * v_gtkAdjustment = _vAdjustment.getAdjustmentStruct;
+            GtkAdjustment * hGtkAdjustment = _hAdjustment.getAdjustmentStruct;
+            GtkAdjustment * vGtkAdjustment = _vAdjustment.getAdjustmentStruct;
 
-            Point viewLeftTop = Point(gtk_adjustment_get_value(h_gtkAdjustment),
-                                      gtk_adjustment_get_value(v_gtkAdjustment));
+            Point viewLeftTop = Point(gtk_adjustment_get_value(hGtkAdjustment),
+                                      gtk_adjustment_get_value(vGtkAdjustment));
 
             Vector modelSize = pixelToModel(_viewSize);
 
@@ -501,22 +597,22 @@
 
             // Update the adjustments
 
-            GtkAdjustment * h_gtkAdjustment = _hAdjustment.getAdjustmentStruct;
-            GtkAdjustment * v_gtkAdjustment = _vAdjustment.getAdjustmentStruct;
+            GtkAdjustment * hGtkAdjustment = _hAdjustment.getAdjustmentStruct;
+            GtkAdjustment * vGtkAdjustment = _vAdjustment.getAdjustmentStruct;
 
-            gtk_adjustment_set_lower(h_gtkAdjustment, _canvasBounds.minCorner.x);
-            gtk_adjustment_set_upper(h_gtkAdjustment, _canvasBounds.maxCorner.x);
-            gtk_adjustment_set_value(h_gtkAdjustment, viewLeftBottom.x);
-            gtk_adjustment_set_step_increment(h_gtkAdjustment, _canvasBounds.size.x / 16.0);
-            gtk_adjustment_set_page_increment(h_gtkAdjustment, _canvasBounds.size.x / 4.0);
-            gtk_adjustment_set_page_size(h_gtkAdjustment, modelSize.x);
+            gtk_adjustment_set_lower(hGtkAdjustment, _canvasBounds.minCorner.x);
+            gtk_adjustment_set_upper(hGtkAdjustment, _canvasBounds.maxCorner.x);
+            gtk_adjustment_set_value(hGtkAdjustment, viewLeftBottom.x);
+            gtk_adjustment_set_step_increment(hGtkAdjustment, _canvasBounds.size.x / 16.0);
+            gtk_adjustment_set_page_increment(hGtkAdjustment, _canvasBounds.size.x / 4.0);
+            gtk_adjustment_set_page_size(hGtkAdjustment, modelSize.x);
 
-            gtk_adjustment_set_lower(v_gtkAdjustment, _canvasBounds.minCorner.y);
-            gtk_adjustment_set_upper(v_gtkAdjustment, _canvasBounds.maxCorner.y);
-            gtk_adjustment_set_value(v_gtkAdjustment, viewLeftBottom.y);
-            gtk_adjustment_set_step_increment(v_gtkAdjustment, _canvasBounds.size.y / 16.0);
-            gtk_adjustment_set_page_increment(v_gtkAdjustment, _canvasBounds.size.y / 4.0);
-            gtk_adjustment_set_page_size(v_gtkAdjustment, modelSize.y);
+            gtk_adjustment_set_lower(vGtkAdjustment, _canvasBounds.minCorner.y);
+            gtk_adjustment_set_upper(vGtkAdjustment, _canvasBounds.maxCorner.y);
+            gtk_adjustment_set_value(vGtkAdjustment, viewLeftBottom.y);
+            gtk_adjustment_set_step_increment(vGtkAdjustment, _canvasBounds.size.y / 16.0);
+            gtk_adjustment_set_page_increment(vGtkAdjustment, _canvasBounds.size.y / 4.0);
+            gtk_adjustment_set_page_size(vGtkAdjustment, modelSize.y);
 
             _hAdjustment.changed;
             _hAdjustment.valueChanged;
@@ -526,18 +622,14 @@
 
         void fixDamage() {
             if (_damage.valid) {
-                //writefln("Damage: %s", _damage);
                 int x, y, w, h;
                 _damage.getQuantised(x, y, w, h);
                 _drawingArea.queueDrawArea(x, cast(int)_viewSize.y - (y + h), w, h);
                 _damage = Rectangle.DEFAULT;
             }
-            else {
-                //writefln("No damage");
-            }
         }
 
-        double clampZoom(in double zoom) { return clamp(zoom, 0.2, 10.0); }
+        static double clampZoom(in double zoom) { return clamp(zoom, 0.2, 10.0); }
 
         Point modelToPixel(in Point model) const {
             return Point.DEFAULT + _viewSize / 2.0 + _zoom * (model - _viewCentre);
@@ -566,9 +658,10 @@
         void onRealize(Widget widget) {
             assert(widget is _drawingArea);
             //writefln("Got realize\n");
+            _drawingArea.grabFocus();
         }
 
-        bool _hadConfigure;
+        bool _boundsValid;
         Rectangle _damage;          // pixels
 
         // Model units are in millimetres
@@ -591,5 +684,7 @@
         IEventHandler _eventHandler;
         IGrid _grid;
         double _ppi;
+
+        static immutable MM_PER_INCH = 25.4;
     }
 }