don't center cursor on message tree rebuild
[knntp.git] / egfx.d
blob5aa5e93374b287ed2070def03e1efb56f74d4341
1 /* DigitalMars NNTP reader
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, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 module egfx is aliced;
19 private:
21 import core.atomic;
22 import std.concurrency;
24 import arsd.simpledisplay;
26 import iv.bclamp;
27 import iv.cmdcon;
28 import iv.utfutil;
29 import iv.vfs;
32 // ////////////////////////////////////////////////////////////////////////// //
33 static immutable dchar[128] charMap8662U = [
34 '\u0410','\u0411','\u0412','\u0413','\u0414','\u0415','\u0416','\u0417','\u0418','\u0419','\u041A','\u041B','\u041C','\u041D','\u041E','\u041F',
35 '\u0420','\u0421','\u0422','\u0423','\u0424','\u0425','\u0426','\u0427','\u0428','\u0429','\u042A','\u042B','\u042C','\u042D','\u042E','\u042F',
36 '\u0430','\u0431','\u0432','\u0433','\u0434','\u0435','\u0436','\u0437','\u0438','\u0439','\u043A','\u043B','\u043C','\u043D','\u043E','\u043F',
37 '\u2591','\u2592','\u2593','\u2502','\u2524','\u2561','\u2562','\u2556','\u2555','\u2563','\u2551','\u2557','\u255D','\u255C','\u255B','\u2510',
38 '\u2514','\u2534','\u252C','\u251C','\u2500','\u253C','\u255E','\u255F','\u255A','\u2554','\u2569','\u2566','\u2560','\u2550','\u256C','\u2567',
39 '\u2568','\u2564','\u2565','\u2559','\u2558','\u2552','\u2553','\u256B','\u256A','\u2518','\u250C','\u2588','\u2584','\u258C','\u2590','\u2580',
40 '\u0440','\u0441','\u0442','\u0443','\u0444','\u0445','\u0446','\u0447','\u0448','\u0449','\u044A','\u044B','\u044C','\u044D','\u044E','\u044F',
41 '\u0401','\u0451','\u0404','\u0454','\u0407','\u0457','\u040E','\u045E','\u00B0','\u2219','\u00B7','\u221A','\u2116','\u00A4','\u25A0','\u00A0',
44 static immutable char[dchar] charMapU2866;
46 shared static this () {
47 foreach (immutable ubyte ch; 0..128) charMapU2866[cast(dchar)ch] = cast(char)ch;
48 foreach (immutable idx, dchar dc; charMap8662U[]) charMapU2866[dc] = cast(char)(idx+128);
49 charMapU2866[Utf8DecoderFast.replacement] = '\xb0';
50 charMapU2866['\u00F6'] = 'o';
51 charMapU2866['\u0308'] = 'C';
52 charMapU2866['\u032A'] = 's';
53 charMapU2866['\u033B'] = 'o';
54 charMapU2866['\u00E1'] = 'a';
55 charMapU2866['\u00ED'] = 'i';
59 // ////////////////////////////////////////////////////////////////////////// //
60 public int utflen (const(char)[] s) nothrow @trusted @nogc {
61 Utf8DecoderFast dc;
62 int res = 0;
63 foreach (char ch; s) if (dc.decode(cast(ubyte)ch)) ++res;
64 return res;
68 public T utfchop(T : const(char)[]) (T s) nothrow @trusted @nogc {
69 Utf8DecoderFast dc;
70 int last = 0;
71 foreach (immutable idx, char ch; s) if (dc.decode(cast(ubyte)ch)) last = cast(int)idx;
72 return s[0..last];
76 public T utfskip(T : const(char)[]) (T s, int len) nothrow @trusted @nogc {
77 if (len < 1) return s;
78 Utf8DecoderFast dc;
79 foreach (immutable idx, char ch; s) {
80 if (dc.decode(cast(ubyte)ch)) {
81 if (--len == 0) return s[idx+1..$];
84 return null;
88 public T utfleft(T : const(char)[]) (T s, int len) nothrow @trusted @nogc {
89 if (len < 1) return null;
90 Utf8DecoderFast dc;
91 foreach (immutable idx, char ch; s) {
92 if (dc.decode(cast(ubyte)ch)) {
93 if (--len == 0) return s[0..idx+1];
96 return s;
100 public T utfright(T : const(char)[]) (T s, int len) nothrow @trusted @nogc {
101 if (len < 1) return null;
102 auto fulllen = s.utflen;
103 if (len >= fulllen) return s;
104 Utf8DecoderFast dc;
105 foreach (immutable idx, char ch; s) {
106 if (dc.decode(cast(ubyte)ch)) {
107 if (--fulllen == len) return s[idx+1..$];
110 return null;
114 public T utfmid(T : const(char)[]) (T s, int pos, int len) nothrow @trusted @nogc {
115 if (len < 1 || pos >= s.length) return null;
116 Utf8DecoderFast dc;
117 int ds = -1, de = -1;
118 if (pos == 0) ds = 0;
119 foreach (immutable idx, char ch; s) {
120 if (dc.decode(cast(ubyte)ch)) {
121 if (ds < 0) {
122 if (pos > 0) --pos; else ++pos;
123 if (pos == 0) ds = cast(int)idx+1;
124 } else if (de < 0) {
125 if (--len == 0) { de = cast(int)idx+1; break; }
126 } else {
127 assert(0, "wtf?!");
131 if (ds < 0) return null;
132 if (de < 0) return s[ds..$];
133 return s[ds..de];
137 public T utfdel(T : const(char)[]) (T s, int pos, int len) {
138 if (len < 1 || pos >= s.length) return s;
139 Utf8DecoderFast dc;
140 int ds = -1, de = -1;
141 if (pos == 0) ds = 0;
142 foreach (immutable idx, char ch; s) {
143 if (dc.decode(cast(ubyte)ch)) {
144 if (ds < 0) {
145 if (pos > 0) --pos; else ++pos;
146 if (pos == 0) ds = cast(int)idx+1;
147 } else if (de < 0) {
148 if (--len == 0) { de = cast(int)idx+1; break; }
149 } else {
150 assert(0, "wtf?!");
154 if (ds < 0) return s;
155 if (de < 0) return s[0..ds];
156 return s[0..ds]~s[de..$];
160 // ////////////////////////////////////////////////////////////////////////// //
161 void utfBy866 (const(char)[] s, scope void delegate (char ch) nothrow @trusted @nogc dg) nothrow @trusted @nogc {
162 if (dg is null) return;
163 Utf8DecoderFast dc;
164 foreach (char ch; s) {
165 if (dc.decode(cast(ubyte)ch)) {
166 if (auto dcp = dc.codepoint in charMapU2866) dg(*dcp); else dg('\xb0');
172 // ////////////////////////////////////////////////////////////////////////// //
173 //mixin(import("egfxfont.d"));
174 // font: (hi nibble: lshift; lo nibble: width); 8 data bytes
175 static immutable ubyte[] font6x8p = cast(immutable(ubyte)[])import("databin/zxpfont.fnt");
178 // ////////////////////////////////////////////////////////////////////////// //
179 //public enum GLTexType = GL_RGBA;
180 public enum GLTexType = GL_BGRA;
182 public __gshared int VBufWidth = 640;
183 public __gshared int VBufHeight = 480;
184 public __gshared ubyte VBufScale = 2; // new window scale
185 public __gshared ubyte vbufEffScale = 2; // effective (current) window scale
187 public __gshared uint[] zxtexbuf; // OpenGL texture buffer
188 public __gshared uint vArrowTextureId = 0;
190 shared static this () {
191 zxtexbuf.length = VBufWidth*VBufHeight+4;
195 public @property int winWidth () nothrow @trusted @nogc { pragma(inline, true); return VBufWidth; }
196 public @property int winHeight () nothrow @trusted @nogc { pragma(inline, true); return VBufHeight; }
198 public @property int winWidthScaled () nothrow @trusted @nogc { pragma(inline, true); return VBufWidth*vbufEffScale; }
199 public @property int winHeightScaled () nothrow @trusted @nogc { pragma(inline, true); return VBufHeight*vbufEffScale; }
202 // ////////////////////////////////////////////////////////////////////////// //
203 public void createArrowTexture () {
204 import iv.glbinds;
206 enum wrapOpt = GL_REPEAT;
207 enum filterOpt = GL_NEAREST; //GL_LINEAR;
208 enum ttype = GL_UNSIGNED_BYTE;
210 if (vArrowTextureId) glDeleteTextures(1, &vArrowTextureId);
211 vArrowTextureId = 0;
212 glGenTextures(1, &vArrowTextureId);
213 if (vArrowTextureId == 0) assert(0, "can't create arrow texture");
215 //GLint gltextbinding;
216 //glGetIntegerv(GL_TEXTURE_BINDING_2D, &gltextbinding);
217 //scope(exit) glBindTexture(GL_TEXTURE_2D, gltextbinding);
219 glBindTexture(GL_TEXTURE_2D, vArrowTextureId);
220 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapOpt);
221 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapOpt);
222 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filterOpt);
223 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filterOpt);
224 //glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
225 //glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
227 GLfloat[4] bclr = 0.0;
228 glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, bclr.ptr);
230 uint[16*8] pmap = 0x00_000000U;
231 // sprite,sprite,mask,mask
232 static immutable ushort[$] spx = [
233 0b11111111_10000000, 0b00000000_01111111,
234 0b01000000_10000000, 0b10000000_01111111,
235 0b00100000_10000000, 0b11000000_01111111,
236 0b00010000_01100000, 0b11100000_00011111,
237 0b00001001_10011000, 0b11110000_00000111,
238 0b00000110_01100110, 0b11111001_10000001,
239 0b00000000_00011001, 0b11111111_11100000,
240 0b00000000_00000110, 0b11111111_11111001,
243 foreach (immutable dy; 0..8) {
244 ushort spr = spx[dy*2+0];
245 ushort msk = spx[dy*2+1];
246 foreach (immutable dx; 0..16) {
247 if ((msk&0x8000) == 0) {
248 pmap[dy*16+dx] = (spr&0x8000 ? 0xff_ffffffU : 0xff_000000U);
250 msk <<= 1;
251 spr <<= 1;
254 //pmap = 0xff_ff0000U;
255 //pmap[0] = 0;
257 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 8, 0, GLTexType, GL_UNSIGNED_BYTE, pmap.ptr);
260 // ////////////////////////////////////////////////////////////////////////// //
261 // mix dc with ARGB (or ABGR) col; dc A is ignored (removed)
262 public uint gxColMix (uint dc, uint col) pure nothrow @trusted @nogc {
263 pragma(inline, true);
264 immutable uint a = 256-(col>>24); // to not loose bits
265 //immutable uint dc = (da)&0xffffff;
266 dc &= 0xffffff;
267 immutable uint srb = (col&0xff00ff);
268 immutable uint sg = (col&0x00ff00);
269 immutable uint drb = (dc&0xff00ff);
270 immutable uint dg = (dc&0x00ff00);
271 immutable uint orb = (drb+(((srb-drb)*a+0x800080)>>8))&0xff00ff;
272 immutable uint og = (dg+(((sg-dg)*a+0x008000)>>8))&0x00ff00;
273 return orb|og;
277 // ////////////////////////////////////////////////////////////////////////// //
278 private template isGoodRGBInt(T) {
279 import std.traits : Unqual;
280 alias TT = Unqual!T;
281 enum isGoodRGBInt =
282 is(TT == ubyte) ||
283 is(TT == short) || is(TT == ushort) ||
284 is(TT == int) || is(TT == uint) ||
285 is(TT == long) || is(TT == ulong);
289 // ////////////////////////////////////////////////////////////////////////// //
290 public uint ztrgb(T0, T1, T2) (T0 r, T1 g, T2 b) pure nothrow @trusted @nogc if (isGoodRGBInt!T0 && isGoodRGBInt!T1 && isGoodRGBInt!T2) {
291 pragma(inline, true);
292 return (clampToByte(r)<<16)|(clampToByte(g)<<8)|clampToByte(b);
296 public template gxRGB(int r, int g, int b) {
297 enum gxRGB = (clampToByte(r)<<16)|(clampToByte(g)<<8)|clampToByte(b);
300 public template gxRGBA(int r, int g, int b, int a) {
301 enum gxRGBA = (clampToByte(a)<<24)|(clampToByte(r)<<16)|(clampToByte(g)<<8)|clampToByte(b);
305 // ////////////////////////////////////////////////////////////////////////// //
306 public __gshared int clipX0 = 0, clipY0 = 0;
307 public __gshared int clipX1 = 65535, clipY1 = 65535;
310 public void clipReset () nothrow @trusted @nogc {
311 clipX0 = clipY0 = 0;
312 clipX1 = clipY1 = 65535;
316 public int clipWidth () nothrow @trusted @nogc { return (clipX0 <= clipX1 ? clipX1-clipX0+1 : 0); }
317 public int clipHeight () nothrow @trusted @nogc { return (clipY0 <= clipY1 ? clipY1-clipY0+1 : 0); }
319 public void clipShrink (int dx, int dy) nothrow @trusted @nogc {
320 clipX0 += dx;
321 clipY0 += dy;
322 clipX1 -= dx;
323 clipY1 -= dy;
326 public void clipGrow (int dx, int dy) nothrow @trusted @nogc {
327 clipX0 -= dx;
328 clipY0 -= dy;
329 clipX1 += dx;
330 clipY1 += dy;
334 // ////////////////////////////////////////////////////////////////////////// //
335 public void gxPutPixel (int x, int y, uint c) nothrow @trusted @nogc {
336 pragma(inline, true);
337 if (x >= 0 && y >= 0 && x < VBufWidth && y < VBufHeight && (c&0xff000000) != 0xff000000) {
338 if (x >= clipX0 && y >= clipY0 && x <= clipX1 && y <= clipY1) {
340 ubyte* dp = cast(ubyte*)zxtexbuf.ptr;
341 dp += y*(VBufWidth*4)+x*4;
342 *dp++ = (c>>16)&0xff; //R
343 *dp++ = (c>>8)&0xff; //G
344 *dp++ = c&0xff; //B
345 *dp = 0;
347 uint* dp = cast(uint*)(cast(ubyte*)zxtexbuf.ptr)+y*VBufWidth+x;
348 *dp = gxColMix(*dp, c);
354 // ////////////////////////////////////////////////////////////////////////// //
355 public void gxDrawCharM (int x, int y, char ch, uint fg, uint bg=0xff000000) nothrow @trusted @nogc {
356 foreach (immutable dy; 1..9) {
357 ubyte b = font6x8p.ptr[cast(uint)ch*9+dy];
358 if (b) {
359 foreach (immutable dx; 0..6) {
360 gxPutPixel(x+dx, y, (b&0x80 ? fg : bg));
361 b <<= 1;
364 ++y;
369 // return char width
370 public int gxDrawCharP (int x, int y, char ch, uint fg, uint bg=0xff000000) nothrow @trusted @nogc {
371 ubyte wdt = font6x8p.ptr[cast(uint)ch*9];
372 ubyte shift = wdt>>4;
373 wdt &= 0x0f;
374 foreach (immutable dy; 1..9) {
375 ubyte b = cast(ubyte)(font6x8p.ptr[cast(uint)ch*9+dy]<<shift);
376 if (b) {
377 foreach (immutable dx; 0..wdt) {
378 gxPutPixel(x+dx, y, (b&0x80 ? fg : bg));
379 b <<= 1;
382 ++y;
384 return wdt;
388 public int gxCharWidthP (char ch) nothrow @trusted @nogc { return font6x8p.ptr[cast(uint)ch*9]; }
391 public int gxCharWidthScaledP (int scale, char ch) nothrow @trusted @nogc { return (scale < 1 ? 0 : font6x8p.ptr[cast(uint)ch*9]*scale); }
394 // return char width
395 public int gxDrawCharScaledP (int scale, int x, int y, char ch, uint fg, uint bg=0xff000000) nothrow @trusted @nogc {
396 if (scale < 1) return 0;
397 ubyte wdt = font6x8p.ptr[cast(uint)ch*9];
398 ubyte shift = wdt>>4;
399 wdt &= 0x0f;
400 foreach (immutable dy; 1..9) {
401 ubyte b = cast(ubyte)(font6x8p.ptr[cast(uint)ch*9+dy]<<shift);
402 if (b) {
403 foreach (immutable dx; 0..wdt) {
404 foreach (immutable scx; 0..scale) {
405 foreach (immutable scy; 0..scale) {
406 gxPutPixel(x+dx*scale+scx, y+scy, (b&0x80 ? fg : bg));
409 b <<= 1;
412 y += scale;
414 return wdt*scale;
418 // ////////////////////////////////////////////////////////////////////////// //
419 public int gxTextWidthM (const(char)[] s) nothrow @trusted @nogc { return cast(int)s.length*6; }
420 public int gxTextHeightM () nothrow @trusted @nogc { return 8; }
423 public int gxTextWidthP (const(char)[] s) nothrow @trusted @nogc {
424 int res = 0;
425 foreach (char ch; s) {
426 if (res) ++res;
427 res += font6x8p.ptr[cast(uint)ch*9];
429 return res;
431 public int gxTextHeightP () nothrow @trusted @nogc { return 8; }
434 public int gxTextWidthScaledP (int scale, const(char)[] s) nothrow @trusted @nogc {
435 if (scale < 1) return 0;
436 int res = 0;
437 foreach (char ch; s) {
438 res += (font6x8p.ptr[cast(uint)ch*9]+1)*scale;
440 if (res) res -= scale;
441 return res;
443 public int gxTextHeightScaledP (int scale) nothrow @trusted @nogc { return (scale < 1 ? 0 : 8*scale); }
446 // ////////////////////////////////////////////////////////////////////////// //
447 public void gxDrawTextM (int x, int y, const(char)[] s, uint fg, uint bg=0xff000000) nothrow @trusted @nogc {
448 foreach (char ch; s) {
449 gxDrawCharM(x, y, ch, fg, bg);
450 x += 6;
455 // return text width
456 public int gxDrawTextP (int x, int y, const(char)[] s, uint fg, uint bg=0xff000000) nothrow @trusted @nogc {
457 int res = 0;
458 foreach (char ch; s) {
459 auto w = gxDrawCharP(x, y, ch, fg, bg);
460 if (res) ++res;
461 res += w;
462 x += w+1;
464 return res;
468 // return text width
469 public int gxDrawTextScaledP (int scale, int x, int y, const(char)[] s, uint fg, uint bg=0xff000000) nothrow @trusted @nogc {
470 if (scale < 1) return 0;
471 int res = 0;
472 foreach (char ch; s) {
473 auto w = gxDrawCharScaledP(scale, x, y, ch, fg, bg)+scale;
474 res += w;
475 x += w;
477 if (res) res -= scale;
478 return res;
482 // ////////////////////////////////////////////////////////////////////////// //
483 public void gxDrawTextOutM (int x, int y, const(char)[] s, uint fg, uint ot) nothrow @trusted @nogc {
484 foreach (immutable dy; -1..2) {
485 foreach (immutable dx; -1..2) {
486 if (dx == 0 && dy == 0) continue;
487 gxDrawTextM(x+dx, y+dy, s, ot);
490 gxDrawTextM(x, y, s, fg);
494 // return text width
495 public int gxDrawTextOutP (int x, int y, const(char)[] s, uint fg, uint ot) nothrow @trusted @nogc {
496 foreach (immutable dy; -1..2) {
497 foreach (immutable dx; -1..2) {
498 if (dx == 0 && dy == 0) continue;
499 gxDrawTextP(x+dx, y+dy, s, ot);
502 return gxDrawTextP(x, y, s, fg);
506 // ////////////////////////////////////////////////////////////////////////// //
507 public int gxTextHeightUtf () nothrow @trusted @nogc { return 8; }
509 public int gxTextWidthUtf (const(char)[] s) nothrow @trusted @nogc {
510 int res = 0;
511 s.utfBy866(delegate (char ch) @trusted { res += gxCharWidthP(ch)+1; });
512 if (res) --res;
513 return res;
516 public int gxTextHeightScaledUtf (int scale) nothrow @trusted @nogc { return gxTextHeightScaledP(scale); }
518 public int gxTextWidthScaledUtf (int scale, const(char)[] s) nothrow @trusted @nogc {
519 if (scale < 1) return 0;
520 int res = 0;
521 s.utfBy866(delegate (char ch) { res += gxCharWidthScaledP(scale, ch)+scale; });
522 if (res) res -= scale;
523 return res;
526 public int gxDrawTextUtf (int x, int y, const(char)[] s, uint clr) nothrow @trusted @nogc {
527 int res = 0;
528 s.utfBy866(delegate (char ch) { int rc = gxDrawCharP(x, y, ch, clr)+1; res += rc; x += rc; });
529 if (res) --res;
530 return res;
534 public int gxDrawTextScaledUtf (int scale, int x, int y, const(char)[] s, uint clr) nothrow @trusted @nogc {
535 if (scale < 1) return 0;
536 int res = 0;
537 s.utfBy866(delegate (char ch) { int rc = gxDrawCharScaledP(scale, x, y, ch, clr)+scale; res += rc; x += rc; });
538 if (res) res -= scale;
539 return res;
543 // ////////////////////////////////////////////////////////////////////////// //
544 public void gxHLine (int x, int y, int w, uint clr) nothrow @trusted @nogc {
545 if (w < 1 || y < 0 || y >= VBufHeight || x >= VBufWidth) return;
546 if (x < 0) {
547 if (x+w <= 0) return;
548 w += x;
549 assert(w > 0);
551 if (x+w > VBufWidth) {
552 w = VBufWidth-x;
553 assert(w > 0);
555 while (w-- > 0) gxPutPixel(x++, y, clr);
559 public void gxVLine (int x, int y, int h, uint clr) nothrow @trusted @nogc {
560 if (h < 1 || x < 0 || x >= VBufWidth || y >= VBufHeight) return;
561 if (y < 0) {
562 if (y+h <= 0) return;
563 h += y;
564 assert(h > 0);
566 if (y+h > VBufHeight) {
567 h = VBufHeight-y;
568 assert(h > 0);
570 while (h-- > 0) gxPutPixel(x, y++, clr);
574 public void gxFillRect (int x, int y, int w, int h, uint clr) nothrow @trusted @nogc {
575 if (w < 1 || h < 1 || x >= VBufWidth || y >= VBufHeight) return;
576 while (h-- > 0) gxHLine(x, y++, w, clr);
580 public void gxDrawRect (int x, int y, int w, int h, uint clr) nothrow @trusted @nogc {
581 if (w < 1 || h < 1 || x >= VBufWidth || y >= VBufHeight) return;
582 gxHLine(x, y, w, clr);
583 gxHLine(x, y+h-1, w, clr);
584 gxVLine(x, y+1, h-2, clr);
585 gxVLine(x+w-1, y+1, h-2, clr);
589 // ////////////////////////////////////////////////////////////////////////// //
590 // use clip region as boundaries
591 public void gxDrawShadow () nothrow @trusted @nogc {
592 clipX1 += 8;
593 clipY1 += 8;
594 gxFillRect(clipX1-7, clipY0+8, 8, clipHeight-8, gxRGBA!(0, 0, 0, 127));
595 gxFillRect(clipX0+8, clipY1-7, clipWidth, 8, gxRGBA!(0, 0, 0, 127));
596 clipX1 -= 8;
597 clipY1 -= 8;
601 // shrink cliprect from "whole window" to "client"
602 public void gxClipClient () nothrow @trusted @nogc {
603 clipX0 += 1;
604 clipY0 += 10;
605 clipX1 -= 1;
606 clipY1 -= 1;
610 public void gxDrawWindow (const(char)[] title, uint framecolor, uint titlecolor, uint windowcolor) nothrow @trusted @nogc {
611 gxDrawShadow();
613 gxFillRect(clipX0, clipY0, clipWidth, clipHeight, windowcolor);
614 gxDrawRect(clipX0, clipY0, clipWidth, clipHeight, framecolor);
616 if (title !is null) {
617 gxFillRect(clipX0, clipY0, clipWidth, 10, framecolor);
618 gxDrawTextUtf(clipX0+(clipWidth-gxTextWidthUtf(title))/2, clipY0+1, title, titlecolor);