1 (* coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
2 * Understanding is not required. Only obedience.
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
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 {$INCLUDE ../shared/a_modes.inc}
27 fui_common
, fui_events
, fui_style
,
32 // ////////////////////////////////////////////////////////////////////////// //
34 TUIControlClass
= class of TUIControl
;
38 type TActionCB
= procedure (me
: TUIControl
);
39 type TCloseRequestCB
= function (me
: TUIControl
): Boolean; // top-level windows will call this before closing with icon/keyboard
41 // return `true` to stop
42 type TCtlEnumCB
= function (ctl
: TUIControl
): Boolean is nested
;
45 const ClrIdxActive
= 0;
46 const ClrIdxDisabled
= 1;
47 const ClrIdxInactive
= 2;
55 mWidth
, mHeight
: Integer;
56 mFrameWidth
, mFrameHeight
: Integer;
57 mScrollX
, mScrollY
: Integer;
60 mChildren
: array of TUIControl
;
61 mFocused
: TUIControl
; // valid only for top-level controls
62 mEscClose
: Boolean; // valid only for top-level controls
67 mCtl4Style
: AnsiString;
68 mBackColor
: array[0..ClrIdxMax
] of TGxRGBA
;
69 mTextColor
: array[0..ClrIdxMax
] of TGxRGBA
;
70 mFrameColor
: array[0..ClrIdxMax
] of TGxRGBA
;
71 mFrameTextColor
: array[0..ClrIdxMax
] of TGxRGBA
;
72 mFrameIconColor
: array[0..ClrIdxMax
] of TGxRGBA
;
73 mDarken
: array[0..ClrIdxMax
] of Integer; // >255: none
76 procedure updateStyle (); virtual;
77 procedure cacheStyle (root
: TUIStyle
); virtual;
78 function getColorIndex (): Integer; inline;
81 function getEnabled (): Boolean;
82 procedure setEnabled (v
: Boolean); inline;
84 function getFocused (): Boolean; inline;
85 procedure setFocused (v
: Boolean); inline;
87 function getActive (): Boolean; inline;
89 function getCanFocus (): Boolean; inline;
91 function isMyChild (ctl
: TUIControl
): Boolean;
93 function findFirstFocus (): TUIControl
;
94 function findLastFocus (): TUIControl
;
96 function findNextFocus (cur
: TUIControl
; wrap
: Boolean): TUIControl
;
97 function findPrevFocus (cur
: TUIControl
; wrap
: Boolean): TUIControl
;
99 function findCancelControl (): TUIControl
;
100 function findDefaulControl (): TUIControl
;
102 function findControlById (const aid
: AnsiString): TUIControl
;
104 procedure activated (); virtual;
105 procedure blurred (); virtual;
107 procedure calcFullClientSize ();
109 procedure drawFrame (gx
, gy
, resx
, thalign
: Integer; const text: AnsiString; dbl
: Boolean);
112 var savedClip
: TGxRect
; // valid only in `draw*()` calls
113 //WARNING! do not call scissor functions outside `.draw*()` API!
114 // set scissor to this rect (in local coords)
115 procedure setScissor (lx
, ly
, lw
, lh
: Integer); // valid only in `draw*()` calls
119 closeRequestCB
: TCloseRequestCB
;
122 mDefSize
: TLaySize
; // default size
123 mMaxSize
: TLaySize
; // maximum size
130 mLayDefSize
: TLaySize
;
131 mLayMaxSize
: TLaySize
;
137 // layouter interface
138 function getDefSize (): TLaySize
; inline; // default size; <0: use max size
139 //procedure setDefSize (const sz: TLaySize); inline; // default size; <0: use max size
140 function getMargins (): TLayMargins
; inline;
141 function getPadding (): TLaySize
; inline; // children padding (each non-first child will get this on left/top)
142 function getMaxSize (): TLaySize
; inline; // max size; <0: set to some huge value
143 //procedure setMaxSize (const sz: TLaySize); inline; // max size; <0: set to some huge value
144 function getFlex (): Integer; inline; // <=0: not flexible
145 function isHorizBox (): Boolean; inline; // horizontal layout for children?
146 function noPad (): Boolean; inline; // ignore padding in box direction for this control
147 function getAlign (): Integer; inline; // aligning in non-main direction: <0: left/up; 0: center; >0: right/down
148 function getExpand (): Boolean; inline; // expanding in non-main direction: `true` will ignore align and eat all available space
149 function getHGroup (): AnsiString; inline; // empty: not grouped
150 function getVGroup (): AnsiString; inline; // empty: not grouped
152 procedure setActualSizePos (constref apos
: TLayPos
; constref asize
: TLaySize
); inline;
154 procedure layPrepare (); virtual; // called before registering control in layouter
157 property flex
: Integer read mFlex write mFlex
;
158 property flDefaultSize
: TLaySize read mDefSize write mDefSize
;
159 property flMaxSize
: TLaySize read mMaxSize write mMaxSize
;
160 property flPadding
: TLaySize read mPadding write mPadding
;
161 property flHoriz
: Boolean read mHoriz write mHoriz
;
162 property flAlign
: Integer read mAlign write mAlign
;
163 property flExpand
: Boolean read mExpand write mExpand
;
164 property flHGroup
: AnsiString read mHGroup write mHGroup
;
165 property flVGroup
: AnsiString read mVGroup write mVGroup
;
166 property flNoPad
: Boolean read mNoPad write mNoPad
;
167 property fullSize
: TLaySize read mFullSize
;
170 function parsePos (par
: TTextParser
): TLayPos
;
171 function parseSize (par
: TTextParser
): TLaySize
;
172 function parsePadding (par
: TTextParser
): TLaySize
;
173 function parseHPadding (par
: TTextParser
; def
: Integer): TLaySize
;
174 function parseVPadding (par
: TTextParser
; def
: Integer): TLaySize
;
175 function parseBool (par
: TTextParser
): Boolean;
176 function parseAnyAlign (par
: TTextParser
): Integer;
177 function parseHAlign (par
: TTextParser
): Integer;
178 function parseVAlign (par
: TTextParser
): Integer;
179 function parseOrientation (const prname
: AnsiString; par
: TTextParser
): Boolean;
180 procedure parseTextAlign (par
: TTextParser
; var h
, v
: Integer);
181 procedure parseChildren (par
: TTextParser
); // par should be on '{'; final '}' is eaten
184 // par is on property data
185 // there may be more data in text stream, don't eat it!
186 // return `true` if property name is valid and value was parsed
187 // return `false` if property name is invalid; don't advance parser in this case
188 // throw on property data errors
189 function parseProperty (const prname
: AnsiString; par
: TTextParser
): Boolean; virtual;
191 // par should be on '{'; final '}' is eaten
192 procedure parseProperties (par
: TTextParser
);
195 constructor Create ();
196 destructor Destroy (); override;
198 procedure AfterConstruction (); override; // so it will be correctly initialized when created from parser
200 // `sx` and `sy` are screen coordinates
201 procedure drawControl (gx
, gy
: Integer); virtual;
203 // called after all children drawn
204 procedure drawControlPost (gx
, gy
: Integer); virtual;
206 procedure draw (); virtual;
208 function topLevel (): TUIControl
; inline;
210 // returns `true` if global coords are inside this control
211 function toLocal (var x
, y
: Integer): Boolean;
212 function toLocal (gx
, gy
: Integer; out x
, y
: Integer): Boolean; inline;
213 procedure toGlobal (var x
, y
: Integer);
214 procedure toGlobal (lx
, ly
: Integer; out x
, y
: Integer); inline;
216 procedure getDrawRect (out gx
, gy
, wdt
, hgt
: Integer);
218 // x and y are global coords
219 function controlAtXY (x
, y
: Integer; allowDisabled
: Boolean=false): TUIControl
;
221 function parentScrollX (): Integer; inline;
222 function parentScrollY (): Integer; inline;
224 procedure makeVisibleInParent ();
226 procedure doAction (); virtual; // so user controls can override it
228 procedure mouseEvent (var ev
: THMouseEvent
); virtual; // returns `true` if event was eaten
229 procedure keyEvent (var ev
: THKeyEvent
); virtual; // returns `true` if event was eaten
230 procedure keyEventPre (var ev
: THKeyEvent
); virtual; // will be called before dispatching the event
231 procedure keyEventPost (var ev
: THKeyEvent
); virtual; // will be called after if nobody processed the event
233 function prevSibling (): TUIControl
;
234 function nextSibling (): TUIControl
;
235 function firstChild (): TUIControl
; inline;
236 function lastChild (): TUIControl
; inline;
238 procedure appendChild (ctl
: TUIControl
); virtual;
240 function setActionCBFor (const aid
: AnsiString; cb
: TActionCB
): TActionCB
; // returns previous cb
242 function forEachChildren (cb
: TCtlEnumCB
): TUIControl
; // doesn't recurse
243 function forEachControl (cb
: TCtlEnumCB
; includeSelf
: Boolean=true): TUIControl
;
245 procedure close (); // this closes *top-level* control
248 property id
: AnsiString read mId write mId
;
249 property styleId
: AnsiString read mStyleId
;
250 property scrollX
: Integer read mScrollX write mScrollX
;
251 property scrollY
: Integer read mScrollY write mScrollY
;
252 property x0
: Integer read mX write mX
;
253 property y0
: Integer read mY write mY
;
254 property width
: Integer read mWidth write mWidth
;
255 property height
: Integer read mHeight write mHeight
;
256 property enabled
: Boolean read getEnabled write setEnabled
;
257 property parent
: TUIControl read mParent
;
258 property focused
: Boolean read getFocused write setFocused
;
259 property active
: Boolean read getActive
;
260 property escClose
: Boolean read mEscClose write mEscClose
;
261 property cancel
: Boolean read mCancel write mCancel
;
262 property defctl
: Boolean read mDefault write mDefault
;
263 property canFocus
: Boolean read getCanFocus write mCanFocus
;
264 property ctlById
[const aid
: AnsiString]: TUIControl read findControlById
; default
;
268 TUITopWindow
= class(TUIControl
)
270 type TXMode
= (None
, Drag
, Scroll
);
275 mDragStartX
, mDragStartY
: Integer;
276 mWaitingClose
: Boolean;
278 mFreeOnClose
: Boolean; // default: false
279 mDoCenter
: Boolean; // after layouting
280 mFitToScreen
: Boolean;
283 procedure activated (); override;
284 procedure blurred (); override;
287 closeCB
: TActionCB
; // called after window was removed from ui window list
290 constructor Create (const atitle
: AnsiString);
292 procedure AfterConstruction (); override; // so it will be correctly initialized when created from parser
294 function parseProperty (const prname
: AnsiString; par
: TTextParser
): Boolean; override;
296 procedure flFitToScreen (); // call this before layouting
298 procedure centerInScreen ();
300 // `sx` and `sy` are screen coordinates
301 procedure drawControl (gx
, gy
: Integer); override;
302 procedure drawControlPost (gx
, gy
: Integer); override;
304 procedure keyEvent (var ev
: THKeyEvent
); override; // returns `true` if event was eaten
305 procedure mouseEvent (var ev
: THMouseEvent
); override; // returns `true` if event was eaten
308 property freeOnClose
: Boolean read mFreeOnClose write mFreeOnClose
;
309 property fitToScreen
: Boolean read mFitToScreen write mFitToScreen
;
312 // ////////////////////////////////////////////////////////////////////// //
313 TUIBox
= class(TUIControl
)
316 mCaption
: AnsiString;
317 mHAlign
: Integer; // -1: left; 0: center; 1: right; default: left
320 procedure setCaption (const acap
: AnsiString);
321 procedure setHasFrame (v
: Boolean);
324 constructor Create (ahoriz
: Boolean);
326 procedure AfterConstruction (); override; // so it will be correctly initialized when created from parser
328 function parseProperty (const prname
: AnsiString; par
: TTextParser
): Boolean; override;
330 procedure drawControl (gx
, gy
: Integer); override;
332 procedure mouseEvent (var ev
: THMouseEvent
); override;
333 procedure keyEvent (var ev
: THKeyEvent
); override;
336 property caption
: AnsiString read mCaption write setCaption
;
337 property hasFrame
: Boolean read mHasFrame write setHasFrame
;
338 property captionAlign
: Integer read mHAlign write mHAlign
;
341 TUIHBox
= class(TUIBox
)
343 constructor Create ();
345 procedure AfterConstruction (); override; // so it will be correctly initialized when created from parser
348 TUIVBox
= class(TUIBox
)
350 constructor Create ();
352 procedure AfterConstruction (); override; // so it will be correctly initialized when created from parser
355 // ////////////////////////////////////////////////////////////////////// //
356 TUISpan
= class(TUIControl
)
358 procedure AfterConstruction (); override; // so it will be correctly initialized when created from parser
360 function parseProperty (const prname
: AnsiString; par
: TTextParser
): Boolean; override;
362 procedure drawControl (gx
, gy
: Integer); override;
365 // ////////////////////////////////////////////////////////////////////// //
366 TUILine
= class(TUIControl
)
368 procedure AfterConstruction (); override; // so it will be correctly initialized when created from parser
370 function parseProperty (const prname
: AnsiString; par
: TTextParser
): Boolean; override;
372 procedure layPrepare (); override; // called before registering control in layouter
374 procedure drawControl (gx
, gy
: Integer); override;
377 // ////////////////////////////////////////////////////////////////////// //
378 TUIStaticText
= class(TUIControl
)
381 mHAlign
: Integer; // -1: left; 0: center; 1: right; default: left
382 mVAlign
: Integer; // -1: top; 0: center; 1: bottom; default: center
383 mHeader
: Boolean; // true: draw with frame text color
384 mLine
: Boolean; // true: draw horizontal line
387 procedure setText (const atext
: AnsiString);
390 procedure AfterConstruction (); override; // so it will be correctly initialized when created from parser
392 function parseProperty (const prname
: AnsiString; par
: TTextParser
): Boolean; override;
394 procedure drawControl (gx
, gy
: Integer); override;
397 property text: AnsiString read mText write setText
;
398 property halign
: Integer read mHAlign write mHAlign
;
399 property valign
: Integer read mVAlign write mVAlign
;
400 property header
: Boolean read mHeader write mHeader
;
401 property line
: Boolean read mLine write mLine
;
404 // ////////////////////////////////////////////////////////////////////// //
405 TUITextLabel
= class(TUIControl
)
408 mHAlign
: Integer; // -1: left; 0: center; 1: right; default: left
409 mVAlign
: Integer; // -1: top; 0: center; 1: bottom; default: center
411 mHotOfs
: Integer; // from text start, in pixels
412 mHotColor
: array[0..ClrIdxMax
] of TGxRGBA
;
413 mLinkId
: AnsiString; // linked control
416 procedure cacheStyle (root
: TUIStyle
); override;
418 procedure setText (const s
: AnsiString); virtual;
421 procedure AfterConstruction (); override; // so it will be correctly initialized when created from parser
423 function parseProperty (const prname
: AnsiString; par
: TTextParser
): Boolean; override;
425 procedure doAction (); override;
427 procedure drawControl (gx
, gy
: Integer); override;
429 procedure mouseEvent (var ev
: THMouseEvent
); override;
430 procedure keyEventPost (var ev
: THKeyEvent
); override;
433 property text: AnsiString read mText write setText
;
434 property halign
: Integer read mHAlign write mHAlign
;
435 property valign
: Integer read mVAlign write mVAlign
;
438 // ////////////////////////////////////////////////////////////////////// //
439 TUIButton
= class(TUITextLabel
)
441 procedure setText (const s
: AnsiString); override;
444 procedure AfterConstruction (); override; // so it will be correctly initialized when created from parser
446 procedure drawControl (gx
, gy
: Integer); override;
448 procedure mouseEvent (var ev
: THMouseEvent
); override;
449 procedure keyEvent (var ev
: THKeyEvent
); override;
452 // ////////////////////////////////////////////////////////////////////// //
453 TUISwitchBox
= class(TUITextLabel
)
457 mIcon
: TGxContext
.TMarkIcon
;
458 mSwitchColor
: array[0..ClrIdxMax
] of TGxRGBA
;
461 procedure cacheStyle (root
: TUIStyle
); override;
463 procedure setText (const s
: AnsiString); override;
465 function getChecked (): Boolean; virtual;
466 procedure setChecked (v
: Boolean); virtual; abstract;
469 procedure AfterConstruction (); override; // so it will be correctly initialized when created from parser
471 function parseProperty (const prname
: AnsiString; par
: TTextParser
): Boolean; override;
473 procedure drawControl (gx
, gy
: Integer); override;
475 procedure mouseEvent (var ev
: THMouseEvent
); override;
476 procedure keyEvent (var ev
: THKeyEvent
); override;
478 procedure setVar (pvar
: PBoolean);
481 property checked
: Boolean read getChecked write setChecked
;
484 TUICheckBox
= class(TUISwitchBox
)
486 procedure setChecked (v
: Boolean); override;
489 procedure AfterConstruction (); override; // so it will be correctly initialized when created from parser
491 procedure doAction (); override;
494 TUIRadioBox
= class(TUISwitchBox
)
496 mRadioGroup
: AnsiString;
499 procedure setChecked (v
: Boolean); override;
502 procedure AfterConstruction (); override; // so it will be correctly initialized when created from parser
504 function parseProperty (const prname
: AnsiString; par
: TTextParser
): Boolean; override;
506 procedure doAction (); override;
509 property radioGroup
: AnsiString read mRadioGroup write mRadioGroup
; //FIXME
513 // ////////////////////////////////////////////////////////////////////////// //
514 procedure uiMouseEvent (var evt
: THMouseEvent
);
515 procedure uiKeyEvent (var evt
: THKeyEvent
);
519 // ////////////////////////////////////////////////////////////////////////// //
520 procedure uiAddWindow (ctl
: TUIControl
);
521 procedure uiRemoveWindow (ctl
: TUIControl
); // will free window if `mFreeOnClose` is `true`
522 function uiVisibleWindow (ctl
: TUIControl
): Boolean;
524 procedure uiUpdateStyles ();
527 // ////////////////////////////////////////////////////////////////////////// //
529 procedure uiLayoutCtl (ctl
: TUIControl
);
532 // ////////////////////////////////////////////////////////////////////////// //
534 fuiRenderScale
: Single = 1.0;
535 uiContext
: TGxContext
= nil;
545 // ////////////////////////////////////////////////////////////////////////// //
547 ctlsToKill
: array of TUIControl
= nil;
550 procedure scheduleKill (ctl
: TUIControl
);
554 if (ctl
= nil) then exit
;
556 for f
:= 0 to High(ctlsToKill
) do
558 if (ctlsToKill
[f
] = ctl
) then exit
;
559 if (ctlsToKill
[f
] = nil) then begin ctlsToKill
[f
] := ctl
; exit
; end;
561 SetLength(ctlsToKill
, Length(ctlsToKill
)+1);
562 ctlsToKill
[High(ctlsToKill
)] := ctl
;
566 procedure processKills ();
571 for f
:= 0 to High(ctlsToKill
) do
573 ctl
:= ctlsToKill
[f
];
574 if (ctl
= nil) then break
;
575 ctlsToKill
[f
] := nil;
578 if (Length(ctlsToKill
) > 0) then ctlsToKill
[0] := nil; // just in case
582 // ////////////////////////////////////////////////////////////////////////// //
584 knownCtlClasses
: array of record
585 klass
: TUIControlClass
;
590 procedure registerCtlClass (aklass
: TUIControlClass
; const aname
: AnsiString);
592 assert(aklass
<> nil);
593 assert(Length(aname
) > 0);
594 SetLength(knownCtlClasses
, Length(knownCtlClasses
)+1);
595 knownCtlClasses
[High(knownCtlClasses
)].klass
:= aklass
;
596 knownCtlClasses
[High(knownCtlClasses
)].name
:= aname
;
600 function findCtlClass (const aname
: AnsiString): TUIControlClass
;
604 for f
:= 0 to High(knownCtlClasses
) do
606 if (strEquCI1251(aname
, knownCtlClasses
[f
].name
)) then
608 result
:= knownCtlClasses
[f
].klass
;
616 // ////////////////////////////////////////////////////////////////////////// //
618 TFlexLayouter
= specialize TFlexLayouterBase
<TUIControl
>;
620 procedure uiLayoutCtl (ctl
: TUIControl
);
624 if (ctl
= nil) then exit
;
625 lay
:= TFlexLayouter
.Create();
627 if (ctl
is TUITopWindow
) and (TUITopWindow(ctl
).fitToScreen
) then TUITopWindow(ctl
).flFitToScreen();
632 //writeln('============================'); lay.dumpFlat();
634 //writeln('=== initial ==='); lay.dump();
636 //lay.calcMaxSizeInternal(0);
639 writeln('=== after first pass ===');
643 writeln('=== after second pass ===');
648 //writeln('=== final ==='); lay.dump();
650 if (ctl
.mParent
= nil) and (ctl
is TUITopWindow
) and (TUITopWindow(ctl
).mDoCenter
) then
652 TUITopWindow(ctl
).centerInScreen();
655 // calculate full size
656 ctl
.calcFullClientSize();
659 if (ctl
.mParent
= nil) then
661 if (ctl
.mFocused
= nil) or (ctl
.mFocused
= ctl
) or (not ctl
.mFocused
.enabled
) then
663 ctl
.mFocused
:= ctl
.findFirstFocus();
673 // ////////////////////////////////////////////////////////////////////////// //
675 uiTopList
: array of TUIControl
= nil;
676 uiGrabCtl
: TUIControl
= nil;
679 procedure uiUpdateStyles ();
683 for ctl
in uiTopList
do ctl
.updateStyle();
687 procedure uiMouseEvent (var evt
: THMouseEvent
);
695 if (evt
.eaten
) or (evt
.cancelled
) then exit
;
697 ev
.x
:= trunc(ev
.x
/fuiRenderScale
);
698 ev
.y
:= trunc(ev
.y
/fuiRenderScale
);
699 ev
.dx
:= trunc(ev
.dx
/fuiRenderScale
); //FIXME
700 ev
.dy
:= trunc(ev
.dy
/fuiRenderScale
); //FIXME
702 if (uiGrabCtl
<> nil) then
704 uiGrabCtl
.mouseEvent(ev
);
705 if (ev
.release
) and ((ev
.bstate
and (not ev
.but
)) = 0) then uiGrabCtl
:= nil;
709 if (Length(uiTopList
) > 0) and (uiTopList
[High(uiTopList
)].enabled
) then uiTopList
[High(uiTopList
)].mouseEvent(ev
);
710 if (not ev
.eaten
) and (not ev
.cancelled
) and (ev
.press
) then
712 for f
:= High(uiTopList
) downto 0 do
714 if uiTopList
[f
].toLocal(ev
.x
, ev
.y
, lx
, ly
) then
716 if (uiTopList
[f
].enabled
) and (f
<> High(uiTopList
)) then
718 uiTopList
[High(uiTopList
)].blurred();
719 ctmp
:= uiTopList
[f
];
721 for c
:= f
+1 to High(uiTopList
) do uiTopList
[c
-1] := uiTopList
[c
];
722 uiTopList
[High(uiTopList
)] := ctmp
;
732 if (ev
.eaten
) then evt
.eat();
733 if (ev
.cancelled
) then evt
.cancel();
738 procedure uiKeyEvent (var evt
: THKeyEvent
);
743 if (evt
.eaten
) or (evt
.cancelled
) then exit
;
745 ev
.x
:= trunc(ev
.x
/fuiRenderScale
);
746 ev
.y
:= trunc(ev
.y
/fuiRenderScale
);
748 if (Length(uiTopList
) > 0) and (uiTopList
[High(uiTopList
)].enabled
) then uiTopList
[High(uiTopList
)].keyEvent(ev
);
749 //if (ev.release) then begin ev.eat(); exit; end;
751 if (ev
.eaten
) then evt
.eat();
752 if (ev
.cancelled
) then evt
.cancel();
763 //if (uiContext = nil) then uiContext := TGxContext.Create();
764 gxSetContext(uiContext
, fuiRenderScale
);
765 uiContext
.resetClip();
767 for f
:= 0 to High(uiTopList
) do
771 if (f
<> High(uiTopList
)) then
773 cidx
:= ctl
.getColorIndex
;
774 uiContext
.darkenRect(ctl
.x0
, ctl
.y0
, ctl
.width
, ctl
.height
, ctl
.mDarken
[cidx
]);
783 procedure uiAddWindow (ctl
: TUIControl
);
787 if (ctl
= nil) then exit
;
789 if not (ctl
is TUITopWindow
) then exit
; // alas
790 for f
:= 0 to High(uiTopList
) do
792 if (uiTopList
[f
] = ctl
) then
794 if (f
<> High(uiTopList
)) then
796 uiTopList
[High(uiTopList
)].blurred();
797 for c
:= f
+1 to High(uiTopList
) do uiTopList
[c
-1] := uiTopList
[c
];
798 uiTopList
[High(uiTopList
)] := ctl
;
804 if (Length(uiTopList
) > 0) then uiTopList
[High(uiTopList
)].blurred();
805 SetLength(uiTopList
, Length(uiTopList
)+1);
806 uiTopList
[High(uiTopList
)] := ctl
;
812 procedure uiRemoveWindow (ctl
: TUIControl
);
816 if (ctl
= nil) then exit
;
818 if not (ctl
is TUITopWindow
) then exit
; // alas
819 for f
:= 0 to High(uiTopList
) do
821 if (uiTopList
[f
] = ctl
) then
824 for c
:= f
+1 to High(uiTopList
) do uiTopList
[c
-1] := uiTopList
[c
];
825 SetLength(uiTopList
, Length(uiTopList
)-1);
826 if (ctl
is TUITopWindow
) then
829 if assigned(TUITopWindow(ctl
).closeCB
) then TUITopWindow(ctl
).closeCB(ctl
);
831 if (TUITopWindow(ctl
).mFreeOnClose
) then scheduleKill(ctl
);
840 function uiVisibleWindow (ctl
: TUIControl
): Boolean;
845 if (ctl
= nil) then exit
;
847 if not (ctl
is TUITopWindow
) then exit
; // alas
848 for f
:= 0 to High(uiTopList
) do
850 if (uiTopList
[f
] = ctl
) then begin result
:= true; exit
; end;
855 // ////////////////////////////////////////////////////////////////////////// //
856 constructor TUIControl
.Create ();
861 procedure TUIControl
.AfterConstruction ();
869 mHeight
:= uiContext
.charHeight(' ');
877 mDrawShadow
:= false;
879 // layouter interface
880 //mDefSize := TLaySize.Create(64, uiContext.charHeight(' ')); // default size
881 mDefSize
:= TLaySize
.Create(0, 0); // default size: hidden control
882 mMaxSize
:= TLaySize
.Create(-1, -1); // maximum size
883 mPadding
:= TLaySize
.Create(0, 0);
891 mAlign
:= -1; // left/top
896 destructor TUIControl
.Destroy ();
900 if (mParent
<> nil) then
903 for f
:= 0 to High(mParent
.mChildren
) do
905 if (mParent
.mChildren
[f
] = self
) then
907 for c
:= f
+1 to High(mParent
.mChildren
) do mParent
.mChildren
[c
-1] := mParent
.mChildren
[c
];
908 SetLength(mParent
.mChildren
, Length(mParent
.mChildren
)-1);
912 for f
:= 0 to High(mChildren
) do
914 mChildren
[f
].mParent
:= nil;
921 function TUIControl
.getColorIndex (): Integer; inline;
923 if (not enabled
) then begin result
:= ClrIdxDisabled
; exit
; end;
924 // top windows: no focus hack
925 if (self
is TUITopWindow
) then
927 if (getActive
) then begin result
:= ClrIdxActive
; exit
; end;
931 // if control cannot be focused, take "active" color scheme for it (it is easier this way)
932 if (not canFocus
) or (getActive
) then begin result
:= ClrIdxActive
; exit
; end;
934 result
:= ClrIdxInactive
;
937 procedure TUIControl
.updateStyle ();
943 while (ctl
<> nil) do
945 if (Length(ctl
.mStyleId
) <> 0) then begin stl
:= uiFindStyle(ctl
.mStyleId
); break
; end;
948 if (stl
= nil) then stl
:= uiFindStyle(''); // default
950 for ctl
in mChildren
do ctl
.updateStyle();
953 procedure TUIControl
.cacheStyle (root
: TUIStyle
);
957 //writeln('caching style for <', className, '> (', mCtl4Style, ')...');
960 mBackColor
[ClrIdxActive
] := root
.get('back-color', 'active', cst
).asRGBADef(TGxRGBA
.Create(0, 0, 128));
961 mTextColor
[ClrIdxActive
] := root
.get('text-color', 'active', cst
).asRGBADef(TGxRGBA
.Create(255, 255, 255));
962 mFrameColor
[ClrIdxActive
] := root
.get('frame-color', 'active', cst
).asRGBADef(TGxRGBA
.Create(255, 255, 255));
963 mFrameTextColor
[ClrIdxActive
] := root
.get('frame-text-color', 'active', cst
).asRGBADef(TGxRGBA
.Create(255, 255, 255));
964 mFrameIconColor
[ClrIdxActive
] := root
.get('frame-icon-color', 'active', cst
).asRGBADef(TGxRGBA
.Create(0, 255, 0));
965 mDarken
[ClrIdxActive
] := root
.get('darken', 'active', cst
).asInt(666);
967 mBackColor
[ClrIdxDisabled
] := root
.get('back-color', 'disabled', cst
).asRGBADef(TGxRGBA
.Create(0, 0, 128));
968 mTextColor
[ClrIdxDisabled
] := root
.get('text-color', 'disabled', cst
).asRGBADef(TGxRGBA
.Create(127, 127, 127));
969 mFrameColor
[ClrIdxDisabled
] := root
.get('frame-color', 'disabled', cst
).asRGBADef(TGxRGBA
.Create(127, 127, 127));
970 mFrameTextColor
[ClrIdxDisabled
] := root
.get('frame-text-color', 'disabled', cst
).asRGBADef(TGxRGBA
.Create(127, 127, 127));
971 mFrameIconColor
[ClrIdxDisabled
] := root
.get('frame-icon-color', 'disabled', cst
).asRGBADef(TGxRGBA
.Create(0, 127, 0));
972 mDarken
[ClrIdxDisabled
] := root
.get('darken', 'disabled', cst
).asInt(666);
974 mBackColor
[ClrIdxInactive
] := root
.get('back-color', 'inactive', cst
).asRGBADef(TGxRGBA
.Create(0, 0, 128));
975 mTextColor
[ClrIdxInactive
] := root
.get('text-color', 'inactive', cst
).asRGBADef(TGxRGBA
.Create(255, 255, 255));
976 mFrameColor
[ClrIdxInactive
] := root
.get('frame-color', 'inactive', cst
).asRGBADef(TGxRGBA
.Create(255, 255, 255));
977 mFrameTextColor
[ClrIdxInactive
] := root
.get('frame-text-color', 'inactive', cst
).asRGBADef(TGxRGBA
.Create(255, 255, 255));
978 mFrameIconColor
[ClrIdxInactive
] := root
.get('frame-icon-color', 'inactive', cst
).asRGBADef(TGxRGBA
.Create(0, 255, 0));
979 mDarken
[ClrIdxInactive
] := root
.get('darken', 'inactive', cst
).asInt(666);
983 // ////////////////////////////////////////////////////////////////////////// //
984 function TUIControl
.getDefSize (): TLaySize
; inline; begin result
:= mLayDefSize
; end;
985 function TUIControl
.getMaxSize (): TLaySize
; inline; begin result
:= mLayMaxSize
; end;
986 function TUIControl
.getPadding (): TLaySize
; inline; begin result
:= mPadding
; end;
987 function TUIControl
.getFlex (): Integer; inline; begin result
:= mFlex
; end;
988 function TUIControl
.isHorizBox (): Boolean; inline; begin result
:= mHoriz
; end;
989 function TUIControl
.noPad (): Boolean; inline; begin result
:= mNoPad
; end;
990 function TUIControl
.getAlign (): Integer; inline; begin result
:= mAlign
; end;
991 function TUIControl
.getExpand (): Boolean; inline; begin result
:= mExpand
; end;
992 function TUIControl
.getHGroup (): AnsiString; inline; begin result
:= mHGroup
; end;
993 function TUIControl
.getVGroup (): AnsiString; inline; begin result
:= mVGroup
; end;
994 function TUIControl
.getMargins (): TLayMargins
; inline; begin result
:= TLayMargins
.Create(mFrameHeight
, mFrameWidth
, mFrameHeight
, mFrameWidth
); end;
996 procedure TUIControl
.setActualSizePos (constref apos
: TLayPos
; constref asize
: TLaySize
); inline;
998 //writeln(self.className, '; pos=', apos.toString, '; size=', asize.toString);
999 if (mParent
<> nil) then
1006 if (mLayMaxSize
.w
>= 0) then mWidth
:= nmin(mWidth
, mLayMaxSize
.w
);
1007 if (mLayMaxSize
.h
>= 0) then mHeight
:= nmin(mHeight
, mLayMaxSize
.h
);
1010 procedure TUIControl
.layPrepare ();
1012 mLayDefSize
:= mDefSize
;
1013 if (mLayDefSize
.w
<> 0) and (mLayDefSize
.h
<> 0) then
1015 mLayMaxSize
:= mMaxSize
;
1016 if (mLayMaxSize
.w
>= 0) then begin mLayDefSize
.w
+= mFrameWidth
*2; mLayMaxSize
.w
+= mFrameWidth
*2; end;
1017 if (mLayMaxSize
.h
>= 0) then begin mLayDefSize
.h
+= mFrameHeight
*2; mLayMaxSize
.h
+= mFrameHeight
*2; end;
1021 mLayMaxSize
:= TLaySize
.Create(0, 0);
1026 // ////////////////////////////////////////////////////////////////////////// //
1027 function TUIControl
.parsePos (par
: TTextParser
): TLayPos
;
1029 ech
: AnsiChar = ')';
1031 if (par
.eatDelim('[')) then ech
:= ']' else par
.expectDelim('(');
1032 result
.x
:= par
.expectInt();
1033 par
.eatDelim(','); // optional comma
1034 result
.y
:= par
.expectInt();
1035 par
.eatDelim(','); // optional comma
1036 par
.expectDelim(ech
);
1039 function TUIControl
.parseSize (par
: TTextParser
): TLaySize
;
1041 ech
: AnsiChar = ')';
1043 if (par
.eatDelim('[')) then ech
:= ']' else par
.expectDelim('(');
1044 result
.w
:= par
.expectInt();
1045 par
.eatDelim(','); // optional comma
1046 result
.h
:= par
.expectInt();
1047 par
.eatDelim(','); // optional comma
1048 par
.expectDelim(ech
);
1051 function TUIControl
.parsePadding (par
: TTextParser
): TLaySize
;
1053 result
:= parseSize(par
);
1056 function TUIControl
.parseHPadding (par
: TTextParser
; def
: Integer): TLaySize
;
1061 result
.w
:= par
.expectInt();
1065 result
:= parsePadding(par
);
1069 function TUIControl
.parseVPadding (par
: TTextParser
; def
: Integer): TLaySize
;
1074 result
.h
:= par
.expectInt();
1078 result
:= parsePadding(par
);
1082 function TUIControl
.parseBool (par
: TTextParser
): Boolean;
1085 par
.eatIdOrStrCI('true') or
1086 par
.eatIdOrStrCI('yes') or
1087 par
.eatIdOrStrCI('tan');
1090 if (not par
.eatIdOrStrCI('false')) and (not par
.eatIdOrStrCI('no')) and (not par
.eatIdOrStrCI('ona')) then
1092 par
.error('boolean value expected');
1097 function TUIControl
.parseAnyAlign (par
: TTextParser
): Integer;
1099 if (par
.eatIdOrStrCI('left')) or (par
.eatIdOrStrCI('top')) then result
:= -1
1100 else if (par
.eatIdOrStrCI('right')) or (par
.eatIdOrStrCI('bottom')) then result
:= 1
1101 else if (par
.eatIdOrStrCI('center')) then result
:= 0
1102 else par
.error('invalid align value');
1105 function TUIControl
.parseHAlign (par
: TTextParser
): Integer;
1107 if (par
.eatIdOrStrCI('left')) then result
:= -1
1108 else if (par
.eatIdOrStrCI('right')) then result
:= 1
1109 else if (par
.eatIdOrStrCI('center')) then result
:= 0
1110 else par
.error('invalid horizontal align value');
1113 function TUIControl
.parseVAlign (par
: TTextParser
): Integer;
1115 if (par
.eatIdOrStrCI('top')) then result
:= -1
1116 else if (par
.eatIdOrStrCI('bottom')) then result
:= 1
1117 else if (par
.eatIdOrStrCI('center')) then result
:= 0
1118 else par
.error('invalid vertical align value');
1121 procedure TUIControl
.parseTextAlign (par
: TTextParser
; var h
, v
: Integer);
1123 wasH
: Boolean = false;
1124 wasV
: Boolean = false;
1128 if (par
.eatIdOrStrCI('left')) then
1130 if wasH
then par
.error('too many align directives');
1135 if (par
.eatIdOrStrCI('right')) then
1137 if wasH
then par
.error('too many align directives');
1142 if (par
.eatIdOrStrCI('hcenter')) then
1144 if wasH
then par
.error('too many align directives');
1149 if (par
.eatIdOrStrCI('top')) then
1151 if wasV
then par
.error('too many align directives');
1156 if (par
.eatIdOrStrCI('bottom')) then
1158 if wasV
then par
.error('too many align directives');
1163 if (par
.eatIdOrStrCI('vcenter')) then
1165 if wasV
then par
.error('too many align directives');
1170 if (par
.eatIdOrStrCI('center')) then
1172 if wasV
or wasH
then par
.error('too many align directives');
1181 if not wasV
and not wasH
then par
.error('invalid align value');
1184 function TUIControl
.parseOrientation (const prname
: AnsiString; par
: TTextParser
): Boolean;
1186 if (strEquCI1251(prname
, 'orientation')) or (strEquCI1251(prname
, 'orient')) then
1188 if (par
.eatIdOrStrCI('horizontal')) or (par
.eatIdOrStrCI('horiz')) then mHoriz
:= true
1189 else if (par
.eatIdOrStrCI('vertical')) or (par
.eatIdOrStrCI('vert')) then mHoriz
:= false
1190 else par
.error('`horizontal` or `vertical` expected');
1199 // par should be on '{'; final '}' is eaten
1200 procedure TUIControl
.parseProperties (par
: TTextParser
);
1204 if (not par
.eatDelim('{')) then exit
;
1205 while (not par
.eatDelim('}')) do
1207 if (not par
.isIdOrStr
) then par
.error('property name expected');
1210 par
.eatDelim(':'); // optional
1211 if not parseProperty(pn
, par
) then par
.errorfmt('invalid property name ''%s''', [pn
]);
1212 par
.eatDelim(','); // optional
1216 // par should be on '{'
1217 procedure TUIControl
.parseChildren (par
: TTextParser
);
1219 cc
: TUIControlClass
;
1222 par
.expectDelim('{');
1223 while (not par
.eatDelim('}')) do
1225 if (not par
.isIdOrStr
) then par
.error('control name expected');
1226 cc
:= findCtlClass(par
.tokStr
);
1227 if (cc
= nil) then par
.errorfmt('unknown control name: ''%s''', [par
.tokStr
]);
1228 //writeln('children for <', par.tokStr, '>: <', cc.className, '>');
1230 par
.eatDelim(':'); // optional
1232 //writeln(' mHoriz=', ctl.mHoriz);
1234 ctl
.parseProperties(par
);
1239 //writeln(': ', ctl.mDefSize.toString);
1241 par
.eatDelim(','); // optional
1246 function TUIControl
.parseProperty (const prname
: AnsiString; par
: TTextParser
): Boolean;
1249 if (strEquCI1251(prname
, 'id')) then begin mId
:= par
.expectIdOrStr(true); exit
; end; // allow empty strings
1250 if (strEquCI1251(prname
, 'style')) then begin mStyleId
:= par
.expectIdOrStr(); exit
; end; // no empty strings
1251 if (strEquCI1251(prname
, 'flex')) then begin flex
:= par
.expectInt(); exit
; end;
1253 if (strEquCI1251(prname
, 'defsize')) or (strEquCI1251(prname
, 'size')) then begin mDefSize
:= parseSize(par
); exit
; end;
1254 if (strEquCI1251(prname
, 'maxsize')) then begin mMaxSize
:= parseSize(par
); exit
; end;
1255 if (strEquCI1251(prname
, 'defwidth')) or (strEquCI1251(prname
, 'width')) then begin mDefSize
.w
:= par
.expectInt(); exit
; end;
1256 if (strEquCI1251(prname
, 'defheight')) or (strEquCI1251(prname
, 'height')) then begin mDefSize
.h
:= par
.expectInt(); exit
; end;
1257 if (strEquCI1251(prname
, 'maxwidth')) then begin mMaxSize
.w
:= par
.expectInt(); exit
; end;
1258 if (strEquCI1251(prname
, 'maxheight')) then begin mMaxSize
.h
:= par
.expectInt(); exit
; end;
1260 if (strEquCI1251(prname
, 'padding')) then begin mPadding
:= parsePadding(par
); exit
; end;
1261 if (strEquCI1251(prname
, 'nopad')) then begin mNoPad
:= true; exit
; end;
1263 if (strEquCI1251(prname
, 'expand')) then begin mExpand
:= parseBool(par
); exit
; end;
1265 if (strEquCI1251(prname
, 'align')) then begin mAlign
:= parseAnyAlign(par
); exit
; end;
1266 if (strEquCI1251(prname
, 'hgroup')) then begin mHGroup
:= par
.expectIdOrStr(true); exit
; end; // allow empty strings
1267 if (strEquCI1251(prname
, 'vgroup')) then begin mVGroup
:= par
.expectIdOrStr(true); exit
; end; // allow empty strings
1269 if (strEquCI1251(prname
, 'canfocus')) then begin mCanFocus
:= true; exit
; end;
1270 if (strEquCI1251(prname
, 'nofocus')) then begin mCanFocus
:= false; exit
; end;
1271 if (strEquCI1251(prname
, 'disabled')) then begin mEnabled
:= false; exit
; end;
1272 if (strEquCI1251(prname
, 'enabled')) then begin mEnabled
:= true; exit
; end;
1273 if (strEquCI1251(prname
, 'escclose')) then begin mEscClose
:= not parseBool(par
); exit
; end;
1274 if (strEquCI1251(prname
, 'default')) then begin mDefault
:= true; exit
; end;
1275 if (strEquCI1251(prname
, 'cancel')) then begin mCancel
:= true; exit
; end;
1280 // ////////////////////////////////////////////////////////////////////////// //
1281 procedure TUIControl
.activated ();
1283 makeVisibleInParent();
1287 procedure TUIControl
.blurred ();
1289 if (uiGrabCtl
= self
) then uiGrabCtl
:= nil;
1293 procedure TUIControl
.calcFullClientSize ();
1297 mFullSize
:= TLaySize
.Create(0, 0);
1298 if (mWidth
< 1) or (mHeight
< 1) then exit
;
1299 for ctl
in mChildren
do
1301 ctl
.calcFullClientSize();
1302 mFullSize
.w
:= nmax(mFullSize
.w
, ctl
.mX
-mFrameWidth
+ctl
.mFullSize
.w
);
1303 mFullSize
.h
:= nmax(mFullSize
.h
, ctl
.mY
-mFrameHeight
+ctl
.mFullSize
.h
);
1305 mFullSize
.w
:= nmax(mFullSize
.w
, mWidth
-mFrameWidth
*2);
1306 mFullSize
.h
:= nmax(mFullSize
.h
, mHeight
-mFrameHeight
*2);
1310 function TUIControl
.topLevel (): TUIControl
; inline;
1313 while (result
.mParent
<> nil) do result
:= result
.mParent
;
1317 function TUIControl
.getEnabled (): Boolean;
1322 if (not mEnabled
) then exit
;
1324 while (ctl
<> nil) do
1326 if (not ctl
.mEnabled
) then exit
;
1333 procedure TUIControl
.setEnabled (v
: Boolean); inline;
1335 if (mEnabled
= v
) then exit
;
1337 if (not v
) and focused
then setFocused(false);
1341 function TUIControl
.getFocused (): Boolean; inline;
1343 if (mParent
= nil) then
1345 result
:= (Length(uiTopList
) > 0) and (uiTopList
[High(uiTopList
)] = self
);
1349 result
:= (topLevel
.mFocused
= self
);
1350 if (result
) then result
:= (Length(uiTopList
) > 0) and (uiTopList
[High(uiTopList
)] = topLevel
);
1355 function TUIControl
.getActive (): Boolean; inline;
1359 if (mParent
= nil) then
1361 result
:= (Length(uiTopList
) > 0) and (uiTopList
[High(uiTopList
)] = self
);
1365 ctl
:= topLevel
.mFocused
;
1366 while (ctl
<> nil) and (ctl
<> self
) do ctl
:= ctl
.mParent
;
1367 result
:= (ctl
= self
);
1368 if (result
) then result
:= (Length(uiTopList
) > 0) and (uiTopList
[High(uiTopList
)] = topLevel
);
1373 procedure TUIControl
.setFocused (v
: Boolean); inline;
1380 if (tl
.mFocused
= self
) then
1382 blurred(); // this will reset grab, but still...
1383 if (uiGrabCtl
= self
) then uiGrabCtl
:= nil;
1384 tl
.mFocused
:= tl
.findNextFocus(self
, true);
1385 if (tl
.mFocused
= self
) then tl
.mFocused
:= nil;
1386 if (tl
.mFocused
<> nil) then tl
.mFocused
.activated();
1390 if (not canFocus
) then exit
;
1391 if (tl
.mFocused
<> self
) then
1393 if (tl
.mFocused
<> nil) then tl
.mFocused
.blurred();
1394 tl
.mFocused
:= self
;
1395 if (uiGrabCtl
<> self
) then uiGrabCtl
:= nil;
1401 function TUIControl
.getCanFocus (): Boolean; inline;
1403 result
:= (getEnabled
) and (mCanFocus
) and (mWidth
> 0) and (mHeight
> 0);
1407 function TUIControl
.isMyChild (ctl
: TUIControl
): Boolean;
1410 while (ctl
<> nil) do
1412 if (ctl
.mParent
= self
) then exit
;
1419 // returns `true` if global coords are inside this control
1420 function TUIControl
.toLocal (var x
, y
: Integer): Boolean;
1422 if (mParent
= nil) then
1426 result
:= true; // hack
1430 result
:= mParent
.toLocal(x
, y
);
1431 Inc(x
, mParent
.mScrollX
);
1432 Inc(y
, mParent
.mScrollY
);
1435 if result
then result
:= (x
>= 0) and (y
>= 0) and (x
< mParent
.mWidth
) and (y
< mParent
.mHeight
);
1437 if result
then result
:= (x
>= 0) and (y
>= 0) and (x
< mWidth
) and (y
< mHeight
);
1440 function TUIControl
.toLocal (gx
, gy
: Integer; out x
, y
: Integer): Boolean; inline;
1444 result
:= toLocal(x
, y
);
1448 procedure TUIControl
.toGlobal (var x
, y
: Integer);
1452 if (mParent
<> nil) then
1454 Dec(x
, mParent
.mScrollX
);
1455 Dec(y
, mParent
.mScrollY
);
1456 mParent
.toGlobal(x
, y
);
1460 procedure TUIControl
.toGlobal (lx
, ly
: Integer; out x
, y
: Integer); inline;
1467 procedure TUIControl
.getDrawRect (out gx
, gy
, wdt
, hgt
: Integer);
1471 if (mParent
= nil) then
1480 toGlobal(0, 0, cgx
, cgy
);
1481 mParent
.getDrawRect(gx
, gy
, wdt
, hgt
);
1482 if (wdt
> 0) and (hgt
> 0) then
1484 if (not intersectRect(gx
, gy
, wdt
, hgt
, cgx
, cgy
, mWidth
, mHeight
)) then
1494 // x and y are global coords
1495 function TUIControl
.controlAtXY (x
, y
: Integer; allowDisabled
: Boolean=false): TUIControl
;
1501 if (not allowDisabled
) and (not enabled
) then exit
;
1502 if (mWidth
< 1) or (mHeight
< 1) then exit
;
1503 if not toLocal(x
, y
, lx
, ly
) then exit
;
1504 for f
:= High(mChildren
) downto 0 do
1506 result
:= mChildren
[f
].controlAtXY(x
, y
, allowDisabled
);
1507 if (result
<> nil) then exit
;
1513 function TUIControl
.parentScrollX (): Integer; inline; begin if (mParent
<> nil) then result
:= mParent
.mScrollX
else result
:= 0; end;
1514 function TUIControl
.parentScrollY (): Integer; inline; begin if (mParent
<> nil) then result
:= mParent
.mScrollY
else result
:= 0; end;
1517 procedure TUIControl
.makeVisibleInParent ();
1519 sy
, ey
, cy
: Integer;
1522 if (mWidth
< 1) or (mHeight
< 1) then exit
;
1524 if (p
= nil) then exit
;
1525 if (p
.mFullSize
.w
< 1) or (p
.mFullSize
.h
< 1) then
1531 p
.makeVisibleInParent();
1532 cy
:= mY
-p
.mFrameHeight
;
1534 ey
:= sy
+(p
.mHeight
-p
.mFrameHeight
*2);
1537 p
.mScrollY
:= nmax(0, cy
);
1539 else if (cy
+mHeight
> ey
) then
1541 p
.mScrollY
:= nmax(0, cy
+mHeight
-(p
.mHeight
-p
.mFrameHeight
*2));
1546 // ////////////////////////////////////////////////////////////////////////// //
1547 function TUIControl
.prevSibling (): TUIControl
;
1551 if (mParent
<> nil) then
1553 for f
:= 1 to High(mParent
.mChildren
) do
1555 if (mParent
.mChildren
[f
] = self
) then begin result
:= mParent
.mChildren
[f
-1]; exit
; end;
1561 function TUIControl
.nextSibling (): TUIControl
;
1565 if (mParent
<> nil) then
1567 for f
:= 0 to High(mParent
.mChildren
)-1 do
1569 if (mParent
.mChildren
[f
] = self
) then begin result
:= mParent
.mChildren
[f
+1]; exit
; end;
1575 function TUIControl
.firstChild (): TUIControl
; inline;
1577 if (Length(mChildren
) <> 0) then result
:= mChildren
[0] else result
:= nil;
1580 function TUIControl
.lastChild (): TUIControl
; inline;
1582 if (Length(mChildren
) <> 0) then result
:= mChildren
[High(mChildren
)] else result
:= nil;
1586 function TUIControl
.findFirstFocus (): TUIControl
;
1593 for f
:= 0 to High(mChildren
) do
1595 result
:= mChildren
[f
].findFirstFocus();
1596 if (result
<> nil) then exit
;
1598 if (canFocus
) then result
:= self
;
1603 function TUIControl
.findLastFocus (): TUIControl
;
1610 for f
:= High(mChildren
) downto 0 do
1612 result
:= mChildren
[f
].findLastFocus();
1613 if (result
<> nil) then exit
;
1615 if (canFocus
) then result
:= self
;
1620 function TUIControl
.findNextFocus (cur
: TUIControl
; wrap
: Boolean): TUIControl
;
1622 curHit
: Boolean = false;
1624 function checkFocus (ctl
: TUIControl
): Boolean;
1628 result
:= (ctl
.canFocus
);
1632 curHit
:= (ctl
= cur
);
1633 result
:= false; // don't stop
1641 if not isMyChild(cur
) then
1643 result
:= findFirstFocus();
1647 result
:= forEachControl(checkFocus
);
1648 if (result
= nil) and (wrap
) then result
:= findFirstFocus();
1654 function TUIControl
.findPrevFocus (cur
: TUIControl
; wrap
: Boolean): TUIControl
;
1656 lastCtl
: TUIControl
= nil;
1658 function checkFocus (ctl
: TUIControl
): Boolean;
1667 if (ctl
.canFocus
) then lastCtl
:= ctl
;
1675 if not isMyChild(cur
) then
1677 result
:= findLastFocus();
1681 forEachControl(checkFocus
);
1682 if (lastCtl
= nil) and (wrap
) then lastCtl
:= findLastFocus();
1684 //if (lastCtl <> nil) then writeln('ctl<', lastCtl.className, '>: {', lastCtl.id, '}');
1690 function TUIControl
.findDefaulControl (): TUIControl
;
1696 if (mDefault
) then begin result
:= self
; exit
; end;
1697 for ctl
in mChildren
do
1699 result
:= ctl
.findDefaulControl();
1700 if (result
<> nil) then exit
;
1706 function TUIControl
.findCancelControl (): TUIControl
;
1712 if (mCancel
) then begin result
:= self
; exit
; end;
1713 for ctl
in mChildren
do
1715 result
:= ctl
.findCancelControl();
1716 if (result
<> nil) then exit
;
1723 function TUIControl
.findControlById (const aid
: AnsiString): TUIControl
;
1727 if (strEquCI1251(aid
, mId
)) then begin result
:= self
; exit
; end;
1728 for ctl
in mChildren
do
1730 result
:= ctl
.findControlById(aid
);
1731 if (result
<> nil) then exit
;
1737 procedure TUIControl
.appendChild (ctl
: TUIControl
);
1739 if (ctl
= nil) then exit
;
1740 if (ctl
.mParent
<> nil) then exit
;
1741 SetLength(mChildren
, Length(mChildren
)+1);
1742 mChildren
[High(mChildren
)] := ctl
;
1743 ctl
.mParent
:= self
;
1744 Inc(ctl
.mX
, mFrameWidth
);
1745 Inc(ctl
.mY
, mFrameHeight
);
1746 if (ctl
.mWidth
> 0) and (ctl
.mHeight
> 0) and
1747 (ctl
.mX
+ctl
.mWidth
> mFrameWidth
) and (ctl
.mY
+ctl
.mHeight
> mFrameHeight
) then
1749 if (mWidth
+mFrameWidth
< ctl
.mX
+ctl
.mWidth
) then mWidth
:= ctl
.mX
+ctl
.mWidth
+mFrameWidth
;
1750 if (mHeight
+mFrameHeight
< ctl
.mY
+ctl
.mHeight
) then mHeight
:= ctl
.mY
+ctl
.mHeight
+mFrameHeight
;
1755 function TUIControl
.setActionCBFor (const aid
: AnsiString; cb
: TActionCB
): TActionCB
;
1760 if (ctl
<> nil) then
1762 result
:= ctl
.actionCB
;
1772 function TUIControl
.forEachChildren (cb
: TCtlEnumCB
): TUIControl
;
1777 if (not assigned(cb
)) then exit
;
1778 for ctl
in mChildren
do
1780 if cb(ctl
) then begin result
:= ctl
; exit
; end;
1785 function TUIControl
.forEachControl (cb
: TCtlEnumCB
; includeSelf
: Boolean=true): TUIControl
;
1787 function forChildren (p
: TUIControl
; incSelf
: Boolean): TUIControl
;
1792 if (p
= nil) then exit
;
1793 if (incSelf
) and (cb(p
)) then begin result
:= p
; exit
; end;
1794 for ctl
in p
.mChildren
do
1796 result
:= forChildren(ctl
, true);
1797 if (result
<> nil) then break
;
1803 if (not assigned(cb
)) then exit
;
1804 result
:= forChildren(self
, includeSelf
);
1808 procedure TUIControl
.close (); // this closes *top-level* control
1813 uiRemoveWindow(ctl
);
1814 if (ctl
is TUITopWindow
) and (TUITopWindow(ctl
).mFreeOnClose
) then scheduleKill(ctl
); // just in case
1818 procedure TUIControl
.doAction ();
1820 if assigned(actionCB
) then actionCB(self
);
1824 // ////////////////////////////////////////////////////////////////////////// //
1825 procedure TUIControl
.setScissor (lx
, ly
, lw
, lh
: Integer);
1827 gx
, gy
, wdt
, hgt
, cgx
, cgy
: Integer;
1829 if (not intersectRect(lx
, ly
, lw
, lh
, 0, 0, mWidth
, mHeight
)) then
1831 uiContext
.clip
:= TGxRect
.Create(0, 0, 0, 0);
1835 getDrawRect(gx
, gy
, wdt
, hgt
);
1837 toGlobal(lx
, ly
, cgx
, cgy
);
1838 if (not intersectRect(gx
, gy
, wdt
, hgt
, cgx
, cgy
, lw
, lh
)) then
1840 uiContext
.clip
:= TGxRect
.Create(0, 0, 0, 0);
1844 uiContext
.clip
:= savedClip
;
1845 uiContext
.combineClip(TGxRect
.Create(gx
, gy
, wdt
, hgt
));
1846 //uiContext.clip := TGxRect.Create(gx, gy, wdt, hgt);
1851 // ////////////////////////////////////////////////////////////////////////// //
1852 procedure TUIControl
.drawFrame (gx
, gy
, resx
, thalign
: Integer; const text: AnsiString; dbl
: Boolean);
1854 cidx
, tx
, tw
: Integer;
1856 if (mFrameWidth
< 1) or (mFrameHeight
< 1) then exit
;
1857 cidx
:= getColorIndex
;
1858 uiContext
.color
:= mFrameColor
[cidx
];
1859 case mFrameHeight
of
1864 uiContext
.rect(gx
+3, gy
+3, mWidth
-6, mHeight
-6);
1865 uiContext
.rect(gx
+5, gy
+5, mWidth
-10, mHeight
-10);
1869 uiContext
.rect(gx
+4, gy
+4, mWidth
-8, mHeight
-8);
1876 uiContext
.rect(gx
+3, gy
+3+3, mWidth
-6, mHeight
-6-6);
1877 uiContext
.rect(gx
+5, gy
+5+3, mWidth
-10, mHeight
-10-6);
1881 uiContext
.rect(gx
+4, gy
+4+3, mWidth
-8, mHeight
-8-6);
1888 uiContext
.rect(gx
+3, gy
+3+4, mWidth
-6, mHeight
-6-8);
1889 uiContext
.rect(gx
+5, gy
+5+4, mWidth
-10, mHeight
-10-8);
1893 uiContext
.rect(gx
+4, gy
+4+4, mWidth
-8, mHeight
-8-8);
1909 if (Length(text) > 0) then
1911 if (resx
< 0) then resx
:= 0;
1912 tw
:= uiContext
.textWidth(text);
1913 setScissor(mFrameWidth
+resx
, 0, mWidth
-mFrameWidth
*2-resx
, mFrameHeight
);
1914 if (thalign
< 0) then tx
:= gx
+resx
+mFrameWidth
+2
1915 else if (thalign
> 0) then tx
:= gx
+mWidth
-mFrameWidth
-1-tw
1916 else tx
:= (gx
+resx
+mFrameWidth
)+(mWidth
-mFrameWidth
*2-resx
-tw
) div 2;
1917 uiContext
.color
:= mBackColor
[cidx
];
1918 uiContext
.fillRect(tx
-2, gy
, tw
+4, mFrameHeight
);
1919 uiContext
.color
:= mFrameTextColor
[cidx
];
1920 uiContext
.drawText(tx
, gy
, text);
1925 procedure TUIControl
.draw ();
1930 procedure resetScissor (fullArea
: Boolean); inline;
1932 uiContext
.clip
:= savedClip
;
1933 if (fullArea
) or ((mFrameWidth
= 0) and (mFrameHeight
= 0)) then
1935 setScissor(0, 0, mWidth
, mHeight
);
1939 //writeln('frm: (', mFrameWidth, 'x', mFrameHeight, ')');
1940 setScissor(mFrameWidth
, mFrameHeight
, mWidth
-mFrameWidth
*2, mHeight
-mFrameHeight
*2);
1945 if (mWidth
< 1) or (mHeight
< 1) or (uiContext
= nil) or (not uiContext
.active
) then exit
;
1946 toGlobal(0, 0, gx
, gy
);
1948 savedClip
:= uiContext
.clip
;
1950 resetScissor(true); // full area
1951 drawControl(gx
, gy
);
1952 resetScissor(false); // client area
1953 for f
:= 0 to High(mChildren
) do mChildren
[f
].draw();
1954 resetScissor(true); // full area
1955 if (self
is TUISwitchBox
) then
1957 uiContext
.color
:= TGxRGBA
.Create(255, 0, 0, 255);
1958 //uiContext.fillRect(gx, gy, mWidth, mHeight);
1959 //writeln('frm: (', mFrameWidth, 'x', mFrameHeight, '); sz=(', mWidth, 'x', mHeight, '); clip=', uiContext.clip.toString);
1961 if false and (mId
= 'cbtest') then
1963 uiContext
.color
:= TGxRGBA
.Create(255, 127, 0, 96);
1964 uiContext
.fillRect(gx
, gy
, mWidth
, mHeight
);
1965 if (mFrameWidth
> 0) and (mFrameHeight
> 0) then
1967 uiContext
.color
:= TGxRGBA
.Create(255, 255, 0, 96);
1968 uiContext
.fillRect(gx
+mFrameWidth
, gy
+mFrameHeight
, mWidth
-mFrameWidth
*2, mHeight
-mFrameHeight
*2);
1971 else if false and (self
is TUISwitchBox
) then
1973 uiContext
.color
:= TGxRGBA
.Create(255, 0, 0, 255);
1974 uiContext
.fillRect(gx
, gy
, mWidth
, mHeight
);
1975 //writeln('frm: (', mFrameWidth, 'x', mFrameHeight, ')');
1977 drawControlPost(gx
, gy
);
1979 uiContext
.clip
:= savedClip
;
1983 procedure TUIControl
.drawControl (gx
, gy
: Integer);
1985 //if (mParent = nil) then darkenRect(gx, gy, mWidth, mHeight, 64);
1988 procedure TUIControl
.drawControlPost (gx
, gy
: Integer);
1991 if (mParent
= nil) and (mDrawShadow
) and (mWidth
> 0) and (mHeight
> 0) then
1993 //setScissorGLInternal(gx+8, gy+8, mWidth, mHeight);
1994 uiContext
.resetClip();
1995 uiContext
.darkenRect(gx
+mWidth
, gy
+8, 8, mHeight
, 128);
1996 uiContext
.darkenRect(gx
+8, gy
+mHeight
, mWidth
-8, 8, 128);
2001 // ////////////////////////////////////////////////////////////////////////// //
2002 procedure TUIControl
.mouseEvent (var ev
: THMouseEvent
);
2006 if (not enabled
) then exit
;
2007 if (mWidth
< 1) or (mHeight
< 1) then exit
;
2008 ctl
:= controlAtXY(ev
.x
, ev
.y
);
2009 if (ctl
= nil) then exit
;
2010 if (ctl
.canFocus
) and (ev
.press
) then
2012 if (ctl
<> topLevel
.mFocused
) then ctl
.setFocused(true);
2015 if (ctl
<> self
) then ctl
.mouseEvent(ev
);
2020 procedure TUIControl
.keyEvent (var ev
: THKeyEvent
);
2022 function doPreKey (ctl
: TUIControl
): Boolean;
2024 if (not ctl
.enabled
) then begin result
:= false; exit
; end;
2025 ctl
.keyEventPre(ev
);
2026 result
:= (ev
.eaten
) or (ev
.cancelled
); // stop if event was consumed
2029 function doPostKey (ctl
: TUIControl
): Boolean;
2031 if (not ctl
.enabled
) then begin result
:= false; exit
; end;
2032 ctl
.keyEventPost(ev
);
2033 result
:= (ev
.eaten
) or (ev
.cancelled
); // stop if event was consumed
2039 if (not enabled
) then exit
;
2040 if (ev
.eaten
) or (ev
.cancelled
) then exit
;
2042 if (mParent
= nil) then
2044 forEachControl(doPreKey
);
2045 if (ev
.eaten
) or (ev
.cancelled
) then exit
;
2047 // focused control should process keyboard first
2048 if (topLevel
.mFocused
<> self
) and isMyChild(topLevel
.mFocused
) and (topLevel
.mFocused
.enabled
) then
2050 // bubble keyboard event
2051 ctl
:= topLevel
.mFocused
;
2052 while (ctl
<> nil) and (ctl
<> self
) do
2055 if (ev
.eaten
) or (ev
.cancelled
) then exit
;
2059 // for top-level controls
2060 if (mParent
= nil) then
2062 if (ev
= 'S-Tab') then
2064 ctl
:= findPrevFocus(mFocused
, true);
2065 if (ctl
<> nil) and (ctl
<> mFocused
) then ctl
.setFocused(true);
2069 if (ev
= 'Tab') then
2071 ctl
:= findNextFocus(mFocused
, true);
2072 if (ctl
<> nil) and (ctl
<> mFocused
) then ctl
.setFocused(true);
2076 if (ev
= 'Enter') or (ev
= 'C-Enter') then
2078 ctl
:= findDefaulControl();
2079 if (ctl
<> nil) then
2086 if (ev
= 'Escape') then
2088 ctl
:= findCancelControl();
2089 if (ctl
<> nil) then
2096 if mEscClose
and (ev
= 'Escape') then
2098 if (not assigned(closeRequestCB
)) or (closeRequestCB(self
)) then
2100 uiRemoveWindow(self
);
2106 if (ev
.eaten
) or (ev
.cancelled
) then exit
;
2107 forEachControl(doPostKey
);
2112 procedure TUIControl
.keyEventPre (var ev
: THKeyEvent
);
2117 procedure TUIControl
.keyEventPost (var ev
: THKeyEvent
);
2122 // ////////////////////////////////////////////////////////////////////////// //
2123 constructor TUITopWindow
.Create (const atitle
: AnsiString);
2130 procedure TUITopWindow
.AfterConstruction ();
2133 mFitToScreen
:= true;
2135 mFrameHeight
:= uiContext
.charHeight(#184);
2136 if (mWidth
< mFrameWidth
*2+uiContext
.iconWinWidth(TGxContext
.TWinIcon
.Close
)) then mWidth
:= mFrameWidth
*2+uiContext
.iconWinWidth(TGxContext
.TWinIcon
.Close
);
2137 if (mHeight
< mFrameHeight
*2) then mHeight
:= mFrameHeight
*2;
2138 if (Length(mTitle
) > 0) then
2140 if (mWidth
< uiContext
.textWidth(mTitle
)+mFrameWidth
*2+uiContext
.iconWinWidth(TGxContext
.TWinIcon
.Close
)) then
2142 mWidth
:= uiContext
.textWidth(mTitle
)+mFrameWidth
*2+uiContext
.iconWinWidth(TGxContext
.TWinIcon
.Close
);
2146 mDragScroll
:= TXMode
.None
;
2147 mDrawShadow
:= true;
2148 mWaitingClose
:= false;
2151 mCtl4Style
:= 'window';
2152 mDefSize
.w
:= nmax(1, mDefSize
.w
);
2153 mDefSize
.h
:= nmax(1, mDefSize
.h
);
2157 function TUITopWindow
.parseProperty (const prname
: AnsiString; par
: TTextParser
): Boolean;
2159 if (strEquCI1251(prname
, 'title')) or (strEquCI1251(prname
, 'caption')) then
2161 mTitle
:= par
.expectIdOrStr(true);
2165 if (strEquCI1251(prname
, 'children')) then
2171 if (strEquCI1251(prname
, 'position')) then
2173 if (par
.eatIdOrStrCI('default')) then mDoCenter
:= false
2174 else if (par
.eatIdOrStrCI('center')) then mDoCenter
:= true
2175 else par
.error('`center` or `default` expected');
2179 if (parseOrientation(prname
, par
)) then begin result
:= true; exit
; end;
2180 result
:= inherited parseProperty(prname
, par
);
2184 procedure TUITopWindow
.flFitToScreen ();
2188 nsz
:= TLaySize
.Create(trunc(fuiScrWdt
/fuiRenderScale
)-mFrameWidth
*2-6, trunc(fuiScrHgt
/fuiRenderScale
)-mFrameHeight
*2-6);
2189 if (mMaxSize
.w
< 1) then mMaxSize
.w
:= nsz
.w
;
2190 if (mMaxSize
.h
< 1) then mMaxSize
.h
:= nsz
.h
;
2194 procedure TUITopWindow
.centerInScreen ();
2196 if (mWidth
> 0) and (mHeight
> 0) then
2198 mX
:= trunc((fuiScrWdt
/fuiRenderScale
-mWidth
)/2);
2199 mY
:= trunc((fuiScrHgt
/fuiRenderScale
-mHeight
)/2);
2204 procedure TUITopWindow
.drawControl (gx
, gy
: Integer);
2206 uiContext
.color
:= mBackColor
[getColorIndex
];
2207 uiContext
.fillRect(gx
, gy
, mWidth
, mHeight
);
2210 procedure TUITopWindow
.drawControlPost (gx
, gy
: Integer);
2213 hgt
, sbhgt
, iwdt
, ihgt
: Integer;
2215 cidx
:= getColorIndex
;
2216 iwdt
:= uiContext
.iconWinWidth(TGxContext
.TWinIcon
.Close
);
2217 if (mDragScroll
= TXMode
.Drag
) then
2219 //uiContext.color := mFrameColor[cidx];
2220 drawFrame(gx
, gy
, iwdt
, 0, mTitle
, false);
2224 ihgt
:= uiContext
.iconWinHeight(TGxContext
.TWinIcon
.Close
);
2225 //uiContext.color := mFrameColor[cidx];
2226 drawFrame(gx
, gy
, iwdt
, 0, mTitle
, true);
2227 // vertical scroll bar
2228 hgt
:= mHeight
-mFrameHeight
*2;
2229 if (hgt
> 0) and (mFullSize
.h
> hgt
) then
2231 //writeln(mTitle, ': height=', mHeight-mFrameHeight*2, '; fullsize=', mFullSize.toString);
2232 sbhgt
:= mHeight
-mFrameHeight
*2+2;
2233 uiContext
.fillRect(gx
+mWidth
-mFrameWidth
+1, gy
+mFrameHeight
-1, mFrameWidth
-3, sbhgt
);
2235 if (hgt
> mFullSize
.h
) then hgt
:= mFullSize
.h
;
2236 hgt
:= sbhgt
*hgt
div mFullSize
.h
;
2239 setScissor(mWidth
-mFrameWidth
+1, mFrameHeight
-1, mFrameWidth
-3, sbhgt
);
2240 uiContext
.darkenRect(gx
+mWidth
-mFrameWidth
+1, gy
+mFrameHeight
-1+hgt
, mFrameWidth
-3, sbhgt
, 128);
2244 setScissor(mFrameWidth
, 0, iwdt
, ihgt
);
2245 uiContext
.color
:= mBackColor
[cidx
];
2246 uiContext
.fillRect(gx
+mFrameWidth
, gy
, iwdt
, ihgt
);
2247 uiContext
.color
:= mFrameIconColor
[cidx
];
2248 uiContext
.drawIconWin(TGxContext
.TWinIcon
.Close
, gx
+mFrameWidth
, gy
, mInClose
);
2251 inherited drawControlPost(gx
, gy
);
2255 procedure TUITopWindow
.activated ();
2257 if (mFocused
= nil) or (mFocused
= self
) then
2259 mFocused
:= findFirstFocus();
2261 if (mFocused
<> nil) and (mFocused
<> self
) then mFocused
.activated();
2266 procedure TUITopWindow
.blurred ();
2268 mDragScroll
:= TXMode
.None
;
2269 mWaitingClose
:= false;
2271 if (mFocused
<> nil) and (mFocused
<> self
) then mFocused
.blurred();
2276 procedure TUITopWindow
.keyEvent (var ev
: THKeyEvent
);
2278 inherited keyEvent(ev
);
2279 if (ev
.eaten
) or (ev
.cancelled
) or (not enabled
) {or (not getFocused)} then exit
;
2280 if (ev
= 'M-F3') then
2282 if (not assigned(closeRequestCB
)) or (closeRequestCB(self
)) then
2284 uiRemoveWindow(self
);
2292 procedure TUITopWindow
.mouseEvent (var ev
: THMouseEvent
);
2295 hgt
, sbhgt
: Integer;
2297 if (not enabled
) then exit
;
2298 if (mWidth
< 1) or (mHeight
< 1) then exit
;
2300 if (mDragScroll
= TXMode
.Drag
) then
2302 mX
+= ev
.x
-mDragStartX
;
2303 mY
+= ev
.y
-mDragStartY
;
2304 mDragStartX
:= ev
.x
;
2305 mDragStartY
:= ev
.y
;
2306 if (ev
.release
) and (ev
.but
= ev
.Left
) then mDragScroll
:= TXMode
.None
;
2311 if (mDragScroll
= TXMode
.Scroll
) then
2313 // check for vertical scrollbar
2321 sbhgt
:= mHeight
-mFrameHeight
*2+2;
2322 hgt
:= mHeight
-mFrameHeight
*2;
2323 if (hgt
> 0) and (mFullSize
.h
> hgt
) then
2325 hgt
:= (mFullSize
.h
*(ly
-7) div (sbhgt
-1))-(mHeight
-mFrameHeight
*2);
2326 mScrollY
:= nmax(0, hgt
);
2327 hgt
:= mHeight
-mFrameHeight
*2;
2328 if (mScrollY
+hgt
> mFullSize
.h
) then mScrollY
:= nmax(0, mFullSize
.h
-hgt
);
2331 if (ev
.release
) and (ev
.but
= ev
.Left
) then mDragScroll
:= TXMode
.None
;
2336 if toLocal(ev
.x
, ev
.y
, lx
, ly
) then
2340 if (ly
< mFrameHeight
) then
2343 if (lx
>= mFrameWidth
) and (lx
< mFrameWidth
+uiContext
.iconWinWidth(TGxContext
.TWinIcon
.Close
)) then
2345 //uiRemoveWindow(self);
2346 mWaitingClose
:= true;
2351 mDragScroll
:= TXMode
.Drag
;
2352 mDragStartX
:= ev
.x
;
2353 mDragStartY
:= ev
.y
;
2358 // check for vertical scrollbar
2359 if (lx
>= mWidth
-mFrameWidth
+1) and (ly
>= 7) and (ly
< mHeight
-mFrameHeight
+1) then
2361 sbhgt
:= mHeight
-mFrameHeight
*2+2;
2362 hgt
:= mHeight
-mFrameHeight
*2;
2363 if (hgt
> 0) and (mFullSize
.h
> hgt
) then
2365 hgt
:= (mFullSize
.h
*(ly
-7) div (sbhgt
-1))-(mHeight
-mFrameHeight
*2);
2366 mScrollY
:= nmax(0, hgt
);
2368 mDragScroll
:= TXMode
.Scroll
;
2374 if (lx
< mFrameWidth
) or (lx
>= mWidth
-mFrameWidth
) or (ly
>= mHeight
-mFrameHeight
) then
2377 mDragScroll
:= TXMode
.Drag
;
2378 mDragStartX
:= ev
.x
;
2379 mDragStartY
:= ev
.y
;
2385 if (ev
.release
) then
2387 if mWaitingClose
then
2389 if (lx
>= mFrameWidth
) and (lx
< mFrameWidth
+uiContext
.iconWinWidth(TGxContext
.TWinIcon
.Close
)) then
2391 if (not assigned(closeRequestCB
)) or (closeRequestCB(self
)) then
2393 uiRemoveWindow(self
);
2396 mWaitingClose
:= false;
2405 if mWaitingClose
then
2407 mInClose
:= (lx
>= mFrameWidth
) and (lx
< mFrameWidth
+uiContext
.iconWinWidth(TGxContext
.TWinIcon
.Close
));
2413 inherited mouseEvent(ev
);
2418 if (not ev
.motion
) and (mWaitingClose
) then begin ev
.eat(); mWaitingClose
:= false; exit
; end;
2423 // ////////////////////////////////////////////////////////////////////////// //
2424 constructor TUIBox
.Create (ahoriz
: Boolean);
2431 procedure TUIBox
.AfterConstruction ();
2435 mHAlign
:= -1; // left
2436 mCtl4Style
:= 'box';
2437 mDefSize
:= TLaySize
.Create(-1, -1);
2441 procedure TUIBox
.setCaption (const acap
: AnsiString);
2444 mDefSize
:= TLaySize
.Create(uiContext
.textWidth(mCaption
)+3, uiContext
.textHeight(mCaption
));
2448 procedure TUIBox
.setHasFrame (v
: Boolean);
2451 if (mHasFrame
) then begin mFrameWidth
:= 8; mFrameHeight
:= uiContext
.charHeight(#184); end else begin mFrameWidth
:= 0; mFrameHeight
:= 0; end;
2452 if (mHasFrame
) then mNoPad
:= true;
2456 function TUIBox
.parseProperty (const prname
: AnsiString; par
: TTextParser
): Boolean;
2458 if (parseOrientation(prname
, par
)) then begin result
:= true; exit
; end;
2459 if (strEquCI1251(prname
, 'padding')) then
2461 if (mHoriz
) then mPadding
:= parseHPadding(par
, 0) else mPadding
:= parseVPadding(par
, 0);
2465 if (strEquCI1251(prname
, 'frame')) then
2467 setHasFrame(parseBool(par
));
2471 if (strEquCI1251(prname
, 'title')) or (strEquCI1251(prname
, 'caption')) then
2473 setCaption(par
.expectIdOrStr(true));
2477 if (strEquCI1251(prname
, 'textalign')) or (strEquCI1251(prname
, 'text-align')) then
2479 mHAlign
:= parseHAlign(par
);
2483 if (strEquCI1251(prname
, 'children')) then
2489 result
:= inherited parseProperty(prname
, par
);
2493 procedure TUIBox
.drawControl (gx
, gy
: Integer);
2498 cidx
:= getColorIndex
;
2499 uiContext
.color
:= mBackColor
[cidx
];
2500 uiContext
.fillRect(gx
, gy
, mWidth
, mHeight
);
2504 drawFrame(gx
, gy
, 0, -1, mCaption
, false);
2505 //uiContext.color := mFrameColor[cidx];
2506 //uiContext.rect(gx+3, gy+3, mWidth-6, mHeight-6);
2508 else if (Length(mCaption
) > 0) then
2511 if (mHAlign
< 0) then xpos
:= 3
2512 else if (mHAlign
> 0) then xpos
:= mWidth
-mFrameWidth
*2-uiContext
.textWidth(mCaption
)
2513 else xpos
:= (mWidth
-mFrameWidth
*2-uiContext
.textWidth(mCaption
)) div 2;
2514 xpos
+= gx
+mFrameWidth
;
2516 setScissor(mFrameWidth
+1, 0, mWidth
-mFrameWidth
-2, uiContext
.textHeight(mCaption
));
2520 uiContext.color := mBackColor[cidx];
2521 uiContext.fillRect(xpos-3, gy, uiContext.textWidth(mCaption)+4, 8);
2524 uiContext
.color
:= mFrameTextColor
[cidx
];
2525 uiContext
.drawText(xpos
, gy
, mCaption
);
2530 procedure TUIBox
.mouseEvent (var ev
: THMouseEvent
);
2534 inherited mouseEvent(ev
);
2535 if (not ev
.eaten
) and (not ev
.cancelled
) and (enabled
) and toLocal(ev
.x
, ev
.y
, lx
, ly
) then
2542 procedure TUIBox
.keyEvent (var ev
: THKeyEvent
);
2545 cur
, ctl
: TUIControl
;
2547 inherited keyEvent(ev
);
2548 if (ev
.eaten
) or (ev
.cancelled
) or (not ev
.press
) or (not enabled
) or (not getActive
) then exit
;
2549 if (Length(mChildren
) = 0) then exit
;
2550 if (mHoriz
) and (ev
= 'Left') then dir
:= -1
2551 else if (mHoriz
) and (ev
= 'Right') then dir
:= 1
2552 else if (not mHoriz
) and (ev
= 'Up') then dir
:= -1
2553 else if (not mHoriz
) and (ev
= 'Down') then dir
:= 1;
2554 if (dir
= 0) then exit
;
2556 cur
:= topLevel
.mFocused
;
2557 while (cur
<> nil) and (cur
.mParent
<> self
) do cur
:= cur
.mParent
;
2558 //if (cur = nil) then writeln('CUR: nil') else writeln('CUR: ', cur.className, '#', cur.id);
2559 if (dir
< 0) then ctl
:= findPrevFocus(cur
, true) else ctl
:= findNextFocus(cur
, true);
2560 //if (ctl = nil) then writeln('CTL: nil') else writeln('CTL: ', ctl.className, '#', ctl.id);
2561 if (ctl
<> nil) and (ctl
<> self
) then
2563 ctl
.focused
:= true;
2568 // ////////////////////////////////////////////////////////////////////////// //
2569 constructor TUIHBox
.Create ();
2574 procedure TUIHBox
.AfterConstruction ();
2581 // ////////////////////////////////////////////////////////////////////////// //
2582 constructor TUIVBox
.Create ();
2587 procedure TUIVBox
.AfterConstruction ();
2594 // ////////////////////////////////////////////////////////////////////////// //
2595 procedure TUISpan
.AfterConstruction ();
2601 mCtl4Style
:= 'span';
2602 mDefSize
:= TLaySize
.Create(-1, -1);
2606 function TUISpan
.parseProperty (const prname
: AnsiString; par
: TTextParser
): Boolean;
2608 if (parseOrientation(prname
, par
)) then begin result
:= true; exit
; end;
2609 result
:= inherited parseProperty(prname
, par
);
2613 procedure TUISpan
.drawControl (gx
, gy
: Integer);
2618 // ////////////////////////////////////////////////////////////////////// //
2619 procedure TUILine
.AfterConstruction ();
2625 mCtl4Style
:= 'line';
2626 mDefSize
:= TLaySize
.Create(-1, -1);
2630 function TUILine
.parseProperty (const prname
: AnsiString; par
: TTextParser
): Boolean;
2632 if (parseOrientation(prname
, par
)) then begin result
:= true; exit
; end;
2633 result
:= inherited parseProperty(prname
, par
);
2637 procedure TUILine
.layPrepare ();
2639 inherited layPrepare();
2640 if (mParent
<> nil) then mHoriz
:= not mParent
.mHoriz
;
2643 if (mLayDefSize
.w
< 0) then mLayDefSize
.w
:= 1;
2644 if (mLayDefSize
.h
< 0) then mLayDefSize
.h
:= 7;
2648 if (mLayDefSize
.w
< 0) then mLayDefSize
.w
:= 7;
2649 if (mLayDefSize
.h
< 0) then mLayDefSize
.h
:= 1;
2654 procedure TUILine
.drawControl (gx
, gy
: Integer);
2658 cidx
:= getColorIndex
;
2659 uiContext
.color
:= mTextColor
[cidx
];
2660 if mHoriz
then uiContext
.hline(gx
, gy
+(mHeight
div 2), mWidth
)
2661 else uiContext
.vline(gx
+(mWidth
div 2), gy
, mHeight
);
2665 // ////////////////////////////////////////////////////////////////////////// //
2666 procedure TUIStaticText
.AfterConstruction ();
2672 mHoriz
:= true; // nobody cares
2675 mCtl4Style
:= 'static';
2679 procedure TUIStaticText
.setText (const atext
: AnsiString);
2682 mDefSize
:= TLaySize
.Create(uiContext
.textWidth(mText
), uiContext
.textHeight(mText
));
2686 function TUIStaticText
.parseProperty (const prname
: AnsiString; par
: TTextParser
): Boolean;
2688 if (strEquCI1251(prname
, 'title')) or (strEquCI1251(prname
, 'caption')) or (strEquCI1251(prname
, 'text')) then
2690 setText(par
.expectIdOrStr(true));
2694 if (strEquCI1251(prname
, 'textalign')) or (strEquCI1251(prname
, 'text-align')) then
2696 parseTextAlign(par
, mHAlign
, mVAlign
);
2700 if (strEquCI1251(prname
, 'header')) then
2706 if (strEquCI1251(prname
, 'line')) then
2712 result
:= inherited parseProperty(prname
, par
);
2716 procedure TUIStaticText
.drawControl (gx
, gy
: Integer);
2718 xpos
, ypos
: Integer;
2721 cidx
:= getColorIndex
;
2722 uiContext
.color
:= mBackColor
[cidx
];
2723 uiContext
.fillRect(gx
, gy
, mWidth
, mHeight
);
2725 if (mHAlign
< 0) then xpos
:= 0
2726 else if (mHAlign
> 0) then xpos
:= mWidth
-uiContext
.textWidth(mText
)
2727 else xpos
:= (mWidth
-uiContext
.textWidth(mText
)) div 2;
2729 if (Length(mText
) > 0) then
2731 if (mHeader
) then uiContext
.color
:= mFrameTextColor
[cidx
] else uiContext
.color
:= mTextColor
[cidx
];
2733 if (mVAlign
< 0) then ypos
:= 0
2734 else if (mVAlign
> 0) then ypos
:= mHeight
-uiContext
.textHeight(mText
)
2735 else ypos
:= (mHeight
-uiContext
.textHeight(mText
)) div 2;
2737 uiContext
.drawText(gx
+xpos
, gy
+ypos
, mText
);
2742 if (mHeader
) then uiContext
.color
:= mFrameColor
[cidx
] else uiContext
.color
:= mTextColor
[cidx
];
2744 if (mVAlign
< 0) then ypos
:= 0
2745 else if (mVAlign
> 0) then ypos
:= mHeight
-1
2746 else ypos
:= (mHeight
div 2);
2749 if (Length(mText
) = 0) then
2751 uiContext
.hline(gx
, ypos
, mWidth
);
2755 uiContext
.hline(gx
, ypos
, xpos
-1);
2756 uiContext
.hline(gx
+xpos
+uiContext
.textWidth(mText
), ypos
, mWidth
);
2762 // ////////////////////////////////////////////////////////////////////////// //
2763 procedure TUITextLabel
.AfterConstruction ();
2769 mCtl4Style
:= 'label';
2774 procedure TUITextLabel
.cacheStyle (root
: TUIStyle
);
2776 inherited cacheStyle(root
);
2778 mHotColor
[ClrIdxActive
] := root
.get('hot-color', 'active', mCtl4Style
).asRGBADef(TGxRGBA
.Create(0, 128, 0));
2780 mHotColor
[ClrIdxDisabled
] := root
.get('hot-color', 'disabled', mCtl4Style
).asRGBADef(TGxRGBA
.Create(0, 64, 0));
2782 mHotColor
[ClrIdxInactive
] := root
.get('hot-color', 'inactive', mCtl4Style
).asRGBADef(TGxRGBA
.Create(0, 64, 0));
2786 procedure TUITextLabel
.setText (const s
: AnsiString);
2794 while (f
<= Length(s
)) do
2796 if (s
[f
] = '\\') then
2799 if (f
<= Length(s
)) then mText
+= s
[f
];
2802 else if (s
[f
] = '~') then
2805 if (f
<= Length(s
)) then
2807 if (mHotChar
= #0) then
2810 mHotOfs
:= Length(mText
);
2822 // fix hotchar offset
2823 if (mHotChar
<> #0) and (mHotOfs
> 0) then
2825 mHotOfs
:= uiContext
.textWidth(Copy(mText
, 1, mHotOfs
+1))-uiContext
.charWidth(mText
[mHotOfs
+1]);
2828 mDefSize
:= TLaySize
.Create(uiContext
.textWidth(mText
), uiContext
.textHeight(mText
));
2832 function TUITextLabel
.parseProperty (const prname
: AnsiString; par
: TTextParser
): Boolean;
2834 if (strEquCI1251(prname
, 'title')) or (strEquCI1251(prname
, 'caption')) or (strEquCI1251(prname
, 'text')) then
2836 setText(par
.expectIdOrStr(true));
2840 if (strEquCI1251(prname
, 'link')) then
2842 mLinkId
:= par
.expectIdOrStr(true);
2846 if (strEquCI1251(prname
, 'textalign')) or (strEquCI1251(prname
, 'text-align')) then
2848 parseTextAlign(par
, mHAlign
, mVAlign
);
2852 result
:= inherited parseProperty(prname
, par
);
2856 procedure TUITextLabel
.drawControl (gx
, gy
: Integer);
2858 xpos
, ypos
: Integer;
2861 cidx
:= getColorIndex
;
2862 uiContext
.color
:= mBackColor
[cidx
];
2863 uiContext
.fillRect(gx
, gy
, mWidth
, mHeight
);
2864 if (Length(mText
) > 0) then
2866 if (mHAlign
< 0) then xpos
:= 0
2867 else if (mHAlign
> 0) then xpos
:= mWidth
-uiContext
.textWidth(mText
)
2868 else xpos
:= (mWidth
-uiContext
.textWidth(mText
)) div 2;
2870 if (mVAlign
< 0) then ypos
:= 0
2871 else if (mVAlign
> 0) then ypos
:= mHeight
-uiContext
.textHeight(mText
)
2872 else ypos
:= (mHeight
-uiContext
.textHeight(mText
)) div 2;
2874 uiContext
.color
:= mTextColor
[cidx
];
2875 uiContext
.drawText(gx
+xpos
, gy
+ypos
, mText
);
2877 if (Length(mLinkId
) > 0) and (mHotChar
<> #0) and (mHotChar
<> ' ') then
2879 uiContext
.color
:= mHotColor
[cidx
];
2880 uiContext
.drawChar(gx
+xpos
+mHotOfs
, gy
+ypos
, mHotChar
);
2886 procedure TUITextLabel
.mouseEvent (var ev
: THMouseEvent
);
2890 inherited mouseEvent(ev
);
2891 if (not ev
.eaten
) and (not ev
.cancelled
) and (enabled
) and toLocal(ev
.x
, ev
.y
, lx
, ly
) then
2898 procedure TUITextLabel
.doAction ();
2902 if (assigned(actionCB
)) then
2908 ctl
:= topLevel
[mLinkId
];
2909 if (ctl
<> nil) then
2911 if (ctl
.canFocus
) then ctl
.focused
:= true;
2917 procedure TUITextLabel
.keyEventPost (var ev
: THKeyEvent
);
2919 if (not enabled
) then exit
;
2920 if (mHotChar
= #0) then exit
;
2921 if (ev
.eaten
) or (ev
.cancelled
) or (not ev
.press
) then exit
;
2922 if (ev
.kstate
<> ev
.ModAlt
) then exit
;
2923 if (not ev
.isHot(mHotChar
)) then exit
;
2925 if (canFocus
) then focused
:= true;
2930 // ////////////////////////////////////////////////////////////////////////// //
2931 procedure TUIButton
.AfterConstruction ();
2937 mDefSize
:= TLaySize
.Create(uiContext
.textWidth(mText
)+8*2, uiContext
.textHeight(mText
)+2);
2938 mCtl4Style
:= 'button';
2942 procedure TUIButton
.setText (const s
: AnsiString);
2944 inherited setText(s
);
2945 mDefSize
:= TLaySize
.Create(uiContext
.textWidth(mText
)+8*2, uiContext
.textHeight(mText
)+2);
2949 procedure TUIButton
.drawControl (gx
, gy
: Integer);
2951 xpos
, ypos
: Integer;
2954 cidx
:= getColorIndex
;
2956 uiContext
.color
:= mBackColor
[cidx
];
2957 uiContext
.fillRect(gx
+1, gy
, mWidth
-2, mHeight
);
2958 uiContext
.fillRect(gx
, gy
+1, 1, mHeight
-2);
2959 uiContext
.fillRect(gx
+mWidth
-1, gy
+1, 1, mHeight
-2);
2961 if (Length(mText
) > 0) then
2963 if (mHAlign
< 0) then xpos
:= 0
2964 else if (mHAlign
> 0) then xpos
:= mWidth
-uiContext
.textWidth(mText
)
2965 else xpos
:= (mWidth
-uiContext
.textWidth(mText
)) div 2;
2967 if (mVAlign
< 0) then ypos
:= 0
2968 else if (mVAlign
> 0) then ypos
:= mHeight
-uiContext
.textHeight(mText
)
2969 else ypos
:= (mHeight
-uiContext
.textHeight(mText
)) div 2;
2971 setScissor(8, 0, mWidth
-16, mHeight
);
2972 uiContext
.color
:= mTextColor
[cidx
];
2973 uiContext
.drawText(gx
+xpos
+8, gy
+ypos
, mText
);
2975 if (mHotChar
<> #0) and (mHotChar
<> ' ') then
2977 uiContext
.color
:= mHotColor
[cidx
];
2978 uiContext
.drawChar(gx
+xpos
+8+mHotOfs
, gy
+ypos
, mHotChar
);
2984 procedure TUIButton
.mouseEvent (var ev
: THMouseEvent
);
2988 inherited mouseEvent(ev
);
2989 if (uiGrabCtl
= self
) then
2992 if (ev
= '-lmb') and focused
and toLocal(ev
.x
, ev
.y
, lx
, ly
) then
2998 if (ev
.eaten
) or (ev
.cancelled
) or (not enabled
) or not focused
then exit
;
3003 procedure TUIButton
.keyEvent (var ev
: THKeyEvent
);
3005 inherited keyEvent(ev
);
3006 if (not ev
.eaten
) and (not ev
.cancelled
) and (enabled
) then
3008 if (ev
= 'Enter') or (ev
= 'Space') then
3018 // ////////////////////////////////////////////////////////////////////////// //
3019 procedure TUISwitchBox
.AfterConstruction ();
3025 mIcon
:= TGxContext
.TMarkIcon
.Checkbox
;
3026 mDefSize
:= TLaySize
.Create(uiContext
.textWidth(mText
)+3+uiContext
.iconMarkWidth(mIcon
), nmax(uiContext
.iconMarkHeight(mIcon
), uiContext
.textHeight(mText
)));
3027 mCtl4Style
:= 'switchbox';
3029 mBoolVar
:= @mChecked
;
3033 procedure TUISwitchBox
.cacheStyle (root
: TUIStyle
);
3035 inherited cacheStyle(root
);
3037 mSwitchColor
[ClrIdxActive
] := root
.get('switch-color', 'active', mCtl4Style
).asRGBADef(TGxRGBA
.Create(255, 255, 255));
3039 mSwitchColor
[ClrIdxDisabled
] := root
.get('switch-color', 'disabled', mCtl4Style
).asRGBADef(TGxRGBA
.Create(255, 255, 255));
3041 mSwitchColor
[ClrIdxInactive
] := root
.get('switch-color', 'inactive', mCtl4Style
).asRGBADef(TGxRGBA
.Create(255, 255, 255));
3045 procedure TUISwitchBox
.setText (const s
: AnsiString);
3047 inherited setText(s
);
3048 mDefSize
:= TLaySize
.Create(uiContext
.textWidth(mText
)+3+uiContext
.iconMarkWidth(mIcon
), nmax(uiContext
.iconMarkHeight(mIcon
), uiContext
.textHeight(mText
)));
3052 function TUISwitchBox
.parseProperty (const prname
: AnsiString; par
: TTextParser
): Boolean;
3054 if (strEquCI1251(prname
, 'checked')) then
3060 result
:= inherited parseProperty(prname
, par
);
3064 function TUISwitchBox
.getChecked (): Boolean;
3066 if (mBoolVar
<> nil) then result
:= mBoolVar
^ else result
:= false;
3070 procedure TUISwitchBox
.setVar (pvar
: PBoolean);
3072 if (pvar
= nil) then pvar
:= @mChecked
;
3073 if (pvar
<> mBoolVar
) then
3076 setChecked(mBoolVar
^);
3081 procedure TUISwitchBox
.drawControl (gx
, gy
: Integer);
3083 xpos
, ypos
, iwdt
, dy
: Integer;
3086 cidx
:= getColorIndex
;
3088 iwdt
:= uiContext
.iconMarkWidth(mIcon
);
3089 if (mHAlign
< 0) then xpos
:= 0
3090 else if (mHAlign
> 0) then xpos
:= mWidth
-(uiContext
.textWidth(mText
)+3+iwdt
)
3091 else xpos
:= (mWidth
-(uiContext
.textWidth(mText
)+3+iwdt
)) div 2;
3093 if (mVAlign
< 0) then ypos
:= 0
3094 else if (mVAlign
> 0) then ypos
:= mHeight
-uiContext
.textHeight(mText
)
3095 else ypos
:= (mHeight
-uiContext
.textHeight(mText
)) div 2;
3097 uiContext
.color
:= mBackColor
[cidx
];
3098 uiContext
.fillRect(gx
, gy
, mWidth
, mHeight
);
3100 uiContext
.color
:= mSwitchColor
[cidx
];
3101 if (uiContext
.iconMarkHeight(mIcon
) < uiContext
.textHeight(mText
)) then
3103 case uiContext
.textHeight(mText
) of
3108 uiContext
.drawIconMark(mIcon
, gx
, gy
+ypos
+uiContext
.textHeight(mText
)-uiContext
.iconMarkHeight(mIcon
)-dy
, checked
);
3112 uiContext
.drawIconMark(mIcon
, gx
, gy
, checked
);
3115 uiContext
.color
:= mTextColor
[cidx
];
3116 uiContext
.drawText(gx
+xpos
+3+iwdt
, gy
+ypos
, mText
);
3118 if (mHotChar
<> #0) and (mHotChar
<> ' ') then
3120 uiContext
.color
:= mHotColor
[cidx
];
3121 uiContext
.drawChar(gx
+xpos
+3+iwdt
+mHotOfs
, gy
+ypos
, mHotChar
);
3126 procedure TUISwitchBox
.mouseEvent (var ev
: THMouseEvent
);
3130 inherited mouseEvent(ev
);
3131 if (uiGrabCtl
= self
) then
3134 if (ev
= '-lmb') and focused
and toLocal(ev
.x
, ev
.y
, lx
, ly
) then
3140 if (ev
.eaten
) or (ev
.cancelled
) or (not enabled
) or not focused
then exit
;
3145 procedure TUISwitchBox
.keyEvent (var ev
: THKeyEvent
);
3147 inherited keyEvent(ev
);
3148 if (not ev
.eaten
) and (not ev
.cancelled
) and (enabled
) then
3150 if (ev
= 'Space') then
3160 // ////////////////////////////////////////////////////////////////////////// //
3161 procedure TUICheckBox
.AfterConstruction ();
3165 mBoolVar
:= @mChecked
;
3166 mIcon
:= TGxContext
.TMarkIcon
.Checkbox
;
3171 procedure TUICheckBox
.setChecked (v
: Boolean);
3177 procedure TUICheckBox
.doAction ();
3179 if (assigned(actionCB
)) then
3185 setChecked(not getChecked
);
3190 // ////////////////////////////////////////////////////////////////////////// //
3191 procedure TUIRadioBox
.AfterConstruction ();
3195 mBoolVar
:= @mChecked
;
3197 mIcon
:= TGxContext
.TMarkIcon
.Radiobox
;
3202 function TUIRadioBox
.parseProperty (const prname
: AnsiString; par
: TTextParser
): Boolean;
3204 if (strEquCI1251(prname
, 'group')) then
3206 mRadioGroup
:= par
.expectIdOrStr(true);
3207 if (getChecked
) then setChecked(true);
3211 if (strEquCI1251(prname
, 'checked')) then
3217 result
:= inherited parseProperty(prname
, par
);
3221 procedure TUIRadioBox
.setChecked (v
: Boolean);
3223 function resetGroup (ctl
: TUIControl
): Boolean;
3226 if (ctl
<> self
) and (ctl
is TUIRadioBox
) and (TUIRadioBox(ctl
).mRadioGroup
= mRadioGroup
) then
3228 TUIRadioBox(ctl
).mBoolVar
^ := false;
3234 if v
then topLevel
.forEachControl(resetGroup
);
3238 procedure TUIRadioBox
.doAction ();
3240 if (assigned(actionCB
)) then
3251 // ////////////////////////////////////////////////////////////////////////// //
3253 registerCtlClass(TUIHBox
, 'hbox');
3254 registerCtlClass(TUIVBox
, 'vbox');
3255 registerCtlClass(TUISpan
, 'span');
3256 registerCtlClass(TUILine
, 'line');
3257 registerCtlClass(TUITextLabel
, 'label');
3258 registerCtlClass(TUIStaticText
, 'static');
3259 registerCtlClass(TUIButton
, 'button');
3260 registerCtlClass(TUICheckBox
, 'checkbox');
3261 registerCtlClass(TUIRadioBox
, 'radiobox');
3263 uiContext
:= TGxContext
.Create();