1 module easyd.gtk.base; 2 3 // (C) 2014-2019 by Matthias Rossmy 4 // This file is distributed under the "Fair Use License v2" 5 6 //TODO: .tr wieder einkommentieren, sobald die Funktion portiert wurde 7 8 /* Example: 9 mixin GuiApp!(TestWin); 10 11 class TestWin : ILayoutWindow 12 { 13 void init() 14 { 15 title="GtkTest"; 16 } 17 } 18 */ 19 20 import easyd.base; 21 import std.conv; 22 import std.stdio; 23 24 public import gtk.Main; 25 import gtk.Clipboard; 26 import gtk.Grid; 27 import gtk.Box; 28 import gtk.Widget; 29 import gtk.MainWindow; 30 import gtk.MenuBar; 31 import gtk.Menu; 32 import gtk.ImageMenuItem; 33 import gtk.SeparatorMenuItem; 34 import gtk.AccelGroup; 35 import gdk.Atom; 36 import gdk.Event; 37 import gobject.ObjectG; 38 39 mixin template GuiApp(T) 40 { 41 T gMainWin; 42 43 void main(string[] args) 44 { 45 Main.init(args); 46 gMainWin = new T(); 47 Main.run(); 48 } 49 } 50 51 void handleGtkEvents(bool sleep=false, bool dbg=false) 52 { 53 bool eventsProcessed=false; 54 while(Main.eventsPending) 55 { 56 Main.iteration; 57 eventsProcessed=true; 58 } 59 if(dbg) writeln(eventsProcessed?"Events processed":"No events"); 60 if(sleep) sleepMsec(1); 61 } 62 63 Clipboard clipBoard(string name="CLIPBOARD") 64 { 65 return Clipboard.get(intern(name, true)); 66 } 67 68 class GridLayout : Grid 69 { 70 protected Widget lastWidget = null; 71 protected bool hasColStretch = false; 72 protected bool hasRowStretch = false; 73 74 @property void spacing(int value) 75 { 76 setRowSpacing(value); 77 setColumnSpacing(value); 78 } 79 80 @property void margin(int value) 81 { 82 setMarginLeft(value); 83 setMarginRight(value); 84 setMarginTop(value); 85 setMarginBottom(value); 86 } 87 88 T add(T)(T widget, int colspan=1, int rowspan=1, bool colstretch=false, bool rowstretch=false, int width=0, int height=0) 89 { 90 if(getOrientation()==GtkOrientation.HORIZONTAL) 91 { 92 return addRight(widget, colspan, rowspan, colstretch, rowstretch, width, height); 93 } 94 else 95 { 96 return addBelow(widget, colspan, rowspan, colstretch, rowstretch, width, height); 97 } 98 } 99 100 T addBelow(T)(T widget, int colspan=1, int rowspan=1, bool colstretch=false, bool rowstretch=false, int width=0, int height=0) 101 { 102 return addBelow(lastWidget,widget,colspan,rowspan,colstretch,rowstretch,width,height); 103 } 104 105 T addAbove(T)(T widget, int colspan=1, int rowspan=1, bool colstretch=false, bool rowstretch=false, int width=0, int height=0) 106 { 107 return addAbove(lastWidget,widget,colspan,rowspan,colstretch,rowstretch,width,height); 108 } 109 110 T addLeft(T)(T widget, int colspan=1, int rowspan=1, bool colstretch=false, bool rowstretch=false, int width=0, int height=0) 111 { 112 return addLeftOf(lastWidget,widget,colspan,rowspan,colstretch,rowstretch,width,height); 113 } 114 115 T addRight(T)(T widget, int colspan=1, int rowspan=1, bool colstretch=false, bool rowstretch=false, int width=0, int height=0) 116 { 117 return addRightOf(lastWidget,widget,colspan,rowspan,colstretch,rowstretch,width,height); 118 } 119 120 T addBelow(T)(Widget reference, T widget, int colspan=1, int rowspan=1, bool colstretch=false, bool rowstretch=false, int width=0, int height=0) 121 { 122 setupWidget(widget,colstretch,rowstretch,width,height); 123 attachNextTo(widget,reference,GtkPositionType.BOTTOM,colspan,rowspan); 124 lastWidget = widget; 125 return widget; 126 } 127 128 T addAbove(T)(Widget reference, T widget, int colspan=1, int rowspan=1, bool colstretch=false, bool rowstretch=false, int width=0, int height=0) 129 { 130 setupWidget(widget,colstretch,rowstretch,width,height); 131 attachNextTo(widget,reference,GtkPositionType.TOP,colspan,rowspan); 132 lastWidget = widget; 133 return widget; 134 } 135 136 T addLeftOf(T)(Widget reference, T widget, int colspan=1, int rowspan=1, bool colstretch=false, bool rowstretch=false, int width=0, int height=0) 137 { 138 setupWidget(widget,colstretch,rowstretch,width,height); 139 attachNextTo(widget,reference,GtkPositionType.LEFT,colspan,rowspan); 140 lastWidget = widget; 141 return widget; 142 } 143 144 T addRightOf(T)(Widget reference, T widget, int colspan=1, int rowspan=1, bool colstretch=false, bool rowstretch=false, int width=0, int height=0) 145 { 146 setupWidget(widget,colstretch,rowstretch,width,height); 147 attachNextTo(widget,reference,GtkPositionType.RIGHT,colspan,rowspan); 148 lastWidget = widget; 149 return widget; 150 } 151 152 typeof(this) addSubLayout(int colspan=1, int rowspan=1, bool colstretch=false, bool rowstretch=false) 153 { 154 if(getOrientation()==GtkOrientation.HORIZONTAL) 155 { 156 return addSubLayoutRight(colspan, rowspan, colstretch, rowstretch); 157 } 158 else 159 { 160 return addSubLayoutBelow(colspan, rowspan, colstretch, rowstretch); 161 } 162 } 163 164 typeof(this) addSubLayoutBelow(int colspan=1, int rowspan=1, bool colstretch=false, bool rowstretch=false) 165 { 166 return addSubLayoutBelow(lastWidget, colspan, rowspan, colstretch, rowstretch); 167 } 168 169 typeof(this) addSubLayoutAbove(int colspan=1, int rowspan=1, bool colstretch=false, bool rowstretch=false) 170 { 171 return addSubLayoutAbove(lastWidget, colspan, rowspan, colstretch, rowstretch); 172 } 173 174 typeof(this) addSubLayoutLeft(int colspan=1, int rowspan=1, bool colstretch=false, bool rowstretch=false) 175 { 176 return addSubLayoutLeftOf(lastWidget, colspan, rowspan, colstretch, rowstretch); 177 } 178 179 typeof(this) addSubLayoutRight(int colspan=1, int rowspan=1, bool colstretch=false, bool rowstretch=false) 180 { 181 return addSubLayoutRightOf(lastWidget, colspan, rowspan, colstretch, rowstretch); 182 } 183 184 typeof(this) addSubLayoutBelow(Widget reference, int colspan=1, int rowspan=1, bool colstretch=false, bool rowstretch=false) 185 { 186 auto widget = createSubLayout(colstretch, rowstretch); 187 attachNextTo(widget,reference,GtkPositionType.BOTTOM,colspan,rowspan); 188 lastWidget = widget; 189 return widget; 190 } 191 192 typeof(this) addSubLayoutAbove(Widget reference, int colspan=1, int rowspan=1, bool colstretch=false, bool rowstretch=false) 193 { 194 auto widget = createSubLayout(colstretch, rowstretch); 195 attachNextTo(widget,reference,GtkPositionType.TOP,colspan,rowspan); 196 lastWidget = widget; 197 return widget; 198 } 199 200 typeof(this) addSubLayoutLeftOf(Widget reference, int colspan=1, int rowspan=1, bool colstretch=false, bool rowstretch=false) 201 { 202 auto widget = createSubLayout(colstretch, rowstretch); 203 attachNextTo(widget,reference,GtkPositionType.LEFT,colspan,rowspan); 204 lastWidget = widget; 205 return widget; 206 } 207 208 typeof(this) addSubLayoutRightOf(Widget reference, int colspan=1, int rowspan=1, bool colstretch=false, bool rowstretch=false) 209 { 210 auto widget = createSubLayout(colstretch, rowstretch); 211 attachNextTo(widget,reference,GtkPositionType.RIGHT,colspan,rowspan); 212 lastWidget = widget; 213 return widget; 214 } 215 216 void autoStretch() 217 { 218 auto children = getChildren(); 219 for(;children !is null;children=children.next) 220 { 221 Widget w = ObjectG.getDObject!(Widget)(cast(GtkWidget*) children.data); 222 if(typeid(w).to!string == typeid(this).to!string) 223 { 224 (cast(typeof(this)) w).autoStretch(); 225 } 226 else 227 { 228 if(!hasColStretch) w.setHexpand(1); 229 if(!hasRowStretch) w.setVexpand(1); 230 } 231 } 232 } 233 234 protected void setupWidget(T)(T widget, bool colstretch, bool rowstretch, int width, int height) 235 { 236 if(width>0 || height>0) 237 { 238 widget.setSizeRequest(width,height); 239 } 240 if(colstretch) 241 { 242 widget.setHexpand(1); 243 hasColStretch = true; 244 } 245 if(rowstretch) 246 { 247 widget.setVexpand(1); 248 hasRowStretch = true; 249 } 250 } 251 252 typeof(this) createSubLayout(bool colstretch, bool rowstretch) 253 { 254 auto l = new typeof(this); 255 l.spacing = getRowSpacing; 256 if(!colstretch) l.hasColStretch = true; 257 if(!rowstretch) l.hasRowStretch = true; 258 return l; 259 } 260 } 261 262 interface IInit 263 { 264 void init(); 265 } 266 267 abstract class ILayoutWindow : MainWindow, IInit 268 { 269 public GridLayout layout; 270 public bool autoStretchAfterInit = true; 271 void delegate()[] onClose; 272 protected MenuBar topMenuBarIntern; 273 protected MenuBar bottomMenuBarIntern; 274 public AccelGroup accelGroup; 275 protected Box vBox; 276 277 public MenuBar topMenuBar() 278 { 279 return topMenuBarIntern.create; 280 } 281 282 public MenuBar bottomMenuBar() 283 { 284 return bottomMenuBarIntern.create; 285 } 286 287 this(int minWidth=200, int minHeight=30) 288 { 289 super(""); 290 setDefaultSize(minWidth,minHeight); 291 setPosition(WindowPosition.CENTER); 292 293 layout = new GridLayout(); 294 layout.margin = 5; 295 layout.spacing = 5; 296 297 init(); 298 299 vBox = new Box(Orientation.VERTICAL,0); 300 add(vBox); 301 if(topMenuBarIntern !is null) vBox.add(topMenuBarIntern); 302 vBox.add(layout); 303 if(bottomMenuBarIntern !is null) vBox.add(bottomMenuBarIntern); 304 305 if(autoStretchAfterInit) layout.autoStretch(); 306 307 addOnDelete(&onCloseFunc); 308 309 showAll(); 310 } 311 312 protected bool onCloseFunc(Event e, Widget w) 313 { 314 onClose.trigger(); 315 return false; 316 } 317 318 @property void title(string value) 319 { 320 setTitle(value); 321 } 322 } 323 324 interface IBindable(T) 325 { 326 T value(); 327 void value(T val); 328 } 329 330 abstract class IBinding 331 { 332 void delegate()[] onEdit; 333 void load(); 334 void apply(); 335 } 336 337 class Binding(T) : IBinding 338 { 339 T* data; 340 IBindable!T control; 341 string oldValue; 342 343 override void load() 344 { 345 control.value = *data; 346 oldValue = control.value.to!string; //TODO: to!string durch String-Serialisierung ersetzen (damit Binding auch strukturierte Datentypen unterstützt) 347 } 348 349 override void apply() 350 { 351 string newvalue = control.value.to!string; //TODO: to!string durch String-Serialisierung ersetzen 352 if(newvalue != oldValue) //strings vergleichen, weil der Vergleich von 2 gleichen class-Objekten trotzdem ungleich zurückgeben kann 353 { 354 *data = control.value; 355 static if(__traits(hasMember, T, "update")) (*data).update; 356 onEdit.trigger; 357 oldValue = newvalue; 358 } 359 } 360 } 361 362 struct BindingSet 363 { 364 IBinding[] bindings; 365 alias bindings this; 366 367 void add(T)(T* data, IBindable!T control) 368 { 369 auto b = new Binding!T; 370 b.data=data; 371 b.control=control; 372 bindings ~= b; 373 } 374 375 void add(T)(ref T data, IBindable!T control) 376 { 377 add(&data,control); 378 } 379 380 void load() 381 { 382 foreach(b;bindings) b.load; 383 } 384 385 void apply() 386 { 387 foreach(b;bindings) b.apply; 388 } 389 } 390 391 class MenuItem : ImageMenuItem 392 { 393 Menu subMenuIntern; 394 void delegate()[] onClick; 395 396 this(string s, void delegate() onclick=null) 397 { 398 super(s); 399 setupEvents(onclick); 400 } 401 402 this(StockID id, void delegate() onclick=null) 403 { 404 super(id,null); 405 setupEvents(onclick); 406 } 407 408 this(string s, StockID id, void delegate() onclick=null) 409 { 410 super(id,null); 411 setLabel(s); 412 setupEvents(onclick); 413 } 414 415 this(Widget w, void delegate() onclick=null) 416 { 417 super(); 418 setImage(w); 419 setupEvents(onclick); 420 } 421 422 Menu subMenu() 423 { 424 if(subMenuIntern is null) 425 { 426 subMenuIntern = new Menu(); 427 setSubmenu(subMenuIntern); 428 } 429 return subMenuIntern; 430 } 431 432 MenuItem add(string s, void delegate() onclick=null) 433 { 434 auto item = new MenuItem(s,onclick); 435 subMenu.append(item); 436 return item; 437 } 438 439 MenuItem add(StockID id, void delegate() onclick=null) 440 { 441 auto item = new MenuItem(id,onclick); 442 subMenu.append(item); 443 return item; 444 } 445 446 MenuItem add(string s, StockID id, void delegate() onclick=null) 447 { 448 auto item = new MenuItem(s,id,onclick); 449 subMenu.append(item); 450 return item; 451 } 452 453 MenuItem addWidget(Widget w, void delegate() onclick=null) 454 { 455 auto item = new MenuItem(w,onclick); 456 subMenu.append(item); 457 return item; 458 } 459 460 void addSeparator() 461 { 462 subMenu.append(new SeparatorMenuItem); 463 } 464 465 void addAccel(ILayoutWindow win, string shortcut) 466 { 467 uint accelKey; 468 GdkModifierType accelMods; 469 AccelGroup.acceleratorParse(shortcut,accelKey,accelMods); 470 addAccelerator("activate",win.accelGroup,accelKey,accelMods,GtkAccelFlags.VISIBLE); 471 } 472 473 protected void setupEvents(void delegate() onclick) 474 { 475 addOnActivate(&onClickFunc); 476 //addOnButtonPress(&onPressFunc); 477 if(onclick !is null) 478 { 479 onClick ~= onclick; 480 } 481 } 482 483 /*protected bool onPressFunc(Event event, Widget widget) 484 { 485 onClick.trigger(); 486 return onClick.length>0; 487 }*/ 488 489 protected void onClickFunc(gtk.MenuItem.MenuItem item) 490 { 491 onClick.trigger(); 492 } 493 } 494 495 mixin template MenuAdd() 496 { 497 MenuItem add(string s, void delegate() onclick=null) 498 { 499 auto item = new MenuItem(s,onclick); 500 append(item); 501 return item; 502 } 503 504 MenuItem add(StockID id, void delegate() onclick=null) 505 { 506 auto item = new MenuItem(id,onclick); 507 append(item); 508 return item; 509 } 510 511 MenuItem add(string s, StockID id, void delegate() onclick=null) 512 { 513 auto item = new MenuItem(s,id,onclick); 514 append(item); 515 return item; 516 } 517 518 MenuItem addWidget(Widget w, void delegate() onclick=null) 519 { 520 auto item = new MenuItem(w,onclick); 521 append(item); 522 return item; 523 } 524 } 525 526 class Menu : gtk.Menu.Menu 527 { 528 mixin MenuAdd; 529 } 530 531 class MenuBar : gtk.MenuBar.MenuBar 532 { 533 mixin MenuAdd; 534 } 535