DEADSOFTWARE

Holmes now require "data/flexui.wad" (it is not fatal to not have this file; Holmes...
[d2df-sdl.git] / src / flexui / fui_ctls.pas
index 7f722fea97090a79a967d319758fefc3e020ea09..ef5ab367e43e2bd11e0d8fe558c20dfb2fca4f52 100644 (file)
@@ -64,6 +64,7 @@ type
     mCancel: Boolean;
     mDefault: Boolean;
     // colors
+    mStyleLoaded: Boolean;
     mCtl4Style: AnsiString;
     mBackColor: array[0..ClrIdxMax] of TGxRGBA;
     mTextColor: array[0..ClrIdxMax] of TGxRGBA;
@@ -437,18 +438,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;
+
   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;
   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;
+
   // ////////////////////////////////////////////////////////////////////// //
   TUISwitchBox = class(TUITextLabel)
   protected
@@ -624,6 +649,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);
@@ -804,7 +830,7 @@ begin
   if (Length(uiTopList) > 0) 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;
 
@@ -890,6 +916,7 @@ begin
   mCtl4Style := '';
   mAlign := -1; // left/top
   mExpand := false;
+  mStyleLoaded := false;
 end;
 
 
@@ -948,6 +975,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);
@@ -1010,7 +1038,7 @@ end;
 procedure TUIControl.layPrepare ();
 begin
   mLayDefSize := mDefSize;
-  if (mLayDefSize.w <> 0) and (mLayDefSize.h <> 0) then
+  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;
@@ -2931,51 +2959,182 @@ end;
 procedure TUIButton.AfterConstruction ();
 begin
   inherited;
-  mHAlign := -1;
+  mHAlign := 0;
   mVAlign := 0;
+  mShadowSize := 0;
   mCanFocus := true;
-  mDefSize := TLaySize.Create(uiContext.textWidth(mText)+8*2, uiContext.textHeight(mText)+2);
+  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(uiContext.textWidth(mText)+8*2, uiContext.textHeight(mText)+2);
+  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.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;
 
+  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+1, gy, mWidth-2, mHeight);
-  uiContext.fillRect(gx, gy+1, 1, mHeight-2);
-  uiContext.fillRect(gx+mWidth-1, gy+1, 1, mHeight-2);
+  //setScissor(sofs, sofs, wdt, hgt);
+  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-uiContext.textWidth(mText)
-    else xpos := (mWidth-uiContext.textWidth(mText)) 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-uiContext.textHeight(mText)
-    else ypos := (mHeight-uiContext.textHeight(mText)) 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);
-    uiContext.color := mTextColor[cidx];
-    uiContext.drawText(gx+xpos+8, gy+ypos, mText);
+    //setScissor(xofsl+sofs, sofs, wdt-xofsl-xofsr, hgt);
+    uiContext.drawText(gx+xpos, ypos, mText);
 
     if (mHotChar <> #0) and (mHotChar <> ' ') then
     begin
       uiContext.color := mHotColor[cidx];
-      uiContext.drawChar(gx+xpos+8+mHotOfs, gy+ypos, mHotChar);
+      uiContext.drawChar(gx+xpos+mHotOfs, ypos, mHotChar);
     end;
   end;
 end;
@@ -2989,13 +3148,16 @@ 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;
+  mPushed := true;
   ev.eat();
 end;
 
@@ -3015,6 +3177,75 @@ begin
 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
@@ -3256,6 +3487,7 @@ initialization
   registerCtlClass(TUILine, 'line');
   registerCtlClass(TUITextLabel, 'label');
   registerCtlClass(TUIStaticText, 'static');
+  registerCtlClass(TUIButtonRound, 'round-button');
   registerCtlClass(TUIButton, 'button');
   registerCtlClass(TUICheckBox, 'checkbox');
   registerCtlClass(TUIRadioBox, 'radiobox');