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 egui
.widgets
/*is aliced*/;
20 import arsd
.simpledisplay
;
29 import egui
.subwindows
;
32 // ////////////////////////////////////////////////////////////////////////// //
33 public abstract class Widget
{
39 void delegate (Widget self
) onAction
;
42 int svcx0
, svcy0
, svcx1
, svcy1
; // saved clip, valid after `setupClip()` call
45 this (SubWindow aparent
) {
46 if (aparent
is null) throw new Exception("no parent");
47 aparent
.addWidget(this);
50 final @property bool active () nothrow @safe @nogc { return (parent
!is null ?
(parent
.activeWidget
is this) : false); }
52 final @property int x0 () const nothrow @safe { pragma(inline
, true); return (parent
!is null ? parent
.winx
+parent
.clientOffsetX
+wx
: 0); }
53 final @property int y0 () const nothrow @safe { pragma(inline
, true); return (parent
!is null ? parent
.winy
+parent
.clientOffsetY
+wy
: 0); }
54 final @property int x1 () const nothrow @safe { pragma(inline
, true); return (parent
!is null ? parent
.winx
+parent
.clientOffsetX
+wx
+ww
-1 : 0); }
55 final @property int y1 () const nothrow @safe { pragma(inline
, true); return (parent
!is null ? parent
.winy
+parent
.clientOffsetY
+wy
+wh
-1 : 0); }
57 final @property int width () const nothrow @safe @nogc { pragma(inline
, true); return ww
; }
58 final @property int height () const nothrow @safe @nogc { pragma(inline
, true); return wh
; }
60 final @property void width (int v
) nothrow @safe @nogc { pragma(inline
, true); ww
= v
; }
61 final @property void height (int v
) nothrow @safe @nogc { pragma(inline
, true); wh
= v
; }
63 protected void setupClip () {
64 parent
.setupClientClip();
65 gxClipRect
.intersect(GxRect(GxPoint(x0
, y0
), GxPoint(x1
, y1
)));
66 svcx0
= gxClipRect
.x0
;
67 svcy0
= gxClipRect
.y0
;
68 svcx1
= gxClipRect
.x1
;
69 svcy1
= gxClipRect
.y1
;
72 protected void restoreClip () {
73 gxClipRect
.x0
= svcx0
;
74 gxClipRect
.y0
= svcy0
;
75 gxClipRect
.x1
= svcx1
;
76 gxClipRect
.y1
= svcy1
;
79 // this is called with set clip
80 protected void doPaint () {}
83 if (parent
is null || ww
< 1 || wh
< 1) return;
88 // return `true` if event was eaten
89 bool onKey (KeyEvent event
) { return false; }
90 bool onMouse (MouseEvent event
) { return false; }
91 bool onChar (dchar ch
) { return false; }
95 // ////////////////////////////////////////////////////////////////////////// //
96 public class LabelWidget
: Widget
{
114 HAlign halign
= HAlign
.Left
;
115 VAlign valign
= VAlign
.Center
;
120 this (SubWindow aparent
, string atext
, HAlign horiz
=HAlign
.Left
, VAlign vert
=VAlign
.Center
) {
123 ww
= gxTextWidthUtf(atext
);
124 wh
= gxTextHeightUtf
;
130 @property string
text () const nothrow @safe @nogc { return mText
; }
131 @property void text (string v
) nothrow @safe @nogc { mText
= v
; }
133 protected override void doPaint () {
134 if (mText
.length
== 0) return;
136 final switch (halign
) {
137 case HAlign
.Left
: x
= x0
+hpad
; break;
138 case HAlign
.Center
: x
= x0
+(width
-gxTextWidthUtf(mText
))/2; break;
139 case HAlign
.Right
: x
= x0
+width
-hpad
-gxTextWidthUtf(mText
); break;
142 final switch (valign
) {
143 case VAlign
.Top
: y
= y0
+vpad
; break;
144 case VAlign
.Center
: y
= y0
+(height
-gxTextHeightUtf
)/2; break;
145 case VAlign
.Bottom
: y
= y0
+height
-vpad
-gxTextHeightUtf
; break;
147 //gxDrawTextUtf(x0+(width-gxTextWidthUtf(mText))/2, y0+(height-gxTextHeightUtf)/2, mText, parent.clrWinText);
148 gxDrawTextUtf(x
, y
, mText
, parent
.clrWinText
);
153 // ////////////////////////////////////////////////////////////////////////// //
154 public class CheckboxWidget
: Widget
{
163 this (SubWindow aparent
, string atitle
, string ahotkey
=null) {
167 if (title
.length
> 1) {
168 auto umpos
= title
.indexOf('&');
169 if (umpos
>= 0 && umpos
< title
.length
-1) {
170 hotch
= title
[umpos
+1..$];
171 while (hotch
.utflen
> 1) hotch
= hotch
.utfchop
;
172 title
= title
[0..umpos
]~title
[umpos
+1..$];
173 hotxpos
= gxTextWidthUtf(title
[0..umpos
]);
174 if (umpos
> 0) hotxpos
+= 1;
175 if (ahotkey
is null) hotkey
= "M-"~hotch
;
178 ww
= gxTextWidthUtf(title
)+6+gxTextWidthUtf("[")+gxTextWidthUtf("x")+gxTextWidthUtf("]")+6;
179 wh
= gxTextHeightUtf
+4;
182 @property bool checked () const nothrow @safe @nogc { return mChecked
; }
183 @property void checked (bool v
) nothrow @safe @nogc { mChecked
= v
; }
185 protected override void doPaint () {
187 uint bclr = (active ? gxRGB!(255, 255, 255) : gxRGB!(180, 180, 180));
188 uint fclr = (active ? gxRGB!(0, 0, 60) : gxRGB!(0, 0, 0));
189 gxFillRect(x0+1, y0+1, width-2, height-2, bclr);
190 gxHLine(x0+1, y0, width-2, bclr);
191 gxHLine(x0+1, y1, width-2, bclr);
192 gxVLine(x0, y0+1, height-2, bclr);
193 gxVLine(x1, y0+1, height-2, bclr);
196 uint fclr
= (active ? gxRGB
!(255, 255, 255) : gxRGB
!(192, 192, 192));
197 uint xclr
= (active ? gxRGB
!(0, 255, 0) : gxRGB
!(0, 192, 0));
199 if (active
) gxFillRect(x0
, y0
, width
, height
, gxRGB
!(0, 0, 64));
201 gxClipRect
.shrinkBy(1, 1);
202 int tx
= x0
+3; //(width-gxTextWidthUtf(title))/2;
203 int ty
= y0
+(height
-gxTextHeightUtf
)/2;
205 gxDrawTextUtf(tx
, ty
, "[", fclr
);
206 tx
+= gxTextWidthUtf("[");
208 if (mChecked
) gxDrawTextUtf(tx
, ty
, "x", xclr
);
209 tx
+= gxTextWidthUtf("x");
211 gxDrawTextUtf(tx
, ty
, "]", fclr
);
212 tx
+= gxTextWidthUtf("]")+6;
214 gxDrawTextUtf(tx
, ty
, title
, fclr
);
215 if (hotch
.length
) gxHLine(tx
+hotxpos
, ty
+gxTextHeightUtf
, gxTextWidthUtf(hotch
), fclr
);
218 override bool onKey (KeyEvent event
) {
219 if (!event
.pressed
) return super.onKey(event
);
220 if (hotkey
.length
&& event
== hotkey
) {
221 parent
.activeWidget
= this;
222 if (onAction
!is null) onAction(this); else mChecked
= !mChecked
;
225 if (!active
) return false;
226 if (event
== "Up" || event
== "Left") { parent
.doShiftTab(); return true; }
227 if (event
== "Down" || event
== "Right") { parent
.doTab(); return true; }
228 if (event
== "Enter" || event
== "Space") {
229 if (onAction
!is null) onAction(this); else mChecked
= !mChecked
;
232 return super.onKey(event
);
235 override bool onMouse (MouseEvent event
) {
237 event
.mouse2xy(mx
, my
);
238 if (mx
>= x0
&& my
>= y0
&& mx
<= x1
&& my
<= y1
) {
239 if (event
.type
== MouseEventType
.buttonPressed
&& event
.button
== MouseButton
.left
) {
240 parent
.activeWidget
= this;
241 } else if (event
.type
== MouseEventType
.buttonReleased
&& event
.button
== MouseButton
.left
) {
242 if (active
&& onAction
!is null) onAction(this); else mChecked
= !mChecked
;
251 // ////////////////////////////////////////////////////////////////////////// //
252 public class ButtonWidget
: Widget
{
260 this (SubWindow aparent
, string atitle
, string ahotkey
=null) {
264 if (title
.length
> 1) {
265 auto umpos
= title
.indexOf('&');
266 if (umpos
>= 0 && umpos
< title
.length
-1) {
267 hotch
= title
[umpos
+1..$];
268 while (hotch
.utflen
> 1) hotch
= hotch
.utfchop
;
269 title
= title
[0..umpos
]~title
[umpos
+1..$];
270 hotxpos
= gxTextWidthUtf(title
[0..umpos
]);
271 if (umpos
> 0) hotxpos
+= 1;
274 ww
= gxTextWidthUtf(title
)+6;
275 wh
= gxTextHeightUtf
+4;
278 protected override void doPaint () {
279 uint bclr
= (active ? gxRGB
!(255, 255, 255) : gxRGB
!(180, 180, 180));
280 uint fclr
= (active ? gxRGB
!(0, 0, 60) : gxRGB
!(0, 0, 0));
281 gxFillRect(x0
+1, y0
+1, width
-2, height
-2, bclr
);
282 gxHLine(x0
+1, y0
, width
-2, bclr
);
283 gxHLine(x0
+1, y1
, width
-2, bclr
);
284 gxVLine(x0
, y0
+1, height
-2, bclr
);
285 gxVLine(x1
, y0
+1, height
-2, bclr
);
286 gxClipRect
.shrinkBy(1, 1);
287 int tx
= x0
+(width
-gxTextWidthUtf(title
))/2;
288 int ty
= y0
+(height
-gxTextHeightUtf
)/2;
289 gxDrawTextUtf(tx
, ty
, title
, fclr
);
290 if (hotch
.length
) gxHLine(tx
+hotxpos
, ty
+gxTextHeightUtf
, gxTextWidthUtf(hotch
), fclr
);
293 override bool onKey (KeyEvent event
) {
294 if (!event
.pressed
) return super.onKey(event
);
295 if (hotkey
.length
&& event
== hotkey
) { if (onAction
!is null) onAction(this); return true; }
296 if (!active
) return false;
297 if (event
== "Up" || event
== "Left") { parent
.doShiftTab(); return true; }
298 if (event
== "Down" || event
== "Right") { parent
.doTab(); return true; }
299 if (event
== "Enter" || event
== "Space") { if (onAction
!is null) onAction(this); return true; }
300 return super.onKey(event
);
303 override bool onMouse (MouseEvent event
) {
305 event
.mouse2xy(mx
, my
);
306 if (mx
>= x0
&& my
>= y0
&& mx
<= x1
&& my
<= y1
) {
307 if (event
.type
== MouseEventType
.buttonPressed
&& event
.button
== MouseButton
.left
) {
308 parent
.activeWidget
= this;
309 } else if (event
.type
== MouseEventType
.buttonReleased
&& event
.button
== MouseButton
.left
) {
310 if (active
&& onAction
!is null) onAction(this);
319 // ////////////////////////////////////////////////////////////////////////// //
320 public class ButtonExWidget
: ButtonWidget
{
322 this (SubWindow aparent
, string atitle
, string ahotkey
=null) {
323 super(aparent
, atitle
, ahotkey
);
327 protected override void doPaint () {
328 uint bclr
= (active ? gxRGB
!(0x93, 0xb6, 0xfc) : gxRGB
!(0x73, 0x96, 0xdc));
329 uint hclr
= (active ? gxRGB
!(0xa3, 0xc6, 0xff) : gxRGB
!(0x83, 0xa6, 0xec));
330 uint fclr
= (active ? gxRGB
!(0xff, 0xff, 0xff) : gxRGB
!(0xff, 0xff, 0xff));
331 uint brc
= gxRGB
!(0x40, 0x70, 0xcf);
332 gxFillRect(x0
+1, y0
+1, width
-2, height
-2, bclr
);
333 gxDrawRect(x0
, y0
, width
, height
, brc
);
334 gxHLine(x0
+1, y0
+1, width
-2, hclr
);
335 gxClipRect
.shrinkBy(1, 1);
336 int tx
= x0
+(width
-gxTextWidthUtf(title
))/2;
337 int ty
= y0
+(height
-gxTextHeightUtf
)/2;
338 gxDrawTextUtf(tx
, ty
, title
, fclr
);
339 if (hotch
.length
) gxHLine(tx
+hotxpos
, ty
+gxTextHeightUtf
, gxTextWidthUtf(hotch
), fclr
);
344 // ////////////////////////////////////////////////////////////////////////// //
345 public final class SimpleListBoxWidget
: Widget
{
353 this (SubWindow aparent
) {
357 @property int curidx () const nothrow @safe @nogc { return (mCurIdx
>= 0 && mCurIdx
< mItems
.length ? mCurIdx
: 0); }
359 @property void curidx (int idx
) nothrow @safe @nogc {
360 if (mItems
.length
== 0) return;
361 if (idx
< 0) idx
= 0;
362 if (idx
>= mItems
.length
) idx
= cast(int)(mItems
.length
-1);
366 @property int length () const nothrow @safe @nogc { return cast(int)mItems
.length
; }
367 @property string
opIndex (usize idx
) const nothrow @safe @nogc { return (idx
< mItems
.length ? mItems
[idx
] : null); }
368 @property Object
itemData (usize idx
) nothrow @safe @nogc { return (idx
< mItems
.length ? mItemData
[idx
] : null); }
370 void appendItem(T
:const(char)[]) (T s
, Object o
=null) {
371 //conwriteln("new item: ", s);
372 static if (is(T
== typeof(null))) {
374 } else static if (is(T
== string
)) {
382 @property int visibleItemsCount () const nothrow @trusted {
383 pragma(inline
, true);
384 return (height
< gxTextHeightUtf
*2 ?
1 : height
/gxTextHeightUtf
);
387 void makeCursorVisible () {
388 if (mItems
.length
== 0) return;
389 if (mCurIdx
< 0) mCurIdx
= 0;
390 if (mCurIdx
>= mItems
.length
) mCurIdx
= cast(int)(mItems
.length
-1);
391 if (mCurIdx
< mTopIdx
) { mTopIdx
= mCurIdx
; return; }
392 int icnt
= visibleItemsCount
-1;
393 if (mTopIdx
+icnt
< mCurIdx
) mTopIdx
= mCurIdx
-icnt
;
396 protected override void doPaint () {
398 gxFillRect(x0
, y0
, ww
, wh
, gxRGB
!(0, 0, 60));
401 while (idx
< mItems
.length
&& y
< wh
) {
403 uint clr
= gxRGB
!(255, 255, 0);
404 if (idx
== mCurIdx
) {
405 gxFillRect(x0
, y0
+y
, ww
, gxTextHeightUtf
, gxRGB
!(0, 110, 110));
406 clr
= gxRGB
!(255, 255, 255);
408 gxClipRect
.intersect(GxRect(GxPoint(x0
, y0
), GxPoint(x1
-1, y1
)));
409 gxDrawTextUtf(gxClipRect
.x0
+1, gxClipRect
.y0
+y
, mItems
[idx
], clr
);
413 y
+= gxTextHeightUtf
;
417 override bool onKey (KeyEvent event
) {
418 if (!active
) return false;
419 if (!event
.pressed
) return super.onKey(event
);
420 if (event
== "Up") { curidx
= curidx
-1; return true; }
421 if (event
== "Down") { curidx
= curidx
+1; return true; }
422 if (event
== "Home") { curidx
= 0; return true; }
423 if (event
== "End") { curidx
= cast(int)(mItems
.length
-1); return true; }
424 if (event
== "PageUp") {
426 if (curidx
> mTopIdx
) {
429 curidx
= curidx
-(visibleItemsCount
-1);
433 if (event
== "PageDown") {
435 int icnt
= visibleItemsCount
-1;
437 if (mTopIdx
+icnt
< curidx
) {
438 curidx
= mTopIdx
+icnt
;
440 curidx
= curidx
+icnt
;
445 return super.onKey(event
);
448 override bool onMouse (MouseEvent event
) {
449 if (!active
) return false;
451 event
.mouse2xy(mx
, my
);
452 if (mx
>= x0
&& my
>= y0
&& mx
<= x1
&& my
<= y1
) {
454 int idx
= mTopIdx
+(my
-y0
)/gxTextHeightUtf
;
455 if (event
.type
== MouseEventType
.buttonPressed
&& event
.button
== MouseButton
.left
) {
461 } else if (event
.type
== MouseEventType
.buttonPressed
&& event
.button
== MouseButton
.wheelUp
) {
463 } else if (event
.type
== MouseEventType
.buttonPressed
&& event
.button
== MouseButton
.wheelDown
) {