X-Git-Url: http://deadsoftware.ru/gitweb?a=blobdiff_plain;f=src%2Fflexui%2Ffui_ctls.pas;h=12f710c21bbc3aae1845bc6b1c68bc8d8e5fd79e;hb=987c4a835a103345b59937e8e1be8524a6228712;hp=b7d5bfa2a27dca30cc392399161d64abca808b53;hpb=f9becfd79bc5b789311f7167304d426daed71577;p=d2df-sdl.git diff --git a/src/flexui/fui_ctls.pas b/src/flexui/fui_ctls.pas index b7d5bfa..12f710c 100644 --- a/src/flexui/fui_ctls.pas +++ b/src/flexui/fui_ctls.pas @@ -3,8 +3,7 @@ * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * the Free Software Foundation, version 3 of the License ONLY. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -64,17 +63,16 @@ type mCancel: Boolean; mDefault: Boolean; // colors + mStyleLoaded: Boolean; mCtl4Style: AnsiString; mBackColor: array[0..ClrIdxMax] of TGxRGBA; mTextColor: array[0..ClrIdxMax] of TGxRGBA; mFrameColor: array[0..ClrIdxMax] of TGxRGBA; mFrameTextColor: array[0..ClrIdxMax] of TGxRGBA; mFrameIconColor: array[0..ClrIdxMax] of TGxRGBA; - mDarken: array[0..ClrIdxMax] of Integer; // -1: none - - private - scis: TScissorSave; - scallowed: Boolean; + mSBarFullColor: array[0..ClrIdxMax] of TGxRGBA; + mSBarEmptyColor: array[0..ClrIdxMax] of TGxRGBA; + mDarken: array[0..ClrIdxMax] of Integer; // >255: none protected procedure updateStyle (); virtual; @@ -110,15 +108,15 @@ type procedure calcFullClientSize (); + procedure drawFrame (gx, gy, resx, thalign: Integer; const text: AnsiString; dbl: Boolean); + + protected + var savedClip: TGxRect; // valid only in `draw*()` calls //WARNING! do not call scissor functions outside `.draw*()` API! // set scissor to this rect (in local coords) - procedure setScissor (lx, ly, lw, lh: Integer); - // reset scissor to whole control - procedure resetScissor (fullArea: Boolean); inline; // "full area" means "with frame" - - // DO NOT USE! - // set scissor to this rect (in global coords) - procedure setScissorGLInternal (x, y, w, h: Integer); + procedure setScissor (lx, ly, lw, lh: Integer); // valid only in `draw*()` calls + procedure resetScissor (); inline; // only client area, w/o frame + procedure resetScissorNC (); inline; // full drawing area, with frame public actionCB: TActionCB; @@ -129,8 +127,6 @@ type mMaxSize: TLaySize; // maximum size mFlex: Integer; mHoriz: Boolean; - mCanWrap: Boolean; - mLineStart: Boolean; mHGroup: AnsiString; mVGroup: AnsiString; mAlign: Integer; @@ -151,9 +147,7 @@ type //procedure setMaxSize (const sz: TLaySize); inline; // max size; <0: set to some huge value function getFlex (): Integer; inline; // <=0: not flexible function isHorizBox (): Boolean; inline; // horizontal layout for children? - function canWrap (): Boolean; inline; // for horizontal boxes: can wrap children? for child: `false` means 'nonbreakable at *next* ctl' function noPad (): Boolean; inline; // ignore padding in box direction for this control - function isLineStart (): Boolean; inline; // `true` if this ctl should start a new line; ignored for vertical boxes function getAlign (): Integer; inline; // aligning in non-main direction: <0: left/up; 0: center; >0: right/down function getExpand (): Boolean; inline; // expanding in non-main direction: `true` will ignore align and eat all available space function getHGroup (): AnsiString; inline; // empty: not grouped @@ -169,8 +163,6 @@ type property flMaxSize: TLaySize read mMaxSize write mMaxSize; property flPadding: TLaySize read mPadding write mPadding; property flHoriz: Boolean read mHoriz write mHoriz; - property flCanWrap: Boolean read mCanWrap write mCanWrap; - property flLineStart: Boolean read mLineStart write mLineStart; property flAlign: Integer read mAlign write mAlign; property flExpand: Boolean read mExpand write mExpand; property flHGroup: AnsiString read mHGroup write mHGroup; @@ -237,10 +229,15 @@ type procedure doAction (); virtual; // so user controls can override it - procedure mouseEvent (var ev: THMouseEvent); virtual; // returns `true` if event was eaten - procedure keyEvent (var ev: THKeyEvent); virtual; // returns `true` if event was eaten - procedure keyEventPre (var ev: THKeyEvent); virtual; // will be called before dispatching the event - procedure keyEventPost (var ev: THKeyEvent); virtual; // will be called after if nobody processed the event + procedure onEvent (var ev: TFUIEvent); virtual; // general dispatcher + + procedure mouseEvent (var ev: TFUIEvent); virtual; + procedure mouseEventSink (var ev: TFUIEvent); virtual; + procedure mouseEventBubble (var ev: TFUIEvent); virtual; + + procedure keyEvent (var ev: TFUIEvent); virtual; + procedure keyEventSink (var ev: TFUIEvent); virtual; + procedure keyEventBubble (var ev: TFUIEvent); virtual; function prevSibling (): TUIControl; function nextSibling (): TUIControl; @@ -257,7 +254,7 @@ type procedure close (); // this closes *top-level* control public - property id: AnsiString read mId; + property id: AnsiString read mId write mId; property styleId: AnsiString read mStyleId; property scrollX: Integer read mScrollX write mScrollX; property scrollY: Integer read mScrollY write mScrollY; @@ -279,7 +276,7 @@ type TUITopWindow = class(TUIControl) private - type TXMode = (None, Drag, Scroll); + type TXMode = (None, Drag, VScroll, HScroll); private mTitle: AnsiString; @@ -313,8 +310,8 @@ type procedure drawControl (gx, gy: Integer); override; procedure drawControlPost (gx, gy: Integer); override; - procedure keyEvent (var ev: THKeyEvent); override; // returns `true` if event was eaten - procedure mouseEvent (var ev: THMouseEvent); override; // returns `true` if event was eaten + procedure keyEventBubble (var ev: TFUIEvent); override; // returns `true` if event was eaten + procedure mouseEvent (var ev: TFUIEvent); override; // returns `true` if event was eaten public property freeOnClose: Boolean read mFreeOnClose write mFreeOnClose; @@ -341,8 +338,8 @@ type procedure drawControl (gx, gy: Integer); override; - procedure mouseEvent (var ev: THMouseEvent); override; - procedure keyEvent (var ev: THKeyEvent); override; + procedure mouseEvent (var ev: TFUIEvent); override; + procedure keyEvent (var ev: TFUIEvent); override; public property caption: AnsiString read mCaption write setCaption; @@ -370,8 +367,6 @@ type procedure AfterConstruction (); override; // so it will be correctly initialized when created from parser function parseProperty (const prname: AnsiString; par: TTextParser): Boolean; override; - - procedure drawControl (gx, gy: Integer); override; end; // ////////////////////////////////////////////////////////////////////// // @@ -381,17 +376,9 @@ type function parseProperty (const prname: AnsiString; par: TTextParser): Boolean; override; - procedure drawControl (gx, gy: Integer); override; - end; - - TUIHLine = class(TUILine) - public - procedure AfterConstruction (); override; // so it will be correctly initialized when created from parser - end; + procedure layPrepare (); override; // called before registering control in layouter - TUIVLine = class(TUILine) - public - procedure AfterConstruction (); override; // so it will be correctly initialized when created from parser + procedure drawControl (gx, gy: Integer); override; end; // ////////////////////////////////////////////////////////////////////// // @@ -446,8 +433,8 @@ type procedure drawControl (gx, gy: Integer); override; - procedure mouseEvent (var ev: THMouseEvent); override; - procedure keyEventPost (var ev: THKeyEvent); override; + procedure mouseEvent (var ev: TFUIEvent); override; + procedure keyEventBubble (var ev: TFUIEvent); override; public property text: AnsiString read mText write setText; @@ -457,16 +444,42 @@ type // ////////////////////////////////////////////////////////////////////// // TUIButton = class(TUITextLabel) + protected + mSkipLayPrepare: Boolean; + mShadowSize: Integer; + mAddMarkers: Boolean; + mHideMarkers: Boolean; + mPushed: Boolean; + protected procedure setText (const s: AnsiString); override; + procedure cacheStyle (root: TUIStyle); override; + + procedure blurred (); override; + public procedure AfterConstruction (); override; // so it will be correctly initialized when created from parser + procedure layPrepare (); override; // called before registering control in layouter + procedure drawControl (gx, gy: Integer); override; - procedure mouseEvent (var ev: THMouseEvent); override; - procedure keyEvent (var ev: THKeyEvent); override; + procedure mouseEvent (var ev: TFUIEvent); override; + procedure keyEvent (var ev: TFUIEvent); override; + end; + + // ////////////////////////////////////////////////////////////////////// // + TUIButtonRound = class(TUIButton) + protected + procedure setText (const s: AnsiString); override; + + public + procedure AfterConstruction (); override; // so it will be correctly initialized when created from parser + + procedure layPrepare (); override; // called before registering control in layouter + + procedure drawControl (gx, gy: Integer); override; end; // ////////////////////////////////////////////////////////////////////// // @@ -474,8 +487,7 @@ type protected mBoolVar: PBoolean; mChecked: Boolean; - mCheckedStr: AnsiString; - mUncheckedStr: AnsiString; + mIcon: TGxContext.TMarkIcon; mSwitchColor: array[0..ClrIdxMax] of TGxRGBA; protected @@ -493,8 +505,8 @@ type procedure drawControl (gx, gy: Integer); override; - procedure mouseEvent (var ev: THMouseEvent); override; - procedure keyEvent (var ev: THKeyEvent); override; + procedure mouseEvent (var ev: TFUIEvent); override; + procedure keyEvent (var ev: TFUIEvent); override; procedure setVar (pvar: PBoolean); @@ -532,16 +544,21 @@ type // ////////////////////////////////////////////////////////////////////////// // -procedure uiMouseEvent (var evt: THMouseEvent); -procedure uiKeyEvent (var evt: THKeyEvent); +procedure uiDispatchEvent (var evt: TFUIEvent); procedure uiDraw (); +procedure uiFocus (); +procedure uiBlur (); + // ////////////////////////////////////////////////////////////////////////// // procedure uiAddWindow (ctl: TUIControl); procedure uiRemoveWindow (ctl: TUIControl); // will free window if `mFreeOnClose` is `true` function uiVisibleWindow (ctl: TUIControl): Boolean; +// this can return `nil` or disabled control +function uiGetFocusedCtl (): TUIControl; + procedure uiUpdateStyles (); @@ -550,9 +567,15 @@ procedure uiUpdateStyles (); procedure uiLayoutCtl (ctl: TUIControl); +// ////////////////////////////////////////////////////////////////////////// // +procedure uiInitialize (); +procedure uiDeinitialize (); + + // ////////////////////////////////////////////////////////////////////////// // var fuiRenderScale: Single = 1.0; + uiContext: TGxContext = nil; implementation @@ -562,6 +585,26 @@ uses utils; +var + uiInsideDispatcher: Boolean = false; + uiTopList: array of TUIControl = nil; + uiGrabCtl: TUIControl = nil; + + +// ////////////////////////////////////////////////////////////////////////// // +procedure uiDeinitialize (); +begin + FreeAndNil(uiContext); +end; + + +procedure uiInitialize (); +begin + if (uiContext <> nil) then raise Exception.Create('FlexUI already initialized'); + uiContext := TGxContext.Create(); +end; + + // ////////////////////////////////////////////////////////////////////////// // var ctlsToKill: array of TUIControl = nil; @@ -592,6 +635,7 @@ begin begin ctl := ctlsToKill[f]; if (ctl = nil) then break; + if (uiGrabCtl <> nil) and (ctl.isMyChild(uiGrabCtl)) then uiGrabCtl := nil; // just in case ctlsToKill[f] := nil; FreeAndNil(ctl); end; @@ -644,6 +688,7 @@ begin if (ctl = nil) then exit; lay := TFlexLayouter.Create(); try + if (not ctl.mStyleLoaded) then ctl.updateStyle(); if (ctl is TUITopWindow) and (TUITopWindow(ctl).fitToScreen) then TUITopWindow(ctl).flFitToScreen(); lay.setup(ctl); @@ -691,11 +736,6 @@ end; // ////////////////////////////////////////////////////////////////////////// // -var - uiTopList: array of TUIControl = nil; - uiGrabCtl: TUIControl = nil; - - procedure uiUpdateStyles (); var ctl: TUIControl; @@ -704,73 +744,180 @@ begin end; -procedure uiMouseEvent (var evt: THMouseEvent); +procedure uiDispatchEvent (var evt: TFUIEvent); var - ev: THMouseEvent; - f, c: Integer; - lx, ly: Integer; - ctmp: TUIControl; -begin - processKills(); - if (evt.eaten) or (evt.cancelled) then exit; - ev := evt; - ev.x := trunc(ev.x/fuiRenderScale); - ev.y := trunc(ev.y/fuiRenderScale); - ev.dx := trunc(ev.dx/fuiRenderScale); //FIXME - ev.dy := trunc(ev.dy/fuiRenderScale); //FIXME - try + ev: TFUIEvent; + destCtl: TUIControl; + + procedure doSink (ctl: TUIControl); + begin + if (ctl = nil) or (not ev.alive) then exit; + if (ctl.mParent <> nil) then + begin + doSink(ctl.mParent); + if (not ev.alive) then exit; + end; + //if (ctl = destCtl) then writeln(' SINK: MINE! <', ctl.className, '>'); + ev.setSinking(); + ctl.onEvent(ev); + if (ctl = destCtl) and (ev.alive) then + begin + ev.setMine(); + ctl.onEvent(ev); + end; + end; + + procedure dispatchTo (ctl: TUIControl); + begin + if (ctl = nil) then exit; + destCtl := ctl; + // sink + doSink(ctl); + // bubble + //ctl := ctl.mParent; // 'cause "mine" is processed in `doSink()` + while (ctl <> nil) and (ev.alive) do + begin + ev.setBubbling(); + ctl.onEvent(ev); + ctl := ctl.mParent; + end; + end; + + procedure doMouseEvent (); + var + doUngrab: Boolean; + ctl: TUIControl; + win: TUIControl; + lx, ly: Integer; + f, c: Integer; + begin + // pass mouse events to control with grab, if there is any if (uiGrabCtl <> nil) then begin - uiGrabCtl.mouseEvent(ev); - if (ev.release) and ((ev.bstate and (not ev.but)) = 0) then uiGrabCtl := nil; + //writeln('GRABBED: ', uiGrabCtl.className); + doUngrab := (ev.release) and ((ev.bstate and (not ev.but)) = 0); + dispatchTo(uiGrabCtl); + //FIXME: create API to get grabs, so control can regrab itself event on release + if (doUngrab) and (uiGrabCtl = destCtl) then uiGrabCtl := nil; ev.eat(); exit; end; - if (Length(uiTopList) > 0) and (uiTopList[High(uiTopList)].enabled) then uiTopList[High(uiTopList)].mouseEvent(ev); - if (not ev.eaten) and (not ev.cancelled) and (ev.press) then + // get top window + if (Length(uiTopList) > 0) then win := uiTopList[High(uiTopList)] else win := nil; + // check if we're still in top window + if (ev.press) and (win <> nil) and (not win.toLocal(0, 0, lx, ly)) then begin - for f := High(uiTopList) downto 0 do + // we have other windows too; check for window switching + for f := High(uiTopList)-1 downto 0 do begin - if uiTopList[f].toLocal(ev.x, ev.y, lx, ly) then + if (uiTopList[f].enabled) and (uiTopList[f].toLocal(ev.x, ev.y, lx, ly)) then begin - if (uiTopList[f].enabled) and (f <> High(uiTopList)) then - begin - uiTopList[High(uiTopList)].blurred(); - ctmp := uiTopList[f]; - uiGrabCtl := nil; - for c := f+1 to High(uiTopList) do uiTopList[c-1] := uiTopList[c]; - uiTopList[High(uiTopList)] := ctmp; - ctmp.activated(); - ctmp.mouseEvent(ev); - end; - ev.eat(); - exit; + // switch + win.blurred(); + win := uiTopList[f]; + for c := f+1 to High(uiTopList) do uiTopList[c-1] := uiTopList[c]; + uiTopList[High(uiTopList)] := win; + win.activated(); + break; end; end; end; - finally - if (ev.eaten) then evt.eat(); - if (ev.cancelled) then evt.cancel(); + // dispatch event + if (win <> nil) and (win.toLocal(ev.x, ev.y, lx, ly)) then + begin + ctl := win.controlAtXY(ev.x, ev.y); // don't allow disabled controls + if (ctl = nil) or (not ctl.canFocus) or (not ctl.enabled) then ctl := win; + // pass focus to another event and set grab, if necessary + if (ev.press) then + begin + // pass focus, if necessary + if (win.mFocused <> ctl) then + begin + if (win.mFocused <> nil) then win.mFocused.blurred(); + uiGrabCtl := ctl; + win.mFocused := ctl; + if (ctl <> win) then ctl.activated(); + end + else + begin + uiGrabCtl := ctl; + end; + end; + dispatchTo(ctl); + end; end; -end; - -procedure uiKeyEvent (var evt: THKeyEvent); var - ev: THKeyEvent; + svx, svy, svdx, svdy: Integer; + svscale: Single; + odp: Boolean; begin processKills(); - if (evt.eaten) or (evt.cancelled) then exit; + if (not evt.alive) then exit; + odp := uiInsideDispatcher; + uiInsideDispatcher := true; + //writeln('ENTER: FUI DISPATCH'); ev := evt; - ev.x := trunc(ev.x/fuiRenderScale); - ev.y := trunc(ev.y/fuiRenderScale); + // normalize mouse coordinates + svscale := fuiRenderScale; + ev.x := trunc(ev.x/svscale); + ev.y := trunc(ev.y/svscale); + ev.dx := trunc(ev.dx/svscale); //FIXME + ev.dy := trunc(ev.dy/svscale); //FIXME + svx := ev.x; + svy := ev.y; + svdx := ev.dx; + svdy := ev.dy; try - if (Length(uiTopList) > 0) and (uiTopList[High(uiTopList)].enabled) then uiTopList[High(uiTopList)].keyEvent(ev); - //if (ev.release) then begin ev.eat(); exit; end; + // "event grab" eats only mouse events + if (ev.mouse) then + begin + // we need to so some special processing here + doMouseEvent(); + end + else + begin + // simply dispatch to focused control + dispatchTo(uiGetFocusedCtl); + end; finally - if (ev.eaten) then evt.eat(); - if (ev.cancelled) then evt.cancel(); + uiInsideDispatcher := odp; + if (ev.x = svx) and (ev.y = svy) and (ev.dx = svdx) and (ev.dy = svdy) then + begin + // due to possible precision loss + svx := evt.x; + svy := evt.y; + svdx := evt.dx; + svdy := evt.dy; + evt := ev; + evt.x := svx; + evt.y := svy; + evt.dx := svdx; + evt.dy := svdy; + end + else + begin + // scale back + evt := ev; + evt.x := trunc(evt.x*svscale); + evt.y := trunc(evt.y*svscale); + evt.dx := trunc(evt.dx*svscale); + evt.dy := trunc(evt.dy*svscale); + end; end; + processKills(); + //writeln('EXIT: FUI DISPATCH'); +end; + +procedure uiFocus (); +begin + if (Length(uiTopList) > 0) and (uiTopList[High(uiTopList)].enabled) then uiTopList[High(uiTopList)].activated(); +end; + + +procedure uiBlur (); +begin + if (Length(uiTopList) > 0) and (uiTopList[High(uiTopList)].enabled) then uiTopList[High(uiTopList)].blurred(); end; @@ -780,7 +927,9 @@ var ctl: TUIControl; begin processKills(); - gxBeginUIDraw(fuiRenderScale); + //if (uiContext = nil) then uiContext := TGxContext.Create(); + gxSetContext(uiContext, fuiRenderScale); + uiContext.resetClip(); try for f := 0 to High(uiTopList) do begin @@ -789,11 +938,22 @@ begin if (f <> High(uiTopList)) then begin cidx := ctl.getColorIndex; - if (ctl.mDarken[cidx] > 0) then darkenRect(ctl.x0, ctl.y0, ctl.width, ctl.height, ctl.mDarken[cidx]); + uiContext.darkenRect(ctl.x0, ctl.y0, ctl.width, ctl.height, ctl.mDarken[cidx]); end; end; finally - gxEndUIDraw(); + gxSetContext(nil); + end; +end; + + +function uiGetFocusedCtl (): TUIControl; +begin + result := nil; + if (Length(uiTopList) > 0) and (uiTopList[High(uiTopList)].enabled) then + begin + result := uiTopList[High(uiTopList)].mFocused; + if (result = nil) then result := uiTopList[High(uiTopList)]; end; end; @@ -811,7 +971,7 @@ begin begin if (f <> High(uiTopList)) then begin - uiTopList[High(uiTopList)].blurred(); + if (Length(uiTopList) > 0) and (uiTopList[High(uiTopList)].enabled) then uiTopList[High(uiTopList)].blurred(); for c := f+1 to High(uiTopList) do uiTopList[c-1] := uiTopList[c]; uiTopList[High(uiTopList)] := ctl; ctl.activated(); @@ -819,10 +979,10 @@ begin exit; end; end; - if (Length(uiTopList) > 0) then uiTopList[High(uiTopList)].blurred(); + if (Length(uiTopList) > 0) and (uiTopList[High(uiTopList)].enabled) then uiTopList[High(uiTopList)].blurred(); SetLength(uiTopList, Length(uiTopList)+1); uiTopList[High(uiTopList)] := ctl; - ctl.updateStyle(); + if (not ctl.mStyleLoaded) then ctl.updateStyle(); ctl.activated(); end; @@ -841,6 +1001,7 @@ begin ctl.blurred(); for c := f+1 to High(uiTopList) do uiTopList[c-1] := uiTopList[c]; SetLength(uiTopList, Length(uiTopList)-1); + if (Length(uiTopList) > 0) and (uiTopList[High(uiTopList)].enabled) then uiTopList[High(uiTopList)].activated(); if (ctl is TUITopWindow) then begin try @@ -884,7 +1045,7 @@ begin mX := 0; mY := 0; mWidth := 64; - mHeight := 8; + mHeight := uiContext.charHeight(' '); mFrameWidth := 0; mFrameHeight := 0; mEnabled := true; @@ -892,32 +1053,50 @@ begin mChildren := nil; mFocused := nil; mEscClose := false; - scallowed := false; mDrawShadow := false; actionCB := nil; // layouter interface - //mDefSize := TLaySize.Create(64, 8); // default size - mDefSize := TLaySize.Create(0, 0); // default size + //mDefSize := TLaySize.Create(64, uiContext.charHeight(' ')); // default size + mDefSize := TLaySize.Create(0, 0); // default size: hidden control mMaxSize := TLaySize.Create(-1, -1); // maximum size mPadding := TLaySize.Create(0, 0); mNoPad := false; mFlex := 0; mHoriz := true; - mCanWrap := false; - mLineStart := false; mHGroup := ''; mVGroup := ''; mStyleId := ''; mCtl4Style := ''; mAlign := -1; // left/top mExpand := false; + mStyleLoaded := false; end; destructor TUIControl.Destroy (); var f, c: Integer; + doActivateOtherWin: Boolean = false; begin + if (uiInsideDispatcher) then raise Exception.Create('FlexUI: cannot destroy objects in event dispatcher'); + if (uiGrabCtl = self) then uiGrabCtl := nil; + // just in case, check if this is top-level shit + for f := 0 to High(uiTopList) do + begin + if (uiTopList[f] = self) then + begin + if (uiGrabCtl <> nil) and (isMyChild(uiGrabCtl)) then uiGrabCtl := nil; + for c := f+1 to High(uiTopList) do uiTopList[c-1] := uiTopList[c]; + SetLength(uiTopList, Length(uiTopList)-1); + doActivateOtherWin := true; + break; + end; + end; + if (doActivateOtherWin) and (Length(uiTopList) > 0) and (uiTopList[High(uiTopList)].enabled) then + begin + uiTopList[High(uiTopList)].activated(); + end; + // other checks if (mParent <> nil) then begin setFocused(false); @@ -969,6 +1148,7 @@ begin if (stl = nil) then stl := uiFindStyle(''); // default cacheStyle(stl); for ctl in mChildren do ctl.updateStyle(); + mStyleLoaded := true; end; procedure TUIControl.cacheStyle (root: TUIStyle); @@ -983,21 +1163,27 @@ begin mFrameColor[ClrIdxActive] := root.get('frame-color', 'active', cst).asRGBADef(TGxRGBA.Create(255, 255, 255)); mFrameTextColor[ClrIdxActive] := root.get('frame-text-color', 'active', cst).asRGBADef(TGxRGBA.Create(255, 255, 255)); mFrameIconColor[ClrIdxActive] := root.get('frame-icon-color', 'active', cst).asRGBADef(TGxRGBA.Create(0, 255, 0)); - mDarken[ClrIdxActive] := root.get('darken', 'active', cst).asInt(-1); + mSBarFullColor[ClrIdxActive] := root.get('scrollbar-full-color', 'active', cst).asRGBADef(TGxRGBA.Create(255, 255, 255)); + mSBarEmptyColor[ClrIdxActive] := root.get('scrollbar-empty-color', 'active', cst).asRGBADef(TGxRGBA.Create(128, 128, 128)); + mDarken[ClrIdxActive] := root.get('darken', 'active', cst).asInt(666); // disabled mBackColor[ClrIdxDisabled] := root.get('back-color', 'disabled', cst).asRGBADef(TGxRGBA.Create(0, 0, 128)); mTextColor[ClrIdxDisabled] := root.get('text-color', 'disabled', cst).asRGBADef(TGxRGBA.Create(127, 127, 127)); mFrameColor[ClrIdxDisabled] := root.get('frame-color', 'disabled', cst).asRGBADef(TGxRGBA.Create(127, 127, 127)); mFrameTextColor[ClrIdxDisabled] := root.get('frame-text-color', 'disabled', cst).asRGBADef(TGxRGBA.Create(127, 127, 127)); mFrameIconColor[ClrIdxDisabled] := root.get('frame-icon-color', 'disabled', cst).asRGBADef(TGxRGBA.Create(0, 127, 0)); - mDarken[ClrIdxDisabled] := root.get('darken', 'disabled', cst).asInt(-1); + mSBarFullColor[ClrIdxDisabled] := root.get('scrollbar-full-color', 'disabled', cst).asRGBADef(TGxRGBA.Create(127, 127, 127)); + mSBarEmptyColor[ClrIdxDisabled] := root.get('scrollbar-empty-color', 'disabled', cst).asRGBADef(TGxRGBA.Create(98, 98, 98)); + mDarken[ClrIdxDisabled] := root.get('darken', 'disabled', cst).asInt(666); // inactive mBackColor[ClrIdxInactive] := root.get('back-color', 'inactive', cst).asRGBADef(TGxRGBA.Create(0, 0, 128)); mTextColor[ClrIdxInactive] := root.get('text-color', 'inactive', cst).asRGBADef(TGxRGBA.Create(255, 255, 255)); mFrameColor[ClrIdxInactive] := root.get('frame-color', 'inactive', cst).asRGBADef(TGxRGBA.Create(255, 255, 255)); mFrameTextColor[ClrIdxInactive] := root.get('frame-text-color', 'inactive', cst).asRGBADef(TGxRGBA.Create(255, 255, 255)); mFrameIconColor[ClrIdxInactive] := root.get('frame-icon-color', 'inactive', cst).asRGBADef(TGxRGBA.Create(0, 255, 0)); - mDarken[ClrIdxInactive] := root.get('darken', 'inactive', cst).asInt(-1); + mSBarFullColor[ClrIdxInactive] := root.get('scrollbar-full-color', 'inactive', cst).asRGBADef(TGxRGBA.Create(255, 255, 255)); + mSBarEmptyColor[ClrIdxInactive] := root.get('scrollbar-empty-color', 'inactive', cst).asRGBADef(TGxRGBA.Create(128, 128, 128)); + mDarken[ClrIdxInactive] := root.get('darken', 'inactive', cst).asInt(666); end; @@ -1007,9 +1193,7 @@ function TUIControl.getMaxSize (): TLaySize; inline; begin result := mLayMaxSize function TUIControl.getPadding (): TLaySize; inline; begin result := mPadding; end; function TUIControl.getFlex (): Integer; inline; begin result := mFlex; end; function TUIControl.isHorizBox (): Boolean; inline; begin result := mHoriz; end; -function TUIControl.canWrap (): Boolean; inline; begin result := mCanWrap; end; function TUIControl.noPad (): Boolean; inline; begin result := mNoPad; end; -function TUIControl.isLineStart (): Boolean; inline; begin result := mLineStart; end; function TUIControl.getAlign (): Integer; inline; begin result := mAlign; end; function TUIControl.getExpand (): Boolean; inline; begin result := mExpand; end; function TUIControl.getHGroup (): AnsiString; inline; begin result := mHGroup; end; @@ -1026,14 +1210,23 @@ begin end; mWidth := asize.w; mHeight := asize.h; + if (mLayMaxSize.w >= 0) then mWidth := nmin(mWidth, mLayMaxSize.w); + if (mLayMaxSize.h >= 0) then mHeight := nmin(mHeight, mLayMaxSize.h); end; procedure TUIControl.layPrepare (); begin mLayDefSize := mDefSize; - mLayMaxSize := mMaxSize; - if (mLayMaxSize.w >= 0) then mLayMaxSize.w += mFrameWidth*2; - if (mLayMaxSize.h >= 0) then mLayMaxSize.h += mFrameHeight*2; + if (mLayDefSize.w <> 0) or (mLayDefSize.h <> 0) then + begin + mLayMaxSize := mMaxSize; + if (mLayMaxSize.w >= 0) then begin mLayDefSize.w += mFrameWidth*2; mLayMaxSize.w += mFrameWidth*2; end; + if (mLayMaxSize.h >= 0) then begin mLayDefSize.h += mFrameHeight*2; mLayMaxSize.h += mFrameHeight*2; end; + end + else + begin + mLayMaxSize := TLaySize.Create(0, 0); + end; end; @@ -1274,8 +1467,6 @@ begin if (strEquCI1251(prname, 'padding')) then begin mPadding := parsePadding(par); exit; end; if (strEquCI1251(prname, 'nopad')) then begin mNoPad := true; exit; end; // flags - if (strEquCI1251(prname, 'wrap')) then begin mCanWrap := parseBool(par); exit; end; - if (strEquCI1251(prname, 'linestart')) then begin mLineStart := parseBool(par); exit; end; if (strEquCI1251(prname, 'expand')) then begin mExpand := parseBool(par); exit; end; // align if (strEquCI1251(prname, 'align')) then begin mAlign := parseAnyAlign(par); exit; end; @@ -1497,7 +1688,7 @@ begin mParent.getDrawRect(gx, gy, wdt, hgt); if (wdt > 0) and (hgt > 0) then begin - if not intersectRect(gx, gy, wdt, hgt, cgx, cgy, mWidth, mHeight) then + if (not intersectRect(gx, gy, wdt, hgt, cgx, cgy, mWidth, mHeight)) then begin wdt := 0; hgt := 0; @@ -1838,45 +2029,40 @@ end; // ////////////////////////////////////////////////////////////////////////// // -procedure TUIControl.setScissorGLInternal (x, y, w, h: Integer); -begin - if not scallowed then exit; - x := trunc(x*fuiRenderScale); - y := trunc(y*fuiRenderScale); - w := trunc(w*fuiRenderScale); - h := trunc(h*fuiRenderScale); - scis.combineRect(x, y, w, h); -end; - procedure TUIControl.setScissor (lx, ly, lw, lh: Integer); var gx, gy, wdt, hgt, cgx, cgy: Integer; begin - if not scallowed then exit; - - if not intersectRect(lx, ly, lw, lh, 0, 0, mWidth, mHeight) then + if (not intersectRect(lx, ly, lw, lh, 0, 0, mWidth, mHeight)) then begin - scis.combineRect(0, 0, 0, 0); + uiContext.clip := TGxRect.Create(0, 0, 0, 0); exit; end; getDrawRect(gx, gy, wdt, hgt); + toGlobal(lx, ly, cgx, cgy); - if not intersectRect(gx, gy, wdt, hgt, cgx, cgy, lw, lh) then + if (not intersectRect(gx, gy, wdt, hgt, cgx, cgy, lw, lh)) then begin - scis.combineRect(0, 0, 0, 0); + uiContext.clip := TGxRect.Create(0, 0, 0, 0); exit; end; - setScissorGLInternal(gx, gy, wdt, hgt); + uiContext.clip := savedClip; + uiContext.combineClip(TGxRect.Create(gx, gy, wdt, hgt)); + //uiContext.clip := TGxRect.Create(gx, gy, wdt, hgt); +end; + +procedure TUIControl.resetScissorNC (); inline; +begin + setScissor(0, 0, mWidth, mHeight); end; -procedure TUIControl.resetScissor (fullArea: Boolean); inline; +procedure TUIControl.resetScissor (); inline; begin - if not scallowed then exit; - if (fullArea) then + if ((mFrameWidth <= 0) and (mFrameHeight <= 0)) then begin - setScissor(0, 0, mWidth, mHeight); + resetScissorNC(); end else begin @@ -1886,163 +2072,214 @@ end; // ////////////////////////////////////////////////////////////////////////// // +procedure TUIControl.drawFrame (gx, gy, resx, thalign: Integer; const text: AnsiString; dbl: Boolean); +var + cidx, tx, tw: Integer; +begin + if (mFrameWidth < 1) or (mFrameHeight < 1) then exit; + cidx := getColorIndex; + uiContext.color := mFrameColor[cidx]; + case mFrameHeight of + 8: + begin + if dbl then + begin + uiContext.rect(gx+3, gy+3, mWidth-6, mHeight-6); + uiContext.rect(gx+5, gy+5, mWidth-10, mHeight-10); + end + else + begin + uiContext.rect(gx+4, gy+4, mWidth-8, mHeight-8); + end; + end; + 14: + begin + if dbl then + begin + uiContext.rect(gx+3, gy+3+3, mWidth-6, mHeight-6-6); + uiContext.rect(gx+5, gy+5+3, mWidth-10, mHeight-10-6); + end + else + begin + uiContext.rect(gx+4, gy+4+3, mWidth-8, mHeight-8-6); + end; + end; + 16: + begin + if dbl then + begin + uiContext.rect(gx+3, gy+3+4, mWidth-6, mHeight-6-8); + uiContext.rect(gx+5, gy+5+4, mWidth-10, mHeight-10-8); + end + else + begin + uiContext.rect(gx+4, gy+4+4, mWidth-8, mHeight-8-8); + end; + end; + else + begin + //TODO! + if dbl then + begin + end + else + begin + end; + end; + end; + + // title + if (Length(text) > 0) then + begin + if (resx < 0) then resx := 0; + tw := uiContext.textWidth(text); + setScissor(mFrameWidth+resx, 0, mWidth-mFrameWidth*2-resx, mFrameHeight); + if (thalign < 0) then tx := gx+resx+mFrameWidth+2 + else if (thalign > 0) then tx := gx+mWidth-mFrameWidth-1-tw + else tx := (gx+resx+mFrameWidth)+(mWidth-mFrameWidth*2-resx-tw) div 2; + uiContext.color := mBackColor[cidx]; + uiContext.fillRect(tx-2, gy, tw+4, mFrameHeight); + uiContext.color := mFrameTextColor[cidx]; + uiContext.drawText(tx, gy, text); + end; +end; + + procedure TUIControl.draw (); var f: Integer; gx, gy: Integer; + begin - if (mWidth < 1) or (mHeight < 1) then exit; + if (mWidth < 1) or (mHeight < 1) or (uiContext = nil) or (not uiContext.active) then exit; toGlobal(0, 0, gx, gy); - scis.save(true); // scissoring enabled + savedClip := uiContext.clip; try - scallowed := true; - resetScissor(true); // full area + resetScissorNC(); drawControl(gx, gy); - resetScissor(false); // client area + resetScissor(); for f := 0 to High(mChildren) do mChildren[f].draw(); - resetScissor(true); // full area + resetScissorNC(); drawControlPost(gx, gy); finally - scis.restore(); - scallowed := false; + uiContext.clip := savedClip; end; end; procedure TUIControl.drawControl (gx, gy: Integer); begin - //if (mParent = nil) then darkenRect(gx, gy, mWidth, mHeight, 64); end; procedure TUIControl.drawControlPost (gx, gy: Integer); begin - // shadow - if mDrawShadow and (mWidth > 0) and (mHeight > 0) then + // shadow for top-level controls + if (mParent = nil) and (mDrawShadow) and (mWidth > 0) and (mHeight > 0) then begin - setScissorGLInternal(gx+8, gy+8, mWidth, mHeight); - darkenRect(gx+mWidth, gy+8, 8, mHeight, 128); - darkenRect(gx+8, gy+mHeight, mWidth-8, 8, 128); + uiContext.resetClip(); + uiContext.darkenRect(gx+mWidth, gy+8, 8, mHeight, 128); + uiContext.darkenRect(gx+8, gy+mHeight, mWidth-8, 8, 128); end; end; // ////////////////////////////////////////////////////////////////////////// // -procedure TUIControl.mouseEvent (var ev: THMouseEvent); -var - ctl: TUIControl; +procedure TUIControl.onEvent (var ev: TFUIEvent); begin - if (not enabled) then exit; - if (mWidth < 1) or (mHeight < 1) then exit; - ctl := controlAtXY(ev.x, ev.y); - if (ctl = nil) then exit; - if (ctl.canFocus) and (ev.press) then + if (not ev.alive) or (not enabled) then exit; + //if (ev.mine) then writeln(' MINE: <', className, '>'); + if (ev.key) then begin - if (ctl <> topLevel.mFocused) then ctl.setFocused(true); - uiGrabCtl := ctl; + if (ev.sinking) then keyEventSink(ev) + else if (ev.bubbling) then keyEventBubble(ev) + else if (ev.mine) then keyEvent(ev); + end + else if (ev.mouse) then + begin + if (ev.sinking) then mouseEventSink(ev) + else if (ev.bubbling) then mouseEventBubble(ev) + else if (ev.mine) then mouseEvent(ev); end; - if (ctl <> self) then ctl.mouseEvent(ev); - //ev.eat(); end; -procedure TUIControl.keyEvent (var ev: THKeyEvent); +procedure TUIControl.mouseEventSink (var ev: TFUIEvent); +begin +end; - function doPreKey (ctl: TUIControl): Boolean; - begin - if (not ctl.enabled) then begin result := false; exit; end; - ctl.keyEventPre(ev); - result := (ev.eaten) or (ev.cancelled); // stop if event was consumed - end; +procedure TUIControl.mouseEventBubble (var ev: TFUIEvent); +begin +end; + +procedure TUIControl.mouseEvent (var ev: TFUIEvent); +begin +end; - function doPostKey (ctl: TUIControl): Boolean; - begin - if (not ctl.enabled) then begin result := false; exit; end; - ctl.keyEventPost(ev); - result := (ev.eaten) or (ev.cancelled); // stop if event was consumed - end; +procedure TUIControl.keyEventSink (var ev: TFUIEvent); var ctl: TUIControl; begin if (not enabled) then exit; - if (ev.eaten) or (ev.cancelled) then exit; - // call pre-key - if (mParent = nil) then - begin - forEachControl(doPreKey); - if (ev.eaten) or (ev.cancelled) then exit; - end; - // focused control should process keyboard first - if (topLevel.mFocused <> self) and isMyChild(topLevel.mFocused) and (topLevel.mFocused.enabled) then + if (not ev.alive) then exit; + // for top-level controls + if (mParent <> nil) then exit; + if (mEscClose) and (ev = 'Escape') then begin - // bubble keyboard event - ctl := topLevel.mFocused; - while (ctl <> nil) and (ctl <> self) do + if (not assigned(closeRequestCB)) or (closeRequestCB(self)) then begin - ctl.keyEvent(ev); - if (ev.eaten) or (ev.cancelled) then exit; - ctl := ctl.mParent; + uiRemoveWindow(self); end; + ev.eat(); + exit; end; - // for top-level controls - if (mParent = nil) then + if (ev = 'Enter') or (ev = 'C-Enter') then begin - if (ev = 'S-Tab') then - begin - ctl := findPrevFocus(mFocused, true); - if (ctl <> nil) and (ctl <> mFocused) then ctl.setFocused(true); - ev.eat(); - exit; - end; - if (ev = 'Tab') then + ctl := findDefaulControl(); + if (ctl <> nil) then begin - ctl := findNextFocus(mFocused, true); - if (ctl <> nil) and (ctl <> mFocused) then ctl.setFocused(true); ev.eat(); + ctl.doAction(); exit; end; - if (ev = 'Enter') or (ev = 'C-Enter') then - begin - ctl := findDefaulControl(); - if (ctl <> nil) then - begin - ev.eat(); - ctl.doAction(); - exit; - end; - end; - if (ev = 'Escape') then - begin - ctl := findCancelControl(); - if (ctl <> nil) then - begin - ev.eat(); - ctl.doAction(); - exit; - end; - end; - if mEscClose and (ev = 'Escape') then + end; + if (ev = 'Escape') then + begin + ctl := findCancelControl(); + if (ctl <> nil) then begin - if (not assigned(closeRequestCB)) or (closeRequestCB(self)) then - begin - uiRemoveWindow(self); - end; ev.eat(); + ctl.doAction(); exit; end; - // call post-keys - if (ev.eaten) or (ev.cancelled) then exit; - forEachControl(doPostKey); end; end; - -procedure TUIControl.keyEventPre (var ev: THKeyEvent); +procedure TUIControl.keyEventBubble (var ev: TFUIEvent); +var + ctl: TUIControl; begin + if (not enabled) then exit; + if (not ev.alive) then exit; + // for top-level controls + if (mParent <> nil) then exit; + if (ev = 'S-Tab') then + begin + ctl := findPrevFocus(mFocused, true); + if (ctl <> nil) and (ctl <> mFocused) then ctl.setFocused(true); + ev.eat(); + exit; + end; + if (ev = 'Tab') then + begin + ctl := findNextFocus(mFocused, true); + if (ctl <> nil) and (ctl <> mFocused) then ctl.setFocused(true); + ev.eat(); + exit; + end; end; - -procedure TUIControl.keyEventPost (var ev: THKeyEvent); +procedure TUIControl.keyEvent (var ev: TFUIEvent); begin end; @@ -2060,12 +2297,15 @@ begin inherited; mFitToScreen := true; mFrameWidth := 8; - mFrameHeight := 8; - if (mWidth < mFrameWidth*2+3*8) then mWidth := mFrameWidth*2+3*8; + mFrameHeight := uiContext.charHeight(#184); + if (mWidth < mFrameWidth*2+uiContext.iconWinWidth(TGxContext.TWinIcon.Close)) then mWidth := mFrameWidth*2+uiContext.iconWinWidth(TGxContext.TWinIcon.Close); if (mHeight < mFrameHeight*2) then mHeight := mFrameHeight*2; if (Length(mTitle) > 0) then begin - if (mWidth < Length(mTitle)*8+mFrameWidth*2+3*8) then mWidth := Length(mTitle)*8+mFrameWidth*2+3*8; + if (mWidth < uiContext.textWidth(mTitle)+mFrameWidth*2+uiContext.iconWinWidth(TGxContext.TWinIcon.Close)) then + begin + mWidth := uiContext.textWidth(mTitle)+mFrameWidth*2+uiContext.iconWinWidth(TGxContext.TWinIcon.Close); + end; end; mCanFocus := false; mDragScroll := TXMode.None; @@ -2074,6 +2314,8 @@ begin mInClose := false; closeCB := nil; mCtl4Style := 'window'; + mDefSize.w := nmax(1, mDefSize.w); + mDefSize.h := nmax(1, mDefSize.h); end; @@ -2124,62 +2366,57 @@ begin end; +// ////////////////////////////////////////////////////////////////////////// // procedure TUITopWindow.drawControl (gx, gy: Integer); begin - fillRect(gx, gy, mWidth, mHeight, mBackColor[getColorIndex]); + uiContext.color := mBackColor[getColorIndex]; + uiContext.fillRect(gx, gy, mWidth, mHeight); end; - procedure TUITopWindow.drawControlPost (gx, gy: Integer); var - cidx: Integer; - tx, hgt, sbhgt: Integer; + cidx, iwdt, ihgt: Integer; + ybot, xend, vhgt, vwdt: Integer; begin cidx := getColorIndex; + iwdt := uiContext.iconWinWidth(TGxContext.TWinIcon.Close); if (mDragScroll = TXMode.Drag) then begin - drawRectUI(gx+4, gy+4, mWidth-8, mHeight-8, mFrameColor[cidx]); + drawFrame(gx, gy, iwdt, 0, mTitle, false); end else begin - drawRectUI(gx+3, gy+3, mWidth-6, mHeight-6, mFrameColor[cidx]); - drawRectUI(gx+5, gy+5, mWidth-10, mHeight-10, mFrameColor[cidx]); + ihgt := uiContext.iconWinHeight(TGxContext.TWinIcon.Close); + drawFrame(gx, gy, iwdt, 0, mTitle, true); // vertical scroll bar - hgt := mHeight-mFrameHeight*2; - if (hgt > 0) and (mFullSize.h > hgt) then - begin - //writeln(mTitle, ': height=', mHeight-mFrameHeight*2, '; fullsize=', mFullSize.toString); - sbhgt := mHeight-mFrameHeight*2+2; - fillRect(gx+mWidth-mFrameWidth+1, gy+7, mFrameWidth-3, sbhgt, mFrameColor[cidx]); - hgt += mScrollY; - if (hgt > mFullSize.h) then hgt := mFullSize.h; - hgt := sbhgt*hgt div mFullSize.h; - if (hgt > 0) then - begin - setScissor(mWidth-mFrameWidth+1, 7, mFrameWidth-3, sbhgt); - darkenRect(gx+mWidth-mFrameWidth+1, gy+7+hgt, mFrameWidth-3, sbhgt, 128); - end; + vhgt := mHeight-mFrameHeight*2; + if (mFullSize.h > vhgt) then + begin + ybot := mScrollY+vhgt; + resetScissorNC(); + uiContext.drawVSBar(gx+mWidth-mFrameWidth+1, gy+mFrameHeight-1, mFrameWidth-3, vhgt+2, ybot, 0, mFullSize.h, mSBarFullColor[cidx], mSBarEmptyColor[cidx]); + end; + // horizontal scroll bar + vwdt := mWidth-mFrameWidth*2; + if (mFullSize.w > vwdt) then + begin + xend := mScrollX+vwdt; + resetScissorNC(); + uiContext.drawHSBar(gx+mFrameWidth+1, gy+mHeight-mFrameHeight+1, vwdt-2, mFrameHeight-3, xend, 0, mFullSize.w, mSBarFullColor[cidx], mSBarEmptyColor[cidx]); end; // frame icon - setScissor(mFrameWidth, 0, 3*8, 8); - fillRect(gx+mFrameWidth, gy, 3*8, 8, mBackColor[cidx]); - drawText8(gx+mFrameWidth, gy, '[ ]', mFrameColor[cidx]); - if mInClose then drawText8(gx+mFrameWidth+7, gy, '#', mFrameIconColor[cidx]) - else drawText8(gx+mFrameWidth+7, gy, '*', mFrameIconColor[cidx]); + setScissor(mFrameWidth, 0, iwdt, ihgt); + uiContext.color := mBackColor[cidx]; + uiContext.fillRect(gx+mFrameWidth, gy, iwdt, ihgt); + uiContext.color := mFrameIconColor[cidx]; + uiContext.drawIconWin(TGxContext.TWinIcon.Close, gx+mFrameWidth, gy, mInClose); end; - // title - if (Length(mTitle) > 0) then - begin - setScissor(mFrameWidth+3*8, 0, mWidth-mFrameWidth*2-3*8, 8); - tx := (gx+3*8)+((mWidth-3*8)-Length(mTitle)*8) div 2; - fillRect(tx-3, gy, Length(mTitle)*8+3+2, 8, mBackColor[cidx]); - drawText8(tx, gy, mTitle, mFrameTextColor[cidx]); - end; - // shadow + // shadow (no need to reset scissor, as draw should do it) inherited drawControlPost(gx, gy); end; +// ////////////////////////////////////////////////////////////////////////// // procedure TUITopWindow.activated (); begin if (mFocused = nil) or (mFocused = self) then @@ -2201,10 +2438,10 @@ begin end; -procedure TUITopWindow.keyEvent (var ev: THKeyEvent); +procedure TUITopWindow.keyEventBubble (var ev: TFUIEvent); begin inherited keyEvent(ev); - if (ev.eaten) or (ev.cancelled) or (not enabled) {or (not getFocused)} then exit; + if (not ev.alive) or (not enabled) {or (not getFocused)} then exit; if (ev = 'M-F3') then begin if (not assigned(closeRequestCB)) or (closeRequestCB(self)) then @@ -2217,10 +2454,11 @@ begin end; -procedure TUITopWindow.mouseEvent (var ev: THMouseEvent); +procedure TUITopWindow.mouseEvent (var ev: TFUIEvent); var lx, ly: Integer; - hgt, sbhgt: Integer; + vhgt, ytop: Integer; + vwdt, xend: Integer; begin if (not enabled) then exit; if (mWidth < 1) or (mHeight < 1) then exit; @@ -2236,26 +2474,23 @@ begin exit; end; - if (mDragScroll = TXMode.Scroll) then + if (mDragScroll = TXMode.VScroll) then begin - // check for vertical scrollbar ly := ev.y-mY; - if (ly < 7) then - begin - mScrollY := 0; - end - else - begin - sbhgt := mHeight-mFrameHeight*2+2; - hgt := mHeight-mFrameHeight*2; - if (hgt > 0) and (mFullSize.h > hgt) then - begin - hgt := (mFullSize.h*(ly-7) div (sbhgt-1))-(mHeight-mFrameHeight*2); - mScrollY := nmax(0, hgt); - hgt := mHeight-mFrameHeight*2; - if (mScrollY+hgt > mFullSize.h) then mScrollY := nmax(0, mFullSize.h-hgt); - end; - end; + vhgt := mHeight-mFrameHeight*2; + ytop := uiContext.sbarPos(ly, mFrameHeight-1, vhgt+2, 0, mFullSize.h)-vhgt; + mScrollY := nmax(0, ytop); + if (ev.release) and (ev.but = ev.Left) then mDragScroll := TXMode.None; + ev.eat(); + exit; + end; + + if (mDragScroll = TXMode.HScroll) then + begin + lx := ev.x-mX; + vwdt := mWidth-mFrameWidth*2; + xend := uiContext.sbarPos(lx, mFrameWidth+1, vwdt-2, 0, mFullSize.w)-vwdt; + mScrollX := nmax(0, xend); if (ev.release) and (ev.but = ev.Left) then mDragScroll := TXMode.None; ev.eat(); exit; @@ -2265,10 +2500,10 @@ begin begin if (ev.press) then begin - if (ly < 8) then + if (ly < mFrameHeight) then begin uiGrabCtl := self; - if (lx >= mFrameWidth) and (lx < mFrameWidth+3*8) then + if (lx >= mFrameWidth) and (lx < mFrameWidth+uiContext.iconWinWidth(TGxContext.TWinIcon.Close)) then begin //uiRemoveWindow(self); mWaitingClose := true; @@ -2284,17 +2519,30 @@ begin exit; end; // check for vertical scrollbar - if (lx >= mWidth-mFrameWidth+1) and (ly >= 7) and (ly < mHeight-mFrameHeight+1) then + if (lx >= mWidth-mFrameWidth+1) and (ly >= mFrameHeight-1) and (ly < mHeight-mFrameHeight+2) then + begin + vhgt := mHeight-mFrameHeight*2; + if (mFullSize.h > vhgt) then + begin + uiGrabCtl := self; + mDragScroll := TXMode.VScroll; + ev.eat(); + ytop := uiContext.sbarPos(ly, mFrameHeight-1, vhgt+2, 0, mFullSize.h)-vhgt; + mScrollY := nmax(0, ytop); + exit; + end; + end; + // check for horizontal scrollbar + if (ly >= mHeight-mFrameHeight+1) and (lx >= mFrameWidth+1) and (lx < mWidth-mFrameWidth-1) then begin - sbhgt := mHeight-mFrameHeight*2+2; - hgt := mHeight-mFrameHeight*2; - if (hgt > 0) and (mFullSize.h > hgt) then + vwdt := mWidth-mFrameWidth*2; + if (mFullSize.w > vwdt) then begin - hgt := (mFullSize.h*(ly-7) div (sbhgt-1))-(mHeight-mFrameHeight*2); - mScrollY := nmax(0, hgt); uiGrabCtl := self; - mDragScroll := TXMode.Scroll; + mDragScroll := TXMode.HScroll; ev.eat(); + xend := uiContext.sbarPos(lx, mFrameWidth+1, vwdt-2, 0, mFullSize.w)-vwdt; + mScrollX := nmax(0, xend); exit; end; end; @@ -2314,7 +2562,7 @@ begin begin if mWaitingClose then begin - if (lx >= mFrameWidth) and (lx < mFrameWidth+3*8) then + if (lx >= mFrameWidth) and (lx < mFrameWidth+uiContext.iconWinWidth(TGxContext.TWinIcon.Close)) then begin if (not assigned(closeRequestCB)) or (closeRequestCB(self)) then begin @@ -2332,7 +2580,7 @@ begin begin if mWaitingClose then begin - mInClose := (lx >= mFrameWidth) and (lx < mFrameWidth+3*8); + mInClose := (lx >= mFrameWidth) and (lx < mFrameWidth+uiContext.iconWinWidth(TGxContext.TWinIcon.Close)); ev.eat(); exit; end; @@ -2362,20 +2610,21 @@ begin mCanFocus := false; mHAlign := -1; // left mCtl4Style := 'box'; + mDefSize := TLaySize.Create(-1, -1); end; procedure TUIBox.setCaption (const acap: AnsiString); begin mCaption := acap; - mDefSize := TLaySize.Create(Length(mCaption)*8+3, 8); + mDefSize := TLaySize.Create(uiContext.textWidth(mCaption)+3, uiContext.textHeight(mCaption)); end; procedure TUIBox.setHasFrame (v: Boolean); begin mHasFrame := v; - if (mHasFrame) then begin mFrameWidth := 8; mFrameHeight := 8; end else begin mFrameWidth := 0; mFrameHeight := 0; end; + if (mHasFrame) then begin mFrameWidth := 8; mFrameHeight := uiContext.charHeight(#184); end else begin mFrameWidth := 0; mFrameHeight := 0; end; if (mHasFrame) then mNoPad := true; end; @@ -2420,49 +2669,53 @@ end; procedure TUIBox.drawControl (gx, gy: Integer); var cidx: Integer; - xpos: Integer; + //xpos: Integer; begin cidx := getColorIndex; - fillRect(gx, gy, mWidth, mHeight, mBackColor[cidx]); - if mHasFrame then + uiContext.color := mBackColor[cidx]; + uiContext.fillRect(gx, gy, mWidth, mHeight); + if (mHasFrame) then begin // draw frame - drawRectUI(gx+3, gy+3, mWidth-6, mHeight-6, mFrameColor[cidx]); + drawFrame(gx, gy, 0, mHAlign, mCaption, false); end; - // draw caption - if (Length(mCaption) > 0) then + // no frame -- no caption + { + else if (Length(mCaption) > 0) then begin + // draw caption if (mHAlign < 0) then xpos := 3 - else if (mHAlign > 0) then xpos := mWidth-mFrameWidth*2-Length(mCaption)*8 - else xpos := (mWidth-mFrameWidth*2-Length(mCaption)*8) div 2; + else if (mHAlign > 0) then xpos := mWidth-mFrameWidth*2-uiContext.textWidth(mCaption) + else xpos := (mWidth-mFrameWidth*2-uiContext.textWidth(mCaption)) div 2; xpos += gx+mFrameWidth; - setScissor(mFrameWidth+1, 0, mWidth-mFrameWidth-2, 8); - if mHasFrame then fillRect(xpos-3, gy, Length(mCaption)*8+4, 8, mBackColor[cidx]); - drawText8(xpos, gy, mCaption, mFrameTextColor[cidx]); + setScissor(mFrameWidth+1, 0, mWidth-mFrameWidth-2, uiContext.textHeight(mCaption)); + uiContext.color := mFrameTextColor[cidx]; + uiContext.drawText(xpos, gy, mCaption); end; + } end; -procedure TUIBox.mouseEvent (var ev: THMouseEvent); +procedure TUIBox.mouseEvent (var ev: TFUIEvent); var lx, ly: Integer; begin inherited mouseEvent(ev); - if (not ev.eaten) and (not ev.cancelled) and (enabled) and toLocal(ev.x, ev.y, lx, ly) then + if (ev.alive) and (enabled) and toLocal(ev.x, ev.y, lx, ly) then begin ev.eat(); end; end; -procedure TUIBox.keyEvent (var ev: THKeyEvent); +procedure TUIBox.keyEvent (var ev: TFUIEvent); var dir: Integer = 0; cur, ctl: TUIControl; begin inherited keyEvent(ev); - if (ev.eaten) or (ev.cancelled) or (not ev.press) or (not enabled) or (not getActive) then exit; + if (not ev.alive) or (not ev.press) or (not enabled) or (not getActive) then exit; if (Length(mChildren) = 0) then exit; if (mHoriz) and (ev = 'Left') then dir := -1 else if (mHoriz) and (ev = 'Right') then dir := 1 @@ -2516,6 +2769,7 @@ begin mCanFocus := false; mNoPad := true; mCtl4Style := 'span'; + mDefSize := TLaySize.Create(-1, -1); end; @@ -2526,11 +2780,6 @@ begin end; -procedure TUISpan.drawControl (gx, gy: Integer); -begin -end; - - // ////////////////////////////////////////////////////////////////////// // procedure TUILine.AfterConstruction (); begin @@ -2539,6 +2788,7 @@ begin mExpand := true; mCanFocus := false; mCtl4Style := 'line'; + mDefSize := TLaySize.Create(-1, -1); end; @@ -2549,37 +2799,31 @@ begin end; -procedure TUILine.drawControl (gx, gy: Integer); -var - cidx: Integer; +procedure TUILine.layPrepare (); begin - cidx := getColorIndex; - if mHoriz then + inherited layPrepare(); + if (mParent <> nil) then mHoriz := not mParent.mHoriz; + if (mHoriz) then begin - drawHLine(gx, gy+(mHeight div 2), mWidth, mTextColor[cidx]); + if (mLayDefSize.w < 0) then mLayDefSize.w := 1; + if (mLayDefSize.h < 0) then mLayDefSize.h := 7; end else begin - drawVLine(gx+(mWidth div 2), gy, mHeight, mTextColor[cidx]); + if (mLayDefSize.w < 0) then mLayDefSize.w := 7; + if (mLayDefSize.h < 0) then mLayDefSize.h := 1; end; end; -// ////////////////////////////////////////////////////////////////////////// // -procedure TUIHLine.AfterConstruction (); -begin - inherited; - mHoriz := true; - mDefSize.h := 7; -end; - - -// ////////////////////////////////////////////////////////////////////////// // -procedure TUIVLine.AfterConstruction (); +procedure TUILine.drawControl (gx, gy: Integer); +var + cidx: Integer; begin - inherited; - mHoriz := false; - mDefSize.w := 7; + cidx := getColorIndex; + uiContext.color := mTextColor[cidx]; + if mHoriz then uiContext.hline(gx, gy+(mHeight div 2), mWidth) + else uiContext.vline(gx+(mWidth div 2), gy, mHeight); end; @@ -2593,7 +2837,6 @@ begin mHoriz := true; // nobody cares mHeader := false; mLine := false; - mDefSize.h := 8; mCtl4Style := 'static'; end; @@ -2601,7 +2844,7 @@ end; procedure TUIStaticText.setText (const atext: AnsiString); begin mText := atext; - mDefSize := TLaySize.Create(Length(mText)*8, 8); + mDefSize := TLaySize.Create(uiContext.textWidth(mText), uiContext.textHeight(mText)); end; @@ -2639,29 +2882,29 @@ procedure TUIStaticText.drawControl (gx, gy: Integer); var xpos, ypos: Integer; cidx: Integer; - clr: TGxRGBA; begin cidx := getColorIndex; - fillRect(gx, gy, mWidth, mHeight, mBackColor[cidx]); + uiContext.color := mBackColor[cidx]; + uiContext.fillRect(gx, gy, mWidth, mHeight); if (mHAlign < 0) then xpos := 0 - else if (mHAlign > 0) then xpos := mWidth-Length(mText)*8 - else xpos := (mWidth-Length(mText)*8) div 2; + else if (mHAlign > 0) then xpos := mWidth-uiContext.textWidth(mText) + else xpos := (mWidth-uiContext.textWidth(mText)) div 2; if (Length(mText) > 0) then begin - if (mHeader) then clr := mFrameTextColor[cidx] else clr := mTextColor[cidx]; + if (mHeader) then uiContext.color := mFrameTextColor[cidx] else uiContext.color := mTextColor[cidx]; if (mVAlign < 0) then ypos := 0 - else if (mVAlign > 0) then ypos := mHeight-8 - else ypos := (mHeight-8) div 2; + else if (mVAlign > 0) then ypos := mHeight-uiContext.textHeight(mText) + else ypos := (mHeight-uiContext.textHeight(mText)) div 2; - drawText8(gx+xpos, gy+ypos, mText, clr); + uiContext.drawText(gx+xpos, gy+ypos, mText); end; if (mLine) then begin - if (mHeader) then clr := mFrameColor[cidx] else clr := mTextColor[cidx]; + if (mHeader) then uiContext.color := mFrameColor[cidx] else uiContext.color := mTextColor[cidx]; if (mVAlign < 0) then ypos := 0 else if (mVAlign > 0) then ypos := mHeight-1 @@ -2670,12 +2913,12 @@ begin if (Length(mText) = 0) then begin - drawHLine(gx, ypos, mWidth, clr); + uiContext.hline(gx, ypos, mWidth); end else begin - drawHLine(gx, ypos, xpos-1, clr); - drawHLine(gx+xpos+Length(mText)*8, ypos, mWidth, clr); + uiContext.hline(gx, ypos, xpos-1); + uiContext.hline(gx+xpos+uiContext.textWidth(mText), ypos, mWidth); end; end; end; @@ -2688,7 +2931,6 @@ begin mHAlign := -1; mVAlign := 0; mCanFocus := false; - mDefSize := TLaySize.Create(Length(mText)*8, 8); mCtl4Style := 'label'; mLinkId := ''; end; @@ -2730,7 +2972,7 @@ begin if (mHotChar = #0) then begin mHotChar := s[f]; - mHotOfs := Length(mText)*8; + mHotOfs := Length(mText); end; mText += s[f]; end; @@ -2742,7 +2984,13 @@ begin Inc(f); end; end; - mDefSize := TLaySize.Create(Length(mText)*8, 8); + // fix hotchar offset + if (mHotChar <> #0) and (mHotOfs > 0) then + begin + mHotOfs := uiContext.textWidth(Copy(mText, 1, mHotOfs+1))-uiContext.charWidth(mText[mHotOfs+1]); + end; + // fix size + mDefSize := TLaySize.Create(uiContext.textWidth(mText), uiContext.textHeight(mText)); end; @@ -2776,33 +3024,36 @@ var cidx: Integer; begin cidx := getColorIndex; - fillRect(gx, gy, mWidth, mHeight, mBackColor[cidx]); + uiContext.color := mBackColor[cidx]; + uiContext.fillRect(gx, gy, mWidth, mHeight); if (Length(mText) > 0) then begin if (mHAlign < 0) then xpos := 0 - else if (mHAlign > 0) then xpos := mWidth-Length(mText)*8 - else xpos := (mWidth-Length(mText)*8) div 2; + else if (mHAlign > 0) then xpos := mWidth-uiContext.textWidth(mText) + else xpos := (mWidth-uiContext.textWidth(mText)) div 2; if (mVAlign < 0) then ypos := 0 - else if (mVAlign > 0) then ypos := mHeight-8 - else ypos := (mHeight-8) div 2; + else if (mVAlign > 0) then ypos := mHeight-uiContext.textHeight(mText) + else ypos := (mHeight-uiContext.textHeight(mText)) div 2; - drawText8(gx+xpos, gy+ypos, mText, mTextColor[cidx]); + uiContext.color := mTextColor[cidx]; + uiContext.drawText(gx+xpos, gy+ypos, mText); if (Length(mLinkId) > 0) and (mHotChar <> #0) and (mHotChar <> ' ') then begin - drawText8(gx+xpos+8+mHotOfs, gy+ypos, mHotChar, mHotColor[cidx]); + uiContext.color := mHotColor[cidx]; + uiContext.drawChar(gx+xpos+mHotOfs, gy+ypos, mHotChar); end; end; end; -procedure TUITextLabel.mouseEvent (var ev: THMouseEvent); +procedure TUITextLabel.mouseEvent (var ev: TFUIEvent); var lx, ly: Integer; begin inherited mouseEvent(ev); - if (not ev.eaten) and (not ev.cancelled) and (enabled) and toLocal(ev.x, ev.y, lx, ly) then + if (ev.alive) and (enabled) and toLocal(ev.x, ev.y, lx, ly) then begin ev.eat(); end; @@ -2828,11 +3079,11 @@ begin end; -procedure TUITextLabel.keyEventPost (var ev: THKeyEvent); +procedure TUITextLabel.keyEventBubble (var ev: TFUIEvent); begin if (not enabled) then exit; if (mHotChar = #0) then exit; - if (ev.eaten) or (ev.cancelled) or (not ev.press) then exit; + if (not ev.alive) or (not ev.press) then exit; if (ev.kstate <> ev.ModAlt) then exit; if (not ev.isHot(mHotChar)) then exit; ev.eat(); @@ -2845,51 +3096,193 @@ end; procedure TUIButton.AfterConstruction (); begin inherited; - mHAlign := -1; + mHAlign := 0; mVAlign := 0; + mShadowSize := 0; mCanFocus := true; - mDefSize := TLaySize.Create(Length(mText)*8+8, 10); + mDefSize := TLaySize.Create(uiContext.textWidth(mText)+uiContext.textWidth('[ ]'), uiContext.textHeight(mText)); mCtl4Style := 'button'; + mSkipLayPrepare := false; + mAddMarkers := false; + mHideMarkers := false; +end; + + +procedure TUIButton.cacheStyle (root: TUIStyle); +var + sz: Integer = 0; +begin + inherited cacheStyle(root); + // shadow size + sz := nmax(0, root.get('shadow-size', 'active', mCtl4Style).asInt(0)); + sz := nmax(sz, root.get('shadow-size', 'disabled', mCtl4Style).asInt(0)); + sz := nmax(sz, root.get('shadow-size', 'inactive', mCtl4Style).asInt(0)); + mShadowSize := sz; + // markers mode + mAddMarkers := root.get('add-markers', 'active', mCtl4Style).asBool(false); + mAddMarkers := mAddMarkers or root.get('add-markers', 'disabled', mCtl4Style).asBool(false); + mAddMarkers := mAddMarkers or root.get('add-markers', 'inactive', mCtl4Style).asBool(false); + // hide markers? + mHideMarkers := root.get('hide-markers', 'active', mCtl4Style).asBool(false); + mHideMarkers := mHideMarkers or root.get('hide-markers', 'disabled', mCtl4Style).asBool(false); + mHideMarkers := mHideMarkers or root.get('hide-markers', 'inactive', mCtl4Style).asBool(false); end; procedure TUIButton.setText (const s: AnsiString); begin inherited setText(s); - mDefSize := TLaySize.Create(Length(mText)*8+8*2, 10); + if (mHideMarkers) then + begin + mDefSize := TLaySize.Create(uiContext.textWidth(mText)+10, uiContext.textHeight(mText)); + end + else if (mAddMarkers) then + begin + mDefSize := TLaySize.Create(uiContext.textWidth(mText)+uiContext.textWidth('[<>]'), uiContext.textHeight(mText)); + end + else + begin + mDefSize := TLaySize.Create(uiContext.textWidth(mText)+uiContext.textWidth('<>'), uiContext.textHeight(mText)); + end; +end; + + +procedure TUIButton.layPrepare (); +var + ods: TLaySize; + ww: Integer; +begin + if (not mSkipLayPrepare) then + begin + ods := mDefSize; + if (ods.w <> 0) or (ods.h <> 0) then + begin + mDefSize := TLaySize.Create(uiContext.textWidth(mText), uiContext.textHeight(mText)); + if (mHideMarkers) then + begin + ww := 10; + end + else if (mAddMarkers) then + begin + if (mDefault) then ww := uiContext.textWidth('[< >]') + else if (mCancel) then ww := uiContext.textWidth('[{ }]') + else ww := uiContext.textWidth('[ ]'); + end + else + begin + ww := nmax(0, uiContext.textWidth('< >')); + ww := nmax(ww, uiContext.textWidth('{ }')); + ww := nmax(ww, uiContext.textWidth('[ ]')); + end; + mDefSize.w += ww+mShadowSize; + mDefSize.h += mShadowSize; + end; + end + else + begin + ods := TLaySize.Create(0, 0); // fpc is dumb! + end; + inherited layPrepare(); + if (not mSkipLayPrepare) then mDefSize := ods; +end; + + +procedure TUIButton.blurred (); +begin + mPushed := false; end; procedure TUIButton.drawControl (gx, gy: Integer); var - xpos, ypos: Integer; + wdt, hgt: Integer; + xpos, ypos, xofsl, xofsr, sofs: Integer; cidx: Integer; + lch, rch: AnsiChar; + lstr, rstr: AnsiString; begin cidx := getColorIndex; - fillRect(gx+1, gy, mWidth-2, mHeight, mBackColor[cidx]); - fillRect(gx, gy+1, 1, mHeight-2, mBackColor[cidx]); - fillRect(gx+mWidth-1, gy+1, 1, mHeight-2, mBackColor[cidx]); + wdt := mWidth-mShadowSize; + hgt := mHeight-mShadowSize; + if (mPushed) {or (cidx = ClrIdxActive)} then + begin + sofs := mShadowSize; + gx += mShadowSize; + gy += mShadowSize; + end + else + begin + sofs := 0; + if (mShadowSize > 0) then + begin + uiContext.darkenRect(gx+mShadowSize, gy+hgt, wdt, mShadowSize, 96); + uiContext.darkenRect(gx+wdt, gy+mShadowSize, mShadowSize, hgt-mShadowSize, 96); + end; + end; + + uiContext.color := mBackColor[cidx]; + uiContext.fillRect(gx, gy, wdt, hgt); - if (Length(mText) > 0) then + if (mVAlign < 0) then ypos := 0 + else if (mVAlign > 0) then ypos := hgt-uiContext.textHeight(mText) + else ypos := (hgt-uiContext.textHeight(mText)) div 2; + ypos += gy; + + uiContext.color := mTextColor[cidx]; + + if (mHideMarkers) then begin - if (mHAlign < 0) then xpos := 0 - else if (mHAlign > 0) then xpos := mWidth-Length(mText)*8 - else xpos := (mWidth-Length(mText)*8) div 2; + xofsl := 5; + xofsr := 5; + end + else + begin + if (mAddMarkers) then + begin + if (mDefault) then begin lstr := '[< '; rstr := ' >]'; end + else if (mCancel) then begin lstr := '[{ '; rstr := ' }]'; end + else begin lstr := '[ '; rstr := ' ]'; end; + xofsl := uiContext.textWidth(lstr); + xofsr := uiContext.textWidth(rstr); + uiContext.drawText(gx, ypos, lstr); + uiContext.drawText(gx+wdt-uiContext.textWidth(rstr), ypos, rstr); + end + else + begin + xofsl := nmax(0, uiContext.textWidth('< ')); + xofsl := nmax(xofsl, uiContext.textWidth('{ ')); + xofsl := nmax(xofsl, uiContext.textWidth('[ ')); + xofsr := nmax(0, uiContext.textWidth(' >')); + xofsr := nmax(xofsr, uiContext.textWidth(' }')); + xofsr := nmax(xofsr, uiContext.textWidth(' ]')); + if (mDefault) then begin lch := '<'; rch := '>'; end + else if (mCancel) then begin lch := '{'; rch := '}'; end + else begin lch := '['; rch := ']'; end; + uiContext.drawChar(gx, ypos, lch); + uiContext.drawChar(gx+wdt-uiContext.charWidth(rch), ypos, rch); + end; + end; - if (mVAlign < 0) then ypos := 0 - else if (mVAlign > 0) then ypos := mHeight-8 - else ypos := (mHeight-8) div 2; + if (Length(mText) > 0) then + begin + if (mHAlign < 0) then xpos := 0 + else begin xpos := wdt-xofsl-xofsr-uiContext.textWidth(mText); if (mHAlign = 0) then xpos := xpos div 2; end; + xpos += xofsl; - setScissor(8, 0, mWidth-16, mHeight); - drawText8(gx+xpos+8, gy+ypos, mText, mTextColor[cidx]); + setScissor(sofs+xofsl, sofs, wdt-xofsl-xofsr, hgt); + uiContext.drawText(gx+xpos, ypos, mText); - if (mHotChar <> #0) and (mHotChar <> ' ') then drawText8(gx+xpos+8+mHotOfs, gy+ypos, mHotChar, mHotColor[cidx]); + if (mHotChar <> #0) and (mHotChar <> ' ') then + begin + uiContext.color := mHotColor[cidx]; + uiContext.drawChar(gx+xpos+mHotOfs, ypos, mHotChar); + end; end; end; -procedure TUIButton.mouseEvent (var ev: THMouseEvent); +procedure TUIButton.mouseEvent (var ev: TFUIEvent); var lx, ly: Integer; begin @@ -2897,32 +3290,119 @@ begin if (uiGrabCtl = self) then begin ev.eat(); - if (ev = '-lmb') and focused and toLocal(ev.x, ev.y, lx, ly) then + mPushed := toLocal(ev.x, ev.y, lx, ly); + if (ev = '-lmb') and (focused) and (mPushed) then begin + mPushed := false; doAction(); end; exit; end; - if (ev.eaten) or (ev.cancelled) or (not enabled) or not focused then exit; + if (not ev.alive) or (not enabled) or (not focused) then exit; + mPushed := true; ev.eat(); end; -procedure TUIButton.keyEvent (var ev: THKeyEvent); +procedure TUIButton.keyEvent (var ev: TFUIEvent); begin inherited keyEvent(ev); - if (not ev.eaten) and (not ev.cancelled) and (enabled) then + if (ev.alive) and (enabled) then begin - if (ev = 'Enter') or (ev = 'Space') then + if (ev = '+Enter') or (ev = '+Space') then begin + focused := true; + mPushed := true; ev.eat(); - doAction(); + exit; + end; + if (focused) and ((ev = '-Enter') or (ev = '-Space')) then + begin + if (mPushed) then + begin + mPushed := false; + ev.eat(); + doAction(); + end + else + begin + ev.eat(); + end; exit; end; end; end; +// ////////////////////////////////////////////////////////////////////////// // +procedure TUIButtonRound.AfterConstruction (); +begin + inherited; + mHAlign := -1; + mVAlign := 0; + mCanFocus := true; + mDefSize := TLaySize.Create(uiContext.textWidth(mText)+8*2, uiContext.textHeight(mText)+2); + mCtl4Style := 'button-round'; + mSkipLayPrepare := true; +end; + + +procedure TUIButtonRound.setText (const s: AnsiString); +begin + inherited setText(s); + mDefSize := TLaySize.Create(uiContext.textWidth(mText)+8*2, uiContext.textHeight(mText)+2); +end; + + +procedure TUIButtonRound.layPrepare (); +var + ods: TLaySize; +begin + ods := mDefSize; + if (ods.w <> 0) or (ods.h <> 0) then + begin + mDefSize := TLaySize.Create(uiContext.textWidth(mText)+8*2, uiContext.textHeight(mText)+2); + end; + inherited layPrepare(); + mDefSize := ods; +end; + + +procedure TUIButtonRound.drawControl (gx, gy: Integer); +var + xpos, ypos: Integer; + cidx: Integer; +begin + cidx := getColorIndex; + + uiContext.color := mBackColor[cidx]; + uiContext.fillRect(gx+1, gy, mWidth-2, mHeight); + uiContext.fillRect(gx, gy+1, 1, mHeight-2); + uiContext.fillRect(gx+mWidth-1, gy+1, 1, mHeight-2); + + if (Length(mText) > 0) then + begin + if (mHAlign < 0) then xpos := 0 + else if (mHAlign > 0) then xpos := mWidth-uiContext.textWidth(mText) + else xpos := (mWidth-uiContext.textWidth(mText)) div 2; + + if (mVAlign < 0) then ypos := 0 + else if (mVAlign > 0) then ypos := mHeight-uiContext.textHeight(mText) + else ypos := (mHeight-uiContext.textHeight(mText)) div 2; + + setScissor(8, 0, mWidth-16, mHeight); + uiContext.color := mTextColor[cidx]; + uiContext.drawText(gx+xpos+8, gy+ypos, mText); + + if (mHotChar <> #0) and (mHotChar <> ' ') then + begin + uiContext.color := mHotColor[cidx]; + uiContext.drawChar(gx+xpos+8+mHotOfs, gy+ypos, mHotChar); + end; + end; +end; + + // ////////////////////////////////////////////////////////////////////////// // procedure TUISwitchBox.AfterConstruction (); begin @@ -2930,7 +3410,8 @@ begin mHAlign := -1; mVAlign := 0; mCanFocus := true; - mDefSize := TLaySize.Create(Length(mText)*8+8*3, 8); + mIcon := TGxContext.TMarkIcon.Checkbox; + mDefSize := TLaySize.Create(uiContext.textWidth(mText)+3+uiContext.iconMarkWidth(mIcon), nmax(uiContext.iconMarkHeight(mIcon), uiContext.textHeight(mText))); mCtl4Style := 'switchbox'; mChecked := false; mBoolVar := @mChecked; @@ -2952,7 +3433,7 @@ end; procedure TUISwitchBox.setText (const s: AnsiString); begin inherited setText(s); - mDefSize := TLaySize.Create(Length(mText)*8+8*3, 8); + mDefSize := TLaySize.Create(uiContext.textWidth(mText)+3+uiContext.iconMarkWidth(mIcon), nmax(uiContext.iconMarkHeight(mIcon), uiContext.textHeight(mText))); end; @@ -2987,47 +3468,50 @@ end; procedure TUISwitchBox.drawControl (gx, gy: Integer); var - xpos, ypos: Integer; + xpos, ypos, iwdt, dy: Integer; cidx: Integer; begin cidx := getColorIndex; + iwdt := uiContext.iconMarkWidth(mIcon); if (mHAlign < 0) then xpos := 0 - else if (mHAlign > 0) then xpos := mWidth-(Length(mText)+4)*8 - else xpos := (mWidth-(Length(mText)+4)*8) div 2; + else if (mHAlign > 0) then xpos := mWidth-(uiContext.textWidth(mText)+3+iwdt) + else xpos := (mWidth-(uiContext.textWidth(mText)+3+iwdt)) div 2; if (mVAlign < 0) then ypos := 0 - else if (mVAlign > 0) then ypos := mHeight-8 - else ypos := (mHeight-8) div 2; + else if (mVAlign > 0) then ypos := mHeight-uiContext.textHeight(mText) + else ypos := (mHeight-uiContext.textHeight(mText)) div 2; + uiContext.color := mBackColor[cidx]; + uiContext.fillRect(gx, gy, mWidth, mHeight); - fillRect(gx, gy, mWidth, mHeight, mBackColor[cidx]); - - if (checked) then + uiContext.color := mSwitchColor[cidx]; + if (uiContext.iconMarkHeight(mIcon) < uiContext.textHeight(mText)) then begin - if (Length(mCheckedStr) <> 3) or (mCheckedStr[2] <> '*') then - begin - drawText8(gx+xpos, gy+ypos, mCheckedStr, mSwitchColor[cidx]); - end - else - begin - drawText8(gx+xpos, gy+ypos, mCheckedStr[1], mSwitchColor[cidx]); - drawText8(gx+xpos+2*8, gy+ypos, mCheckedStr[3], mSwitchColor[cidx]); - drawText8(gx+xpos+7, gy+ypos, '*', mSwitchColor[cidx]); + case uiContext.textHeight(mText) of + 14: dy := 2; + 16: dy := 3; + else dy := 1; end; + uiContext.drawIconMark(mIcon, gx, gy+ypos+uiContext.textHeight(mText)-uiContext.iconMarkHeight(mIcon)-dy, checked); end else begin - drawText8(gx+xpos, gy+ypos, mUncheckedStr, mSwitchColor[cidx]); + uiContext.drawIconMark(mIcon, gx, gy, checked); end; - drawText8(gx+xpos+8*3, gy+ypos, mText, mTextColor[cidx]); + uiContext.color := mTextColor[cidx]; + uiContext.drawText(gx+xpos+3+iwdt, gy+ypos, mText); - if (mHotChar <> #0) and (mHotChar <> ' ') then drawText8(gx+xpos+8*3+mHotOfs, gy+ypos, mHotChar, mHotColor[cidx]); + if (mHotChar <> #0) and (mHotChar <> ' ') then + begin + uiContext.color := mHotColor[cidx]; + uiContext.drawChar(gx+xpos+3+iwdt+mHotOfs, gy+ypos, mHotChar); + end; end; -procedure TUISwitchBox.mouseEvent (var ev: THMouseEvent); +procedure TUISwitchBox.mouseEvent (var ev: TFUIEvent); var lx, ly: Integer; begin @@ -3041,15 +3525,15 @@ begin end; exit; end; - if (ev.eaten) or (ev.cancelled) or (not enabled) or not focused then exit; + if (not ev.alive) or (not enabled) or not focused then exit; ev.eat(); end; -procedure TUISwitchBox.keyEvent (var ev: THKeyEvent); +procedure TUISwitchBox.keyEvent (var ev: TFUIEvent); begin inherited keyEvent(ev); - if (not ev.eaten) and (not ev.cancelled) and (enabled) then + if (ev.alive) and (enabled) then begin if (ev = 'Space') then begin @@ -3067,8 +3551,8 @@ begin inherited; mChecked := false; mBoolVar := @mChecked; - mCheckedStr := '[x]'; - mUncheckedStr := '[ ]'; + mIcon := TGxContext.TMarkIcon.Checkbox; + setText(''); end; @@ -3097,9 +3581,9 @@ begin inherited; mChecked := false; mBoolVar := @mChecked; - mCheckedStr := '(*)'; - mUncheckedStr := '( )'; mRadioGroup := ''; + mIcon := TGxContext.TMarkIcon.Radiobox; + setText(''); end; @@ -3153,15 +3637,27 @@ end; // ////////////////////////////////////////////////////////////////////////// // +var + oldFocus: procedure () = nil; + oldBlur: procedure () = nil; + +procedure onWinFocus (); begin uiFocus(); if (assigned(oldFocus)) then oldFocus(); end; +procedure onWinBlur (); begin fuiResetKMState(true); uiBlur(); if (assigned(oldBlur)) then oldBlur(); end; + initialization registerCtlClass(TUIHBox, 'hbox'); registerCtlClass(TUIVBox, 'vbox'); registerCtlClass(TUISpan, 'span'); - registerCtlClass(TUIHLine, 'hline'); - registerCtlClass(TUIVLine, 'vline'); + registerCtlClass(TUILine, 'line'); registerCtlClass(TUITextLabel, 'label'); registerCtlClass(TUIStaticText, 'static'); + registerCtlClass(TUIButtonRound, 'round-button'); registerCtlClass(TUIButton, 'button'); registerCtlClass(TUICheckBox, 'checkbox'); registerCtlClass(TUIRadioBox, 'radiobox'); + + oldFocus := winFocusCB; + oldBlur := winBlurCB; + winFocusCB := onWinFocus; + winBlurCB := onWinBlur; end.