2 * Simple Framebuffer Gfx/GUI lib
4 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
5 * Understanding is not required. Only obedience.
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, version 3 of the License ONLY.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 module iv
.egra
.gui
.subwindows
/*is aliced*/;
24 import arsd
.simpledisplay
;
33 public import iv
.egra
.gui
.style
;
34 import iv
.egra
.gui
.widgets
: Widget
, RootWidget
;
37 // ////////////////////////////////////////////////////////////////////////// //
38 public __gshared SimpleWindow vbwin
; // main window; MUST be set!
39 public __gshared
bool vbfocused
= false;
42 // ////////////////////////////////////////////////////////////////////////// //
43 __gshared
public int lastMouseXUnscaled
= 10000, lastMouseYUnscaled
= 10000;
44 __gshared
/*MouseButton*/public int lastMouseButton
;
46 public int lastMouseX () nothrow @trusted @nogc { pragma(inline
, true); return lastMouseXUnscaled
/screenEffScale
; }
47 public int lastMouseY () nothrow @trusted @nogc { pragma(inline
, true); return lastMouseYUnscaled
/screenEffScale
; }
49 public bool lastMouseLeft () nothrow @trusted @nogc { pragma(inline
, true); return ((lastMouseButton
&MouseButton
.left
) != 0); }
50 public bool lastMouseRight () nothrow @trusted @nogc { pragma(inline
, true); return ((lastMouseButton
&MouseButton
.right
) != 0); }
51 public bool lastMouseMiddle () nothrow @trusted @nogc { pragma(inline
, true); return ((lastMouseButton
&MouseButton
.middle
) != 0); }
54 // ////////////////////////////////////////////////////////////////////////// //
55 public class ScreenRebuildEvent
{}
56 public class ScreenRepaintEvent
{}
57 public class QuitEvent
{}
58 public class CursorBlinkEvent
{}
59 public class HideMouseEvent
{}
61 __gshared ScreenRebuildEvent evScrRebuild
;
62 __gshared ScreenRepaintEvent evScreenRepaint
;
63 __gshared CursorBlinkEvent evCurBlink
;
64 __gshared HideMouseEvent evHideMouse
;
66 shared static this () {
67 evScrRebuild
= new ScreenRebuildEvent();
68 evScreenRepaint
= new ScreenRepaintEvent();
69 evCurBlink
= new CursorBlinkEvent();
70 evHideMouse
= new HideMouseEvent();
74 // ////////////////////////////////////////////////////////////////////////// //
75 public void postScreenRebuild () { if (vbwin
!is null && !vbwin
.eventQueued
!ScreenRebuildEvent
) vbwin
.postEvent(evScrRebuild
); }
76 public void postScreenRepaint () { if (vbwin
!is null && !vbwin
.eventQueued
!ScreenRepaintEvent
&& !vbwin
.eventQueued
!ScreenRebuildEvent
) vbwin
.postEvent(evScreenRepaint
); }
77 public void postScreenRepaintDelayed () { if (vbwin
!is null && !vbwin
.eventQueued
!ScreenRepaintEvent
&& !vbwin
.eventQueued
!ScreenRebuildEvent
) vbwin
.postTimeout(evScreenRepaint
, 35); }
79 public void postCurBlink () {
80 if (vbwin
!is null && !vbwin
.eventQueued
!CursorBlinkEvent
) {
81 //conwriteln("curblink posted!");
82 vbwin
.postTimeout(evCurBlink
, 100);
83 //vbwin.postTimeout(evCurBlink, 500);
88 // ////////////////////////////////////////////////////////////////////////// //
89 __gshared MonoTime lastMouseMove
;
90 __gshared
uint MouseHideTime
= 3000;
93 shared static this () {
94 conRegVar("mouse_hide_time", "mouse cursor hiding time (in milliseconds); 0 to not hide it",
98 delegate (self
, uint nv
) {
99 if (MouseHideTime
!= nv
) {
100 if (MouseHideTime
== 0) mouseMoved();
102 conwriteln("mouse hiding time: ", nv
);
109 //==========================================================================
113 //==========================================================================
114 public int mshtime_dbg () {
115 if (MouseHideTime
> 0) {
116 auto ctt
= MonoTime
.currTime
;
117 auto mt
= (ctt
-lastMouseMove
).total
!"msecs";
125 //==========================================================================
129 //==========================================================================
130 public bool isMouseVisible () {
131 if (MouseHideTime
> 0) {
132 auto ctt
= MonoTime
.currTime
;
133 return ((ctt
-lastMouseMove
).total
!"msecs" < MouseHideTime
+500);
140 //==========================================================================
144 //==========================================================================
145 public float mouseAlpha () {
146 if (MouseHideTime
> 0) {
147 auto ctt
= MonoTime
.currTime
;
148 auto msc
= (ctt
-lastMouseMove
).total
!"msecs";
149 if (msc
>= MouseHideTime
+500) return 0.0f;
150 if (msc
< MouseHideTime
) return 1.0f;
151 msc
-= MouseHideTime
;
152 return 1.0f-msc
/500.0f;
159 //==========================================================================
163 // returns `true` if mouse should be redrawn
165 //==========================================================================
166 public bool repostHideMouse () {
167 if (vbwin
is null || vbwin
.eventQueued
!HideMouseEvent
) return false;
168 if (MouseHideTime
> 0) {
169 auto ctt
= MonoTime
.currTime
;
170 auto tms
= (ctt
-lastMouseMove
).total
!"msecs";
171 if (tms
>= MouseHideTime
) {
172 if (tms
>= MouseHideTime
+500) return true; // hide it
173 vbwin
.postTimeout(evHideMouse
, 50);
174 return true; // fade it
176 vbwin
.postTimeout(evHideMouse
, cast(int)(MouseHideTime
-tms
));
182 //==========================================================================
186 //==========================================================================
187 public void mouseMoved () {
188 if (MouseHideTime
> 0) {
189 lastMouseMove
= MonoTime
.currTime
;
190 if (vbwin
!is null && !vbwin
.eventQueued
!HideMouseEvent
) vbwin
.postTimeout(evHideMouse
, MouseHideTime
);
195 //==========================================================================
199 //==========================================================================
200 public void drawTextCursor (bool active
, int x
, int y
, int hgt
=-666) {
201 if (hgt
== -666) hgt
= gxTextHeightUtf
;
204 auto ctt
= (MonoTime
.currTime
.ticks
*1000/MonoTime
.ticksPerSecond
)/100;
205 int doty
= ctt
%(hgt
*2-1);
206 if (doty
>= hgt
) doty
= hgt
*2-doty
-1;
207 gxVLine(x
, y
, hgt
, (ctt
%10 < 5 ? gxRGB
!(255, 255, 255) : gxRGB
!(200, 200, 200)));
208 gxPutPixel(x
, y
+doty
, gxRGB
!(0, 255, 255));
211 gxVLine(x
, y
, hgt
, gxRGB
!(170, 170, 170));
216 // ////////////////////////////////////////////////////////////////////////// //
217 private __gshared SubWindow subwinLast
;
218 private __gshared
bool ignoreSubWinChar
= false;
219 // make a package and move that to package
220 private __gshared SubWindow subwinDrag
= null;
221 private __gshared
int subwinDragXSpot
, subwinDragYSpot
;
224 public @property bool isSubWinDragging () nothrow @trusted @nogc { pragma(inline
, true); return (subwinDrag
!is null); }
225 public @property bool isSubWinDraggingKeyboard () nothrow @trusted @nogc { pragma(inline
, true); return (subwinDrag
!is null && subwinDragXSpot
== int.min
&& subwinDragYSpot
== int.min
); }
228 //==========================================================================
230 // eguiLostGlobalFocus
232 //==========================================================================
233 public void eguiLostGlobalFocus () {
234 ignoreSubWinChar
= false;
236 SubWindow aw
= getActiveSubWindow();
237 if (aw
!is null) aw
.releaseWidgetGrab();
241 private bool insertSubWindow (SubWindow nw
) {
242 if (nw
is null || nw
.mClosed
) return false;
243 assert(nw
.mPrev
is null);
244 assert(nw
.mNext
is null);
245 assert(!nw
.mInWinList
);
246 nw
.releaseWidgetGrab();
247 SubWindow law
= getActiveSubWindow();
248 if (nw
.mType
== SubWindow
.Type
.OnBottom
) {
249 SubWindow w
= subwinLast
;
251 while (w
.mPrev
!is null) w
= w
.mPrev
;
253 if (w
!is null) w
.mPrev
= nw
; else subwinLast
= nw
;
257 nw
.mInWinList
= true;
258 if (law
!is null && law
!is getActiveSubWindow()) law
.releaseWidgetGrab();
261 SubWindow aw
= getActiveSubWindow();
263 if (nw
.mType
== SubWindow
.Type
.OnTop || aw
is null) {
264 nw
.mPrev
= subwinLast
;
265 if (subwinLast
!is null) subwinLast
.mNext
= nw
;
267 nw
.mInWinList
= true;
268 if (law
!is null && law
!is getActiveSubWindow()) law
.releaseWidgetGrab();
271 if (aw
.mModal
&& !nw
.mModal
) return false; // can't insert normal windows while modal window is active
276 if (nw
.mNext
!is null) nw
.mNext
.mPrev
= nw
;
277 if (aw
is subwinLast
) subwinLast
= nw
;
278 nw
.mInWinList
= true;
279 if (law
!is null && law
!is getActiveSubWindow()) law
.releaseWidgetGrab();
284 private bool removeSubWindow (SubWindow nw
) {
285 if (nw
is null ||
!nw
.mInWinList
) return false;
286 nw
.releaseWidgetGrab();
287 SubWindow law
= getActiveSubWindow();
288 if (nw
.mPrev
!is null) nw
.mPrev
.mNext
= nw
.mNext
;
289 if (nw
.mNext
!is null) nw
.mNext
.mPrev
= nw
.mPrev
;
290 if (nw
is subwinLast
) subwinLast
= nw
.mPrev
;
293 nw
.mInWinList
= false;
294 if (law
!is null && law
!is getActiveSubWindow()) law
.releaseWidgetGrab();
299 //==========================================================================
303 //==========================================================================
304 public void mouse2xy (MouseEvent event
, out int mx
, out int my
) nothrow @trusted @nogc {
305 mx
= event
.x
/screenEffScale
;
306 my
= event
.y
/screenEffScale
;
310 //==========================================================================
314 //==========================================================================
315 public SubWindow
subWindowAt (in GxPoint p
) nothrow @trusted @nogc {
316 for (SubWindow w
= subwinLast
; w
!is null; w
= w
.mPrev
) {
319 if (p
.x
>= w
.winminx
&& p
.y
>= w
.winminy
&& p
.x
< w
.winminx
+w
.MinSizeX
&& p
.y
< w
.winminy
+w
.MinSizeY
) return w
;
321 if (p
.inside(w
.winrect
)) return w
;
329 public SubWindow
subWindowAt (int mx
, int my
) nothrow @trusted @nogc { return subWindowAt(GxPoint(mx
, my
)); }
332 //==========================================================================
336 //==========================================================================
337 public SubWindow
subWindowAt (MouseEvent event
) nothrow @trusted @nogc {
338 pragma(inline
, true);
339 return subWindowAt(event
.x
/screenEffScale
, event
.y
/screenEffScale
);
343 //==========================================================================
345 // getActiveSubWindow
347 //==========================================================================
348 public SubWindow
getActiveSubWindow () nothrow @trusted @nogc {
349 for (SubWindow w
= subwinLast
; w
!is null; w
= w
.mPrev
) {
350 if (!w
.mClosed
&& w
.type
!= SubWindow
.Type
.OnTop
&& !w
.mMinimized
) return w
;
356 //==========================================================================
360 //==========================================================================
361 public bool dispatchEvent (KeyEvent event
) {
363 if (isSubWinDragging
) {
364 if (isSubWinDraggingKeyboard
) {
365 if (!event
.pressed
) return true;
366 if (event
== "Left") subwinDrag
.x0
= subwinDrag
.x0
-1;
367 else if (event
== "Right") subwinDrag
.x0
= subwinDrag
.x0
+1;
368 else if (event
== "Up") subwinDrag
.y0
= subwinDrag
.y0
-1;
369 else if (event
== "Down") subwinDrag
.y0
= subwinDrag
.y0
+1;
370 else if (event
== "C-Left") subwinDrag
.x0
= subwinDrag
.x0
-8;
371 else if (event
== "C-Right") subwinDrag
.x0
= subwinDrag
.x0
+8;
372 else if (event
== "C-Up") subwinDrag
.y0
= subwinDrag
.y0
-8;
373 else if (event
== "C-Down") subwinDrag
.y0
= subwinDrag
.y0
+8;
374 else if (event
== "Home") subwinDrag
.x0
= 0;
375 else if (event
== "End") subwinDrag
.x0
= screenWidth
-subwinDrag
.width
;
376 else if (event
== "PageUp") subwinDrag
.y0
= 0;
377 else if (event
== "PageDown") subwinDrag
.y0
= screenHeight
-subwinDrag
.height
;
378 else if (event
== "Escape" || event
== "Enter") subwinDrag
= null;
382 } else if (auto aw
= getActiveSubWindow()) {
383 res
= aw
.onKeyEvent(event
);
384 if (res
) postScreenRebuild();
390 //==========================================================================
394 //==========================================================================
395 public bool dispatchEvent (MouseEvent event
) {
396 __gshared SubWindow lastHover
= null;
398 if (subwinLast
is null) { postScreenRepaint(); return false; }
402 auto aw
= getActiveSubWindow();
403 auto msw
= subWindowAt(event
);
405 if (msw
!is lastHover
) {
410 bool curIsModal
= (aw
!is null && aw
.mModal
);
412 // switch window by button press
413 if (event
.type
== MouseEventType
.buttonReleased
&& msw
!is aw
&& !curIsModal
) {
414 if (msw
!is null && msw
.mType
== SubWindow
.Type
.Normal
) {
415 if (aw
!is null) aw
.releaseWidgetGrab();
416 msw
.releaseWidgetGrab();
424 if (isSubWinDragging
) {
425 subwinDrag
.releaseWidgetGrab(); // just in case
426 if (!isSubWinDraggingKeyboard
) {
427 subwinDrag
.x0
= mx
+subwinDragXSpot
;
428 subwinDrag
.y0
= my
+subwinDragYSpot
;
430 if (event
.type
== MouseEventType
.buttonReleased
&& event
.button
== MouseButton
.left
) subwinDrag
= null;
438 if (msw
is null || msw
!is aw || msw
.mMinimized
) {
439 if (msw
is null ||
!msw
.onTop
) {
444 assert(msw
!is null);
446 if (msw
.onMouseEvent(event
)) {
456 //==========================================================================
460 //==========================================================================
461 public bool dispatchEvent (dchar ch
) {
462 if (ignoreSubWinChar
) { ignoreSubWinChar
= false; return (subwinLast
!is null); }
464 if (!isSubWinDragging
) {
465 if (auto aw
= getActiveSubWindow()) {
466 res
= aw
.onCharEvent(ch
);
467 if (res
) postScreenRebuild();
474 //==========================================================================
478 //==========================================================================
479 public void paintSubWindows () {
481 SubWindow firstWin
= subwinLast
;
482 if (firstWin
is null) return;
483 while (firstWin
.mPrev
!is null) firstWin
= firstWin
.mPrev
;
485 SubWindow firstMin
, firstNormal
, firstTop
;
486 SubWindow lastMin
, lastNormal
, lastTop
;
490 void doDraw (SubWindow w
) {
494 if (w
is subwinDrag
) { gxClipReset(); gxFillRect(w
.x0
, w
.y0
, w
.width
, w
.height
, gxRGBA
!(255, 127, 0, 176)); }
498 // paint background windows
499 for (SubWindow w
= firstWin
; w
!is null; w
= w
.mNext
) {
500 if (w
.mClosed
) continue;
502 if (firstMin
is null) firstMin
= w
;
504 } else if (w
.mType
== SubWindow
.Type
.Normal
) {
505 if (firstNormal
is null) firstNormal
= w
;
507 } else if (w
.mType
== SubWindow
.Type
.OnTop
) {
508 if (firstTop
is null) firstTop
= w
;
510 } else if (w
.mType
== SubWindow
.Type
.OnBottom
) {
515 // paint minimized windows
516 for (SubWindow w
= firstMin
; w
!is null; w
= w
.mNext
) {
517 if (!w
.mClosed
&& w
.mMinimized
) doDraw(w
);
518 if (w
is lastMin
) break;
521 // paint normal windows
522 for (SubWindow w
= firstNormal
; w
!is null; w
= w
.mNext
) {
523 if (!w
.mClosed
&& !w
.mMinimized
&& w
.mType
== SubWindow
.Type
.Normal
) doDraw(w
);
524 if (w
is lastNormal
) break;
527 // paint ontop windows
528 for (SubWindow w
= firstTop
; w
!is null; w
= w
.mNext
) {
529 if (!w
.mClosed
&& !w
.mMinimized
&& w
.mType
== SubWindow
.Type
.OnTop
) doDraw(w
);
530 if (w
is lastTop
) break;
533 // paint hint for minimized window
534 if (auto msw
= subWindowAt(lastMouseX
, lastMouseY
)) {
535 if (!msw
.mClosed
&& msw
.mMinimized
&& msw
.title
.length
) {
536 auto wdt
= gxTextWidthUtf(msw
.title
)+2;
537 auto hgt
= gxTextHeightUtf
+2;
538 int y
= msw
.winminy
-hgt
;
540 if (wdt
>= screenWidth
) {
541 x
= (screenWidth
-wdt
)/2;
543 x
= (msw
.winminx
+msw
.MinSizeX
)/2-wdt
/2;
547 gxFillRect(x
, y
, wdt
, hgt
, gxRGB
!(255, 255, 255));
548 gxDrawTextUtf(x
+1, y
+1, msw
.title
, gxRGB
!(0, 0, 0));
554 // ////////////////////////////////////////////////////////////////////////// //
555 public class SubWindow
{
564 SubWindow mPrev
, mNext
;
565 Type mType
= Type
.Normal
;
570 int awidx
; // active widget index
573 ColorStyle colorStyle
;
576 int winminx
, winminy
;
585 final uint getStyleColor (in Object obj
, const(char)[] type
, const(char)[] mod
=null) {
586 pragma(inline
, true);
587 ColorStyle st
= colorStyle
;
588 if (st
is null) st
= defaultColorStyle
;
589 return st
.findColor(this, obj
, type
, mod
);
592 final uint getColor (const(char)[] type
, const(char)[] mod
) {
593 return getStyleColor(this, type
, mod
);
596 final uint getColor (const(char)[] type
) {
597 return getStyleColor(this, type
, (active ?
null : "inactive"));
602 if (mRoot
is null) setRoot(new RootWidget(this)); else fixRoot();
607 mRoot
.rect
.pos
= GxPoint(0, 0);
608 mRoot
.rect
.size
= GxSize(clientWidth
, clientHeight
);
612 void setStyle (ColorStyle stl
) {
616 void finishConstruction () {
617 winrect
.size
.sanitize();
620 mRoot
.enter(&createWidgets
);
626 winrect
.pos
= GxPoint(0, 0);
627 winrect
.size
= GxSize(0, 0);
628 finishConstruction();
631 this (string atitle
) {
633 winrect
.pos
= GxPoint(0, 0);
634 winrect
.size
= GxSize(0, 0);
635 finishConstruction();
638 this (string atitle
, in GxPoint apos
, in GxSize asize
) {
641 winrect
.size
= asize
;
642 finishConstruction();
645 this (string atitle
, in GxSize asize
) {
647 winrect
.size
= asize
;
648 winrect
.size
.sanitize();
649 winrect
.pos
.x
= (screenWidth
-winrect
.width
)/2;
650 winrect
.pos
.y
= (screenHeight
-winrect
.height
)/2;
651 finishConstruction();
654 // this doesn't perform relayouting
655 void setRoot (RootWidget w
) {
660 // this is called from constructor
661 void createStyle () {
664 // this is called from constructor
665 void createWidgets () {
668 // this is called from constructor
669 // you can call `addModal()` here, for example
670 void finishCreating () {
674 void relayout (bool resizeWindow
) {
675 if (mRoot
is null) return;
677 FuiFlexLayouter
!Widget lay
;
679 lay
.isValidBoxId
= delegate bool (Widget id
) { return (id
!is null); };
681 lay
.firstChild
= delegate Widget (Widget id
) { return id
.firstChild
; };
682 lay
.nextSibling
= delegate Widget (Widget id
) { return id
.nextSibling
; };
684 lay
.getMinSize
= delegate int (Widget id
, in bool horiz
) { return (horiz ? id
.minSize
.w
: id
.minSize
.h
); };
685 lay
.getMaxSize
= delegate int (Widget id
, in bool horiz
) { return (horiz ? id
.maxSize
.w
: id
.maxSize
.h
); };
686 lay
.getPrefSize
= delegate int (Widget id
, in bool horiz
) { return (horiz ? id
.prefSize
.w
: id
.prefSize
.h
); };
688 lay
.isHorizBox
= delegate bool (Widget id
) { return (id
.childDir
== GxDir
.Horiz
); };
690 lay
.getFlex
= delegate int (Widget id
) { return id
.flex
; };
692 lay
.getSize
= delegate int (Widget id
, in bool horiz
) { return (horiz ? id
.boxsize
.w
: id
.boxsize
.h
); };
693 lay
.setSize
= delegate void (Widget id
, in bool horiz
, int val
) { if (horiz
) id
.boxsize
.w
= val
; else id
.boxsize
.h
= val
; };
695 lay
.getFinalSize
= delegate int (Widget id
, in bool horiz
) { return (horiz ? id
.finalSize
.w
: id
.finalSize
.h
); };
696 lay
.setFinalSize
= delegate void (Widget id
, in bool horiz
, int val
) { if (horiz
) id
.finalSize
.w
= val
; else id
.finalSize
.h
= val
; };
698 lay
.setFinalPos
= delegate void (Widget id
, in bool horiz
, int val
) { if (horiz
) id
.finalPos
.x
= val
; else id
.finalPos
.y
= val
; };
700 if (winrect
.size
.empty
) {
701 if (maxWinSize
.empty
) maxWinSize
= GxSize(screenWidth
-decorationSizeX
, screenHeight
-decorationSizeY
);
705 if (winrect
.size
.w
> screenWidth
) winrect
.size
.w
= screenWidth
;
706 if (winrect
.size
.h
> screenHeight
) winrect
.size
.h
= screenHeight
;
709 mRoot
.maxSize
= maxWinSize
;
711 mRoot
.maxSize
= winrect
.size
-GxSize(decorationSizeX
, decorationSizeY
);
712 if (mRoot
.maxSize
.w
<= 0) mRoot
.maxSize
.w
= maxWinSize
.w
;
713 if (mRoot
.maxSize
.h
<= 0) mRoot
.maxSize
.h
= maxWinSize
.h
;
716 mRoot
.minSize
= minWinSize
;
717 if (mRoot
.minSize
.w
> screenWidth
-decorationSizeX
) mRoot
.minSize
.w
= screenWidth
-decorationSizeX
;
718 if (mRoot
.minSize
.h
> screenHeight
-decorationSizeY
) mRoot
.minSize
.h
= screenHeight
-decorationSizeY
;
721 if (mRoot
.boxsize
.w
<= 0) mRoot
.boxsize
.w
= gxTextWidthUtf(title
)+2;
723 mRoot
.prefSize
= mRoot
.boxsize
;
725 mRoot
.forEachDepth((Widget w
) { w
.preLayout(); });
729 winrect
.size
= mRoot
.boxsize
+GxSize(decorationSizeX
, decorationSizeY
);
730 if (winrect
.x1
>= screenWidth
) winrect
.pos
.x
-= winrect
.x1
-screenWidth
+1;
731 if (winrect
.y1
>= screenHeight
) winrect
.pos
.y
-= winrect
.y1
-screenHeight
+1;
732 if (winrect
.pos
.x
< 0) winrect
.pos
.x
= 0;
733 if (winrect
.pos
.y
< 0) winrect
.pos
.y
= 0;
735 mRoot
.forEachDepth((Widget w
) { w
.postLayout(); });
738 void relayoutResize () { relayout(true); }
740 // this clones the style
741 void appendStyle (const(char)[] str) {
742 if (colorStyle
is null) {
743 colorStyle
= new ColorStyle
;
744 colorStyle
.cloneFrom(defaultColorStyle
);
746 colorStyle
.parseStyle(str);
749 final @property Widget
rootWidget () pure nothrow @safe @nogc { pragma(inline
, true); return mRoot
; }
751 final @property int x0 () const pure nothrow @safe @nogc { return (mMinimized ? winminx
: winrect
.pos
.x
); }
752 final @property int y0 () const pure nothrow @safe @nogc { return (mMinimized ? winminy
: winrect
.pos
.y
); }
753 final @property int width () const pure nothrow @safe @nogc { return (mMinimized ? MinSizeX
: winrect
.size
.w
); }
754 final @property int height () const pure nothrow @safe @nogc { return (mMinimized ? MinSizeY
: winrect
.size
.h
); }
756 final @property void x0 (int v
) pure nothrow @safe @nogc { if (mMinimized
) winminx
= v
; else winrect
.pos
.x
= v
; }
757 final @property void y0 (int v
) pure nothrow @safe @nogc { if (mMinimized
) winminy
= v
; else winrect
.pos
.y
= v
; }
758 final @property void width (int v
) nothrow @safe @nogc { winrect
.size
.w
= v
; }
759 final @property void height (int v
) nothrow @safe @nogc { winrect
.size
.h
= v
; }
761 final void setSize (int awidth
, int aheight
) {
762 bool changed
= false;
763 if (awidth
> 0 && awidth
!= winrect
.size
.w
) { changed
= true; winrect
.size
.w
= awidth
; }
764 if (aheight
> 0 && aheight
!= winrect
.size
.w
) { changed
= true; winrect
.size
.h
= aheight
; }
765 if (changed
) fixRoot();
768 final void setClientSize (int awidth
, int aheight
) {
769 bool changed
= false;
770 if (awidth
> 0 && awidth
+decorationSizeX
!= winrect
.size
.w
) { changed
= true; winrect
.size
.w
= awidth
+decorationSizeX
; }
771 if (aheight
> 0 && aheight
+decorationSizeY
!= winrect
.size
.h
) { changed
= true; winrect
.size
.h
= aheight
+decorationSizeY
; }
772 if (changed
) fixRoot();
775 final void centerWindow () nothrow @trusted @nogc {
776 winrect
.pos
.x
= (screenWidth
-winrect
.size
.w
)/2;
777 winrect
.pos
.y
= (screenHeight
-winrect
.size
.h
)/2;
780 final @property SubWindow
prev () pure nothrow @safe @nogc { return mPrev
; }
781 final @property SubWindow
next () pure nothrow @safe @nogc { return mNext
; }
783 final @property Type
type () const pure nothrow @safe @nogc { return mType
; }
784 final @property bool onTop () const pure nothrow @safe @nogc { return (mType
== Type
.OnTop
); }
785 final @property bool onBottom () const pure nothrow @safe @nogc { return (mType
== Type
.OnBottom
); }
787 final @property bool inWinList () const pure nothrow @safe @nogc { return mInWinList
; }
789 final @property bool modal () const pure nothrow @safe @nogc { return mModal
; }
790 final @property bool closed () const pure nothrow @safe @nogc { return mClosed
; }
792 final @property bool active () const nothrow @trusted @nogc {
793 if (!mInWinList || mClosed || mMinimized
) return false;
794 return (getActiveSubWindow
is this);
797 @property int decorationSizeX () const nothrow @safe { return 2*2; }
798 @property int decorationSizeY () const nothrow @safe { return (gxTextHeightUtf
< 10 ?
10 : gxTextHeightUtf
+2)+1+2; }
800 @property int clientOffsetX () const nothrow @safe { return 2; }
801 @property int clientOffsetY () const nothrow @safe { return (gxTextHeightUtf
< 10 ?
10 : gxTextHeightUtf
+2)+1; }
803 final @property int clientWidth () const nothrow @safe { return winrect
.size
.w
-decorationSizeX
; }
804 final @property int clientHeight () const nothrow @safe { return winrect
.size
.h
-decorationSizeY
; }
806 protected void drawWidgets () {
808 if (mRoot
!is null) mRoot
.onPaint();
811 // draw window frame and background in "normal" state
812 protected void drawWindowNormal () {
814 immutable string act
= (active ?
null : "inactive");
815 gxDrawWindow(winrect
, title
,
816 getStyleColor(this, "frame", act
),
817 getStyleColor(this, "title-text", act
),
818 getStyleColor(this, "title-back", act
),
819 getStyleColor(this, "back", act
));
822 // draw window frame and background in "minimized" state
823 protected void drawWindowMinimized () {
824 gxClipRect
.x0
= winminx
;
825 gxClipRect
.y0
= winminy
;
826 gxClipRect
.x1
= winminx
+MinSizeX
-1;
827 gxClipRect
.y1
= winminy
+MinSizeY
-1;
828 immutable string act
= (active ?
null : "inactive");
829 gxFillRect(winminx
, winminy
, MinSizeX
, MinSizeY
, getStyleColor(this, "back", act
));
830 gxDrawRect(winminx
, winminy
, MinSizeX
, MinSizeY
, getStyleColor(this, "frame", act
));
833 void releaseWidgetGrab () {
834 if (mRoot
!is null) mRoot
.releaseGrab();
837 // event in our local coords
838 void startMouseDrag (MouseEvent event
) {
841 subwinDragXSpot
= -event
.x
;
842 subwinDragYSpot
= -event
.y
;
846 void startKeyboardDrag () {
849 subwinDragXSpot
= int.min
;
850 subwinDragYSpot
= int.min
;
855 if (subwinDrag
is this) {
869 drawWindowMinimized();
875 bool onKeySink (KeyEvent event
) {
879 bool onKeyBubble (KeyEvent event
) {
880 // global window hotkeys
882 if (event
== "C-F5") { startKeyboardDrag(); return true; }
883 if (event
== "M-M" && !mModal
) { minimize(); return true; }
888 bool onKeyEvent (KeyEvent event
) {
889 if (closed
) return false;
890 if (mMinimized
) return false;
891 if (onKeySink(event
)) return true;
892 if (mRoot
!is null && mRoot
.dispatchKey(event
)) return true;
893 return onKeyBubble(event
);
897 bool onMouseSink (MouseEvent event
) {
899 if (subwinDrag
is null && event
.type
== MouseEventType
.buttonPressed
&& event
.button
== MouseButton
.left
) {
900 if (event
.x
>= 0 && event
.y
>= 0 &&
901 event
.x
< width
&& event
.y
< (!mMinimized ? gxTextHeightUtf
+2 : height
))
903 startMouseDrag(event
);
908 if (mMinimized
) return false;
910 if (event
.type
== MouseEventType
.buttonReleased
&& event
.button
== MouseButton
.right
) {
911 if (event
.x
>= 0 && event
.y
>= 0 &&
912 event
.x
< winrect
.size
.w
&& event
.y
< gxTextHeightUtf
)
914 if (!mModal
&& mType
== Type
.Normal
) minimize();
922 bool onMouseBubble (MouseEvent event
) {
926 bool onMouseEvent (MouseEvent event
) {
927 if (!active
) return false;
928 if (closed
) return false;
931 event
.mouse2xy(mx
, my
);
933 MouseEvent ev
= event
;
936 if (onMouseSink(ev
)) return true;
938 if (mRoot
!is null) {
940 ev
.x
= mx
-(x0
+clientOffsetX
)-mRoot
.rect
.x0
;
941 ev
.y
= my
-(y0
+clientOffsetY
)-mRoot
.rect
.y0
;
942 if (mRoot
.dispatchMouse(ev
)) return true;
948 return onMouseBubble(ev
);
952 bool onCharSink (dchar ch
) {
956 bool onCharBubble (dchar ch
) {
960 bool onCharEvent (dchar ch
) {
961 if (!active
) return false;
962 if (closed
) return false;
963 if (mMinimized
) return false;
964 if (onCharSink(ch
)) return true;
965 if (mRoot
!is null && mRoot
.dispatchChar(ch
)) return true;
966 return onCharBubble(ch
);
970 gxClipRect
.intersect(winrect
);
973 final void setupClientClip () {
975 gxClipRect
.intersect(GxRect(
976 GxPoint(winrect
.pos
.x
+clientOffsetX
, winrect
.pos
.y
+clientOffsetY
),
977 GxPoint(winrect
.pos
.x
+clientOffsetX
+clientWidth
-1, winrect
.pos
.y
+clientOffsetY
+clientHeight
-1)));
982 if (removeSubWindow(this)) postScreenRebuild();
985 protected bool addToSubwinList (bool asModal
, bool fromKeyboard
) {
986 if (fromKeyboard
) ignoreSubWinChar
= true;
987 if (mInWinList
) return true;
989 if (insertSubWindow(this)) {
996 void add (bool fromKeyboard
=false) { addToSubwinList(false, fromKeyboard
); }
998 void addModal (bool fromKeyboard
=false) { addToSubwinList(true, fromKeyboard
); }
1000 void bringToFront () {
1001 if (mClosed ||
!mInWinList
) return;
1002 auto aw
= getActiveSubWindow();
1003 if (aw
is this) { mMinimized
= false; return; }
1004 if (aw
!is null && aw
.mModal
) return; // alas
1005 removeSubWindow(this);
1007 insertSubWindow(this);
1008 if (subwinDrag
!is this) subwinDrag
= null;
1009 postScreenRebuild();
1012 @property bool minimized () const { return mMinimized
; }
1014 @property void minimized (bool v
) {
1015 if (v
== mMinimized
) return;
1016 if (v
) minimize(); else bringToFront();
1020 if (mClosed || mMinimized
) return;
1021 if (!mInWinList
) { mMinimized
= true; return; }
1023 assert(subwinLast
!is null);
1024 releaseWidgetGrab();
1025 findMinimizedPos(winminx
, winminy
);
1026 auto aw
= getActiveSubWindow();
1027 if (aw
is this) subwinDrag
= null;
1029 postScreenRebuild();
1033 releaseWidgetGrab();
1040 enum MinMarginX
= 3;
1041 enum MinMarginY
= 3;
1044 static findMinimizedPos (out int wx
, out int wy
) {
1045 static bool isOccupied (int x
, int y
) {
1046 for (SubWindow w
= subwinLast
; w
!is null; w
= w
.mPrev
) {
1047 if (w
.mInWinList
&& !w
.closed
&& w
.mMinimized
) {
1048 if (x
>= w
.winminx
&& y
>= w
.winminy
&& x
< w
.winminx
+MinSizeX
&& y
< w
.winminy
+MinSizeY
) return true;
1054 int txcount
= screenWidth
/(MinSizeX
+MinMarginX
);
1055 //int tycount = screenHeight/(MinSizeY+MinMarginY);
1056 if (txcount
< 1) txcount
= 1;
1057 //if (tycount < 1) tycount = 1;
1058 foreach (immutable int n
; 0..6/*5535*/) {
1059 int x
= (n
%txcount
)*(MinSizeX
+MinMarginX
)+1;
1060 int y
= screenHeight
-MinSizeY
-(n
/txcount
)*(MinSizeY
+MinMarginY
);
1061 //conwriteln("trying (", x, ",", y, ")");
1062 if (!isOccupied(x
, y
)) { wx
= x
; wy
= y
; /*conwriteln(" HIT!");*/ return; }