1 module ggplotd.gtk; 2 3 version(ggplotdGTK): 4 5 import cairod = cairo.cairo; 6 import gtk.DrawingArea : DrawingArea; 7 8 class SurfaceArea : DrawingArea 9 { 10 import gtkc = cairo.Context; 11 import gtk.Widget : GtkAllocation, Widget; 12 import glib.Timeout : Scoped, Timeout; 13 14 public: 15 this() 16 { 17 surface = new cairod.ImageSurface(cairod.Format.CAIRO_FORMAT_ARGB32, width, height); 18 //Attach our expose callback, which will draw the window. 19 addOnDraw(&drawCallback); 20 } 21 22 protected: 23 //Override default signal handler: 24 bool drawCallback(Scoped!(gtkc.Context) cr, Widget widget) 25 { 26 import gtkp = cairo.Pattern; 27 28 if ( m_timeout is null ) 29 { 30 // Create a new timeout that will ask the window to be drawn 10 times 31 // every second. 32 m_timeout = new Timeout( 100, &onElapsed, false ); 33 } 34 35 // This is where we draw on the window 36 GtkAllocation size; 37 38 getAllocation(size); 39 40 /* 41 Surface to pattern. Scale it appropiatly and then use setSource. 42 */ 43 cairod.SurfacePattern pattern = new cairod.SurfacePattern( surface ); 44 cairod.Matrix matrix; 45 import std.conv : to; 46 matrix.initScale( width.to!double/size.width, height.to!double/size.height ); 47 pattern.setMatrix( matrix ); 48 49 /* 50 gtk-d cairo interface and cairod both wrap the same C cairo_pattern_t 51 so this cast should be save. 52 */ 53 gtkp.Pattern gtkPattern = new gtkp.Pattern(cast (gtkp.cairo_pattern_t*) pattern.nativePointer); 54 55 cr.setSource(gtkPattern); 56 cr.paint(); 57 return true; 58 } 59 60 bool onElapsed() 61 { 62 //force our program to redraw the entire clock once per every second. 63 GtkAllocation area; 64 getAllocation(area); 65 66 queueDrawArea(area.x, area.y, area.width, area.height); 67 68 return true; 69 } 70 71 Timeout m_timeout; 72 73 cairod.Surface surface; 74 75 int width = 470; 76 int height = 470; 77 } 78 79 import core.thread; 80 /** 81 * Helper class to open a GTK window and draw to it 82 * 83 * Examples: 84 * -------------------- 85 * // Code to create the aes here 86 * // ... 87 * 88 * // Start gtk window. 89 * auto gd = new GTKWindow(); 90 * auto tid = new Thread(() { gd.run("plotcli"); }).start(); 91 * auto gg = GGPlotD().put( geomHist3D( aes ) ); 92 * gd.drawGG( gg, 470, 470 ); 93 * Thread.sleep( dur!("seconds")( 2 ) ); // sleep for 5 seconds 94 * 95 * gg = GGPlotD().put( geomPoint( aes ) ); 96 * gd.clearWindow(); 97 * gd.draw( gg, 470, 470 ); 98 * 99 * // Wait for gtk thread to finish (Window closed) 100 * tid.join(); 101 * -------------------- 102 */ 103 class GTKWindow 104 { 105 import gtk.MainWindow : MainWindow; 106 import gtk.Main : Main; 107 import ggplotd.ggplotd : GGPlotD; 108 109 this() { 110 string[] args; 111 Main.init(args); 112 sa = new SurfaceArea(); 113 sa.surface = new cairod.ImageSurface(cairod.Format.CAIRO_FORMAT_ARGB32, 470, 470); 114 } 115 116 /// 117 void draw(T)( T gg, int width, int height ) 118 { 119 // Testing seems to indicate this doesn't need a mutex? 120 sa.width = width; 121 sa.height = height; 122 // Writing to cairo surfaces should be safe. Displayer only reads from it. 123 sa.surface = new cairod.ImageSurface(cairod.Format.CAIRO_FORMAT_ARGB32, width, height); 124 gg.drawToSurface( sa.surface, width, height ); 125 } 126 127 /// 128 void clearWindow() 129 { 130 cairod.RGBA colour = cairod.RGBA(1,1,1,1); 131 auto backcontext = cairod.Context(sa.surface); 132 backcontext.setSourceRGBA(colour); 133 backcontext.paint; 134 } 135 136 /** 137 * Open the window and run the mainloop. This is blocking, due to the mainloop. 138 * 139 * It should be safe to run threaded though 140 */ 141 void run(string title) 142 { 143 MainWindow win = new MainWindow(title); 144 145 win.setDefaultSize( 250, 250 ); 146 147 //c.surface = surface; 148 win.add(sa); 149 sa.show(); 150 win.showAll(); 151 152 Main.run(); 153 } 154 155 SurfaceArea sa; 156 }