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
;
30 import egui
.widgets
: Widget
;
33 // ////////////////////////////////////////////////////////////////////////// //
34 public __gshared SimpleWindow vbwin
; // main window; MUST be set!
35 public __gshared
bool vbfocused
= false;
38 // ////////////////////////////////////////////////////////////////////////// //
39 __gshared
public int lastMouseXUnscaled
= 10000, lastMouseYUnscaled
= 10000;
40 __gshared
/*MouseButton*/public int lastMouseButton
;
42 public int lastMouseX () nothrow @trusted @nogc { pragma(inline
, true); return lastMouseXUnscaled
/screenEffScale
; }
43 public int lastMouseY () nothrow @trusted @nogc { pragma(inline
, true); return lastMouseYUnscaled
/screenEffScale
; }
45 public bool lastMouseLeft () nothrow @trusted @nogc { pragma(inline
, true); return ((lastMouseButton
&MouseButton
.left
) != 0); }
46 public bool lastMouseRight () nothrow @trusted @nogc { pragma(inline
, true); return ((lastMouseButton
&MouseButton
.right
) != 0); }
47 public bool lastMouseMiddle () nothrow @trusted @nogc { pragma(inline
, true); return ((lastMouseButton
&MouseButton
.middle
) != 0); }
50 // ////////////////////////////////////////////////////////////////////////// //
51 public class ScreenRebuildEvent
{}
52 public class ScreenRepaintEvent
{}
53 public class QuitEvent
{}
54 public class CursorBlinkEvent
{}
55 public class HideMouseEvent
{}
57 __gshared ScreenRebuildEvent evScrRebuild
;
58 __gshared ScreenRepaintEvent evScreenRepaint
;
59 __gshared CursorBlinkEvent evCurBlink
;
60 __gshared HideMouseEvent evHideMouse
;
62 shared static this () {
63 evScrRebuild
= new ScreenRebuildEvent();
64 evScreenRepaint
= new ScreenRepaintEvent();
65 evCurBlink
= new CursorBlinkEvent();
66 evHideMouse
= new HideMouseEvent();
70 // ////////////////////////////////////////////////////////////////////////// //
71 public void postScreenRebuild () { if (vbwin
!is null && !vbwin
.eventQueued
!ScreenRebuildEvent
) vbwin
.postEvent(evScrRebuild
); }
72 public void postScreenRepaint () { if (vbwin
!is null && !vbwin
.eventQueued
!ScreenRepaintEvent
&& !vbwin
.eventQueued
!ScreenRebuildEvent
) vbwin
.postEvent(evScreenRepaint
); }
73 public void postScreenRepaintDelayed () { if (vbwin
!is null && !vbwin
.eventQueued
!ScreenRepaintEvent
&& !vbwin
.eventQueued
!ScreenRebuildEvent
) vbwin
.postTimeout(evScreenRepaint
, 35); }
75 public void postCurBlink () {
76 if (vbwin
!is null && !vbwin
.eventQueued
!CursorBlinkEvent
) {
77 //conwriteln("curblink posted!");
78 vbwin
.postTimeout(evCurBlink
, 100);
79 //vbwin.postTimeout(evCurBlink, 500);
84 // ////////////////////////////////////////////////////////////////////////// //
85 __gshared MonoTime lastMouseMove
;
86 __gshared
uint MouseHideTime
= 3000;
89 shared static this () {
90 conRegVar("mouse_hide_time", "mouse cursor hiding time (in milliseconds); 0 to not hide it",
94 delegate (self
, uint nv
) {
95 if (MouseHideTime
!= nv
) {
96 if (MouseHideTime
== 0) mouseMoved();
98 conwriteln("mouse hiding time: ", nv
);
105 //==========================================================================
109 //==========================================================================
110 public int mshtime_dbg () {
111 if (MouseHideTime
> 0) {
112 auto ctt
= MonoTime
.currTime
;
113 auto mt
= (ctt
-lastMouseMove
).total
!"msecs";
121 //==========================================================================
125 //==========================================================================
126 public bool isMouseVisible () {
127 if (MouseHideTime
> 0) {
128 auto ctt
= MonoTime
.currTime
;
129 return ((ctt
-lastMouseMove
).total
!"msecs" < MouseHideTime
+500);
136 //==========================================================================
140 //==========================================================================
141 public float mouseAlpha () {
142 if (MouseHideTime
> 0) {
143 auto ctt
= MonoTime
.currTime
;
144 auto msc
= (ctt
-lastMouseMove
).total
!"msecs";
145 if (msc
>= MouseHideTime
+500) return 0.0f;
146 if (msc
< MouseHideTime
) return 1.0f;
147 msc
-= MouseHideTime
;
148 return 1.0f-msc
/500.0f;
155 //==========================================================================
159 // returns `true` if mouse should be redrawn
161 //==========================================================================
162 public bool repostHideMouse () {
163 if (vbwin
is null || vbwin
.eventQueued
!HideMouseEvent
) return false;
164 if (MouseHideTime
> 0) {
165 auto ctt
= MonoTime
.currTime
;
166 auto tms
= (ctt
-lastMouseMove
).total
!"msecs";
167 if (tms
>= MouseHideTime
) {
168 if (tms
>= MouseHideTime
+500) return true; // hide it
169 vbwin
.postTimeout(evHideMouse
, 50);
170 return true; // fade it
172 vbwin
.postTimeout(evHideMouse
, cast(int)(MouseHideTime
-tms
));
178 //==========================================================================
182 //==========================================================================
183 public void mouseMoved () {
184 if (MouseHideTime
> 0) {
185 lastMouseMove
= MonoTime
.currTime
;
186 if (vbwin
!is null && !vbwin
.eventQueued
!HideMouseEvent
) vbwin
.postTimeout(evHideMouse
, MouseHideTime
);
191 //==========================================================================
195 //==========================================================================
196 public void drawTextCursor (bool active
, int x
, int y
, int hgt
=-666) {
197 if (hgt
== -666) hgt
= gxTextHeightUtf
;
200 auto ctt
= (MonoTime
.currTime
.ticks
*1000/MonoTime
.ticksPerSecond
)/100;
201 int doty
= ctt
%(hgt
*2-1);
202 if (doty
>= hgt
) doty
= hgt
*2-doty
-1;
203 gxVLine(x
, y
, hgt
, (ctt
%10 < 5 ? gxRGB
!(255, 255, 255) : gxRGB
!(200, 200, 200)));
204 gxPutPixel(x
, y
+doty
, gxRGB
!(0, 255, 255));
207 gxVLine(x
, y
, hgt
, gxRGB
!(170, 170, 170));
212 // ////////////////////////////////////////////////////////////////////////// //
213 private __gshared SubWindow subwinLast
;
214 private __gshared
bool ignoreSubWinChar
= false;
215 // make a package and move that to package
216 private __gshared SubWindow subwinDrag
= null;
217 private __gshared
int subwinDragXSpot
, subwinDragYSpot
;
220 public @property bool isSubWinDragging () nothrow @trusted @nogc { pragma(inline
, true); return (subwinDrag
!is null); }
221 public @property bool isSubWinDraggingKeyboard () nothrow @trusted @nogc { pragma(inline
, true); return (subwinDrag
!is null && subwinDragXSpot
== int.min
&& subwinDragYSpot
== int.min
); }
224 //==========================================================================
226 // eguiLostGlobalFocus
228 //==========================================================================
229 public void eguiLostGlobalFocus () {
230 ignoreSubWinChar
= false;
235 private bool insertSubWindow (SubWindow nw
) nothrow @trusted @nogc {
236 if (nw
is null || nw
.mClosed
) return false;
237 assert(nw
.mPrev
is null);
238 assert(nw
.mNext
is null);
239 assert(!nw
.mInWinList
);
240 if (nw
.mType
== SubWindow
.Type
.OnBottom
) {
241 SubWindow w
= subwinLast
;
243 while (w
.mPrev
!is null) w
= w
.mPrev
;
245 if (w
!is null) w
.mPrev
= nw
; else subwinLast
= nw
;
249 nw
.mInWinList
= true;
252 SubWindow aw
= getActiveSubWindow();
254 if (nw
.mType
== SubWindow
.Type
.OnTop || aw
is null) {
255 nw
.mPrev
= subwinLast
;
256 if (subwinLast
!is null) subwinLast
.mNext
= nw
;
258 nw
.mInWinList
= true;
261 if (aw
.mModal
&& !nw
.mModal
) return false; // can't insert normal windows while modal window is active
266 if (nw
.mNext
!is null) nw
.mNext
.mPrev
= nw
;
267 if (aw
is subwinLast
) subwinLast
= nw
;
268 nw
.mInWinList
= true;
273 private bool removeSubWindow (SubWindow nw
) nothrow @trusted @nogc {
274 if (nw
is null ||
!nw
.mInWinList
) return false;
275 if (nw
.mPrev
!is null) nw
.mPrev
.mNext
= nw
.mNext
;
276 if (nw
.mNext
!is null) nw
.mNext
.mPrev
= nw
.mPrev
;
277 if (nw
is subwinLast
) subwinLast
= nw
.mPrev
;
280 nw
.mInWinList
= false;
285 //==========================================================================
289 //==========================================================================
290 public void mouse2xy (MouseEvent event
, out int mx
, out int my
) nothrow @trusted @nogc {
291 mx
= event
.x
/screenEffScale
;
292 my
= event
.y
/screenEffScale
;
296 //==========================================================================
300 //==========================================================================
301 public SubWindow
subWindowAt (int mx
, int my
) nothrow @trusted @nogc {
302 for (SubWindow w
= subwinLast
; w
!is null; w
= w
.mPrev
) {
305 if (mx
>= w
.winminx
&& my
>= w
.winminy
&& mx
< w
.winminx
+w
.MinSizeX
&& my
< w
.winminy
+w
.MinSizeY
) return w
;
307 if (mx
>= w
.winx
&& my
>= w
.winy
&& mx
< w
.winx
+w
.winw
&& my
< w
.winy
+w
.winh
) return w
;
315 //==========================================================================
319 //==========================================================================
320 public SubWindow
subWindowAt (MouseEvent event
) nothrow @trusted @nogc {
321 pragma(inline
, true);
322 return subWindowAt(event
.x
/screenEffScale
, event
.y
/screenEffScale
);
326 //==========================================================================
328 // getActiveSubWindow
330 //==========================================================================
331 public SubWindow
getActiveSubWindow () nothrow @trusted @nogc {
332 for (SubWindow w
= subwinLast
; w
!is null; w
= w
.mPrev
) {
333 if (!w
.mClosed
&& w
.type
!= SubWindow
.Type
.OnTop
&& !w
.mMinimized
) return w
;
339 //==========================================================================
343 //==========================================================================
344 public bool dispatchEvent (KeyEvent event
) {
346 if (isSubWinDragging
) {
347 if (isSubWinDraggingKeyboard
) {
348 if (!event
.pressed
) return true;
349 if (event
== "Left") subwinDrag
.x0
= subwinDrag
.x0
-1;
350 else if (event
== "Right") subwinDrag
.x0
= subwinDrag
.x0
+1;
351 else if (event
== "Up") subwinDrag
.y0
= subwinDrag
.y0
-1;
352 else if (event
== "Down") subwinDrag
.y0
= subwinDrag
.y0
+1;
353 else if (event
== "C-Left") subwinDrag
.x0
= subwinDrag
.x0
-8;
354 else if (event
== "C-Right") subwinDrag
.x0
= subwinDrag
.x0
+8;
355 else if (event
== "C-Up") subwinDrag
.y0
= subwinDrag
.y0
-8;
356 else if (event
== "C-Down") subwinDrag
.y0
= subwinDrag
.y0
+8;
357 else if (event
== "Home") subwinDrag
.x0
= 0;
358 else if (event
== "End") subwinDrag
.x0
= screenWidth
-subwinDrag
.width
;
359 else if (event
== "PageUp") subwinDrag
.y0
= 0;
360 else if (event
== "PageDown") subwinDrag
.y0
= screenHeight
-subwinDrag
.height
;
361 else if (event
== "Escape" || event
== "Enter") subwinDrag
= null;
365 } else if (auto aw
= getActiveSubWindow()) {
366 res
= aw
.onKey(event
);
367 if (res
) postScreenRebuild();
373 //==========================================================================
377 //==========================================================================
378 public bool dispatchEvent (MouseEvent event
) {
379 __gshared SubWindow lastHover
= null;
381 if (subwinLast
is null) { postScreenRepaint(); return false; }
385 auto aw
= getActiveSubWindow();
386 auto msw
= subWindowAt(event
);
388 if (msw
!is lastHover
) {
393 bool curIsModal
= (aw
!is null && aw
.mModal
);
395 // switch window by button press
396 if (event
.type
== MouseEventType
.buttonReleased
&& msw
!is aw
&& !curIsModal
) {
397 if (msw
!is null && msw
.mType
== SubWindow
.Type
.Normal
) {
405 if (isSubWinDragging
) {
406 if (!isSubWinDraggingKeyboard
) {
407 subwinDrag
.x0
= mx
+subwinDragXSpot
;
408 subwinDrag
.y0
= my
+subwinDragYSpot
;
410 if (event
.type
== MouseEventType
.buttonReleased
&& event
.button
== MouseButton
.left
) subwinDrag
= null;
418 if (msw
is null || msw
!is aw || msw
.mMinimized
) {
419 if (msw
is null ||
!msw
.onTop
) {
424 assert(msw
!is null);
426 if (msw
.onMouse(event
)) {
436 //==========================================================================
440 //==========================================================================
441 public bool dispatchEvent (dchar ch
) {
442 if (ignoreSubWinChar
) { ignoreSubWinChar
= false; return (subwinLast
!is null); }
444 if (!isSubWinDragging
) {
445 if (auto aw
= getActiveSubWindow()) {
447 if (res
) postScreenRebuild();
454 //==========================================================================
458 //==========================================================================
459 public void paintSubWindows () {
461 SubWindow firstWin
= subwinLast
;
462 if (firstWin
is null) return;
463 while (firstWin
.mPrev
!is null) firstWin
= firstWin
.mPrev
;
465 SubWindow firstMin
, firstNormal
, firstTop
;
466 SubWindow lastMin
, lastNormal
, lastTop
;
468 void doDraw (SubWindow w
) {
472 if (w
is subwinDrag
) { gxClipReset(); gxFillRect(w
.x0
, w
.y0
, w
.width
, w
.height
, gxRGBA
!(255, 127, 0, 176)); }
476 // paint background windows
477 for (SubWindow w
= firstWin
; w
!is null; w
= w
.mNext
) {
478 if (w
.mClosed
) continue;
480 if (firstMin
is null) firstMin
= w
;
482 } else if (w
.mType
== SubWindow
.Type
.Normal
) {
483 if (firstNormal
is null) firstNormal
= w
;
485 } else if (w
.mType
== SubWindow
.Type
.OnTop
) {
486 if (firstTop
is null) firstTop
= w
;
488 } else if (w
.mType
== SubWindow
.Type
.OnBottom
) {
493 // paint minimized windows
494 for (SubWindow w
= firstMin
; w
!is null; w
= w
.mNext
) {
495 if (!w
.mClosed
&& w
.mMinimized
) doDraw(w
);
496 if (w
is lastMin
) break;
499 // paint normal windows
500 for (SubWindow w
= firstNormal
; w
!is null; w
= w
.mNext
) {
501 if (!w
.mClosed
&& !w
.mMinimized
&& w
.mType
== SubWindow
.Type
.Normal
) doDraw(w
);
502 if (w
is lastNormal
) break;
505 // paint ontop windows
506 for (SubWindow w
= firstTop
; w
!is null; w
= w
.mNext
) {
507 if (!w
.mClosed
&& !w
.mMinimized
&& w
.mType
== SubWindow
.Type
.OnTop
) doDraw(w
);
508 if (w
is lastTop
) break;
511 // paint hint for minimized window
512 if (auto msw
= subWindowAt(lastMouseX
, lastMouseY
)) {
513 if (!msw
.mClosed
&& msw
.mMinimized
&& msw
.title
.length
) {
514 auto wdt
= gxTextWidthUtf(msw
.title
)+2;
515 auto hgt
= gxTextHeightUtf
+2;
516 int y
= msw
.winminy
-hgt
;
518 if (wdt
>= screenWidth
) {
519 x
= (screenWidth
-wdt
)/2;
521 x
= (msw
.winminx
+msw
.MinSizeX
)/2-wdt
/2;
525 gxFillRect(x
, y
, wdt
, hgt
, gxRGB
!(255, 255, 255));
526 gxDrawTextUtf(x
+1, y
+1, msw
.title
, gxRGB
!(0, 0, 0));
532 // ////////////////////////////////////////////////////////////////////////// //
533 public abstract class SubWindow
{
542 SubWindow mPrev
, mNext
;
543 Type mType
= Type
.Normal
;
548 int awidx
; // active widget index
552 int winminx
, winminy
;
553 int winx
, winy
, winw
, winh
;
555 uint clrWinFrame
= gxRGB
!(255, 255, 255);
556 uint clrWinTitleBack
= gxRGB
!(255, 255, 255);
557 uint clrWinTitle
= gxRGB
!(0, 0, 0);
558 uint clrWinBack
= gxRGB
!(0, 0, 180);
559 uint clrWinText
= gxRGB
!(255, 255, 255);
564 this (string atitle
, int ax
, int ay
, int aw
, int ah
) {
572 this (string atitle
, int aw
, int ah
) {
574 winx
= (screenWidth
-aw
)/2;
575 winy
= (screenHeight
-ah
)/2;
580 final @property int x0 () const pure nothrow @safe @nogc { return (mMinimized ? winminx
: winx
); }
581 final @property int y0 () const pure nothrow @safe @nogc { return (mMinimized ? winminy
: winy
); }
582 final @property int width () const pure nothrow @safe @nogc { return (mMinimized ? MinSizeX
: winw
); }
583 final @property int height () const pure nothrow @safe @nogc { return (mMinimized ? MinSizeY
: winh
); }
585 final @property void x0 (int v
) pure nothrow @safe @nogc { if (mMinimized
) winminx
= v
; else winx
= v
; }
586 final @property void y0 (int v
) pure nothrow @safe @nogc { if (mMinimized
) winminy
= v
; else winy
= v
; }
588 final void setSize (int awidth
, int aheight
) nothrow @trusted @nogc {
589 if (awidth
> 0) winw
= awidth
;
590 if (aheight
> 0) winh
= aheight
;
593 final void setClientSize (int awidth
, int aheight
) nothrow @trusted {
594 if (awidth
> 0) winw
= awidth
+decorationSizeX
;
595 if (aheight
> 0) winh
= aheight
+decorationSizeY
;
598 final void centerWindow () nothrow @trusted @nogc {
599 winx
= (screenWidth
-winw
)/2;
600 winy
= (screenHeight
-winh
)/2;
603 final @property SubWindow
prev () pure nothrow @safe @nogc { return mPrev
; }
604 final @property SubWindow
next () pure nothrow @safe @nogc { return mNext
; }
606 final @property Type
type () const pure nothrow @safe @nogc { return mType
; }
607 final @property bool onTop () const pure nothrow @safe @nogc { return (mType
== Type
.OnTop
); }
608 final @property bool onBottom () const pure nothrow @safe @nogc { return (mType
== Type
.OnBottom
); }
610 final @property bool inWinList () const pure nothrow @safe @nogc { return mInWinList
; }
612 final @property bool modal () const pure nothrow @safe @nogc { return mModal
; }
613 final @property bool closed () const pure nothrow @safe @nogc { return mClosed
; }
615 final @property bool active () const nothrow @trusted @nogc {
616 if (!mInWinList || mClosed || mMinimized
) return false;
617 return (getActiveSubWindow
is this);
620 @property int decorationSizeX () const nothrow @safe { return 2*2; }
621 @property int decorationSizeY () const nothrow @safe { return (gxTextHeightUtf
< 10 ?
10 : gxTextHeightUtf
+2)+1+2; }
623 @property int clientOffsetX () const nothrow @safe { return 2; }
624 @property int clientOffsetY () const nothrow @safe { return (gxTextHeightUtf
< 10 ?
10 : gxTextHeightUtf
+2)+1; }
626 final @property int clientWidth () const nothrow @safe { return winw
-decorationSizeX
; }
627 final @property int clientHeight () const nothrow @safe { return winh
-decorationSizeY
; }
629 void addWidget (Widget w
) {
630 if (w
is null) return;
631 if (w
.parent
is this) return;
632 if (w
.parent
!is null) throw new Exception("can't add widget owned by another window");
637 final @property void activeWidget (Widget w
) nothrow @trusted @nogc {
638 if (w
is null) return;
639 foreach (immutable idx
, Widget ww
; widgets
[]) {
641 awidx
= cast(int)idx
;
647 final @property Widget
activeWidget () nothrow @trusted @nogc { return (awidx
>= 0 && awidx
< widgets
.length ? widgets
.ptr
[awidx
] : null); }
649 final int widgetAtXYIndex (int x
, int y
) nothrow @trusted {
650 foreach_reverse (immutable widx
, Widget w
; widgets
) {
651 if (x
>= w
.x0
&& y
>= w
.y0
&& x
<= w
.x1
&& y
<= w
.y1
) return cast(int)widx
;
656 final Widget
widgetAtXY (int x
, int y
) nothrow @trusted {
657 auto idx
= widgetAtXYIndex(x
, y
);
658 return (idx
>= 0 && idx
< widgets
.length ? widgets
[idx
] : null);
662 if (widgets
.length
> 0) {
663 foreach (immutable _
; 0..widgets
.length
) {
664 if (awidx
< 0) awidx
= 0; else if (awidx
>= widgets
.length
) awidx
= cast(int)widgets
.length
;
666 if (awidx
< 0) awidx
= cast(int)(widgets
.length
-1);
667 if (widgets
[awidx
].tabStop
) break;
673 if (widgets
.length
> 0) {
674 foreach (immutable _
; 0..widgets
.length
) {
675 if (awidx
< 0) awidx
= 0; else if (awidx
>= widgets
.length
) awidx
= cast(int)widgets
.length
;
677 if (awidx
>= widgets
.length
) awidx
= 0;
678 if (widgets
[awidx
].tabStop
) break;
683 protected void drawWidgets () {
684 foreach (Widget w
; widgets
) w
.onPaint();
687 // draw window frame and background in "normal" state
688 protected void drawWindowNormal () {
690 gxDrawWindow(title
, clrWinFrame
, clrWinTitle
, clrWinTitleBack
, clrWinBack
);
693 // draw window frame and background in "minimized" state
694 protected void drawWindowMinimized () {
695 gxClipRect
.x0
= winminx
;
696 gxClipRect
.y0
= winminy
;
697 gxClipRect
.x1
= winminx
+MinSizeX
-1;
698 gxClipRect
.y1
= winminy
+MinSizeY
-1;
699 gxFillRect(winminx
, winminy
, MinSizeX
, MinSizeY
, clrWinBack
);
700 gxDrawRect(winminx
, winminy
, MinSizeX
, MinSizeY
, clrWinFrame
);
703 void startMouseDrag (MouseEvent event
) {
705 event
.mouse2xy(mx
, my
);
707 subwinDragXSpot
= x0
-mx
;
708 subwinDragYSpot
= y0
-my
;
712 void startKeyboardDrag () {
714 subwinDragXSpot
= int.min
;
715 subwinDragYSpot
= int.min
;
720 if (subwinDrag
is this) {
732 drawWindowMinimized();
736 bool onKey (KeyEvent event
) {
737 if (closed
) return false;
738 if (mMinimized
) return false;
739 if (awidx
>= 0 && awidx
< widgets
.length
) {
740 if (widgets
[awidx
].onKey(event
)) return true;
743 if (event
== "C-F5") { startKeyboardDrag(); return true; }
744 if (event
== "Tab") { doTab(); return true; }
745 if (event
== "S-Tab") { doShiftTab(); return true; }
746 if (event
== "M-M" && !mModal
) { minimize(); return true; }
748 foreach_reverse (immutable aidx
, Widget w
; widgets
) {
749 if (aidx
!= awidx
&& w
.onKey(event
)) return true;
754 bool onMouse (MouseEvent event
) {
755 if (!active
) return false;
756 if (closed
) return false;
759 event
.mouse2xy(mx
, my
);
762 if (subwinDrag
is null && event
.type
== MouseEventType
.buttonPressed
&& event
.button
== MouseButton
.left
) {
763 if (mx
>= x0
&& my
>= y0
&& mx
< x0
+width
&& my
< (!mMinimized ? y0
+gxTextHeightUtf
+2 : height
)) {
764 startMouseDrag(event
);
769 if (mMinimized
) return false;
771 if (mx
>= winx
&& my
>= winy
&& mx
< winx
+winw
&& my
< winy
+gxTextHeightUtf
) {
772 if (event
.type
== MouseEventType
.buttonReleased
&& event
.button
== MouseButton
.right
) {
773 if (!mModal
&& mType
== Type
.Normal
) minimize();
777 if (awidx
>= 0 && awidx
< widgets
.length
) {
778 auto msw
= widgetAtXY(mx
, my
);
779 if (msw
!is null && msw
.onMouse(event
)) return true;
784 bool onChar (dchar ch
) {
785 if (!active
) return false;
786 if (closed
) return false;
787 if (mMinimized
) return false;
788 if (awidx
>= 0 && awidx
< widgets
.length
) {
789 if (widgets
[awidx
].onChar(ch
)) return true;
790 foreach_reverse (immutable aidx
, Widget w
; widgets
) {
791 if (aidx
!= awidx
&& w
.onChar(ch
)) return true;
799 gxClipRect
.x0
= winx
;
800 gxClipRect
.x1
= winx
+winw
-1;
801 gxClipRect
.y0
= winy
;
802 gxClipRect
.y1
= winy
+winh
-1;
805 final void setupClientClip () {
807 gxClipRect
.intersect(GxRect(
808 GxPoint(winx
+clientOffsetX
, winy
+clientOffsetY
),
809 GxPoint(winx
+clientOffsetX
+clientWidth
-1, winy
+clientOffsetY
+clientHeight
-1)));
814 if (removeSubWindow(this)) postScreenRebuild();
817 protected bool addToSubwinList (bool asModal
, bool fromKeyboard
) {
818 if (fromKeyboard
) ignoreSubWinChar
= true;
819 if (mInWinList
) return true;
821 if (insertSubWindow(this)) {
828 void add (bool fromKeyboard
=false) { addToSubwinList(false, fromKeyboard
); }
830 void addModal (bool fromKeyboard
=false) { addToSubwinList(true, fromKeyboard
); }
832 void bringToFront () {
833 if (mClosed ||
!mInWinList
) return;
834 auto aw
= getActiveSubWindow();
835 if (aw
is this) { mMinimized
= false; return; }
836 if (aw
!is null && aw
.mModal
) return; // alas
837 removeSubWindow(this);
839 insertSubWindow(this);
840 if (subwinDrag
!is this) subwinDrag
= null;
844 @property bool minimized () const { return mMinimized
; }
846 @property void minimized (bool v
) {
847 if (v
== mMinimized
) return;
848 if (v
) minimize(); else bringToFront();
852 if (mClosed || mMinimized
) return;
853 if (!mInWinList
) { mMinimized
= true; return; }
855 assert(subwinLast
!is null);
856 findMinimizedPos(winminx
, winminy
);
857 auto aw
= getActiveSubWindow();
858 if (aw
is this) subwinDrag
= null;
874 static findMinimizedPos (out int wx
, out int wy
) {
875 static bool isOccupied (int x
, int y
) {
876 for (SubWindow w
= subwinLast
; w
!is null; w
= w
.mPrev
) {
877 if (w
.mInWinList
&& !w
.closed
&& w
.mMinimized
) {
878 if (x
>= w
.winminx
&& y
>= w
.winminy
&& x
< w
.winminx
+MinSizeX
&& y
< w
.winminy
+MinSizeY
) return true;
884 int txcount
= screenWidth
/(MinSizeX
+MinMarginX
);
885 //int tycount = screenHeight/(MinSizeY+MinMarginY);
886 if (txcount
< 1) txcount
= 1;
887 //if (tycount < 1) tycount = 1;
888 foreach (immutable int n
; 0..6/*5535*/) {
889 int x
= (n
%txcount
)*(MinSizeX
+MinMarginX
)+1;
890 int y
= screenHeight
-MinSizeY
-(n
/txcount
)*(MinSizeY
+MinMarginY
);
891 //conwriteln("trying (", x, ",", y, ")");
892 if (!isOccupied(x
, y
)) { wx
= x
; wy
= y
; /*conwriteln(" HIT!");*/ return; }