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 2 times 31 // every second. 32 m_timeout = new Timeout( 500, &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 __gshared cairod.Surface surface; 74 75 __gshared int width = 470; 76 __gshared 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 * const width = 470; 90 * const height = 470; 91 * auto gd = new GTKWindow(); 92 * auto tid = new Thread(() { gd.run("plotcli", width, height); }).start(); 93 * auto gg = GGPlotD().put( geomHist2D( aes ) ); 94 * gd.draw( gg, width, height ); 95 * Thread.sleep( dur!("seconds")( 2 ) ); // sleep for 5 seconds 96 * 97 * gg = GGPlotD().put( geomPoint( aes ) ); 98 * gd.clearWindow(); 99 * gd.draw( gg, width, height ); 100 * 101 * // Wait for gtk thread to finish (Window closed) 102 * tid.join(); 103 * -------------------- 104 */ 105 class GTKWindow 106 { 107 import gtk.MainWindow : MainWindow; 108 import gtk.Main : Main; 109 import ggplotd.ggplotd : GGPlotD; 110 111 this() { 112 string[] args; 113 Main.init(args); 114 sa = new SurfaceArea(); 115 sa.surface = new cairod.ImageSurface(cairod.Format.CAIRO_FORMAT_ARGB32, 470, 470); 116 } 117 118 /// 119 void draw(T)( T gg, int width, int height ) 120 { 121 // Testing seems to indicate this doesn't need a mutex? 122 sa.width = width; 123 sa.height = height; 124 // Writing to cairo surfaces should be safe. Displayer only reads from it. 125 sa.surface = new cairod.ImageSurface(cairod.Format.CAIRO_FORMAT_ARGB32, width, height); 126 gg.drawToSurface( sa.surface, width, height ); 127 } 128 129 /// 130 void clearWindow() 131 { 132 cairod.RGBA colour = cairod.RGBA(1,1,1,1); 133 auto backcontext = cairod.Context(sa.surface); 134 backcontext.setSourceRGBA(colour); 135 backcontext.paint; 136 } 137 138 /** 139 * Open the window and run the mainloop. This is blocking, due to the mainloop. 140 * 141 * It should be safe to run threaded though 142 */ 143 void run(string title, int width = 470, int height = 470) 144 { 145 MainWindow win = new MainWindow(title); 146 147 win.setDefaultSize( width, height ); 148 149 win.add(sa); 150 sa.show(); 151 win.showAll(); 152 153 Main.run(); 154 } 155 156 __gshared SurfaceArea sa; 157 }