egra: draw ttf glyph with vertical gradient
[iv.d.git] / egra / test.d
blobfa6aed21b33b3e3ee4de0c36d45f467d621430f7
1 /* E-Mail Client
2 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
3 * Understanding is not required. Only obedience.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 3 of the License ONLY.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 module test /*is aliced*/;
19 import arsd.simpledisplay;
21 import iv.cmdcon;
22 import iv.cmdcongl;
23 import iv.strex;
24 import iv.utfutil;
25 import iv.vfs.io;
26 import iv.vfs.util;
28 import iv.egra;
31 // ////////////////////////////////////////////////////////////////////////// //
32 static immutable string TestStyle = `
33 MainPaneWindow {
34 grouplist-divline: white;
35 grouplist-back: #222;
37 threadlist-divline: white;
38 threadlist-back: #222;
41 TitlerWindow {
42 frame: white;
43 title-back: white;
44 title-text: black;
46 back: rgb(0, 92, 0);
47 text: #7f0;
48 hotline: #7f0;
53 // ////////////////////////////////////////////////////////////////////////// //
54 //__gshared int lastWinWidth, lastWinHeight;
57 // ////////////////////////////////////////////////////////////////////////// //
58 public class TitlerWindow : SubWindow {
59 public:
60 LineEditWidget edtTitle;
61 LineEditWidget fromName;
62 LineEditWidget fromMail;
64 protected:
65 dynstring name;
66 dynstring mail;
67 dynstring folder;
68 dynstring title;
70 bool delegate (dynstring name, dynstring mail, dynstring folder, dynstring title) onSelected;
72 override void createWidgets () {
73 new SpacerWidget(2);
75 version(none) {
76 fromName = new LineEditWidget("Name:");
77 fromName.flex = 1;
79 new SpacerWidget(1);
80 fromMail = new LineEditWidget("Mail:");
81 fromMail.flex = 1;
83 new SpacerWidget(1);
84 edtTitle = new LineEditWidget("Title:");
85 edtTitle.flex = 1;
86 } else {
87 (new HBoxWidget).enter{
88 with (new HotLabelWidget("&Name:", LabelWidget.HAlign.Right)) { width = width+2; hsizeId = "editors"; }
89 new SpacerWidget(4);
90 fromName = new LineEditWidget();
91 fromName.flex = 1;
92 }.flex = 1;
94 new SpacerWidget(1);
95 (new HBoxWidget).enter{
96 with (new HotLabelWidget("&Mail:", LabelWidget.HAlign.Right)) { width = width+2; hsizeId = "editors"; }
97 new SpacerWidget(4);
98 fromMail = new LineEditWidget();
99 fromMail.flex = 1;
100 }.flex = 1;
102 new SpacerWidget(1);
103 (new HBoxWidget).enter{
104 with (new HotLabelWidget("&Title:", LabelWidget.HAlign.Right)) { width = width+2; hsizeId = "editors"; }
105 new SpacerWidget(4);
106 edtTitle = new LineEditWidget();
107 edtTitle.flex = 1;
108 }.flex = 1;
111 new SpacerWidget(4);
112 (new HBoxWidget).enter{
113 new SpacerWidget(2);
114 new SpringWidget(1);
115 with (new ButtonWidget(" O&k ")) {
116 hsizeId = "okcancel";
117 deftype = Default.Accept;
118 onAction = delegate (self) {
119 if (onSelected !is null) {
120 if (!onSelected(fromName.str, fromMail.str, folder, edtTitle.str)) return;
122 close();
125 new SpacerWidget(2);
126 with (new ButtonWidget(" Cancel ")) {
127 hsizeId = "okcancel";
128 deftype = Default.Cancel;
129 onAction = delegate (self) {
130 close();
133 new SpacerWidget(4);
134 with (new ButtonWidget(" ... ")) {
135 hsizeId = "okcancel";
136 hotkey = "M-L";
137 disabled = true;
139 new SpringWidget(1);
140 new SpacerWidget(2);
142 new SpacerWidget(4);
144 fromName.str = name;
145 fromMail.str = mail;
146 edtTitle.str = title;
148 relayoutResize();
149 centerWindow();
151 edtTitle.focus();
154 this (const(char)[] aname, const(char)[] amail, const(char)[] afolder, const(char)[] atitle) {
155 dynstring caption = "Title for "~aname~" <"~amail~">";
156 name = aname;
157 mail = amail;
158 folder = afolder;
159 title = atitle;
160 super(caption);
165 // ////////////////////////////////////////////////////////////////////////// //
166 public class TagOptionsWindow : SubWindow {
167 dynstring tagname;
169 void delegate (const(char)[] tagname) onUpdated;
171 this (const(char)[] atagname) {
172 tagname = atagname;
173 dynstring caption = "options for '"~atagname~"'";
174 super(caption);
177 override void createWidgets () {
178 LabelWidget optPath = new LabelWidget("", LabelWidget.HAlign.Center);
179 optPath.flex = 1;
181 LineEditWidget optMonthes;
182 (new HBoxWidget).enter{
183 with (new HotLabelWidget("&Monthes:", LabelWidget.HAlign.Right)) { width = width+2; /*hsizeId = "editors";*/ }
184 new SpacerWidget(4);
185 optMonthes = new LineEditWidget();
186 //optMonthes.flex = 1;
187 optMonthes.width = gxTextWidthUtf("96669");
188 conwriteln("ISMYSEL: ", optMonthes.isMySelector(" Widget#. "));
189 conwriteln("ISMYSEL: ", optMonthes.isMySelector(" HBoxWidget LineEditWidget#. "));
190 conwriteln("ISMYSEL: ", optMonthes.isMySelector(" HBoxWidget> #. "));
191 conwriteln("ISMYSEL: ", optMonthes.isMySelector(" SubWindow HBoxWidget LineEditWidget#. "));
192 //new SpringWidget(1);
193 }.flex = 1;
195 CheckboxWidget optThreaded = new CheckboxWidget("&Threaded");
196 optThreaded.flex = 1;
198 CheckboxWidget optAttaches = new CheckboxWidget("&Attaches");
199 optAttaches.flex = 1;
201 with (new RadioWidget("Radio &1")) {
202 rgroup = "rg1";
203 flex = 1;
204 id = "r1";
207 with (new RadioWidget("Radio &2")) {
208 rgroup = "rg1";
209 flex = 1;
210 checked = true;
211 id = "r2";
214 new SpacerWidget(4);
215 (new HBoxWidget).enter{
216 new SpacerWidget(2);
217 new SpringWidget(1);
218 with (new ButtonWidget(" O&k ")) {
219 hsizeId = "okcancel";
220 deftype = Default.Accept;
221 onAction = delegate (self) {
222 if (onUpdated !is null) onUpdated(tagname);
223 close();
224 auto rw = self.getSelectedRadio("rg1");
225 if (rw !is null) conwriteln("SELECTED RADIO: <", rw.id, ">");
228 new SpacerWidget(4);
229 with (new ButtonWidget(" Cancel ")) {
230 hsizeId = "okcancel";
231 deftype = Default.Cancel;
232 onAction = delegate (self) {
233 close();
236 new SpacerWidget(4);
237 with (new ButtonWidget(" ... ")) {
238 hsizeId = "okcancel";
239 hotkey = "M-L";
240 onAction = delegate (self) {
241 (new SelectCompletionWindow("", buildAutoCompletion("/home/ketmar/"), aspath:true)).onSelected = (str) {
242 conwriteln("SELECTED: <", str.getData, ">");
244 //close();
246 //disabled = true;
248 new SpringWidget(1);
249 new SpacerWidget(2);
251 new SpacerWidget(4);
253 optMonthes.focus();
255 optPath.text = "booPath";
256 optMonthes.str = "666";
258 optThreaded.enabled = false;
259 optAttaches.enabled = true;
261 optMonthes.killTextOnChar = true;
263 relayoutResize();
264 centerWindow();
265 //add(); // for "focused"
267 forEachSelector("SubWindow > RootWidget CheckboxWidget", (EgraStyledClass w) {
268 import iv.vfs.io; writeln("LEW=", typeid(w).name, "; text=", (cast(CheckboxWidget)w).title.getData);
269 //return false;
272 forEachSelector("SubWindow > RootWidget :focused", (EgraStyledClass w) {
273 import iv.vfs.io; writeln("FOCUSED=", typeid(w).name);
274 //return false;
277 foreach (HotLabelWidget c; querySelectorAll!HotLabelWidget("SubWindow > RootWidget HotLabelWidget")) {
278 import iv.vfs.io;
279 writeln("LBL: <", c.text.getData, ">");
282 { import iv.vfs.io;
283 Widget qf = querySelector(":focused");
284 if (qf !is null) writeln("QFOCUSED=", typeid(qf).name); else writeln("FUCK!");
289 // ////////////////////////////////////////////////////////////////////////// //
290 void initConsole () {
291 // //////////////////////////////////////////////////////////////////// //
292 conRegFunc!(() {
293 import core.memory : GC;
294 conwriteln("starting GC collection...");
295 GC.collect();
296 GC.minimize();
297 conwriteln("GC collection complete.");
298 })("gc_collect", "force GC collection cycle");
301 // //////////////////////////////////////////////////////////////////// //
302 conRegFunc!(() {
303 auto qww = new YesNoWindow("Quit?", "Do you really want to quit?", true);
304 qww.onYes = () { concmd("quit"); };
305 qww.addModal();
306 })("quit_prompt", "quit with prompt");
308 conRegFunc!(() {
309 new TitlerWindow("name", "mail", "folder", "title");
310 })("window_titler", "titler window test");
312 conRegFunc!(() {
313 new TagOptionsWindow("xtag");
314 })("window_tagoptions", "tag options window test");
318 // ////////////////////////////////////////////////////////////////////////// //
319 __gshared MainPaneWindow mainPane;
322 final class MainPaneWindow : SubWindow {
323 ProgressBarWidget pbar;
324 int tessType = -1;
325 AGGTesselation deftess;
326 bool timetest;
327 int lastMX = -10000, lastMY = -10000;
329 this () {
330 super(null, GxPoint(0, 0), GxSize(screenWidth, screenHeight));
331 mType = Type.OnBottom;
333 pbar = new ProgressBarWidget(rootWidget, "progress bar");
334 pbar.setMinMax(0, 100);
335 pbar.width = clientWidth-64;
336 pbar.current = 50;
337 pbar.posy = clientHeight-pbar.height-8;
338 pbar.posx = (clientWidth-pbar.width)/2;
340 deftess = gxagg.tesselator;
342 add();
345 // //////////////////////////////////////////////////////////////////// //
346 override void onPaint () {
347 if (closed) return;
349 enum guiGroupListWidth = 128;
350 enum guiThreadListHeight = 520;
352 gxFillRect(0, 0, guiGroupListWidth, screenHeight, getColor("grouplist-back"));
353 gxVLine(guiGroupListWidth, 0, screenHeight, getColor("grouplist-divline"));
355 gxFillRect(guiGroupListWidth+1, 0, screenWidth, guiThreadListHeight, getColor("threadlist-back"));
356 gxHLine(guiGroupListWidth+1, guiThreadListHeight, screenWidth, getColor("threadlist-divline"));
358 version(all) {
359 import core.stdc.math : roundf;
361 if (tessType >= 0) {
362 if (tessType > AGGTesselation.max) tessType = AGGTesselation.max;
363 gxagg.tesselator = cast(AGGTesselation)tessType;
364 } else {
365 gxagg.tesselator = deftess;
368 gxagg.beginFrame();
369 gxagg.params.width = 1.4f;
370 //gxagg.width = 1.0f;
372 float baphx = roundf((screenWidth-gxagg.BaphometDims)*0.5f)+0.5f;
373 float baphy = roundf((screenHeight-gxagg.BaphometDims)*0.5f)+0.5f;
375 import iv.pxclock;
376 ulong estt;
377 ubyte hit = 0;
378 if (timetest) {
379 timetest = false;
380 estt = 0;
381 foreach (; 0..1000) {
382 gxagg.beginFrame();
383 immutable tstt = clockMicro();
384 gxagg.renderBaphomet(baphx, baphy);
385 estt += (clockMicro()-tstt);
387 estt /= 1000;
388 gxagg.beginFrame();
389 gxagg.renderBaphomet(baphx, baphy);
390 } else {
391 immutable tstt = clockMicro();
392 gxagg.renderBaphomet(baphx, baphy);
393 estt = clockMicro()-tstt;
396 version(none) {
397 gxagg
398 .moveTo(baphx-1, baphy-1)
399 .lineTo(baphx+gxagg.BaphometDims, baphy-1)
400 .lineTo(baphx+gxagg.BaphometDims, baphy+gxagg.BaphometDims)
401 .lineTo(baphx-1, baphy+gxagg.BaphometDims)
402 .lineTo(baphx-1, baphy-1);
404 version(all) {
405 gxagg.roundedRect(baphx-1, baphy-1, gxagg.BaphometDims+1, gxagg.BaphometDims+1, 16.0f);
407 gxagg.stroke();
408 //gxagg.contour();
409 //gxagg.fill();
411 version(none) {
412 gxagg
413 .beginPath();
414 .moveTo(10, 10);
415 .lineTo(30, 20);
416 .width = 1.0f;
417 gxagg.contour();
420 hit = gxagg.hitTest(lastMX, lastMY);
421 gxagg.endFrame(gxrgba(255, 0, 0, (tessType >= 0 ? 255 : 66)));
424 import std.format : format;
425 string s = "tess: %s level: %d; mcsecs: %s".format(gxagg.tesselator, gxagg.bezierLimit, estt);
426 gxDrawTextUtf(200, 20, s, GxColors.k8orange);
427 if (hit) {
428 s = "hit: %3s".format(hit);
429 gxDrawTextUtf(200, 40, s, GxColors.k8orange);
434 version(all) {
435 gxagg.beginFrame();
436 AGGMatrix tmt;
437 version(all) {
439 .rotate(deg2rad(35.0f))
440 .translate(20, 100);
441 assert(!tmt.isIdentity);
445 .translate(0.5f, 0.5f)
446 .translate(-160, 0);
448 gxagg.withTransform(tmt, {
449 gxagg.moveTo(50, 50);
450 gxagg.lineTo(60, 60);
451 gxagg.moveTo(100, 100);
452 gxagg.lineTo(140, 120);
453 version(none) {
454 // CW (because logical coords are upwards)
455 gxagg.moveTo(200, 200);
456 gxagg.lineTo(200, 100);
457 gxagg.lineTo(100, 100);
458 gxagg.lineTo(100, 200);
459 gxagg.lineTo(200, 200);
460 } else {
461 // CCW (because logical coords are upwards)
462 gxagg.moveTo(100, 100);
463 gxagg.lineTo(200, 100);
464 gxagg.lineTo(200, 200);
465 gxagg.lineTo(100, 200);
467 gxagg.closePoly();
468 //gxagg.flipOrientation = true;
469 //gxagg.dumpVertices();
472 version(all) {
473 gxagg.fill();
474 gxagg.render(gxrgba(0, 127, 0, 127));
476 version(all) {
477 //gxagg.dumpVertices();
478 gxagg.stroke();
479 gxagg.render(gxrgba(127, 0, 0, 255));
482 version(all) {
483 gxagg.resetDashes();
484 gxagg.addDash(4, 4);
485 gxagg.addDash(8, 8);
486 gxagg.dashStroke();
487 //gxagg.dashContour();
488 gxagg.render(gxrgba(255, 255, 0, 255));
491 version(all) {
492 gxagg.paramsContour.width = 6;
493 gxagg.contour();
494 gxagg.render(gxrgba(255, 255, 255, 255));
497 version(all) {
498 gxagg.flipOrientation = true;
499 gxagg.contour();
500 gxagg.render(gxrgba(0, 255, 0, 255));
501 gxagg.flipOrientation = false;
504 //gxagg.render(gxrgba(0, 127, 0, 127));
506 version(all) {
507 gxagg
508 .beginPath()
509 .roundedRect(400.5f, 600.5f, 96, 28, 8)
510 .fill()
511 //.render(gxRGB!(192, 192, 192));
512 .renderVGradient(gxRGB!(127, 127, 127), gxRGB!(142, 142, 142), 0.2f, gxRGB!(196, 196, 196));
515 version(all) {
516 float[4] bounds;
517 dchar dch = '@';
518 if (gxFontCharPathBounds(dch, bounds[])) {
519 conwriteln("bounds: (", bounds[0], ",", bounds[1], ")-(", bounds[2], ",", bounds[3], ")");
520 float desthgt = 96;
521 immutable float gwdt = bounds[2]-bounds[0];
522 immutable float ghgt = bounds[3]-bounds[1];
523 float scale = desthgt/ghgt;
524 AGGMatrix tmx;
526 .scale(scale, scale)
527 .translate(-bounds[0]*scale+100.5f, -bounds[1]*scale+100.5f);
528 gxagg.withTransform(tmx, {
529 gxagg
530 .resetParams()
531 .beginPath();
532 if (gxFontCharToPath(dch)) {
533 gxagg
534 //.stroke()
535 .fill()
536 //.render(GxColors.k8orange);
537 .renderVGradient(gxRGB!(200, 73, 0), gxRGB!(255, 128, 0));
539 int emb = cast(int)(4/scale);
540 float[4] ebounds;
541 gxagg
542 .beginPath();
543 if (gxFontCharPathEmboldenBounds(dch, emb, ebounds[])) {
544 immutable float ewdt = ebounds[2]-ebounds[0];
545 immutable float ehgt = ebounds[3]-ebounds[1];
546 gxagg.transform.translate(-(ewdt-gwdt)*0.5f*scale, (ehgt-ghgt)*0.5f*scale);
547 if (gxFontCharToPathEmbolden(dch, emb)) {
548 gxagg
549 .stroke()
550 .render(GxColors.green);
557 gxagg.endFrame();
560 version(test_round_rect) {
561 gxClipReset();
562 gxFillRoundedRect(lastMouseX-16, lastMouseY-16, 128, 96, rrad, /*gxSolidWhite*/gxRGBA!(0, 255, 0, 127));
563 //gxDrawRoundedRect(lastMouseX-16, lastMouseY-16, 128, 96, rrad, gxRGBA!(0, 255, 0, 127));
566 drawWidgets();
569 version(test_round_rect) {
570 int rrad = 16;
573 override bool onKeyBubble (KeyEvent event) {
574 if (event.pressed) {
575 version(test_round_rect) {
576 if (event == "Plus") { ++rrad; return true; }
577 if (event == "Minus") { --rrad; return true; }
579 if (event == "C-Q") { concmd("quit_prompt"); return true; }
580 if (event == "1") { concmd("window_titler"); return true; }
581 if (event == "2") { concmd("window_tagoptions"); return true; }
582 if (event == "Minus") { pbar.current = pbar.current-1; return true; }
583 if (event == "Plus") { pbar.current = pbar.current+1; return true; }
585 if (event == "Q") { if (gxagg.bezierLimit > 1) { --gxagg.bezierLimit; widgetChanged(); } return true; }
586 if (event == "W") { if (gxagg.bezierLimit < 1000) { ++gxagg.bezierLimit; widgetChanged(); } return true; }
588 if (event == "Z") { --tessType; if (tessType < -1) tessType = -1; widgetChanged(); return true; }
589 if (event == "X") { ++tessType; widgetChanged(); return true; }
591 if (event == "T") { timetest = true; widgetChanged(); return true; }
592 //if (dbg_dump_keynames) conwriteln("key: ", event.toStr, ": ", event.modifierState&ModifierState.windows);
593 //foreach (const ref kv; mainAppKeyBindings.byKeyValue) if (event == kv.key) concmd(kv.value);
595 return false;
598 // returning `false` to avoid screen rebuilding by dispatcher
599 override bool onMouseBubble (MouseEvent event) {
600 if (event.type == MouseEventType.buttonPressed || event.type == MouseEventType.buttonReleased) {
601 if (lastMX != event.x || lastMY != event.y) {
602 lastMX = event.x;
603 lastMY = event.y;
604 //widgetChanged();
606 widgetChanged();
607 } else if (event.modifierState) {
608 // for OpenGL, this rebuilds the whole screen anyway
609 lastMX = event.x;
610 lastMY = event.y;
611 widgetChanged();
613 return false;
618 // ////////////////////////////////////////////////////////////////////////// //
619 void main (string[] args) {
620 defaultColorStyle.parseStyle(TestStyle);
621 //egraDefaultFontSize = 48;
623 glconAllowOpenGLRender = false;
625 sdpyWindowClass = "EGRATest";
626 //glconShowKey = "M-Grave";
628 initConsole();
630 conProcessQueue();
631 conProcessArgs!true(args);
633 egraCreateSystemWindow("EGRA Test", allowResize:false);
635 vbwin.addEventListener((QuitEvent evt) {
636 if (vbwin.closed) return;
637 if (isQuitRequested) { vbwin.close(); return; }
638 vbwin.close();
641 static if (is(typeof(&vbwin.closeQuery))) {
642 vbwin.closeQuery = delegate () { concmd("quit"); egraPostDoConCommands(); };
645 mainPane = new MainPaneWindow();
646 //egraSkipScreenClear = true; // main pane is fullscreen
648 postScreenRebuild();
649 repostHideMouse();
651 vbwin.eventLoop(1000*10,
652 delegate () {
653 egraProcessConsole();
655 delegate (KeyEvent event) {
656 if (egraOnKey(event)) return;
658 delegate (MouseEvent event) {
659 if (egraOnMouse(event)) return;
661 delegate (dchar ch) {
662 if (egraOnChar(ch)) return;
666 flushGui();
667 conProcessQueue(int.max/4);