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
.subwindows
/*is aliced*/;
22 import arsd
.simpledisplay
;
31 import egui
.widgets
: Widget
;
34 // ////////////////////////////////////////////////////////////////////////// //
35 public __gshared SimpleWindow vbwin
; // main window; MUST be set!
36 public __gshared
bool vbfocused
= false;
39 // ////////////////////////////////////////////////////////////////////////// //
40 __gshared
public int lastMouseXUnscaled
= 10000, lastMouseYUnscaled
= 10000;
41 __gshared
/*MouseButton*/public int lastMouseButton
;
43 __gshared
public int lastMouseX () nothrow @trusted @nogc { pragma(inline
, true); return lastMouseXUnscaled
/vbufEffScale
; }
44 __gshared
public int lastMouseY () nothrow @trusted @nogc { pragma(inline
, true); return lastMouseYUnscaled
/vbufEffScale
; }
46 __gshared
public bool lastMouseLeft () nothrow @trusted @nogc { pragma(inline
, true); return ((lastMouseButton
&MouseButton
.left
) != 0); }
47 __gshared
public bool lastMouseRight () nothrow @trusted @nogc { pragma(inline
, true); return ((lastMouseButton
&MouseButton
.right
) != 0); }
48 __gshared
public bool lastMouseMiddle () nothrow @trusted @nogc { pragma(inline
, true); return ((lastMouseButton
&MouseButton
.middle
) != 0); }
51 // ////////////////////////////////////////////////////////////////////////// //
52 public class ScreenRebuildEvent
{}
53 public class ScreenRepaintEvent
{}
54 public class QuitEvent
{}
55 public class CursorBlinkEvent
{}
56 public class HideMouseEvent
{}
58 __gshared ScreenRebuildEvent evScrRebuild
;
59 __gshared ScreenRepaintEvent evScreenRepaint
;
60 __gshared CursorBlinkEvent evCurBlink
;
61 __gshared HideMouseEvent evHideMouse
;
63 shared static this () {
64 evScrRebuild
= new ScreenRebuildEvent();
65 evScreenRepaint
= new ScreenRepaintEvent();
66 evCurBlink
= new CursorBlinkEvent();
67 evHideMouse
= new HideMouseEvent();
71 // ////////////////////////////////////////////////////////////////////////// //
72 public void postScreenRebuild () { if (vbwin
!is null && !vbwin
.eventQueued
!ScreenRebuildEvent
) vbwin
.postEvent(evScrRebuild
); }
73 public void postScreenRepaint () { if (vbwin
!is null && !vbwin
.eventQueued
!ScreenRepaintEvent
&& !vbwin
.eventQueued
!ScreenRebuildEvent
) vbwin
.postEvent(evScreenRepaint
); }
74 public void postScreenRepaintDelayed () { if (vbwin
!is null && !vbwin
.eventQueued
!ScreenRepaintEvent
&& !vbwin
.eventQueued
!ScreenRebuildEvent
) vbwin
.postTimeout(evScreenRepaint
, 35); }
76 public void postCurBlink () {
77 if (vbwin
!is null && !vbwin
.eventQueued
!CursorBlinkEvent
) {
78 //conwriteln("curblink posted!");
79 vbwin
.postTimeout(evCurBlink
, 100);
80 //vbwin.postTimeout(evCurBlink, 500);
85 // ////////////////////////////////////////////////////////////////////////// //
86 __gshared MonoTime lastMouseMove
;
87 __gshared
uint MouseHideTime
= 3000;
90 shared static this () {
91 conRegVar("mouse_hide_time", "mouse cursor hiding time (in milliseconds); 0 to not hide it",
95 delegate (self
, uint nv
) {
96 if (MouseHideTime
!= nv
) {
97 if (MouseHideTime
== 0) mouseMoved();
99 conwriteln("mouse hiding time: ", nv
);
106 public int mshtime_dbg () {
107 if (MouseHideTime
> 0) {
108 auto ctt
= MonoTime
.currTime
;
109 auto mt
= (ctt
-lastMouseMove
).total
!"msecs";
117 public bool isMouseVisible () {
118 if (MouseHideTime
> 0) {
119 auto ctt
= MonoTime
.currTime
;
120 return ((ctt
-lastMouseMove
).total
!"msecs" < MouseHideTime
+500);
127 public float mouseAlpha () {
128 if (MouseHideTime
> 0) {
129 auto ctt
= MonoTime
.currTime
;
130 auto msc
= (ctt
-lastMouseMove
).total
!"msecs";
131 if (msc
>= MouseHideTime
+500) return 0.0f;
132 if (msc
< MouseHideTime
) return 1.0f;
133 msc
-= MouseHideTime
;
134 return 1.0f-msc
/500.0f;
141 // returns `true` if mouse should be redrawn
142 public bool repostHideMouse () {
143 if (vbwin
is null || vbwin
.eventQueued
!HideMouseEvent
) return false;
144 if (MouseHideTime
> 0) {
145 auto ctt
= MonoTime
.currTime
;
146 auto tms
= (ctt
-lastMouseMove
).total
!"msecs";
147 if (tms
>= MouseHideTime
) {
148 if (tms
>= MouseHideTime
+500) return true; // hide it
149 vbwin
.postTimeout(evHideMouse
, 50);
150 return true; // fade it
152 vbwin
.postTimeout(evHideMouse
, cast(int)(MouseHideTime
-tms
));
158 public void mouseMoved () {
159 if (MouseHideTime
> 0) {
160 lastMouseMove
= MonoTime
.currTime
;
161 if (vbwin
!is null && !vbwin
.eventQueued
!HideMouseEvent
) vbwin
.postTimeout(evHideMouse
, MouseHideTime
);
166 // ////////////////////////////////////////////////////////////////////////// //
167 public void drawTextCursor (bool active
, int x
, int y
, int hgt
=-666) {
168 if (hgt
== -666) hgt
= gxTextHeightUtf
;
171 auto ctt
= (MonoTime
.currTime
.ticks
*1000/MonoTime
.ticksPerSecond
)/100;
172 int doty
= ctt
%(hgt
*2-1);
173 if (doty
>= hgt
) doty
= hgt
*2-doty
-1;
174 gxVLine(x
, y
, hgt
, (ctt
%10 < 5 ? gxRGB
!(255, 255, 255) : gxRGB
!(200, 200, 200)));
175 gxPutPixel(x
, y
+doty
, gxRGB
!(0, 255, 255));
178 gxVLine(x
, y
, hgt
, gxRGB
!(170, 170, 170));
183 // ////////////////////////////////////////////////////////////////////////// //
184 private __gshared SubWindow subwinLast
;
185 private __gshared
bool ignoreSubWinChar
= false;
186 // make a package and move that to package
187 private __gshared SubWindow subwinDrag
= null;
188 private __gshared
int subwinDragXSpot
, subwinDragYSpot
;
191 public @property bool isSubWinDragging () nothrow @trusted @nogc { pragma(inline
, true); return (subwinDrag
!is null); }
192 public @property bool isSubWinDraggingKeyboard () nothrow @trusted @nogc { pragma(inline
, true); return (subwinDrag
!is null && subwinDragXSpot
== int.min
&& subwinDragYSpot
== int.min
); }
195 public void eguiLostGlobalFocus () {
196 ignoreSubWinChar
= false;
201 private bool insertSubWindow (SubWindow nw
) nothrow @trusted @nogc {
202 if (nw
is null || nw
.mClosed
) return false;
203 assert(nw
.mPrev
is null);
204 assert(nw
.mNext
is null);
205 assert(!nw
.mInWinList
);
206 if (nw
.mType
== SubWindow
.Type
.OnBottom
) {
207 SubWindow w
= subwinLast
;
209 while (w
.mPrev
!is null) w
= w
.mPrev
;
211 if (w
!is null) w
.mPrev
= nw
; else subwinLast
= nw
;
215 nw
.mInWinList
= true;
218 SubWindow aw
= getActiveSubWindow();
220 if (nw
.mType
== SubWindow
.Type
.OnTop || aw
is null) {
221 nw
.mPrev
= subwinLast
;
222 if (subwinLast
!is null) subwinLast
.mNext
= nw
;
224 nw
.mInWinList
= true;
227 if (aw
.mModal
&& !nw
.mModal
) return false; // can't insert normal windows while modal window is active
232 if (nw
.mNext
!is null) nw
.mNext
.mPrev
= nw
;
233 if (aw
is subwinLast
) subwinLast
= nw
;
234 nw
.mInWinList
= true;
239 private bool removeSubWindow (SubWindow nw
) nothrow @trusted @nogc {
240 if (nw
is null ||
!nw
.mInWinList
) return false;
241 if (nw
.mPrev
!is null) nw
.mPrev
.mNext
= nw
.mNext
;
242 if (nw
.mNext
!is null) nw
.mNext
.mPrev
= nw
.mPrev
;
243 if (nw
is subwinLast
) subwinLast
= nw
.mPrev
;
246 nw
.mInWinList
= false;
251 public void mouse2xy (MouseEvent event
, out int mx
, out int my
) nothrow @trusted @nogc {
252 mx
= event
.x
/vbufEffScale
;
253 my
= event
.y
/vbufEffScale
;
257 public SubWindow
subWindowAt (int mx
, int my
) nothrow @trusted @nogc {
258 for (SubWindow w
= subwinLast
; w
!is null; w
= w
.mPrev
) {
261 if (mx
>= w
.winminx
&& my
>= w
.winminy
&& mx
< w
.winminx
+w
.MinSizeX
&& my
< w
.winminy
+w
.MinSizeY
) return w
;
263 if (mx
>= w
.winx
&& my
>= w
.winy
&& mx
< w
.winx
+w
.winw
&& my
< w
.winy
+w
.winh
) return w
;
271 public SubWindow
subWindowAt (MouseEvent event
) nothrow @trusted @nogc { return subWindowAt(event
.x
/vbufEffScale
, event
.y
/vbufEffScale
); }
274 public SubWindow
getActiveSubWindow () nothrow @trusted @nogc {
275 for (SubWindow w
= subwinLast
; w
!is null; w
= w
.mPrev
) {
276 if (!w
.mClosed
&& w
.type
!= SubWindow
.Type
.OnTop
&& !w
.mMinimized
) return w
;
282 public bool dispatchEvent (KeyEvent event
) {
284 if (isSubWinDragging
) {
285 if (isSubWinDraggingKeyboard
) {
286 if (!event
.pressed
) return true;
287 if (event
== "Left") subwinDrag
.x0
= subwinDrag
.x0
-1;
288 else if (event
== "Right") subwinDrag
.x0
= subwinDrag
.x0
+1;
289 else if (event
== "Up") subwinDrag
.y0
= subwinDrag
.y0
-1;
290 else if (event
== "Down") subwinDrag
.y0
= subwinDrag
.y0
+1;
291 else if (event
== "C-Left") subwinDrag
.x0
= subwinDrag
.x0
-8;
292 else if (event
== "C-Right") subwinDrag
.x0
= subwinDrag
.x0
+8;
293 else if (event
== "C-Up") subwinDrag
.y0
= subwinDrag
.y0
-8;
294 else if (event
== "C-Down") subwinDrag
.y0
= subwinDrag
.y0
+8;
295 else if (event
== "Home") subwinDrag
.x0
= 0;
296 else if (event
== "End") subwinDrag
.x0
= VBufWidth
-subwinDrag
.width
;
297 else if (event
== "PageUp") subwinDrag
.y0
= 0;
298 else if (event
== "PageDown") subwinDrag
.y0
= VBufHeight
-subwinDrag
.height
;
299 else if (event
== "Escape" || event
== "Enter") subwinDrag
= null;
303 } else if (auto aw
= getActiveSubWindow()) {
304 res
= aw
.onKey(event
);
305 if (res
) postScreenRebuild();
311 public bool dispatchEvent (MouseEvent event
) {
312 __gshared SubWindow lastHover
= null;
314 if (subwinLast
is null) { postScreenRepaint(); return false; }
318 auto aw
= getActiveSubWindow();
319 auto msw
= subWindowAt(event
);
321 if (msw
!is lastHover
) {
326 bool curIsModal
= (aw
!is null && aw
.mModal
);
328 // switch window by button press
329 if (event
.type
== MouseEventType
.buttonReleased
&& msw
!is aw
&& !curIsModal
) {
330 if (msw
!is null && msw
.mType
== SubWindow
.Type
.Normal
) {
338 if (isSubWinDragging
) {
339 if (!isSubWinDraggingKeyboard
) {
340 subwinDrag
.x0
= mx
+subwinDragXSpot
;
341 subwinDrag
.y0
= my
+subwinDragYSpot
;
343 if (event
.type
== MouseEventType
.buttonReleased
&& event
.button
== MouseButton
.left
) subwinDrag
= null;
351 if (msw
is null || msw
!is aw || msw
.mMinimized
) {
352 if (msw
is null ||
!msw
.onTop
) {
357 assert(msw
!is null);
359 if (msw
.onMouse(event
)) {
369 public bool dispatchEvent (dchar ch
) {
370 if (ignoreSubWinChar
) { ignoreSubWinChar
= false; return (subwinLast
!is null); }
372 if (!isSubWinDragging
) {
373 if (auto aw
= getActiveSubWindow()) {
375 if (res
) postScreenRebuild();
382 // ////////////////////////////////////////////////////////////////////////// //
383 public void paintSubWindows () {
385 SubWindow firstWin
= subwinLast
;
386 if (firstWin
is null) return;
387 while (firstWin
.mPrev
!is null) firstWin
= firstWin
.mPrev
;
389 SubWindow firstMin
, firstNormal
, firstTop
;
390 SubWindow lastMin
, lastNormal
, lastTop
;
392 void doDraw (SubWindow w
) {
396 if (w
is subwinDrag
) { gxClipReset(); gxFillRect(w
.x0
, w
.y0
, w
.width
, w
.height
, gxRGBA
!(255, 127, 0, 80)); }
400 // paint background windows
401 for (SubWindow w
= firstWin
; w
!is null; w
= w
.mNext
) {
402 if (w
.mClosed
) continue;
404 if (firstMin
is null) firstMin
= w
;
406 } else if (w
.mType
== SubWindow
.Type
.Normal
) {
407 if (firstNormal
is null) firstNormal
= w
;
409 } else if (w
.mType
== SubWindow
.Type
.OnTop
) {
410 if (firstTop
is null) firstTop
= w
;
412 } else if (w
.mType
== SubWindow
.Type
.OnBottom
) {
417 // paint minimized windows
418 for (SubWindow w
= firstMin
; w
!is null; w
= w
.mNext
) {
419 if (!w
.mClosed
&& w
.mMinimized
) doDraw(w
);
420 if (w
is lastMin
) break;
423 // paint normal windows
424 for (SubWindow w
= firstNormal
; w
!is null; w
= w
.mNext
) {
425 if (!w
.mClosed
&& !w
.mMinimized
&& w
.mType
== SubWindow
.Type
.Normal
) doDraw(w
);
426 if (w
is lastNormal
) break;
429 // paint ontop windows
430 for (SubWindow w
= firstTop
; w
!is null; w
= w
.mNext
) {
431 if (!w
.mClosed
&& !w
.mMinimized
&& w
.mType
== SubWindow
.Type
.OnTop
) doDraw(w
);
432 if (w
is lastTop
) break;
435 // paint hint for minimized window
436 if (auto msw
= subWindowAt(lastMouseX
, lastMouseY
)) {
437 if (!msw
.mClosed
&& msw
.mMinimized
&& msw
.title
.length
) {
438 auto wdt
= gxTextWidthUtf(msw
.title
)+2;
439 auto hgt
= gxTextHeightUtf
+2;
440 int y
= msw
.winminy
-hgt
;
442 if (wdt
>= VBufWidth
) {
443 x
= (VBufWidth
-wdt
)/2;
445 x
= (msw
.winminx
+msw
.MinSizeX
)/2-wdt
/2;
449 gxFillRect(x
, y
, wdt
, hgt
, gxRGB
!(255, 255, 255));
450 gxDrawTextUtf(x
+1, y
+1, msw
.title
, gxRGB
!(0, 0, 0));
456 // ////////////////////////////////////////////////////////////////////////// //
457 public abstract class SubWindow
{
466 SubWindow mPrev
, mNext
;
467 Type mType
= Type
.Normal
;
472 int awidx
; // active widget index
476 int winminx
, winminy
;
477 int winx
, winy
, winw
, winh
;
479 uint clrWinFrame
= gxRGB
!(255, 255, 255);
480 uint clrWinTitleBack
= gxRGB
!(255, 255, 255);
481 uint clrWinTitle
= gxRGB
!(0, 0, 0);
482 uint clrWinBack
= gxRGB
!(0, 0, 180);
483 uint clrWinText
= gxRGB
!(255, 255, 255);
488 this (string atitle
, int ax
, int ay
, int aw
, int ah
) {
496 this (string atitle
, int aw
, int ah
) {
498 winx
= (VBufWidth
-aw
)/2;
499 winy
= (VBufHeight
-ah
)/2;
504 final @property int x0 () const pure nothrow @safe @nogc { return (mMinimized ? winminx
: winx
); }
505 final @property int y0 () const pure nothrow @safe @nogc { return (mMinimized ? winminy
: winy
); }
506 final @property int width () const pure nothrow @safe @nogc { return (mMinimized ? MinSizeX
: winw
); }
507 final @property int height () const pure nothrow @safe @nogc { return (mMinimized ? MinSizeY
: winh
); }
509 final @property void x0 (int v
) pure nothrow @safe @nogc { if (mMinimized
) winminx
= v
; else winx
= v
; }
510 final @property void y0 (int v
) pure nothrow @safe @nogc { if (mMinimized
) winminy
= v
; else winy
= v
; }
512 final @property SubWindow
prev () pure nothrow @safe @nogc { return mPrev
; }
513 final @property SubWindow
next () pure nothrow @safe @nogc { return mNext
; }
515 final @property Type
type () const pure nothrow @safe @nogc { return mType
; }
516 final @property bool onTop () const pure nothrow @safe @nogc { return (mType
== Type
.OnTop
); }
517 final @property bool onBottom () const pure nothrow @safe @nogc { return (mType
== Type
.OnBottom
); }
519 final @property bool inWinList () const pure nothrow @safe @nogc { return mInWinList
; }
521 final @property bool modal () const pure nothrow @safe @nogc { return mModal
; }
522 final @property bool closed () const pure nothrow @safe @nogc { return mClosed
; }
524 final @property bool active () const nothrow @trusted @nogc {
525 if (!mInWinList || mClosed || mMinimized
) return false;
526 return (getActiveSubWindow
is this);
529 @property int decorationSizeX () const nothrow @safe { return 2*2; }
530 @property int decorationSizeY () const nothrow @safe { return (gxTextHeightUtf
< 10 ?
10 : gxTextHeightUtf
+2)+1+2; }
532 @property int clientOffsetX () const nothrow @safe { return 2; }
533 @property int clientOffsetY () const nothrow @safe { return (gxTextHeightUtf
< 10 ?
10 : gxTextHeightUtf
+2)+1; }
535 final @property int clientWidth () const nothrow @safe { return winw
-decorationSizeX
; }
536 final @property int clientHeight () const nothrow @safe { return winh
-decorationSizeY
; }
538 void addWidget (Widget w
) {
539 if (w
is null) return;
540 if (w
.parent
is this) return;
541 if (w
.parent
!is null) throw new Exception("can't add widget owned by another window");
546 final @property void activeWidget (Widget w
) nothrow @trusted @nogc {
547 if (w
is null) return;
548 foreach (immutable idx
, Widget ww
; widgets
[]) {
550 awidx
= cast(int)idx
;
556 final @property Widget
activeWidget () nothrow @trusted @nogc { return (awidx
>= 0 && awidx
< widgets
.length ? widgets
.ptr
[awidx
] : null); }
558 final int widgetAtXYIndex (int x
, int y
) nothrow @trusted {
559 foreach_reverse (immutable widx
, Widget w
; widgets
) {
560 if (x
>= w
.x0
&& y
>= w
.y0
&& x
<= w
.x1
&& y
<= w
.y1
) return cast(int)widx
;
565 final Widget
widgetAtXY (int x
, int y
) nothrow @trusted {
566 auto idx
= widgetAtXYIndex(x
, y
);
567 return (idx
>= 0 && idx
< widgets
.length ? widgets
[idx
] : null);
571 if (widgets
.length
> 0) {
572 foreach (immutable _
; 0..widgets
.length
) {
573 if (awidx
< 0) awidx
= 0; else if (awidx
>= widgets
.length
) awidx
= cast(int)widgets
.length
;
575 if (awidx
< 0) awidx
= cast(int)(widgets
.length
-1);
576 if (widgets
[awidx
].tabStop
) break;
582 if (widgets
.length
> 0) {
583 foreach (immutable _
; 0..widgets
.length
) {
584 if (awidx
< 0) awidx
= 0; else if (awidx
>= widgets
.length
) awidx
= cast(int)widgets
.length
;
586 if (awidx
>= widgets
.length
) awidx
= 0;
587 if (widgets
[awidx
].tabStop
) break;
592 protected void drawWidgets () {
593 foreach (Widget w
; widgets
) w
.onPaint();
596 // draw window frame and background in "normal" state
597 protected void drawWindowNormal () {
599 gxDrawWindow(title
, clrWinFrame
, clrWinTitle
, clrWinTitleBack
, clrWinBack
);
602 // draw window frame and background in "minimized" state
603 protected void drawWindowMinimized () {
604 gxClipRect
.x0
= winminx
;
605 gxClipRect
.y0
= winminy
;
606 gxClipRect
.x1
= winminx
+MinSizeX
-1;
607 gxClipRect
.y1
= winminy
+MinSizeY
-1;
608 gxFillRect(winminx
, winminy
, MinSizeX
, MinSizeY
, clrWinBack
);
609 gxDrawRect(winminx
, winminy
, MinSizeX
, MinSizeY
, clrWinFrame
);
612 void startMouseDrag (MouseEvent event
) {
614 event
.mouse2xy(mx
, my
);
616 subwinDragXSpot
= x0
-mx
;
617 subwinDragYSpot
= y0
-my
;
621 void startKeyboardDrag () {
623 subwinDragXSpot
= int.min
;
624 subwinDragYSpot
= int.min
;
629 if (subwinDrag
is this) {
641 drawWindowMinimized();
645 bool onKey (KeyEvent event
) {
646 if (closed
) return false;
647 if (mMinimized
) return false;
648 if (awidx
>= 0 && awidx
< widgets
.length
) {
649 if (widgets
[awidx
].onKey(event
)) return true;
652 if (event
== "C-F5") { startKeyboardDrag(); return true; }
653 if (event
== "Tab") { doTab(); return true; }
654 if (event
== "S-Tab") { doShiftTab(); return true; }
655 if (event
== "M-M" && !mModal
) { minimize(); return true; }
657 foreach_reverse (immutable aidx
, Widget w
; widgets
) {
658 if (aidx
!= awidx
&& w
.onKey(event
)) return true;
663 bool onMouse (MouseEvent event
) {
664 if (!active
) return false;
665 if (closed
) return false;
668 event
.mouse2xy(mx
, my
);
671 if (subwinDrag
is null && event
.type
== MouseEventType
.buttonPressed
&& event
.button
== MouseButton
.left
) {
672 if (mx
>= x0
&& my
>= y0
&& mx
< x0
+width
&& my
< (!mMinimized ? y0
+gxTextHeightUtf
+2 : height
)) {
673 startMouseDrag(event
);
678 if (mMinimized
) return false;
680 if (mx
>= winx
&& my
>= winy
&& mx
< winx
+winw
&& my
< winy
+gxTextHeightUtf
) {
681 if (event
.type
== MouseEventType
.buttonReleased
&& event
.button
== MouseButton
.right
) {
682 if (!mModal
&& mType
== Type
.Normal
) minimize();
686 if (awidx
>= 0 && awidx
< widgets
.length
) {
687 auto msw
= widgetAtXY(mx
, my
);
688 if (msw
!is null && msw
.onMouse(event
)) return true;
693 bool onChar (dchar ch
) {
694 if (!active
) return false;
695 if (closed
) return false;
696 if (mMinimized
) return false;
697 if (awidx
>= 0 && awidx
< widgets
.length
) {
698 if (widgets
[awidx
].onChar(ch
)) return true;
699 foreach_reverse (immutable aidx
, Widget w
; widgets
) {
700 if (aidx
!= awidx
&& w
.onChar(ch
)) return true;
708 gxClipRect
.x0
= winx
;
709 gxClipRect
.x1
= winx
+winw
-1;
710 gxClipRect
.y0
= winy
;
711 gxClipRect
.y1
= winy
+winh
-1;
714 final void setupClientClip () {
716 gxClipRect
.intersect(GxRect(
717 GxPoint(winx
+clientOffsetX
, winy
+clientOffsetY
),
718 GxPoint(winx
+clientOffsetX
+clientWidth
-1, winy
+clientOffsetY
+clientHeight
-1)));
723 if (removeSubWindow(this)) postScreenRebuild();
726 protected bool addToSubwinList (bool asModal
, bool fromKeyboard
) {
727 if (fromKeyboard
) ignoreSubWinChar
= true;
728 if (mInWinList
) return true;
730 if (insertSubWindow(this)) {
737 void add (bool fromKeyboard
=false) { addToSubwinList(false, fromKeyboard
); }
739 void addModal (bool fromKeyboard
=false) { addToSubwinList(true, fromKeyboard
); }
741 void bringToFront () {
742 if (mClosed ||
!mInWinList
) return;
743 auto aw
= getActiveSubWindow();
744 if (aw
is this) { mMinimized
= false; return; }
745 if (aw
!is null && aw
.mModal
) return; // alas
746 removeSubWindow(this);
748 insertSubWindow(this);
749 if (subwinDrag
!is this) subwinDrag
= null;
753 @property bool minimized () const { return mMinimized
; }
755 @property void minimized (bool v
) {
756 if (v
== mMinimized
) return;
757 if (v
) minimize(); else bringToFront();
761 if (mClosed || mMinimized
) return;
762 if (!mInWinList
) { mMinimized
= true; return; }
764 assert(subwinLast
!is null);
765 findMinimizedPos(winminx
, winminy
);
766 auto aw
= getActiveSubWindow();
767 if (aw
is this) subwinDrag
= null;
783 static findMinimizedPos (out int wx
, out int wy
) {
784 static bool isOccupied (int x
, int y
) {
785 for (SubWindow w
= subwinLast
; w
!is null; w
= w
.mPrev
) {
786 if (w
.mInWinList
&& !w
.closed
&& w
.mMinimized
) {
787 if (x
>= w
.winminx
&& y
>= w
.winminy
&& x
< w
.winminx
+MinSizeX
&& y
< w
.winminy
+MinSizeY
) return true;
793 int txcount
= VBufWidth
/(MinSizeX
+MinMarginX
);
794 //int tycount = VBufHeight/(MinSizeY+MinMarginY);
795 if (txcount
< 1) txcount
= 1;
796 //if (tycount < 1) tycount = 1;
797 foreach (immutable int n
; 0..6/*5535*/) {
798 int x
= (n
%txcount
)*(MinSizeX
+MinMarginX
)+1;
799 int y
= VBufHeight
-MinSizeY
-(n
/txcount
)*(MinSizeY
+MinMarginY
);
800 //conwriteln("trying (", x, ",", y, ")");
801 if (!isOccupied(x
, y
)) { wx
= x
; wy
= y
; /*conwriteln(" HIT!");*/ return; }