1 (* Copyright (C) DooM 2D:Forever Developers
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 {$INCLUDE ../shared/a_modes.inc}
28 // ////////////////////////////////////////////////////////////////////////// //
32 type TActionCB
= procedure (me
: THControl
; uinfo
: Integer);
37 mWidth
, mHeight
: Integer;
38 mFrameWidth
, mFrameHeight
: Integer;
41 mChildren
: array of THControl
;
42 mFocused
: THControl
; // valid only for top-level controls
43 mGrab
: THControl
; // valid only for top-level controls
44 mEscClose
: Boolean; // valid only for top-level controls
53 function getEnabled (): Boolean;
54 procedure setEnabled (v
: Boolean); inline;
56 function getFocused (): Boolean; inline;
57 procedure setFocused (v
: Boolean); inline;
59 function isMyChild (ctl
: THControl
): Boolean;
61 function findFirstFocus (): THControl
;
62 function findLastFocus (): THControl
;
64 function findNextFocus (cur
: THControl
): THControl
;
65 function findPrevFocus (cur
: THControl
): THControl
;
67 procedure activated (); virtual;
68 procedure blurred (); virtual;
70 //WARNING! do not call scissor functions outside `.draw*()` API!
71 // reset scissor to whole control
72 procedure resetScissor ();
73 // set scissor to this internal rect (in local coords)
74 procedure setScissor (lx
, ly
, lw
, lh
: Integer);
77 procedure setScissorGLInternal (x
, y
, w
, h
: Integer);
83 mSize
: TLaySize
; // default size
84 mMaxSize
: TLaySize
; // maximum size
85 mActSize
: TLaySize
; // actual (calculated) size
86 mActPos
: TLayPos
; // actual (calculated) position
96 function getSize (): TLaySize
; inline; // default size; <0: use max size
97 procedure setSize (constref sz
: TLaySize
); inline; // default size; <0: use max size
98 function getMaxSize (): TLaySize
; inline; // max size; <0: set to some huge value
99 procedure setMaxSize (constref sz
: TLaySize
); inline; // max size; <0: set to some huge value
100 function getFlex (): Integer; inline; // <=0: not flexible
101 function isHorizBox (): Boolean; inline; // horizontal layout for children?
102 procedure setHorizBox (v
: Boolean); inline; // horizontal layout for children?
103 function canWrap (): Boolean; inline; // for horizontal boxes: can wrap children? for child: `false` means 'nonbreakable at *next* ctl'
104 procedure setCanWrap (v
: Boolean); inline; // for horizontal boxes: can wrap children? for child: `false` means 'nonbreakable at *next* ctl'
105 function isLineStart (): Boolean; inline; // `true` if this ctl should start a new line; ignored for vertical boxes
106 procedure setLineStart (v
: Boolean); inline; // `true` if this ctl should start a new line; ignored for vertical boxes
107 procedure setActualSizePos (constref apos
: TLayPos
; constref asize
: TLaySize
); inline;
108 function getHGroup (): AnsiString; inline; // empty: not grouped
109 procedure setHGroup (const v
: AnsiString); inline; // empty: not grouped
110 function getVGroup (): AnsiString; inline; // empty: not grouped
111 procedure setVGroup (const v
: AnsiString); inline; // empty: not grouped
112 function hasSibling (): Boolean; inline;
113 //function nextSibling (): THControl; inline;
114 function hasChildren (): Boolean; inline;
115 //function firstChild (): THControl; inline;
117 property flex
: Integer read mFlex write mFlex
;
120 constructor Create (ax
, ay
, aw
, ah
: Integer; aparent
: THControl
=nil);
121 destructor Destroy (); override;
123 // `sx` and `sy` are screen coordinates
124 procedure drawControl (sx
, sy
: Integer); virtual;
126 // called after all children drawn
127 procedure drawControlPost (sx
, sy
: Integer); virtual;
129 procedure draw (); virtual;
131 function topLevel (): THControl
; inline;
133 // returns `true` if global coords are inside this control
134 function toLocal (var x
, y
: Integer): Boolean;
135 procedure toGlobal (var x
, y
: Integer);
137 // x and y are global coords
138 function controlAtXY (x
, y
: Integer): THControl
;
140 function mouseEvent (var ev
: THMouseEvent
): Boolean; virtual; // returns `true` if event was eaten
141 function keyEvent (var ev
: THKeyEvent
): Boolean; virtual; // returns `true` if event was eaten
143 function prevSibling (): THControl
;
144 function nextSibling (): THControl
;
145 function firstChild (): THControl
; inline;
146 function lastChild (): THControl
; inline;
148 procedure appendChild (ctl
: THControl
); virtual;
151 property x0
: Integer read mX
;
152 property y0
: Integer read mY
;
153 property height
: Integer read mHeight
;
154 property width
: Integer read mWidth
;
155 property enabled
: Boolean read getEnabled write setEnabled
;
156 property parent
: THControl read mParent
;
157 property focused
: Boolean read getFocused write setFocused
;
158 property escClose
: Boolean read mEscClose write mEscClose
;
159 property eatKeys
: Boolean read mEatKeys write mEatKeys
;
163 THTopWindow
= class(THControl
)
167 mDragStartX
, mDragStartY
: Integer;
168 mWaitingClose
: Boolean;
172 procedure blurred (); override;
175 closeCB
: TActionCB
; // called after window was removed from ui window list
178 constructor Create (const atitle
: AnsiString; ax
, ay
: Integer; aw
: Integer=-1; ah
: Integer=-1);
180 procedure centerInScreen ();
182 // `sx` and `sy` are screen coordinates
183 procedure drawControl (sx
, sy
: Integer); override;
184 procedure drawControlPost (sx
, sy
: Integer); override;
186 function keyEvent (var ev
: THKeyEvent
): Boolean; override; // returns `true` if event was eaten
187 function mouseEvent (var ev
: THMouseEvent
): Boolean; override; // returns `true` if event was eaten
191 THCtlSimpleText
= class(THControl
)
201 mItems
: array of TItem
;
204 constructor Create (ax
, ay
: Integer; aparent
: THControl
=nil);
205 destructor Destroy (); override;
207 procedure appendItem (const atext
: AnsiString; acentered
: Boolean=false; ahline
: Boolean=false);
209 procedure drawControl (sx
, sy
: Integer); override;
211 function mouseEvent (var ev
: THMouseEvent
): Boolean; override;
212 function keyEvent (var ev
: THKeyEvent
): Boolean; override;
216 THCtlCBListBox
= class(THControl
)
226 mItems
: array of TItem
;
230 constructor Create (ax
, ay
: Integer; aparent
: THControl
=nil);
231 destructor Destroy (); override;
233 procedure appendItem (const atext
: AnsiString; bv
: PBoolean; aaction
: TActionCB
=nil);
235 procedure drawControl (sx
, sy
: Integer); override;
237 function mouseEvent (var ev
: THMouseEvent
): Boolean; override;
238 function keyEvent (var ev
: THKeyEvent
): Boolean; override;
242 function uiMouseEvent (ev
: THMouseEvent
): Boolean;
243 function uiKeyEvent (ev
: THKeyEvent
): Boolean;
246 procedure uiAddWindow (ctl
: THControl
);
247 procedure uiRemoveWindow (ctl
: THControl
);
248 function uiVisibleWindow (ctl
: THControl
): Boolean;
252 gh_ui_scale
: Single = 1.0;
258 // ////////////////////////////////////////////////////////////////////////// //
260 uiTopList
: array of THControl
= nil;
263 function uiMouseEvent (ev
: THMouseEvent
): Boolean;
269 ev
.x
:= trunc(ev
.x
/gh_ui_scale
);
270 ev
.y
:= trunc(ev
.y
/gh_ui_scale
);
271 ev
.dx
:= trunc(ev
.dx
/gh_ui_scale
); //FIXME
272 ev
.dy
:= trunc(ev
.dy
/gh_ui_scale
); //FIXME
273 if (Length(uiTopList
) = 0) then result
:= false else result
:= uiTopList
[High(uiTopList
)].mouseEvent(ev
);
274 if not result
and (ev
.press
) then
276 for f
:= High(uiTopList
) downto 0 do
280 if uiTopList
[f
].toLocal(lx
, ly
) then
283 if uiTopList
[f
].mEnabled
and (f
<> High(uiTopList
)) then
285 uiTopList
[High(uiTopList
)].blurred();
286 ctmp
:= uiTopList
[f
];
288 for c
:= f
+1 to High(uiTopList
) do uiTopList
[c
-1] := uiTopList
[c
];
289 uiTopList
[High(uiTopList
)] := ctmp
;
291 result
:= ctmp
.mouseEvent(ev
);
300 function uiKeyEvent (ev
: THKeyEvent
): Boolean;
302 ev
.x
:= trunc(ev
.x
/gh_ui_scale
);
303 ev
.y
:= trunc(ev
.y
/gh_ui_scale
);
304 if (Length(uiTopList
) = 0) then result
:= false else result
:= uiTopList
[High(uiTopList
)].keyEvent(ev
);
305 if (ev
.release
) then begin result
:= true; exit
; end;
314 glMatrixMode(GL_MODELVIEW
);
318 glScalef(gh_ui_scale
, gh_ui_scale
, 1);
319 for f
:= 0 to High(uiTopList
) do
323 if (f
<> High(uiTopList
)) then darkenRect(ctl
.x0
, ctl
.y0
, ctl
.width
, ctl
.height
, 128);
326 glMatrixMode(GL_MODELVIEW
);
332 procedure uiAddWindow (ctl
: THControl
);
336 if (ctl
= nil) then exit
;
338 for f
:= 0 to High(uiTopList
) do
340 if (uiTopList
[f
] = ctl
) then
342 if (f
<> High(uiTopList
)) then
344 uiTopList
[High(uiTopList
)].blurred();
345 for c
:= f
+1 to High(uiTopList
) do uiTopList
[c
-1] := uiTopList
[c
];
346 uiTopList
[High(uiTopList
)] := ctl
;
352 if (Length(uiTopList
) > 0) then uiTopList
[High(uiTopList
)].blurred();
353 SetLength(uiTopList
, Length(uiTopList
)+1);
354 uiTopList
[High(uiTopList
)] := ctl
;
360 procedure uiRemoveWindow (ctl
: THControl
);
364 if (ctl
= nil) then exit
;
366 for f
:= 0 to High(uiTopList
) do
368 if (uiTopList
[f
] = ctl
) then
371 for c
:= f
+1 to High(uiTopList
) do uiTopList
[c
-1] := uiTopList
[c
];
372 SetLength(uiTopList
, Length(uiTopList
)-1);
373 if (ctl
is THTopWindow
) then
375 if assigned(THTopWindow(ctl
).closeCB
) then THTopWindow(ctl
).closeCB(ctl
, 0);
383 function uiVisibleWindow (ctl
: THControl
): Boolean;
388 if (ctl
= nil) then exit
;
390 for f
:= 0 to High(uiTopList
) do
392 if (uiTopList
[f
] = ctl
) then begin result
:= true; exit
; end;
397 // ////////////////////////////////////////////////////////////////////////// //
398 constructor THControl
.Create (ax
, ay
, aw
, ah
: Integer; aparent
: THControl
=nil);
415 mDrawShadow
:= false;
417 // layouter interface
418 mSize
:= TLaySize
.Create(64, 10); // default size
419 mMaxSize
:= TLaySize
.Create(-1, -1); // maximum size
420 mActSize
:= TLaySize
.Create(0, 0); // actual (calculated) size
421 mActPos
:= TLayPos
.Create(0, 0); // actual (calculated) position
431 destructor THControl
.Destroy ();
435 if (mParent
<> nil) then
438 for f
:= 0 to High(mParent
.mChildren
) do
440 if (mParent
.mChildren
[f
] = self
) then
442 for c
:= f
+1 to High(mParent
.mChildren
) do mParent
.mChildren
[c
-1] := mParent
.mChildren
[c
];
443 SetLength(mParent
.mChildren
, Length(mParent
.mChildren
)-1);
447 for f
:= 0 to High(mChildren
) do
449 mChildren
[f
].mParent
:= nil;
456 function THControl
.getSize (): TLaySize
; inline; begin result
:= mSize
; end;
457 procedure THControl
.setSize (constref sz
: TLaySize
); inline; begin mSize
:= sz
; end;
458 function THControl
.getMaxSize (): TLaySize
; inline; begin result
:= mMaxSize
; end;
459 procedure THControl
.setMaxSize (constref sz
: TLaySize
); inline; begin mMaxSize
:= sz
; end;
460 function THControl
.getFlex (): Integer; inline; begin result
:= mFlex
; end;
461 function THControl
.isHorizBox (): Boolean; inline; begin result
:= mHoriz
; end;
462 procedure THControl
.setHorizBox (v
: Boolean); inline; begin mHoriz
:= v
; end;
463 function THControl
.canWrap (): Boolean; inline; begin result
:= mCanWrap
; end;
464 procedure THControl
.setCanWrap (v
: Boolean); inline; begin mCanWrap
:= v
; end;
465 function THControl
.isLineStart (): Boolean; inline; begin result
:= mLineStart
; end;
466 procedure THControl
.setLineStart (v
: Boolean); inline; begin mLineStart
:= v
; end;
467 procedure THControl
.setActualSizePos (constref apos
: TLayPos
; constref asize
: TLaySize
); inline; begin mActPos
:= apos
; mActSize
:= asize
; end;
468 function THControl
.getHGroup (): AnsiString; inline; begin result
:= mHGroup
; end;
469 procedure THControl
.setHGroup (const v
: AnsiString); inline; begin mHGroup
:= v
; end;
470 function THControl
.getVGroup (): AnsiString; inline; begin result
:= mVGroup
; end;
471 procedure THControl
.setVGroup (const v
: AnsiString); inline; begin mVGroup
:= v
; end;
472 function THControl
.hasSibling (): Boolean; inline; begin result
:= (nextSibling
<> nil) end;
473 //function THControl.nextSibling (): THControl; inline; begin result := nextSibling; end;
474 function THControl
.hasChildren (): Boolean; inline; begin result
:= (firstChild
<> nil); end;
475 //function THControl.firstChild (): THControl; inline; begin result := firstChild; end;
478 procedure THControl
.activated ();
483 procedure THControl
.blurred ();
489 function THControl
.topLevel (): THControl
; inline;
492 while (result
.mParent
<> nil) do result
:= result
.mParent
;
496 function THControl
.getEnabled (): Boolean;
501 if (not mEnabled
) or (mWidth
< 1) or (mHeight
< 1) then exit
;
503 while (ctl
<> nil) do
505 if (not ctl
.mEnabled
) or (ctl
.mWidth
< 1) or (ctl
.mHeight
< 1) then exit
;
512 procedure THControl
.setEnabled (v
: Boolean); inline;
514 if (mEnabled
= v
) then exit
;
516 if not v
and focused
then setFocused(false);
520 function THControl
.getFocused (): Boolean; inline;
522 if (mParent
= nil) then result
:= (Length(uiTopList
) > 0) and (uiTopList
[High(uiTopList
)] = self
) else result
:= (topLevel
.mFocused
= self
);
526 procedure THControl
.setFocused (v
: Boolean); inline;
533 if (tl
.mFocused
= self
) then
536 tl
.mFocused
:= tl
.findNextFocus(self
);
537 if (tl
.mFocused
= self
) then tl
.mFocused
:= nil;
541 if (not mEnabled
) or (not mCanFocus
) then exit
;
542 if (tl
.mFocused
<> self
) then
544 tl
.mFocused
.blurred();
546 if (tl
.mGrab
<> self
) then tl
.mGrab
:= nil;
552 function THControl
.isMyChild (ctl
: THControl
): Boolean;
555 while (ctl
<> nil) do
557 if (ctl
.mParent
= self
) then exit
;
564 // returns `true` if global coords are inside this control
565 function THControl
.toLocal (var x
, y
: Integer): Boolean;
570 while (ctl
<> nil) do
576 result
:= (x
>= 0) and (y
>= 0) and (x
< mWidth
) and (y
< mHeight
);
580 procedure THControl
.toGlobal (var x
, y
: Integer);
585 while (ctl
<> nil) do
594 // x and y are global coords
595 function THControl
.controlAtXY (x
, y
: Integer): THControl
;
601 if (not mEnabled
) or (mWidth
< 1) or (mHeight
< 1) then exit
;
604 if not toLocal(lx
, ly
) then exit
;
605 for f
:= High(mChildren
) downto 0 do
607 result
:= mChildren
[f
].controlAtXY(x
, y
);
608 if (result
<> nil) then exit
;
614 function THControl
.prevSibling (): THControl
;
618 if (mParent
<> nil) then
620 for f
:= 1 to High(mParent
.mChildren
) do
622 if (mParent
.mChildren
[f
] = self
) then begin result
:= mParent
.mChildren
[f
-1]; exit
; end;
628 function THControl
.nextSibling (): THControl
;
632 if (mParent
<> nil) then
634 for f
:= 0 to High(mParent
.mChildren
)-1 do
636 if (mParent
.mChildren
[f
] = self
) then begin result
:= mParent
.mChildren
[f
+1]; exit
; end;
642 function THControl
.firstChild (): THControl
; inline;
644 if (Length(mChildren
) <> 0) then result
:= mChildren
[0] else result
:= nil;
647 function THControl
.lastChild (): THControl
; inline;
649 if (Length(mChildren
) <> 0) then result
:= mChildren
[High(mChildren
)] else result
:= nil;
653 function THControl
.findFirstFocus (): THControl
;
660 for f
:= 0 to High(mChildren
) do
662 result
:= mChildren
[f
].findFirstFocus();
663 if (result
<> nil) then exit
;
665 if mCanFocus
then result
:= self
;
670 function THControl
.findLastFocus (): THControl
;
677 for f
:= High(mChildren
) downto 0 do
679 result
:= mChildren
[f
].findLastFocus();
680 if (result
<> nil) then exit
;
682 if mCanFocus
then result
:= self
;
687 function THControl
.findNextFocus (cur
: THControl
): THControl
;
692 if not isMyChild(cur
) then cur
:= nil;
693 if (cur
= nil) then begin result
:= findFirstFocus(); exit
; end;
694 result
:= cur
.findFirstFocus();
695 if (result
<> nil) and (result
<> cur
) then exit
;
698 cur
:= cur
.nextSibling
;
699 if (cur
= nil) then break
;
700 result
:= cur
.findFirstFocus();
701 if (result
<> nil) then exit
;
703 result
:= findFirstFocus();
708 function THControl
.findPrevFocus (cur
: THControl
): THControl
;
713 if not isMyChild(cur
) then cur
:= nil;
714 if (cur
= nil) then begin result
:= findLastFocus(); exit
; end;
716 result
:= cur
.findLastFocus();
717 if (result
<> nil) and (result
<> cur
) then exit
;
720 cur
:= cur
.prevSibling
;
721 if (cur
= nil) then break
;
722 result
:= cur
.findLastFocus();
723 if (result
<> nil) then exit
;
725 result
:= findLastFocus();
730 procedure THControl
.appendChild (ctl
: THControl
);
732 if (ctl
= nil) then exit
;
733 if (ctl
.mParent
<> nil) then exit
;
734 SetLength(mChildren
, Length(mChildren
)+1);
735 mChildren
[High(mChildren
)] := ctl
;
737 Inc(ctl
.mX
, mFrameWidth
);
738 Inc(ctl
.mY
, mFrameHeight
);
739 if (ctl
.mWidth
> 0) and (ctl
.mHeight
> 0) and
740 (ctl
.mX
+ctl
.mWidth
> mFrameWidth
) and (ctl
.mY
+ctl
.mHeight
> mFrameHeight
) then
742 if (mWidth
+mFrameWidth
< ctl
.mX
+ctl
.mWidth
) then mWidth
:= ctl
.mX
+ctl
.mWidth
+mFrameWidth
;
743 if (mHeight
+mFrameHeight
< ctl
.mY
+ctl
.mHeight
) then mHeight
:= ctl
.mY
+ctl
.mHeight
+mFrameHeight
;
745 if (mFocused
= nil) and ctl
.mEnabled
and ctl
.mCanFocus
and (ctl
.mWidth
> 0) and (ctl
.mHeight
> 0) then mFocused
:= ctl
;
749 procedure THControl
.setScissorGLInternal (x
, y
, w
, h
: Integer);
751 if not scallowed
then exit
;
752 x
:= trunc(x
*gh_ui_scale
);
753 y
:= trunc(y
*gh_ui_scale
);
754 w
:= trunc(w
*gh_ui_scale
);
755 h
:= trunc(h
*gh_ui_scale
);
756 //y := gWinSizeY-(y+h);
757 scis
.setRect(x
, y
, w
, h
);
761 procedure THControl
.resetScissor ();
765 if not scallowed
then exit
;
769 setScissorGLInternal(x
, y
, mWidth
, mHeight
);
773 procedure THControl
.setScissor (lx
, ly
, lw
, lh
: Integer);
777 if not scallowed
then exit
;
778 if not intersectRect(lx
, ly
, lw
, lh
, 0, 0, mWidth
, mHeight
) then begin glScissor(0, 0, 0, 0); exit
; end;
782 setScissorGLInternal(x
, y
, lw
, lh
);
786 procedure THControl
.draw ();
791 if (mWidth
< 1) or (mHeight
< 1) then exit
;
795 //conwritefln('[%s]: (%d,%d)-(%d,%d) (%d,%d)', [ClassName, mX, mY, mWidth, mHeight, x, y]);
797 scis
.save(true); // scissoring enabled
799 //glEnable(GL_SCISSOR_TEST);
803 if (mFrameWidth
<> 0) or (mFrameHeight
<> 0) then setScissor(mFrameWidth
, mFrameHeight
, mWidth
-mFrameWidth
*2, mHeight
-mFrameHeight
*2);
804 for f
:= 0 to High(mChildren
) do mChildren
[f
].draw();
805 if (mFrameWidth
<> 0) or (mFrameHeight
<> 0) then resetScissor();
806 drawControlPost(x
, y
);
814 procedure THControl
.drawControl (sx
, sy
: Integer);
816 if (mParent
= nil) then darkenRect(sx
, sy
, mWidth
, mHeight
, 64);
820 procedure THControl
.drawControlPost (sx
, sy
: Integer);
822 if mDrawShadow
and (mWidth
> 0) and (mHeight
> 0) then
824 setScissorGLInternal(sx
+8, sy
+8, mWidth
, mHeight
);
825 darkenRect(sx
+mWidth
, sy
+8, 8, mHeight
, 128);
826 darkenRect(sx
+8, sy
+mHeight
, mWidth
-8, 8, 128);
831 function THControl
.mouseEvent (var ev
: THMouseEvent
): Boolean;
836 if not mEnabled
then exit
;
837 if (mParent
= nil) then
839 if (mGrab
<> nil) then
841 result
:= mGrab
.mouseEvent(ev
);
842 if (ev
.release
) then mGrab
:= nil;
846 if (mWidth
< 1) or (mHeight
< 1) then exit
;
847 ctl
:= controlAtXY(ev
.x
, ev
.y
);
848 if (ctl
<> nil) and (ctl
<> self
) then
850 if (ctl
<> topLevel
.mFocused
) then ctl
.setFocused(true);
851 result
:= ctl
.mouseEvent(ev
);
853 else if (ctl
= self
) and assigned(actionCB
) then
860 function THControl
.keyEvent (var ev
: THKeyEvent
): Boolean;
865 if not mEnabled
then exit
;
866 if (topLevel
.mFocused
<> self
) and isMyChild(topLevel
.mFocused
) and topLevel
.mFocused
.mEnabled
then result
:= topLevel
.mFocused
.keyEvent(ev
);
867 if (mParent
= nil) then
869 if (ev
= 'S-Tab') then
872 ctl
:= findPrevFocus(mFocused
);
873 if (ctl
<> mFocused
) then
883 ctl
:= findNextFocus(mFocused
);
884 if (ctl
<> mFocused
) then
891 if mEscClose
and (ev
= 'Escape') then
894 uiRemoveWindow(self
);
898 if mEatKeys
then result
:= true;
902 // ////////////////////////////////////////////////////////////////////////// //
903 constructor THTopWindow
.Create (const atitle
: AnsiString; ax
, ay
: Integer; aw
: Integer=-1; ah
: Integer=-1);
905 inherited Create(ax
, ay
, aw
, ah
, nil);
909 if (mWidth
< mFrameWidth
*2+3*8) then mWidth
:= mFrameWidth
*2+3*8;
910 if (mHeight
< mFrameHeight
*2) then mHeight
:= mFrameHeight
*2;
911 if (Length(mTitle
) > 0) then
913 if (mWidth
< Length(mTitle
)*8+mFrameWidth
*2+3*8) then mWidth
:= Length(mTitle
)*8+mFrameWidth
*2+3*8;
917 mWaitingClose
:= false;
923 procedure THTopWindow
.centerInScreen ();
925 if (mWidth
> 0) and (mHeight
> 0) then
927 mX
:= trunc((gScrWidth
/gh_ui_scale
-mWidth
)/2);
928 mY
:= trunc((gScrHeight
/gh_ui_scale
-mHeight
)/2);
933 procedure THTopWindow
.drawControl (sx
, sy
: Integer);
935 fillRect(sx
, sy
, mWidth
, mHeight
, 0, 0, 128);
939 procedure THTopWindow
.drawControlPost (sx
, sy
: Integer);
948 drawRectUI(mX
+4, mY
+4, mWidth
-8, mHeight
-8, r
, g
, b
);
952 drawRectUI(mX
+3, mY
+3, mWidth
-6, mHeight
-6, r
, g
, b
);
953 drawRectUI(mX
+5, mY
+5, mWidth
-10, mHeight
-10, r
, g
, b
);
954 setScissor(mFrameWidth
, 0, 3*8, 8);
955 fillRect(mX
+mFrameWidth
, mY
, 3*8, 8, 0, 0, 128);
956 drawText8(mX
+mFrameWidth
, mY
, '[ ]', r
, g
, b
);
957 if mInClose
then drawText8(mX
+mFrameWidth
+7, mY
, '#', 0, 255, 0)
958 else drawText8(mX
+mFrameWidth
+7, mY
, '*', 0, 255, 0);
960 if (Length(mTitle
) > 0) then
962 setScissor(mFrameWidth
+3*8, 0, mWidth
-mFrameWidth
*2-3*8, 8);
963 tx
:= (mX
+3*8)+((mWidth
-3*8)-Length(mTitle
)*8) div 2;
964 fillRect(tx
-3, mY
, Length(mTitle
)*8+3+2, 8, 0, 0, 128);
965 drawText8(tx
, mY
, mTitle
, r
, g
, b
);
967 inherited drawControlPost(sx
, sy
);
971 procedure THTopWindow
.blurred ();
974 mWaitingClose
:= false;
980 function THTopWindow
.keyEvent (var ev
: THKeyEvent
): Boolean;
982 result
:= inherited keyEvent(ev
);
983 if not getFocused
then exit
;
984 if (ev
= 'M-F3') then
986 uiRemoveWindow(self
);
993 function THTopWindow
.mouseEvent (var ev
: THMouseEvent
): Boolean;
998 if not mEnabled
then exit
;
999 if (mWidth
< 1) or (mHeight
< 1) then exit
;
1003 mX
+= ev
.x
-mDragStartX
;
1004 mY
+= ev
.y
-mDragStartY
;
1005 mDragStartX
:= ev
.x
;
1006 mDragStartY
:= ev
.y
;
1007 if (ev
.release
) then mDragging
:= false;
1014 if toLocal(lx
, ly
) then
1020 if (lx
>= mFrameWidth
) and (lx
< mFrameWidth
+3*8) then
1022 //uiRemoveWindow(self);
1023 mWaitingClose
:= true;
1029 mDragStartX
:= ev
.x
;
1030 mDragStartY
:= ev
.y
;
1035 if (lx
< mFrameWidth
) or (lx
>= mWidth
-mFrameWidth
) or (ly
>= mHeight
-mFrameHeight
) then
1038 mDragStartX
:= ev
.x
;
1039 mDragStartY
:= ev
.y
;
1045 if (ev
.release
) then
1047 if mWaitingClose
and (lx
>= mFrameWidth
) and (lx
< mFrameWidth
+3*8) then
1049 uiRemoveWindow(self
);
1053 mWaitingClose
:= false;
1059 if mWaitingClose
then
1061 mInClose
:= (lx
>= mFrameWidth
) and (lx
< mFrameWidth
+3*8);
1070 if (not ev
.motion
) then mWaitingClose
:= false;
1073 result
:= inherited mouseEvent(ev
);
1077 // ////////////////////////////////////////////////////////////////////////// //
1078 constructor THCtlSimpleText
.Create (ax
, ay
: Integer; aparent
: THControl
=nil);
1081 inherited Create(ax
, ay
, 4, 4);
1085 destructor THCtlSimpleText
.Destroy ();
1092 procedure THCtlSimpleText
.appendItem (const atext
: AnsiString; acentered
: Boolean=false; ahline
: Boolean=false);
1096 if (Length(atext
)*8+3*8+2 > mWidth
) then mWidth
:= Length(atext
)*8+3*8+2;
1097 SetLength(mItems
, Length(mItems
)+1);
1098 it
:= @mItems
[High(mItems
)];
1100 it
.centered
:= acentered
;
1102 if (Length(mItems
)*8 > mHeight
) then mHeight
:= Length(mItems
)*8;
1106 procedure THCtlSimpleText
.drawControl (sx
, sy
: Integer);
1112 for f
:= 0 to High(mItems
) do
1119 if it
.centered
then begin b
:= 255; tx
:= sx
+(mWidth
-Length(it
.title
)*8) div 2; end;
1123 if (Length(it
.title
) = 0) then
1125 drawHLine(sx
+4, sy
+3, mWidth
-8, r
, g
, b
);
1127 else if (tx
-3 > sx
+4) then
1129 drawHLine(sx
+4, sy
+3, tx
-3-(sx
+3), r
, g
, b
);
1130 drawHLine(tx
+Length(it
.title
)*8, sy
+3, mWidth
-4, r
, g
, b
);
1133 drawText8(tx
, sy
, it
.title
, r
, g
, b
);
1139 function THCtlSimpleText
.mouseEvent (var ev
: THMouseEvent
): Boolean;
1143 result
:= inherited mouseEvent(ev
);
1146 if not result
and toLocal(lx
, ly
) then
1153 function THCtlSimpleText
.keyEvent (var ev
: THKeyEvent
): Boolean;
1155 result
:= inherited keyEvent(ev
);
1159 // ////////////////////////////////////////////////////////////////////////// //
1160 constructor THCtlCBListBox
.Create (ax
, ay
: Integer; aparent
: THControl
=nil);
1164 inherited Create(ax
, ay
, 4, 4);
1168 destructor THCtlCBListBox
.Destroy ();
1175 procedure THCtlCBListBox
.appendItem (const atext
: AnsiString; bv
: PBoolean; aaction
: TActionCB
=nil);
1179 if (Length(atext
)*8+3*8+2 > mWidth
) then mWidth
:= Length(atext
)*8+3*8+2;
1180 SetLength(mItems
, Length(mItems
)+1);
1181 it
:= @mItems
[High(mItems
)];
1184 it
.actionCB
:= aaction
;
1185 if (Length(mItems
)*8 > mHeight
) then mHeight
:= Length(mItems
)*8;
1186 if (mCurIndex
< 0) then mCurIndex
:= 0;
1190 procedure THCtlCBListBox
.drawControl (sx
, sy
: Integer);
1195 for f
:= 0 to High(mItems
) do
1198 if (mCurIndex
= f
) then fillRect(sx
, sy
, mWidth
, 8, 0, 128, 0);
1199 if (it
.varp
<> nil) then
1201 if it
.varp
^ then drawText8(sx
, sy
, '[x]', 255, 255, 255) else drawText8(sx
, sy
, '[ ]', 255, 255, 255);
1202 drawText8(sx
+3*8+2, sy
, it
.title
, 255, 255, 0);
1204 else if (Length(it
.title
) > 0) then
1206 tx
:= sx
+(mWidth
-Length(it
.title
)*8) div 2;
1207 if (tx
-3 > sx
+4) then
1209 drawHLine(sx
+4, sy
+3, tx
-3-(sx
+3), 255, 255, 255);
1210 drawHLine(tx
+Length(it
.title
)*8, sy
+3, mWidth
-4, 255, 255, 255);
1212 drawText8(tx
, sy
, it
.title
, 255, 255, 255);
1216 drawHLine(sx
+4, sy
+3, mWidth
-8, 255, 255, 255);
1223 function THCtlCBListBox
.mouseEvent (var ev
: THMouseEvent
): Boolean;
1228 result
:= inherited mouseEvent(ev
);
1231 if not result
and toLocal(lx
, ly
) then
1234 if (ev
= 'lmb') then
1237 if (ly
>= 0) and (ly
< Length(mItems
)) then
1240 if (it
.varp
<> nil) then
1243 it
.varp
^ := not it
.varp
^;
1244 if assigned(it
.actionCB
) then it
.actionCB(self
, Integer(it
.varp
^));
1245 if assigned(actionCB
) then actionCB(self
, ly
);
1253 function THCtlCBListBox
.keyEvent (var ev
: THKeyEvent
): Boolean;
1257 result
:= inherited keyEvent(ev
);
1258 if not getFocused
then exit
;
1260 if (ev
= 'Home') or (ev
= 'PageUp') then
1265 if (ev
= 'End') or (ev
= 'PageDown') then
1268 mCurIndex
:= High(mItems
);
1273 if (Length(mItems
) > 0) then
1275 if (mCurIndex
< 0) then mCurIndex
:= Length(mItems
);
1276 while (mCurIndex
> 0) do
1279 if (mItems
[mCurIndex
].varp
<> nil) then break
;
1287 if (ev
= 'Down') then
1290 if (Length(mItems
) > 0) then
1292 if (mCurIndex
< 0) then mCurIndex
:= -1;
1293 while (mCurIndex
< High(mItems
)) do
1296 if (mItems
[mCurIndex
].varp
<> nil) then break
;
1304 if (ev
= 'Space') or (ev
= 'Enter') then
1307 if (mCurIndex
>= 0) and (mCurIndex
< Length(mItems
)) and (mItems
[mCurIndex
].varp
<> nil) then
1309 it
:= @mItems
[mCurIndex
];
1310 it
.varp
^ := not it
.varp
^;
1311 if assigned(it
.actionCB
) then it
.actionCB(self
, Integer(it
.varp
^));
1312 if assigned(actionCB
) then actionCB(self
, mCurIndex
);