Browse Source

Merge pull request #239 from bgrabitmap/dev-bgracontrols

Dev bgracontrols v9.0.2
circular17 7 months ago
parent
commit
6273bf7789
71 changed files with 9167 additions and 2114 deletions
  1. 25 13
      .github/workflows/make.pas
  2. 1 1
      .github/workflows/make.yml
  3. 20 12
      README.md
  4. 1 1
      atshapelinebgra.pas
  5. 262 64
      bccombobox.pas
  6. 1 1
      bcfilters.pas
  7. 5 7
      bcfluentprogressring.pas
  8. 9 25
      bckeyboard.pas
  9. 11 29
      bcnumerickeyboard.pas
  10. 0 57
      bcreg.pas
  11. 33 20
      bgracontrols.lpk
  12. 22 16
      bgracontrols.pas
  13. 2 2
      bgracontrolsinfo.pas
  14. 103 9
      bgradialogs.pas
  15. 67 13
      bgraflashprogressbar.pas
  16. 702 0
      bgraformatui.lfm
  17. 674 0
      bgraformatui.pas
  18. 39 100
      bgraimagemanipulation.pas
  19. 1 1
      bgrapascalscriptcomponent.lpk
  20. BIN
      docs/img/SuperSpinner-V100.png
  21. 1 1
      dtthemedclock.pas
  22. 1 1
      dtthemedgauge.pas
  23. 0 249
      dtthemedgauge.pp
  24. 53 0
      icons/superspinner.lrs
  25. BIN
      images/bgracontrols_images.res
  26. 3 0
      images/bgracontrols_images_list.txt
  27. 21 0
      images/svg/tsuperspinner.svg
  28. BIN
      images/tsuperspinner.png
  29. BIN
      images/tsuperspinner_150.png
  30. BIN
      images/tsuperspinner_200.png
  31. 0 80
      mouseandkeyinput/carbonkeyinput.pas
  32. 0 72
      mouseandkeyinput/carbonmouseinput.pas
  33. 0 92
      mouseandkeyinput/keyinputintf.pas
  34. 0 88
      mouseandkeyinput/lazmouseandkeyinput.lpk
  35. 0 15
      mouseandkeyinput/lazmouseandkeyinput.pas
  36. 0 66
      mouseandkeyinput/mouseandkeyinput.pas
  37. 0 284
      mouseandkeyinput/mouseinputintf.pas
  38. 0 21
      mouseandkeyinput/readme.txt
  39. 0 72
      mouseandkeyinput/winkeyinput.pas
  40. 0 127
      mouseandkeyinput/winmouseinput.pas
  41. 0 198
      mouseandkeyinput/xkeyinput.pas
  42. 0 114
      mouseandkeyinput/xmouseinput.pas
  43. 1 1
      supergauge.pas
  44. 1 1
      supergaugecommon.pas
  45. 1656 0
      superspinner.pas
  46. 628 0
      superspinnercommon.pas
  47. 1 1
      test/test_analog_controls/umain.pas
  48. 12 4
      test/test_bccombobox/umain.lfm
  49. 7 0
      test/test_bccombobox/umain.pas
  50. 0 1
      test/test_bckeyboard/test_bckeyboard.lpi
  51. 79 79
      test/test_bcroundedimage_pictdialogs/test_bcroundedimage_pictdialogs_main.lfm
  52. 2 1
      test/test_bcroundedimage_pictdialogs/test_bcroundedimage_pictdialogs_main.pas
  53. 2 2
      test/test_bgraknob/unit1.lfm
  54. BIN
      test/test_formatui/test_formatui.ico
  55. 134 0
      test/test_formatui/test_formatui.lpi
  56. 27 0
      test/test_formatui/test_formatui.lpr
  57. 359 0
      test/test_formatui/test_formatui_main.lfm
  58. 170 0
      test/test_formatui/test_formatui_main.pas
  59. 1 1
      test/test_progressbar/test_progressbar.lpi
  60. 139 145
      test/test_progressbar/umain.lfm
  61. 32 21
      test/test_progressbar/umain.pas
  62. 2 2
      test/test_supergauge/sgtest.lfm
  63. 1 1
      test/test_supergauge/sgtest.pas
  64. 276 0
      test/test_superspinner/about.lfm
  65. 60 0
      test/test_superspinner/about.pas
  66. BIN
      test/test_superspinner/project1.ico
  67. 99 0
      test/test_superspinner/project1.lpi
  68. 26 0
      test/test_superspinner/project1.lpr
  69. 1923 0
      test/test_superspinner/sstest.lfm
  70. 1469 0
      test/test_superspinner/sstest.pas
  71. 3 3
      update_bgracontrols_force.json

+ 25 - 13
.github/workflows/make.pas

@@ -20,7 +20,7 @@ uses
   function OutLog(const Knd: TEventType; const Msg: string): string;
   function OutLog(const Knd: TEventType; const Msg: string): string;
   begin
   begin
     case Knd of
     case Knd of
-      etError: Result := #27'[31m%s'#27'[0m';
+      etError: Result := #27'[91m%s'#27'[0m';
       etInfo:  Result := #27'[32m%s'#27'[0m';
       etInfo:  Result := #27'[32m%s'#27'[0m';
       etDebug: Result := #27'[33m%s'#27'[0m';
       etDebug: Result := #27'[33m%s'#27'[0m';
     end;
     end;
@@ -48,7 +48,11 @@ uses
       Expression := Reg;
       Expression := Reg;
       for Line in Input.Split(LineEnding) do
       for Line in Input.Split(LineEnding) do
         if Exec(Line) then
         if Exec(Line) then
-          Result += Line + LineEnding;
+		begin
+		  if Result <> EmptyStr then
+		    Result += LineEnding;
+          Result += Line;
+		end;
       Free;
       Free;
     end;
     end;
   end;
   end;
@@ -104,7 +108,7 @@ uses
     else
     else
     begin
     begin
       ExitCode += 1;
       ExitCode += 1;
-      OutLog(etError, SelectString(Result, '(Fatal|Error):'));
+      OutLog(etError, SelectString(Result, '(Fatal|Error|/ld(\.[a-z]+)?):'));
     end;
     end;
   end;
   end;
 
 
@@ -161,6 +165,20 @@ uses
         UnZip(DownloadFile('https://packages.lazarus-ide.org/%s.zip'.Format([Path])), Result);
         UnZip(DownloadFile('https://packages.lazarus-ide.org/%s.zip'.Format([Path])), Result);
     end;
     end;
   end;
   end;
+  
+  procedure RetrieveSubmodules;
+  var CommandOutput: string;
+  begin
+    if FileExists('.gitmodules') then
+    if RunCommand('git', ['submodule', 'update', '--init',
+      '--force', '--remote'], CommandOutput, [poStderrToOutPut]) then
+      OutLog(etInfo, CommandOutput)
+    else
+    begin
+      ExitCode += 1;
+      OutLog(etError, CommandOutput);
+    end;
+  end;
 
 
   function BuildAll(const Target: string; const Dependencies: array of string): string;
   function BuildAll(const Target: string; const Dependencies: array of string): string;
   var
   var
@@ -168,15 +186,7 @@ uses
     DT: TDateTime;
     DT: TDateTime;
   begin
   begin
     DT := Time;
     DT := Time;
-    if FileExists('.gitmodules') then
-      if RunCommand('git', ['submodule', 'update', '--init', '--recursive',
-        '--force', '--remote'], Result, [poStderrToOutPut]) then
-        OutLog(etInfo, Result)
-      else
-      begin
-        ExitCode += 1;
-        OutLog(etError, Result);
-      end;
+	// GitHub already retrieves submodules
     List := FindAllFiles(GetCurrentDir, '*.lpk');
     List := FindAllFiles(GetCurrentDir, '*.lpk');
     try
     try
       for Result in Dependencies do
       for Result in Dependencies do
@@ -199,11 +209,13 @@ uses
 begin
 begin
   try
   try
     BuildAll('.', ['UEControls']);
     BuildAll('.', ['UEControls']);
+	OutLog(etDebug,     '------------');
     case ExitCode of
     case ExitCode of
-      0: OutLog(etInfo, 'Errors:'#9 + ExitCode.ToString);
+      0: OutLog(etInfo, 'No Errors 😊');
       else
       else
         OutLog(etError, 'Errors:'#9 + ExitCode.ToString);
         OutLog(etError, 'Errors:'#9 + ExitCode.ToString);
     end;
     end;
+	OutLog(etDebug,     '------------');
   except
   except
     on E: Exception do
     on E: Exception do
       Writeln(E.ClassName, #9, E.Message);
       Writeln(E.ClassName, #9, E.Message);

+ 1 - 1
.github/workflows/make.yml

@@ -34,6 +34,6 @@ jobs:
         shell: bash
         shell: bash
         run: |
         run: |
           set -xeuo pipefail
           set -xeuo pipefail
-          sudo bash -c 'apt-get update; apt-get install -y lazarus' >/dev/null
+          sudo bash -c 'apt-get update; apt-get install -y lazarus libxtst-dev' >/dev/null
           instantfpc -Fu/usr/lib/lazarus/*/components/lazutils \
           instantfpc -Fu/usr/lib/lazarus/*/components/lazutils \
             .github/workflows/make.pas build
             .github/workflows/make.pas build

+ 20 - 12
README.md

@@ -2,7 +2,7 @@
 
 
 BGRA Controls is a set of graphical UI elements that you can use with Lazarus LCL applications.
 BGRA Controls is a set of graphical UI elements that you can use with Lazarus LCL applications.
 
 
-![BGRA Controls](https://raw.githubusercontent.com/bgrabitmap/bgracontrols/dev-bgracontrols/docs/img/logo.png)
+![BGRA Controls](docs/img/logo.png)
 
 
 ### Support Us
 ### Support Us
 
 
@@ -16,17 +16,19 @@ Notice that you must check only the packages "bgrabitmappack.lpk" and "bgracontr
 
 
 ### Optional Components
 ### Optional Components
 
 
-Since v4.4 the components TBCDefaultThemeManager, TBCKeyboard and TBCNumericKeyboard are not installed by default to allow Linux users to get a seamless installation with the Online Package Manager not installing third party stuff. If you want these components turn on the "Register unit" in the package options for each file (bcdefaulthememanager.pas, bckeyboard.pas, bcnumerickeyboard.pas) then compile and rebuild Lazarus. On Linux you need to install libxtst-dev and libgl-dev first.
+From v4.4 to v9.0.1.6, the components TBCDefaultThemeManager, TBCKeyboard and TBCNumericKeyboard were not registerd by default to allow Linux users to get a seamless installation with the Online Package Manager not installing third party stuff. From v9.0.1.7, as the necessary package is now part of Lazarus, these components are registered.
+
+If you would like to include/exclude them from the package locally, you may turn on/off the "Register unit" in the package options for each file (bcdefaulthememanager.pas, bckeyboard.pas, bcnumerickeyboard.pas) then compile and rebuild Lazarus. On Linux you may need to install libxtst-dev and libgl-dev.
 
 
 ### Screenshots macOS 64 Cocoa
 ### Screenshots macOS 64 Cocoa
-![Analog Controls](https://raw.githubusercontent.com/bgrabitmap/bgracontrols/dev-bgracontrols/docs/img/analogcontrols.png)
-![BCButton](https://raw.githubusercontent.com/bgrabitmap/bgracontrols/dev-bgracontrols/docs/img/bcbutton.png)
-![BCButtonFocus](https://raw.githubusercontent.com/bgrabitmap/bgracontrols/dev-bgracontrols/docs/img/bcbuttonfocus.png)
-![BCImageButton](https://raw.githubusercontent.com/bgrabitmap/bgracontrols/dev-bgracontrols/docs/img/bcimagebutton.png)
-![BCToolBar](https://raw.githubusercontent.com/bgrabitmap/bgracontrols/dev-bgracontrols/docs/img/bctoolbar.png)
-![BCXButton](https://raw.githubusercontent.com/bgrabitmap/bgracontrols/dev-bgracontrols/docs/img/bcxbutton.png)
-![BGRA Ribbon](https://raw.githubusercontent.com/bgrabitmap/bgracontrols/dev-bgracontrols/docs/img/bgraribbon.png)
-![ProgressBar](https://raw.githubusercontent.com/bgrabitmap/bgracontrols/dev-bgracontrols/docs/img/progressbar.png)
+![Analog Controls](docs/img/analogcontrols.png)
+![BCButton](docs/img/bcbutton.png)
+![BCButtonFocus](docs/img/bcbuttonfocus.png)
+![BCImageButton](docs/img/bcimagebutton.png)
+![BCToolBar](docs/img/bctoolbar.png)
+![BCXButton](docs/img/bcxbutton.png)
+![BGRA Ribbon](docs/img/bgraribbon.png)
+![ProgressBar](docs/img/progressbar.png)
 
 
 ### TBCButton
 ### TBCButton
 
 
@@ -143,7 +145,7 @@ A tool to manipulate pictures, see the demo that shows all the capability that c
 Author: Emerson Cavalcanti, Massimo Magnano .
 Author: Emerson Cavalcanti, Massimo Magnano .
 
 
 ### TBGRAKnob
 ### TBGRAKnob
-![Knob](https://raw.githubusercontent.com/bgrabitmap/bgracontrols/dev-bgracontrols/docs/img/BGRA-Knob-V2.png)
+![Knob](docs/img/BGRA-Knob-V2.png)
 A knob that can be styled through properties.
 A knob that can be styled through properties.
 
 
 Author: Circular, Sandy Ganz
 Author: Circular, Sandy Ganz
@@ -221,11 +223,17 @@ Another gauge.
 Author: Digeo.
 Author: Digeo.
 
 
 ### TSuperGauge
 ### TSuperGauge
-![SuperGauge](https://raw.githubusercontent.com/bgrabitmap/bgracontrols/dev-bgracontrols/docs/img/SuperGauge-V100.png)
+![SuperGauge](docs/img/SuperGauge-V100.png)
 Updated and Enhanced Analog Gauge. Many new features, faster drawing, additonal events
 Updated and Enhanced Analog Gauge. Many new features, faster drawing, additonal events
 
 
 Author: Sandy Ganz
 Author: Sandy Ganz
 
 
+### TSuperSpinner
+![SuperSpinner](docs/img/SuperSpinner-V100.png)
+Spinner Knob, many visual settings and options, fast drawing, many supported events
+
+Author: Sandy Ganz
+
 ### TPSImport_BGRAPascalScript
 ### TPSImport_BGRAPascalScript
 
 
 A component to load BGRABitmap pascal script utilities.
 A component to load BGRABitmap pascal script utilities.

+ 1 - 1
atshapelinebgra.pas

@@ -19,7 +19,7 @@ For BGRAControls by: Lainz
 
 
 Lazarus: 1.6+}
 Lazarus: 1.6+}
 
 
-unit atshapelinebgra;
+unit ATShapeLineBGRA;
 
 
 interface
 interface
 
 

+ 262 - 64
bccombobox.pas

@@ -23,15 +23,18 @@ type
     FDropDownFontColor: TColor;
     FDropDownFontColor: TColor;
     FDropDownFontHighlight: TColor;
     FDropDownFontHighlight: TColor;
     FDropDownHighlight: TColor;
     FDropDownHighlight: TColor;
+    FDropDownOnSameForm: boolean;
     FFocusBorderColor: TColor;
     FFocusBorderColor: TColor;
     FFocusBorderOpacity: byte;
     FFocusBorderOpacity: byte;
     FItems: TStringList;
     FItems: TStringList;
     FItemIndex: integer;
     FItemIndex: integer;
     FForm: TForm;
     FForm: TForm;
-    FFormHideDate: TDateTime;
+    FPanel: TPanel;
+    FDropDownHideDate: TDateTime;
     FHoverItem: integer;
     FHoverItem: integer;
     FItemHeight: integer;
     FItemHeight: integer;
     FListBox: TListBox;
     FListBox: TListBox;
+    FItemPadding: integer;
     FDropDownBorderColor: TColor;
     FDropDownBorderColor: TColor;
     FOnDrawItem: TDrawItemEvent;
     FOnDrawItem: TDrawItemEvent;
     FOnDrawSelectedItem: TOnAfterRenderBCButton;
     FOnDrawSelectedItem: TOnAfterRenderBCButton;
@@ -39,9 +42,10 @@ type
     FOnDropDown: TNotifyEvent;
     FOnDropDown: TNotifyEvent;
     FDrawingDropDown: boolean;
     FDrawingDropDown: boolean;
     FTimerCheckFormHide: TTimer;
     FTimerCheckFormHide: TTimer;
-    FQueryFormHide: boolean;
+    FQueryDropDownHide: boolean;
     procedure ButtonClick(Sender: TObject);
     procedure ButtonClick(Sender: TObject);
     procedure FormDeactivate(Sender: TObject);
     procedure FormDeactivate(Sender: TObject);
+    procedure PanelExit(Sender: TObject);
     procedure FormHide(Sender: TObject);
     procedure FormHide(Sender: TObject);
     function GetArrowFlip: boolean;
     function GetArrowFlip: boolean;
     function GetComboCanvas: TCanvas;
     function GetComboCanvas: TCanvas;
@@ -115,8 +119,12 @@ type
     procedure UpdateFocus(AFocused: boolean);
     procedure UpdateFocus(AFocused: boolean);
     procedure KeyDown(var Key: Word; {%H-}Shift: TShiftState); override;
     procedure KeyDown(var Key: Word; {%H-}Shift: TShiftState); override;
     procedure UTF8KeyPress(var UTF8Key: TUTF8Char); override;
     procedure UTF8KeyPress(var UTF8Key: TUTF8Char); override;
-    procedure CreateForm;
-    procedure FreeForm;
+    procedure CreateDropDown;
+    procedure FreeDropDown;
+    function CloseDropDown: boolean;
+    procedure PrepareListBoxForDropDown;
+    procedure AutosizeListBox;
+    procedure AdaptDropDownContainerSize;
     function GetListBox: TListBox;
     function GetListBox: TListBox;
     procedure UpdateButtonCanvasScaleMode;
     procedure UpdateButtonCanvasScaleMode;
   public
   public
@@ -151,6 +159,7 @@ type
     property DropDownCount: integer read FDropDownCount write FDropDownCount default 8;
     property DropDownCount: integer read FDropDownCount write FDropDownCount default 8;
     property DropDownHighlight: TColor read FDropDownHighlight write FDropDownHighlight default clHighlight;
     property DropDownHighlight: TColor read FDropDownHighlight write FDropDownHighlight default clHighlight;
     property DropDownFontHighlight: TColor read FDropDownFontHighlight write FDropDownFontHighlight default clHighlightText;
     property DropDownFontHighlight: TColor read FDropDownFontHighlight write FDropDownFontHighlight default clHighlightText;
+    property DropDownOnSameForm: boolean read FDropDownOnSameForm write FDropDownOnSameForm default False;
     property GlobalOpacity: byte read GetGlobalOpacity write SetGlobalOpacity;
     property GlobalOpacity: byte read GetGlobalOpacity write SetGlobalOpacity;
     property MemoryUsage: TBCButtonMemoryUsage read GetMemoryUsage write SetMemoryUsage;
     property MemoryUsage: TBCButtonMemoryUsage read GetMemoryUsage write SetMemoryUsage;
     property Rounding: TBCRounding read GetRounding write SetRounding;
     property Rounding: TBCRounding read GetRounding write SetRounding;
@@ -192,60 +201,77 @@ procedure TBCComboBox.ButtonClick(Sender: TObject);
 const MinDelayReopen = 500/(1000*60*60*24);
 const MinDelayReopen = 500/(1000*60*60*24);
 var
 var
   p: TPoint;
   p: TPoint;
-  h: Integer;
-  s: TSize;
+  f: TCustomForm;
+  monitor: TMonitor;
 begin
 begin
-  {$IFDEF DARWIN}
-  //if Assigned(FForm) and not FForm.Visible then FreeForm;
-  {$ENDIF}
-
-  CreateForm;
+  CreateDropDown;
 
 
-  if FForm.Visible then
-    FForm.Close
-  else
-  if Now > FFormHideDate+MinDelayReopen then
+  if not CloseDropDown and (Now > FDropDownHideDate+MinDelayReopen) then
   begin
   begin
-    p := ControlToScreen(Point(FButton.Left, FButton.Top + FButton.Height));
-    FForm.Left := p.X;
-    FForm.Top := p.Y;
-    FForm.Color := FDropDownBorderColor;
-    FListBox.Font.Name := Button.StateNormal.FontEx.Name;
-    FListBox.Font.Style := Button.StateNormal.FontEx.Style;
-    FListBox.Font.Height := FontEmHeightSign*Button.StateNormal.FontEx.Height;
-    self.Canvas.Font.Assign(FListBox.Font);
-    if Assigned(FOnDrawItem) and (FItemHeight <> 0) then
-      h := FItemHeight else h := self.Canvas.GetTextHeight('Hg');
-    {$IFDEF WINDOWS}inc(h,6);{$ENDIF}
-    FListBox.ItemHeight := h;
-    {$IFDEF LINUX}inc(h,6);{$ENDIF}
-    {$IFDEF DARWIN}inc(h,2);{$ENDIF}
-    s := TSize.Create(FButton.Width, h*min(Items.Count, FDropDownCount) + 2*FDropDownBorderSize);
-    FForm.ClientWidth := s.cx;
-    FForm.ClientHeight := s.cy;
-    FListBox.SetBounds(FDropDownBorderSize,FDropDownBorderSize,
-      s.cx - 2*FDropDownBorderSize,
-      s.cy - 2*FDropDownBorderSize);
-    if FForm.Top + FForm.Height > Screen.WorkAreaTop + Screen.WorkAreaHeight then
-      FForm.Top := FForm.Top - FForm.Height - Self.Height;
-    if Assigned(FOnDropDown) then FOnDropDown(self);
-    //FForm.Visible := True;
-    if FListBox.CanSetFocus then
-      FListBox.SetFocus;
-    FTimerCheckFormHide.Enabled:= true;
-    FQueryFormHide := false;
-    FForm.ShowModal;
+    if DropDownOnSameForm then
+    begin
+      f := GetParentForm(self, False);
+      if Assigned(f) then
+      begin
+        PrepareListBoxForDropDown;
+        p := ControlToScreen(Point(FButton.Left, FButton.Top + FButton.Height));
+        p := f.ScreenToClient(p);
+        FPanel.Parent := f;
+        FPanel.Left := p.X;
+        FPanel.Top := p.Y;
+        FPanel.Color := FDropDownBorderColor;
+        AdaptDropDownContainerSize;
+        if FPanel.Top + FPanel.Height > f.ClientHeight then
+          FPanel.Top := FPanel.Top - FPanel.Height - Self.Height;
+        if Assigned(FOnDropDown) then FOnDropDown(self);
+        FQueryDropDownHide := false;
+        FTimerCheckFormHide.Enabled:= true;
+        FPanel.Visible := true;
+        if FPanel.CanSetFocus then
+          FPanel.SetFocus;
+        if FListBox.CanSetFocus then
+          FListBox.SetFocus;
+      end;
+    end else
+    begin
+      PrepareListBoxForDropDown;
+      p := ControlToScreen(Point(FButton.Left, FButton.Top + FButton.Height));
+      FForm.Left := p.X;
+      FForm.Top := p.Y;
+      FForm.Color := FDropDownBorderColor;
+      AdaptDropDownContainerSize;
+      monitor := Screen.MonitorFromPoint(p);
+      if FForm.Top + FForm.Height > monitor.WorkareaRect.Bottom then
+        FForm.Top := FForm.Top - FForm.Height - Self.Height;
+      if Assigned(FOnDropDown) then FOnDropDown(self);
+      FQueryDropDownHide := false;
+      FTimerCheckFormHide.Enabled:= true;
+      {$IFDEF DARWIN}
+      f := GetParentForm(self, False);
+      if fsModal in f.FormState then FForm.ShowModal else
+      {$ENDIF}
+      begin
+        FForm.Visible := True;
+        if FListBox.CanSetFocus then
+          FListBox.SetFocus;
+      end;
+    end;
   end;
   end;
 end;
 end;
 
 
 procedure TBCComboBox.FormDeactivate(Sender: TObject);
 procedure TBCComboBox.FormDeactivate(Sender: TObject);
 begin
 begin
-  FQueryFormHide := true;
+  FQueryDropDownHide := true;
+end;
+
+procedure TBCComboBox.PanelExit(Sender: TObject);
+begin
+  FQueryDropDownHide := true;
 end;
 end;
 
 
 procedure TBCComboBox.FormHide(Sender: TObject);
 procedure TBCComboBox.FormHide(Sender: TObject);
 begin
 begin
-  FFormHideDate := Now;
+  FDropDownHideDate := Now;
 end;
 end;
 
 
 function TBCComboBox.GetArrowFlip: boolean;
 function TBCComboBox.GetArrowFlip: boolean;
@@ -368,8 +394,7 @@ end;
 procedure TBCComboBox.ListBoxMouseUp(Sender: TObject; Button: TMouseButton;
 procedure TBCComboBox.ListBoxMouseUp(Sender: TObject; Button: TMouseButton;
                           Shift: TShiftState; X, Y: Integer);
                           Shift: TShiftState; X, Y: Integer);
 begin
 begin
-  FForm.Close;
-  FQueryFormHide := true;
+  CloseDropDown;
 end;
 end;
 
 
 procedure TBCComboBox.ListBoxMouseLeave(Sender: TObject);
 procedure TBCComboBox.ListBoxMouseLeave(Sender: TObject);
@@ -398,13 +423,29 @@ procedure TBCComboBox.ListBoxSelectionChange(Sender: TObject; User: boolean);
 begin
 begin
   Button.Caption := GetItemText;
   Button.Caption := GetItemText;
   if User and Assigned(FOnChange) then FOnChange(self);
   if User and Assigned(FOnChange) then FOnChange(self);
+  {$IFDEF WINDOWS}
+  // ensure redrawing of all items
+  (Sender as TListBox).Invalidate;
+  {$ENDIF}
 end;
 end;
 
 
 procedure TBCComboBox.ListBoxDrawItem(Control: TWinControl; Index: Integer;
 procedure TBCComboBox.ListBoxDrawItem(Control: TWinControl; Index: Integer;
   ARect: TRect; State: TOwnerDrawState);
   ARect: TRect; State: TOwnerDrawState);
 var
 var
   aCanvas: TCanvas;
   aCanvas: TCanvas;
+  padding: integer;
+  r: TRect;
 begin
 begin
+  if Index = 0 then
+  begin
+    padding := max(0, ARect.Height - TListBox(Control).ItemHeight);
+    if padding <> FItemPadding then
+    begin
+      // next time, use a better adjustment
+      FItemPadding := padding;
+    end;
+  end;
+
   if Assigned(FOnDrawItem) then
   if Assigned(FOnDrawItem) then
   begin
   begin
     FDrawingDropDown := true;
     FDrawingDropDown := true;
@@ -412,7 +453,12 @@ begin
     if Index = HoverItem then Include(State, odSelected);
     if Index = HoverItem then Include(State, odSelected);
     if Index = ItemIndex then Include(State, odChecked);
     if Index = ItemIndex then Include(State, odChecked);
     try
     try
-      FOnDrawItem(Control, Index, ARect, State);
+      r := ARect;
+      {$IFDEF DARWIN}
+      // on MacOS the vertical scrollbar is over the content
+      Dec(r.Right, 8);
+      {$ENDIF}
+      FOnDrawItem(Control, Index, r, State);
     finally
     finally
       FDrawingDropDown := false;
       FDrawingDropDown := false;
     end;
     end;
@@ -420,6 +466,25 @@ begin
   end;
   end;
 
 
   aCanvas := TListBox(Control).Canvas;
   aCanvas := TListBox(Control).Canvas;
+  aCanvas.Pen.Style := psClear;
+  {$IFDEF DARWIN}
+  // paint top and bottom margin on MacOS
+  aCanvas.Brush.Color := DropDownColor;
+  if Index = 0 then
+  begin
+    r := ARect;
+    r.Bottom := r.Top;
+    dec(r.Top, 10);
+    aCanvas.FillRect(r);
+  end;
+  if Index = TListBox(Control).Count-1 then
+  begin
+    r := ARect;
+    r.Top := r.Bottom;
+    inc(r.Bottom, 10);
+    aCanvas.FillRect(r);
+  end;
+  {$ENDIF}
   if Index = HoverItem then
   if Index = HoverItem then
   begin
   begin
     aCanvas.Brush.Color := DropDownHighlight;
     aCanvas.Brush.Color := DropDownHighlight;
@@ -430,7 +495,6 @@ begin
     aCanvas.Brush.Color := DropDownColor;
     aCanvas.Brush.Color := DropDownColor;
     aCanvas.Font.Color := DropDownFontColor;
     aCanvas.Font.Color := DropDownFontColor;
   end;
   end;
-  aCanvas.Pen.Style := psClear;
   aCanvas.FillRect(ARect);
   aCanvas.FillRect(ARect);
   aCanvas.TextRect(ARect, ARect.Left+4, ARect.Top +
   aCanvas.TextRect(ARect, ARect.Left+4, ARect.Top +
     (ARect.Height - aCanvas.GetTextHeight(Items[Index])) div 2,
     (ARect.Height - aCanvas.GetTextHeight(Items[Index])) div 2,
@@ -467,16 +531,26 @@ procedure TBCComboBox.OnTimerCheckFormHide(Sender: TObject);
   end;
   end;
   {$endif}
   {$endif}
 
 
+  procedure DoClose;
+  begin
+    CloseDropDown;
+    FQueryDropDownHide := false;
+    FTimerCheckFormHide.Enabled := false;
+  end;
+
 begin
 begin
-  //if Assigned(FForm) and FForm.Visible and
-    //({$IFDEF DARWIN}not FForm.Active or {$ENDIF}
-     //{$IFDEF WINDOWS}not IsDropDownOnTop or{$ENDIF}
-     //FQueryFormHide) then
-  //begin
-    //FForm.Visible := false;
-    //FQueryFormHide := false;
-    //FTimerCheckFormHide.Enabled := false;
-  //end;
+  {$IFNDEF LCLgtk3}
+  if not Application.Active then FQueryDropDownHide:= true;
+  {$ENDIF}
+  if not FQueryDropDownHide then exit;
+
+  if Assigned(FPanel) then DoClose;
+
+  {$IFDEF WINDOWS}
+  If Assigned(FForm) and FForm.Visible then DoClose;
+  {$ELSE}
+  If Assigned(FForm) and not FForm.Active then DoClose;
+  {$ENDIF}
 end;
 end;
 
 
 procedure TBCComboBox.SetArrowFlip(AValue: boolean);
 procedure TBCComboBox.SetArrowFlip(AValue: boolean);
@@ -746,9 +820,13 @@ begin
   end;
   end;
 end;
 end;
 
 
-procedure TBCComboBox.CreateForm;
+procedure TBCComboBox.CreateDropDown;
 begin
 begin
-  if FForm = nil then
+  {$IFDEF LINUX}
+  // ensure correct window placement on Linux
+  if Assigned(FForm) and not FForm.Visible then FreeDropDown;
+  {$ENDIF}
+  if (FForm = nil) and not DropDownOnSameForm then
   begin
   begin
     FForm := TForm.Create(Self);
     FForm := TForm.Create(Self);
     FForm.Visible := False;
     FForm.Visible := False;
@@ -757,12 +835,29 @@ begin
     FForm.OnDeactivate:= FormDeactivate;
     FForm.OnDeactivate:= FormDeactivate;
     FForm.OnHide:=FormHide;
     FForm.OnHide:=FormHide;
     FForm.FormStyle := fsStayOnTop;
     FForm.FormStyle := fsStayOnTop;
+  end else
+  if Assigned(FForm) and DropDownOnSameForm then
+  begin
+    If Assigned(FListBox) and (FListBox.Parent = FForm) then FListBox.Parent := nil;
+    FreeAndNil(FForm);
+  end;
+
+  if (FPanel = nil) and DropDownOnSameForm then
+  begin
+    FPanel := TPanel.Create(Self);
+    FPanel.Visible := False;
+    FPanel.BevelInner := bvNone;
+    FPanel.BevelOuter := bvNone;
+  end else
+  if Assigned(FPanel) and not DropDownOnSameForm then
+  begin
+    If Assigned(FListBox) and (FListBox.Parent = FPanel) then FListBox.Parent := nil;
+    FreeAndNil(FPanel);
   end;
   end;
 
 
   if FListBox = nil then
   if FListBox = nil then
   begin
   begin
     FListBox := TListBox.Create(self);
     FListBox := TListBox.Create(self);
-    FListBox.Parent := FForm;
     FListBox.BorderStyle := bsNone;
     FListBox.BorderStyle := bsNone;
 
 
     FListBox.OnMouseLeave:=ListBoxMouseLeave;
     FListBox.OnMouseLeave:=ListBoxMouseLeave;
@@ -781,9 +876,20 @@ begin
     FListBox.Color := FDropDownColor;
     FListBox.Color := FDropDownColor;
     FListBox.OnSelectionChange := ListBoxSelectionChange;
     FListBox.OnSelectionChange := ListBoxSelectionChange;
   end;
   end;
+
+  if DropDownOnSameForm then
+  begin
+    FListBox.Parent := FPanel;
+    FListBox.OnExit:= PanelExit;
+  end
+  else
+  begin
+    FListBox.Parent := FForm;
+    FListBox.OnExit:= nil;
+  end;
 end;
 end;
 
 
-procedure TBCComboBox.FreeForm;
+procedure TBCComboBox.FreeDropDown;
 begin
 begin
   if Assigned(FListBox) then
   if Assigned(FListBox) then
   begin
   begin
@@ -796,11 +902,90 @@ begin
     FreeAndNil(FListBox);
     FreeAndNil(FListBox);
   end;
   end;
   FreeAndNil(FForm);
   FreeAndNil(FForm);
+  FreeAndNil(FPanel);
+end;
+
+function TBCComboBox.CloseDropDown: boolean;
+begin
+  if Assigned(FForm) and FForm.Visible then
+  begin
+    FForm.Close;
+    result := true;
+  end
+  else if Assigned(FPanel) and FPanel.Visible then
+  begin
+    FPanel.Hide;
+    FDropDownHideDate := Now;
+    result := true;
+  end else
+  begin
+    result := false;
+  end;
+  FQueryDropDownHide := true;
+end;
+
+procedure TBCComboBox.PrepareListBoxForDropDown;
+var
+  h: Integer;
+begin
+  FListBox.Font.Name := Button.StateNormal.FontEx.Name;
+  FListBox.Font.Style := Button.StateNormal.FontEx.Style;
+  FListBox.Font.Height := FontEmHeightSign*Button.StateNormal.FontEx.Height;
+  if Assigned(FOnDrawItem) and (FItemHeight <> 0) then
+    h := FItemHeight else h := self.Canvas.GetTextHeight('Hg');
+  {$IF defined(LCLgtk2)}
+  inc(h,2);
+  {$ELSEIF defined(LCLgtk3)}
+  inc(h,4);
+  {$ELSE}
+  inc(h,6); // default
+  {$ENDIF}
+  FListBox.ItemHeight := h;
+  AutosizeListBox;
+end;
+
+procedure TBCComboBox.AutosizeListBox;
+var
+  s: TSize;
+begin
+  s := TSize.Create(FButton.Width,
+    (FListBox.ItemHeight + FItemPadding)*min(Items.Count, FDropDownCount)
+    + 2*FDropDownBorderSize);
+  {$IFDEF DARWIN}
+  // on MacOS there is a top and bottom margin of both 10
+  if Items.Count <= FDropDownCount then
+    inc(s.cy, 20)
+  else
+    // if overflow, keep only either top or bottom margin
+    inc(s.cy, 10);
+  {$ENDIF}
+  FListBox.SetBounds(FDropDownBorderSize,FDropDownBorderSize,
+    s.cx - 2*FDropDownBorderSize,
+    s.cy - 2*FDropDownBorderSize);
+end;
+
+procedure TBCComboBox.AdaptDropDownContainerSize;
+var w, h: integer;
+begin
+  if not Assigned(FListBox) then exit;
+  w := FListBox.Width + 2*FDropDownBorderSize;
+  h := FListBox.Height + 2*FDropDownBorderSize;
+  if Assigned(FPanel) then
+  begin
+    FPanel.ClientWidth := w;
+    FPanel.ClientHeight := h;
+  end;
+  if Assigned(FForm) {$IFDEF LCLgtk2}and not FForm.HandleAllocated{$ENDIF} then
+  begin
+    FForm.SetBounds(FForm.Left, FForm.Top,
+      w + FForm.Width - FForm.ClientWidth,
+      h + FForm.Height - FForm.ClientHeight);
+  end;
 end;
 end;
 
 
 function TBCComboBox.GetListBox: TListBox;
 function TBCComboBox.GetListBox: TListBox;
 begin
 begin
-  CreateForm;
+  CreateDropDown;
   result := FListBox;
   result := FListBox;
 end;
 end;
 
 
@@ -838,8 +1023,21 @@ begin
   DropDownFontHighlight := clHighlightText;
   DropDownFontHighlight := clHighlightText;
 
 
   FTimerCheckFormHide := TTimer.Create(self);
   FTimerCheckFormHide := TTimer.Create(self);
+  {$IFDEF LCLgtk3}
+  FTimerCheckFormHide.Enabled := false;
+  {$ENDIF}
   FTimerCheckFormHide.Interval:= 30;
   FTimerCheckFormHide.Interval:= 30;
   FTimerCheckFormHide.OnTimer:= OnTimerCheckFormHide;
   FTimerCheckFormHide.OnTimer:= OnTimerCheckFormHide;
+
+  {$IFDEF WINDOWS}
+  FItemPadding:= 0;
+  {$ELSE}
+  {$IFDEF LCLgtk2}
+  FItemPadding:= 4;
+  {$ELSE}
+  FItemPadding:= 0; // default
+  {$ENDIF}
+  {$ENDIF}
 end;
 end;
 
 
 destructor TBCComboBox.Destroy;
 destructor TBCComboBox.Destroy;

+ 1 - 1
bcfilters.pas

@@ -12,7 +12,7 @@
   (Compatibility with delphi VCL 11/2018)
   (Compatibility with delphi VCL 11/2018)
 
 
 ***************************** END CONTRIBUTOR(S) *****************************}
 ***************************** END CONTRIBUTOR(S) *****************************}
-unit bcfilters;
+unit BCFilters;
 
 
 {
 {
 // all pixels //
 // all pixels //

+ 5 - 7
bcfluentprogressring.pas

@@ -62,6 +62,8 @@ procedure Register;
 
 
 implementation
 implementation
 
 
+uses Math;
+
 procedure Register;
 procedure Register;
 begin
 begin
   RegisterComponents('BGRA Controls', [TBCFluentProgressRing]);
   RegisterComponents('BGRA Controls', [TBCFluentProgressRing]);
@@ -198,10 +200,10 @@ begin
 
 
   if FIndeterminate and FTimer.Enabled then
   if FIndeterminate and FTimer.Enabled then
   begin
   begin
+    FAnimationTime:= (GetTickCount64 - FStartTickCount) mod FPeriod;
     a:= 3*FAnimationTime*pi2/FPeriod - pi;
     a:= 3*FAnimationTime*pi2/FPeriod - pi;
-    da:= 2*abs(1 - 2*FAnimationTime/FPeriod);
-    if da>0.005 then
-      DoDrawArc(a-da, a+da, FLineColor);
+    da:= max(2*abs(1 - 2*FAnimationTime/FPeriod), 0.01);
+    DoDrawArc(a-da, a+da, FLineColor);
   end
   end
   else if FValue > FMinValue then
   else if FValue > FMinValue then
   begin
   begin
@@ -213,11 +215,7 @@ begin
 end;
 end;
 
 
 procedure TBCFluentProgressRing.TimerEvent(Sender: TObject);
 procedure TBCFluentProgressRing.TimerEvent(Sender: TObject);
-var
-  TickCount: QWord;
 begin
 begin
-  TickCount:= GetTickCount64;
-  FAnimationTime:= (TickCount - FStartTickCount) mod FPeriod;
   DiscardBitmap;
   DiscardBitmap;
 end;
 end;
 
 

+ 9 - 25
bckeyboard.pas

@@ -35,8 +35,8 @@ type
     procedure SetFPanelsColor(AValue: TColor);
     procedure SetFPanelsColor(AValue: TColor);
     procedure SetFThemeManager(AValue: TBCThemeManager);
     procedure SetFThemeManager(AValue: TBCThemeManager);
   protected
   protected
-    procedure PressVirtKey(p: longint);
-    procedure PressShiftVirtKey(p: longint);
+    procedure PressVirtKey(p: PtrInt);
+    procedure PressShiftVirtKey(p: PtrInt);
     procedure OnButtonClick(Sender: TObject; {%H-}Button: TMouseButton;
     procedure OnButtonClick(Sender: TObject; {%H-}Button: TMouseButton;
       {%H-}Shift: TShiftState; {%H-}X, {%H-}Y: integer); virtual;
       {%H-}Shift: TShiftState; {%H-}X, {%H-}Y: integer); virtual;
     { When value is changed by the user }
     { When value is changed by the user }
@@ -84,13 +84,13 @@ begin
   FBCThemeManager := AValue;
   FBCThemeManager := AValue;
 end;
 end;
 
 
-procedure TBCKeyboard.PressVirtKey(p: longint);
+procedure TBCKeyboard.PressVirtKey(p: PtrInt);
 begin
 begin
   KeyInput.Down(p);
   KeyInput.Down(p);
   KeyInput.Up(p);
   KeyInput.Up(p);
 end;
 end;
 
 
-procedure TBCKeyboard.PressShiftVirtKey(p: longint);
+procedure TBCKeyboard.PressShiftVirtKey(p: PtrInt);
 begin
 begin
   KeyInput.Down(VK_SHIFT);
   KeyInput.Down(VK_SHIFT);
   KeyInput.Down(p);
   KeyInput.Down(p);
@@ -171,44 +171,28 @@ begin
   end
   end
   else if str = F_back.Caption then
   else if str = F_back.Caption then
   begin
   begin
-    {$IFDEF CPUX86_64}
-    Application.ProcessMessages;
-    KeyInput.Press(VK_BACK);
-    Application.ProcessMessages;
+    {$IFDEF FPC}
+    Application.QueueAsyncCall(PressVirtKey, VK_BACK);
     {$ELSE}
     {$ELSE}
-      {$IFDEF FPC}
-      Application.QueueAsyncCall(@PressVirtKey, VK_BACK);
-      {$ELSE}
-      SendKey(VK_BACK);
-      {$ENDIF}
+    SendKey(VK_BACK);
     {$ENDIF}
     {$ENDIF}
   end
   end
   else
   else
   begin
   begin
     if str = F_space.Caption then
     if str = F_space.Caption then
       str := ' ';
       str := ' ';
-    {$IFDEF CPUX86_64}
-    Application.ProcessMessages;
-    if F_shift.Down then
-      KeyInput.Down(VK_SHIFT);
-    KeyInput.Press(Ord(UpperCase(str)[1]));
-    if F_shift.Down then
-      KeyInput.Up(VK_SHIFT);
-    Application.ProcessMessages;
-    {$ELSE}
     if F_shift.Down then
     if F_shift.Down then
       {$IFDEF FPC}
       {$IFDEF FPC}
-      Application.QueueAsyncCall(@PressShiftVirtKey, Ord(UpperCase(str)[1]))
+      Application.QueueAsyncCall(PressShiftVirtKey, Ord(UpperCase(str)[1]))
       {$ELSE}
       {$ELSE}
       SendKey(Ord(UpperCase(str)[1]), Shift)
       SendKey(Ord(UpperCase(str)[1]), Shift)
       {$ENDIF}
       {$ENDIF}
     else
     else
       {$IFDEF FPC}
       {$IFDEF FPC}
-      Application.QueueAsyncCall(@PressVirtKey, Ord(UpperCase(str)[1]));
+      Application.QueueAsyncCall(PressVirtKey, Ord(UpperCase(str)[1]));
       {$ELSE}
       {$ELSE}
       SendKey(Ord(UpperCase(str)[1]))
       SendKey(Ord(UpperCase(str)[1]))
       {$ENDIF}
       {$ENDIF}
-    {$ENDIF}
   end;
   end;
 
 
   if Assigned(FOnUserChange) then
   if Assigned(FOnUserChange) then

+ 11 - 29
bcnumerickeyboard.pas

@@ -82,7 +82,7 @@ type
   protected
   protected
     procedure OnButtonClick(Sender: TObject; {%H-}Button: TMouseButton;
     procedure OnButtonClick(Sender: TObject; {%H-}Button: TMouseButton;
       {%H-}Shift: TShiftState; {%H-}X, {%H-}Y: integer); override;
       {%H-}Shift: TShiftState; {%H-}X, {%H-}Y: integer); override;
-    procedure PressVirtKey(p: longint);
+    procedure PressVirtKey(p: PtrInt);
   public
   public
     constructor Create(AOwner: TComponent); override;
     constructor Create(AOwner: TComponent); override;
   published
   published
@@ -121,44 +121,26 @@ begin
 
 
   if num = FBtnClr.Caption then
   if num = FBtnClr.Caption then
   begin
   begin
-    {$IFDEF CPUX86_64}
-    Application.ProcessMessages;
-    KeyInput.Press(VK_BACK);
-    Application.ProcessMessages;
+    {$IFDEF FPC}
+    Application.QueueAsyncCall(PressVirtKey, VK_BACK);
     {$ELSE}
     {$ELSE}
-      {$IFDEF FPC}
-      Application.QueueAsyncCall(@PressVirtKey, VK_BACK);
-      {$ELSE}
-      SendKey(VK_BACK);
-      {$ENDIF}
+    SendKey(VK_BACK);
     {$ENDIF}
     {$ENDIF}
   end
   end
   else if num = FBtnDot.Caption then
   else if num = FBtnDot.Caption then
   begin
   begin
-    {$IFDEF CPUX86_64}
-    Application.ProcessMessages;
-    KeyInput.Press(vk_DotNumPad);
-    Application.ProcessMessages;
+    {$IFDEF FPC}
+    Application.QueueAsyncCall(PressVirtKey, vk_DotNumPad);
     {$ELSE}
     {$ELSE}
-      {$IFDEF FPC}
-      Application.QueueAsyncCall(@PressVirtKey, vk_DotNumPad);
-      {$ELSE}
-      SendKey(vk_DotNumPad);
-      {$ENDIF}
+    SendKey(vk_DotNumPad);
     {$ENDIF}
     {$ENDIF}
   end
   end
   else
   else
   begin
   begin
-    {$IFDEF CPUX86_64}
-    Application.ProcessMessages;
-    KeyInput.Press(Ord(TBCButton(Sender).Caption[1]));
-    Application.ProcessMessages;
+    {$IFDEF FPC}
+    Application.QueueAsyncCall(PressVirtKey, Ord(TBCButton(Sender).Caption[1]));
     {$ELSE}
     {$ELSE}
-      {$IFDEF FPC}
-      Application.QueueAsyncCall(@PressVirtKey, Ord(TBCButton(Sender).Caption[1]));
-      {$ELSE}
-      SendKey(Ord(TBCButton(Sender).Caption[1]));
-      {$ENDIF}
+    SendKey(Ord(TBCButton(Sender).Caption[1]));
     {$ENDIF}
     {$ENDIF}
   end;
   end;
 
 
@@ -166,7 +148,7 @@ begin
     FOnUserChange(Self);
     FOnUserChange(Self);
 end;
 end;
 
 
-procedure TBCRealNumericKeyboard.PressVirtKey(p: longint);
+procedure TBCRealNumericKeyboard.PressVirtKey(p: PtrInt);
 begin
 begin
   KeyInput.Down(p);
   KeyInput.Down(p);
   KeyInput.Up(p);
   KeyInput.Up(p);

+ 0 - 57
bcreg.pas

@@ -1,57 +0,0 @@
-// SPDX-License-Identifier: LGPL-3.0-linking-exception
-{******************************* CONTRIBUTOR(S) ******************************
-- Edivando S. Santos Brasil | [email protected]
-  (Compatibility with delphi VCL 11/2018)
-
-***************************** END CONTRIBUTOR(S) *****************************}
-unit bcReg;
-
-{$I bgracontrols.inc}
-
-interface
-
-uses
-  Classes, SysUtils, BCBaseCtrls,
-  BCButton, BCButtonFocus, BCEffect, bcfilters, BCGameGrid, BCImageButton,
-  BCLabel, BCListBox, BCMaterialDesignButton, BCPanel, BCRadialProgressBar,
-  BCRTTI, BCSamples, BCStylesForm, BCSVGButton, BCSVGViewer, BCToolBar,
-  BCTrackbarUpdown, BGRAFlashProgressBar, BGRAGraphicControl,
-  BGRAImageList, BGRAImageManipulation, BGRAKnob, BGRAResizeSpeedButton,
-  BGRAShape, BGRASpeedButton, BGRASpriteAnimation, BGRAVirtualScreen,
-  ColorSpeedButton, DTAnalogClock, DTAnalogGauge, dtthemedclock,
-  dtthemedgauge, MaterialColors, bcmdbutton, bcmdbuttonfocus, BCFluentProgressRing,
-  BCFluentSlider;
-
-procedure Register;
-
-implementation
-
-procedure Register;
-begin
-  {$R images\bgracontrols_images.res}
-
-  RegisterNoIcon([TBCCustomControl]);
-//  RegisterComponents('BGRA Custom Drawn', [TBCDButton, TBCDEdit,
-//    TBCDStaticText, TBCDProgressBar, TBCDSpinEdit, TBCDCheckBox, TBCDRadioButton, TBCDPanel]);
-
-  RegisterComponents('BGRA Controls', [TBGRAShape, TBCListBox, TBCPaperPanel, TBCPaperListBox,
-  TBCButton, TBCButtonFocus, TDTThemedGauge, TBCLabel, TBCImageButton, TBCXButton, TBCGameGrid,
-  TDTThemedClock, TDTAnalogGauge, TDTAnalogClock, TColorSpeedButton,
-  TBGRAVirtualScreen, TBGRASpriteAnimation, TBGRASpeedButton, TBGRAResizeSpeedButton,
-  TBGRAKnob, TBGRAImageManipulation, TBGRAImageList, TBGRAGraphicControl, TBGRAFlashProgressBar,
-  TBCTrackbarUpdown, TBCToolBar, TBCSVGViewer, TBCSVGButton, TBCRadialProgressBar,
-  TBCPanel,TBCMDButtonFocus, TBCMDButton, TBCMaterialDesignButton,
-  TBCFluentProgressRing, TBCFluentSlider
-  {TBCDefaultThemeManager, TBCKeyboard, TBCNumericKeyboard, TBCRealNumericKeyboard}]);
-
-
-{$IFDEF FPC}
-   RegisterPropertyEditor(TypeInfo(TBCListBox),TBCPaperListBox, 'ListBox', TClassPropertyEditor);
-   RegisterPropertyEditor(TypeInfo(integer), TBCButton,'ImageIndex', TBCButtonImageIndexPropertyEditor);
-   RegisterPropertyEditor(TypeInfo(integer), TBCButtonFocus,'ImageIndex', TBCButtonImageIndexPropertyEditor);
-   RegisterPropertyEditor(TypeInfo(TBCListBox), TBCPaperListBox, 'ListBox', TClassPropertyEditor);
-{$ENDIF}
-end;
-
-end.
-

+ 33 - 20
bgracontrols.lpk

@@ -7,7 +7,7 @@
     <CompilerOptions>
     <CompilerOptions>
       <Version Value="11"/>
       <Version Value="11"/>
       <SearchPaths>
       <SearchPaths>
-        <OtherUnitFiles Value="mouseandkeyinput;bgrasvgimagelistform"/>
+        <OtherUnitFiles Value="bgrasvgimagelistform"/>
         <UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)-$(LCLWidgetType)-$(FPCVer)"/>
         <UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)-$(LCLWidgetType)-$(FPCVer)"/>
       </SearchPaths>
       </SearchPaths>
       <Parsing>
       <Parsing>
@@ -33,21 +33,19 @@
     </CompilerOptions>
     </CompilerOptions>
     <Description Value="BGRA Controls is a set of graphical UI elements that you can use with Lazarus LCL applications."/>
     <Description Value="BGRA Controls is a set of graphical UI elements that you can use with Lazarus LCL applications."/>
     <License Value="Modified LGPL"/>
     <License Value="Modified LGPL"/>
-    <Version Major="9" Release="1" Build="7"/>
-    <Files Count="81">
+    <Version Major="9" Release="2"/>
+    <Files Count="84">
       <Item1>
       <Item1>
         <Filename Value="atshapelinebgra.pas"/>
         <Filename Value="atshapelinebgra.pas"/>
         <HasRegisterProc Value="True"/>
         <HasRegisterProc Value="True"/>
-        <UnitName Value="atshapelinebgra"/>
+        <UnitName Value="ATShapeLineBGRA"/>
       </Item1>
       </Item1>
       <Item2>
       <Item2>
         <Filename Value="bcbasectrls.pas"/>
         <Filename Value="bcbasectrls.pas"/>
-        <AddToUsesPkgSection Value="False"/>
         <UnitName Value="BCBaseCtrls"/>
         <UnitName Value="BCBaseCtrls"/>
       </Item2>
       </Item2>
       <Item3>
       <Item3>
         <Filename Value="bcbrightandcontrast.pas"/>
         <Filename Value="bcbrightandcontrast.pas"/>
-        <AddToUsesPkgSection Value="False"/>
         <UnitName Value="BCBrightAndContrast"/>
         <UnitName Value="BCBrightAndContrast"/>
       </Item3>
       </Item3>
       <Item4>
       <Item4>
@@ -72,6 +70,7 @@
       </Item7>
       </Item7>
       <Item8>
       <Item8>
         <Filename Value="bcdefaultthememanager.pas"/>
         <Filename Value="bcdefaultthememanager.pas"/>
+        <HasRegisterProc Value="True"/>
         <UnitName Value="BCDefaultThemeManager"/>
         <UnitName Value="BCDefaultThemeManager"/>
       </Item8>
       </Item8>
       <Item9>
       <Item9>
@@ -85,7 +84,7 @@
       </Item10>
       </Item10>
       <Item11>
       <Item11>
         <Filename Value="bcfilters.pas"/>
         <Filename Value="bcfilters.pas"/>
-        <UnitName Value="bcfilters"/>
+        <UnitName Value="BCFilters"/>
       </Item11>
       </Item11>
       <Item12>
       <Item12>
         <Filename Value="bcfluentprogressring.pas"/>
         <Filename Value="bcfluentprogressring.pas"/>
@@ -114,6 +113,7 @@
       </Item16>
       </Item16>
       <Item17>
       <Item17>
         <Filename Value="bckeyboard.pas"/>
         <Filename Value="bckeyboard.pas"/>
+        <HasRegisterProc Value="True"/>
         <UnitName Value="BCKeyboard"/>
         <UnitName Value="BCKeyboard"/>
       </Item17>
       </Item17>
       <Item18>
       <Item18>
@@ -167,7 +167,7 @@
       </Item27>
       </Item27>
       <Item28>
       <Item28>
         <Filename Value="bcnumerickeyboard.pas"/>
         <Filename Value="bcnumerickeyboard.pas"/>
-        <AddToUsesPkgSection Value="False"/>
+        <HasRegisterProc Value="True"/>
         <UnitName Value="BCNumericKeyboard"/>
         <UnitName Value="BCNumericKeyboard"/>
       </Item28>
       </Item28>
       <Item29>
       <Item29>
@@ -209,7 +209,6 @@
       </Item36>
       </Item36>
       <Item37>
       <Item37>
         <Filename Value="bcthememanager.pas"/>
         <Filename Value="bcthememanager.pas"/>
-        <AddToUsesPkgSection Value="False"/>
         <UnitName Value="BCThemeManager"/>
         <UnitName Value="BCThemeManager"/>
       </Item37>
       </Item37>
       <Item38>
       <Item38>
@@ -219,7 +218,6 @@
       </Item38>
       </Item38>
       <Item39>
       <Item39>
         <Filename Value="bctools.pas"/>
         <Filename Value="bctools.pas"/>
-        <AddToUsesPkgSection Value="False"/>
         <UnitName Value="BCTools"/>
         <UnitName Value="BCTools"/>
       </Item39>
       </Item39>
       <Item40>
       <Item40>
@@ -229,7 +227,6 @@
       </Item40>
       </Item40>
       <Item41>
       <Item41>
         <Filename Value="bctypes.pas"/>
         <Filename Value="bctypes.pas"/>
-        <AddToUsesPkgSection Value="False"/>
         <UnitName Value="BCTypes"/>
         <UnitName Value="BCTypes"/>
       </Item41>
       </Item41>
       <Item42>
       <Item42>
@@ -239,7 +236,7 @@
       </Item42>
       </Item42>
       <Item43>
       <Item43>
         <Filename Value="bgracontrolsinfo.pas"/>
         <Filename Value="bgracontrolsinfo.pas"/>
-        <UnitName Value="bgracontrolsinfo"/>
+        <UnitName Value="BGRAControlsInfo"/>
       </Item43>
       </Item43>
       <Item44>
       <Item44>
         <Filename Value="bgracustomdrawn.pas"/>
         <Filename Value="bgracustomdrawn.pas"/>
@@ -355,12 +352,12 @@
       <Item66>
       <Item66>
         <Filename Value="dtthemedclock.pas"/>
         <Filename Value="dtthemedclock.pas"/>
         <HasRegisterProc Value="True"/>
         <HasRegisterProc Value="True"/>
-        <UnitName Value="dtthemedclock"/>
+        <UnitName Value="DTThemedClock"/>
       </Item66>
       </Item66>
       <Item67>
       <Item67>
         <Filename Value="dtthemedgauge.pas"/>
         <Filename Value="dtthemedgauge.pas"/>
         <HasRegisterProc Value="True"/>
         <HasRegisterProc Value="True"/>
-        <UnitName Value="dtthemedgauge"/>
+        <UnitName Value="DTThemedGauge"/>
       </Item67>
       </Item67>
       <Item68>
       <Item68>
         <Filename Value="materialcolors.pas"/>
         <Filename Value="materialcolors.pas"/>
@@ -418,28 +415,44 @@
       <Item79>
       <Item79>
         <Filename Value="supergauge.pas"/>
         <Filename Value="supergauge.pas"/>
         <HasRegisterProc Value="True"/>
         <HasRegisterProc Value="True"/>
-        <UnitName Value="supergauge"/>
+        <UnitName Value="SuperGauge"/>
       </Item79>
       </Item79>
       <Item80>
       <Item80>
         <Filename Value="supergaugecommon.pas"/>
         <Filename Value="supergaugecommon.pas"/>
-        <UnitName Value="supergaugecommon"/>
+        <UnitName Value="SuperGaugeCommon"/>
       </Item80>
       </Item80>
       <Item81>
       <Item81>
         <Filename Value="bgradialogs.pas"/>
         <Filename Value="bgradialogs.pas"/>
         <HasRegisterProc Value="True"/>
         <HasRegisterProc Value="True"/>
         <UnitName Value="BGRADialogs"/>
         <UnitName Value="BGRADialogs"/>
       </Item81>
       </Item81>
+      <Item82>
+        <Filename Value="bgraformatui.pas"/>
+        <UnitName Value="BGRAFormatUI"/>
+      </Item82>
+      <Item83>
+        <Filename Value="superspinner.pas"/>
+        <HasRegisterProc Value="True"/>
+        <UnitName Value="SuperSpinner"/>
+      </Item83>
+      <Item84>
+        <Filename Value="superspinnercommon.pas"/>
+        <UnitName Value="SuperSpinnerCommon"/>
+      </Item84>
     </Files>
     </Files>
     <CompatibilityMode Value="True"/>
     <CompatibilityMode Value="True"/>
     <LazDoc Paths="fpdoc"/>
     <LazDoc Paths="fpdoc"/>
-    <RequiredPkgs Count="2">
+    <RequiredPkgs Count="3">
       <Item1>
       <Item1>
-        <PackageName Value="BGRABitmapPack"/>
-        <MinVersion Major="11" Minor="6" Release="4" Valid="True"/>
+        <PackageName Value="lazmouseandkeyinput"/>
       </Item1>
       </Item1>
       <Item2>
       <Item2>
-        <PackageName Value="IDEIntf"/>
+        <PackageName Value="BGRABitmapPack"/>
+        <MinVersion Major="11" Minor="6" Release="4" Valid="True"/>
       </Item2>
       </Item2>
+      <Item3>
+        <PackageName Value="IDEIntf"/>
+      </Item3>
     </RequiredPkgs>
     </RequiredPkgs>
     <UsageOptions>
     <UsageOptions>
       <UnitPath Value="$(PkgOutDir)"/>
       <UnitPath Value="$(PkgOutDir)"/>

+ 22 - 16
bgracontrols.pas

@@ -8,40 +8,44 @@ unit bgracontrols;
 interface
 interface
 
 
 uses
 uses
-  atshapelinebgra, BCButton, BCButtonFocus, BCCheckComboBox, BCComboBox, 
-  BCDefaultThemeManager, BCEffect, BCExpandPanels, bcfilters, 
-  BCFluentProgressRing, BCFluentSlider, BCGameGrid, BCGradientButton, 
-  BCImageButton, BCKeyboard, BCLabel, BCListBox, BCListBoxEx, 
-  BCMaterialDesignButton, BCMaterialEdit, BCMaterialFloatSpinEdit, 
-  BCMaterialProgressBarMarquee, BCMaterialSpinEdit, BCMDButton, 
-  BCMDButtonFocus, BCPanel, BCRadialProgressBar, BCRoundedImage, BCRTTI, 
-  BCSamples, BCStylesForm, BCSVGButton, BCSVGViewer, BCToolBar, 
-  BCTrackbarUpdown, BGRAColorTheme, bgracontrolsinfo, BGRACustomDrawn, 
-  BGRAFlashProgressBar, BGRAGraphicControl, BGRAImageList, 
+  ATShapeLineBGRA, BCBaseCtrls, BCBrightAndContrast, BCButton, BCButtonFocus, 
+  BCCheckComboBox, BCComboBox, BCDefaultThemeManager, BCEffect, 
+  BCExpandPanels, BCFilters, BCFluentProgressRing, BCFluentSlider, BCGameGrid, 
+  BCGradientButton, BCImageButton, BCKeyboard, BCLabel, BCListBox, 
+  BCListBoxEx, BCMaterialDesignButton, BCMaterialEdit, 
+  BCMaterialFloatSpinEdit, BCMaterialProgressBarMarquee, BCMaterialSpinEdit, 
+  BCMDButton, BCMDButtonFocus, BCNumericKeyboard, BCPanel, 
+  BCRadialProgressBar, BCRoundedImage, BCRTTI, BCSamples, BCStylesForm, 
+  BCSVGButton, BCSVGViewer, BCThemeManager, BCToolBar, BCTools, 
+  BCTrackbarUpdown, BCTypes, BGRAColorTheme, BGRAControlsInfo, 
+  BGRACustomDrawn, BGRAFlashProgressBar, BGRAGraphicControl, BGRAImageList, 
   BGRAImageManipulation, BGRAImageTheme, BGRAKnob, BGRAResizeSpeedButton, 
   BGRAImageManipulation, BGRAImageTheme, BGRAKnob, BGRAResizeSpeedButton, 
   BGRAShape, BGRASpeedButton, BGRASpriteAnimation, BGRASVGImageList, 
   BGRAShape, BGRASpeedButton, BGRASpriteAnimation, BGRASVGImageList, 
   BGRASVGTheme, BGRATheme, BGRAThemeButton, BGRAThemeCheckBox, 
   BGRASVGTheme, BGRATheme, BGRAThemeButton, BGRAThemeCheckBox, 
   BGRAThemeRadioButton, BGRAVirtualScreen, ColorSpeedButton, DTAnalogClock, 
   BGRAThemeRadioButton, BGRAVirtualScreen, ColorSpeedButton, DTAnalogClock, 
-  DTAnalogCommon, DTAnalogGauge, dtthemedclock, dtthemedgauge, MaterialColors, 
+  DTAnalogCommon, DTAnalogGauge, DTThemedClock, DTThemedGauge, MaterialColors, 
   bgrasvgimagelistform, BCLeaLCDDisplay, BCLeaLED, BCLeaQLED, BCLeaRingSlider, 
   bgrasvgimagelistform, BCLeaLCDDisplay, BCLeaLED, BCLeaQLED, BCLeaRingSlider, 
   BCLeaSelector, BCLeaTheme, BCLeaLCDDisplay_EditorRegister, BCLeaBoard, 
   BCLeaSelector, BCLeaTheme, BCLeaLCDDisplay_EditorRegister, BCLeaBoard, 
-  BCLeaEngrave, supergauge, supergaugecommon, BGRADialogs, LazarusPackageIntf;
+  BCLeaEngrave, SuperGauge, SuperGaugeCommon, BGRADialogs, BGRAFormatUI, 
+  SuperSpinner, SuperSpinnerCommon, LazarusPackageIntf;
 
 
 implementation
 implementation
 
 
 procedure Register;
 procedure Register;
 begin
 begin
-  RegisterUnit('atshapelinebgra', @atshapelinebgra.Register);
+  RegisterUnit('ATShapeLineBGRA', @ATShapeLineBGRA.Register);
   RegisterUnit('BCButton', @BCButton.Register);
   RegisterUnit('BCButton', @BCButton.Register);
   RegisterUnit('BCButtonFocus', @BCButtonFocus.Register);
   RegisterUnit('BCButtonFocus', @BCButtonFocus.Register);
   RegisterUnit('BCCheckComboBox', @BCCheckComboBox.Register);
   RegisterUnit('BCCheckComboBox', @BCCheckComboBox.Register);
   RegisterUnit('BCComboBox', @BCComboBox.Register);
   RegisterUnit('BCComboBox', @BCComboBox.Register);
+  RegisterUnit('BCDefaultThemeManager', @BCDefaultThemeManager.Register);
   RegisterUnit('BCExpandPanels', @BCExpandPanels.Register);
   RegisterUnit('BCExpandPanels', @BCExpandPanels.Register);
   RegisterUnit('BCFluentProgressRing', @BCFluentProgressRing.Register);
   RegisterUnit('BCFluentProgressRing', @BCFluentProgressRing.Register);
   RegisterUnit('BCFluentSlider', @BCFluentSlider.Register);
   RegisterUnit('BCFluentSlider', @BCFluentSlider.Register);
   RegisterUnit('BCGameGrid', @BCGameGrid.Register);
   RegisterUnit('BCGameGrid', @BCGameGrid.Register);
   RegisterUnit('BCGradientButton', @BCGradientButton.Register);
   RegisterUnit('BCGradientButton', @BCGradientButton.Register);
   RegisterUnit('BCImageButton', @BCImageButton.Register);
   RegisterUnit('BCImageButton', @BCImageButton.Register);
+  RegisterUnit('BCKeyboard', @BCKeyboard.Register);
   RegisterUnit('BCLabel', @BCLabel.Register);
   RegisterUnit('BCLabel', @BCLabel.Register);
   RegisterUnit('BCListBox', @BCListBox.Register);
   RegisterUnit('BCListBox', @BCListBox.Register);
   RegisterUnit('BCMaterialDesignButton', @BCMaterialDesignButton.Register);
   RegisterUnit('BCMaterialDesignButton', @BCMaterialDesignButton.Register);
@@ -52,6 +56,7 @@ begin
   RegisterUnit('BCMaterialSpinEdit', @BCMaterialSpinEdit.Register);
   RegisterUnit('BCMaterialSpinEdit', @BCMaterialSpinEdit.Register);
   RegisterUnit('BCMDButton', @BCMDButton.Register);
   RegisterUnit('BCMDButton', @BCMDButton.Register);
   RegisterUnit('BCMDButtonFocus', @BCMDButtonFocus.Register);
   RegisterUnit('BCMDButtonFocus', @BCMDButtonFocus.Register);
+  RegisterUnit('BCNumericKeyboard', @BCNumericKeyboard.Register);
   RegisterUnit('BCPanel', @BCPanel.Register);
   RegisterUnit('BCPanel', @BCPanel.Register);
   RegisterUnit('BCRadialProgressBar', @BCRadialProgressBar.Register);
   RegisterUnit('BCRadialProgressBar', @BCRadialProgressBar.Register);
   RegisterUnit('BCRoundedImage', @BCRoundedImage.Register);
   RegisterUnit('BCRoundedImage', @BCRoundedImage.Register);
@@ -81,8 +86,8 @@ begin
   RegisterUnit('ColorSpeedButton', @ColorSpeedButton.Register);
   RegisterUnit('ColorSpeedButton', @ColorSpeedButton.Register);
   RegisterUnit('DTAnalogClock', @DTAnalogClock.Register);
   RegisterUnit('DTAnalogClock', @DTAnalogClock.Register);
   RegisterUnit('DTAnalogGauge', @DTAnalogGauge.Register);
   RegisterUnit('DTAnalogGauge', @DTAnalogGauge.Register);
-  RegisterUnit('dtthemedclock', @dtthemedclock.Register);
-  RegisterUnit('dtthemedgauge', @dtthemedgauge.Register);
+  RegisterUnit('DTThemedClock', @DTThemedClock.Register);
+  RegisterUnit('DTThemedGauge', @DTThemedGauge.Register);
   RegisterUnit('BCLeaLCDDisplay', @BCLeaLCDDisplay.Register);
   RegisterUnit('BCLeaLCDDisplay', @BCLeaLCDDisplay.Register);
   RegisterUnit('BCLeaLED', @BCLeaLED.Register);
   RegisterUnit('BCLeaLED', @BCLeaLED.Register);
   RegisterUnit('BCLeaQLED', @BCLeaQLED.Register);
   RegisterUnit('BCLeaQLED', @BCLeaQLED.Register);
@@ -93,8 +98,9 @@ begin
     @BCLeaLCDDisplay_EditorRegister.Register);
     @BCLeaLCDDisplay_EditorRegister.Register);
   RegisterUnit('BCLeaBoard', @BCLeaBoard.Register);
   RegisterUnit('BCLeaBoard', @BCLeaBoard.Register);
   RegisterUnit('BCLeaEngrave', @BCLeaEngrave.Register);
   RegisterUnit('BCLeaEngrave', @BCLeaEngrave.Register);
-  RegisterUnit('supergauge', @supergauge.Register);
+  RegisterUnit('SuperGauge', @SuperGauge.Register);
   RegisterUnit('BGRADialogs', @BGRADialogs.Register);
   RegisterUnit('BGRADialogs', @BGRADialogs.Register);
+  RegisterUnit('SuperSpinner', @SuperSpinner.Register);
 end;
 end;
 
 
 initialization
 initialization

+ 2 - 2
bgracontrolsinfo.pas

@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: LGPL-3.0-linking-exception
 // SPDX-License-Identifier: LGPL-3.0-linking-exception
-unit bgracontrolsinfo;
+unit BGRAControlsInfo;
 
 
 {$mode objfpc}{$H+}
 {$mode objfpc}{$H+}
 
 
@@ -9,7 +9,7 @@ uses
   Classes, SysUtils;
   Classes, SysUtils;
 
 
 const
 const
-  BGRAControlsVersion = 9000107;
+  BGRAControlsVersion = 9000200;
 
 
   function BGRAControlsVersionStr: string;
   function BGRAControlsVersionStr: string;
 
 

+ 103 - 9
bgradialogs.pas

@@ -79,11 +79,13 @@ type
   end;
   end;
 
 
 
 
-//Functions to Get Filters String useful in Dialogs
-function GetBGRAFormatFilter(AFormat: TBGRAImageFormat): String;
-procedure BuildBGRAFilterStrings(AUseReaders: Boolean; var Descriptions, Filters: String);
-function BuildBGRAImageReaderFilter: String;
-function BuildBGRAImageWriterFilter: String;
+{** Get Registered Readers Filters to use in Dialogs}
+function BGRARegisteredImageReaderFilter: String; overload;
+function BGRARegisteredImageReaderFilter(AFormat: TBGRAImageFormat): String; overload;
+
+{** Get Registered Writers Filters to use in Dialogs}
+function BGRARegisteredImageWriterFilter: String; overload;
+function BGRARegisteredImageWriterFilter(AFormat: TBGRAImageFormat): String; overload;
 
 
 procedure Register;
 procedure Register;
 
 
@@ -92,11 +94,102 @@ implementation
 uses
 uses
   WSExtDlgs, Masks, FileUtil, LazFileUtils, LCLStrConsts, LCLType;
   WSExtDlgs, Masks, FileUtil, LazFileUtils, LCLStrConsts, LCLType;
 
 
-function GetBGRAFormatFilter(AFormat: TBGRAImageFormat): String;
+function BGRARegisteredImageReaderFilter: String;
+var
+   iFormat: TBGRAImageFormat;
+   curExt,
+   Extensions: String;
+
+begin
+  Result:= '';
+  Extensions:= '';
+
+  for iFormat:=ifJpeg to High(TBGRAImageFormat) do
+    if (DefaultBGRAImageReader[iFormat] <> nil) then
+    begin
+      if (iFormat>ifJpeg) then
+      begin
+        Result:= Result + '|';
+        Extensions:= Extensions + ';';
+      end;
+
+      curExt:= StringReplace('*'+ExtensionSeparator+BGRAImageFormat[iFormat].Extensions,
+                             ';', ';*'+ExtensionSeparator, [rfReplaceAll]);
+
+      FmtStr(Result, '%s%s (%s)|%s',
+            [Result, BGRAImageFormat[iFormat].TypeName, curExt, curExt]);
+
+      Extensions:= Extensions+curExt;
+    end;
+
+  FmtStr(Result, '%s (%s)|%1:s|%s', [rsGraphic, Extensions, Result]);
+end;
+
+function BGRARegisteredImageReaderFilter(AFormat: TBGRAImageFormat): String;
+var
+   curExt: String;
+
+begin
+  Result:= '';
+  if (DefaultBGRAImageReader[AFormat] <> nil) then
+  begin
+    curExt:= StringReplace('*'+ExtensionSeparator+BGRAImageFormat[AFormat].Extensions,
+                           ';', ';*'+ExtensionSeparator, [rfReplaceAll]);
+
+    FmtStr(Result, '%s (%s)|%s',
+          [BGRAImageFormat[AFormat].TypeName, curExt, curExt]);
+  end;
+end;
+
+function BGRARegisteredImageWriterFilter: String;
+var
+   iFormat: TBGRAImageFormat;
+   curExt,
+   Extensions: String;
+
+begin
+  Result:= '';
+  Extensions:= '';
+
+  for iFormat:=ifJpeg to High(TBGRAImageFormat) do
+    if (DefaultBGRAImageWriter[iFormat] <> nil) then
+    begin
+      if (iFormat>ifJpeg) then
+      begin
+        Result:= Result + '|';
+        Extensions:= Extensions + ';';
+      end;
+
+      curExt:= StringReplace('*'+ExtensionSeparator+BGRAImageFormat[iFormat].Extensions,
+                             ';', ';*'+ExtensionSeparator, [rfReplaceAll]);
+
+      FmtStr(Result, '%s%s (%s)|%s',
+            [Result, BGRAImageFormat[iFormat].TypeName, curExt, curExt]);
+
+      Extensions:= Extensions+curExt;
+    end;
+
+  FmtStr(Result, '%s (%s)|%1:s|%s', [rsGraphic, Extensions, Result]);
+end;
+
+function BGRARegisteredImageWriterFilter(AFormat: TBGRAImageFormat): String;
+var
+   curExt: String;
+
 begin
 begin
-  Result := StringReplace('*.' + BGRAImageFormat[AFormat].Extensions, ';', ';*.', [rfReplaceAll]);
+  Result:= '';
+  if (DefaultBGRAImageWriter[AFormat] <> nil) then
+  begin
+    curExt:= StringReplace('*'+ExtensionSeparator+BGRAImageFormat[AFormat].Extensions,
+                           ';', ';*'+ExtensionSeparator, [rfReplaceAll]);
+
+    FmtStr(Result, '%s (%s)|%s',
+          [BGRAImageFormat[AFormat].TypeName, curExt, curExt]);
+  end;
 end;
 end;
 
 
+
+(*
 procedure BuildBGRAFilterStrings(AUseReaders: Boolean; var Descriptions, Filters: String);
 procedure BuildBGRAFilterStrings(AUseReaders: Boolean; var Descriptions, Filters: String);
 var
 var
   iFormat: TBGRAImageFormat;
   iFormat: TBGRAImageFormat;
@@ -149,6 +242,7 @@ begin
   Result := '';
   Result := '';
   BuildBGRAFilterStrings(False, Result, Filters);
   BuildBGRAFilterStrings(False, Result, Filters);
 end;
 end;
+*)
 
 
 { TBGRAOpenPictureDialog }
 { TBGRAOpenPictureDialog }
 
 
@@ -308,7 +402,7 @@ end;
 constructor TBGRAOpenPictureDialog.Create(TheOwner: TComponent);
 constructor TBGRAOpenPictureDialog.Create(TheOwner: TComponent);
 begin
 begin
   inherited Create(TheOwner);
   inherited Create(TheOwner);
-  FDefaultFilter := BuildBGRAImageReaderFilter+'|'+
+  FDefaultFilter := BGRARegisteredImageReaderFilter+'|'+
                     Format(rsAllFiles,[GetAllFilesMask, GetAllFilesMask,'']);
                     Format(rsAllFiles,[GetAllFilesMask, GetAllFilesMask,'']);
   Filter:=FDefaultFilter;
   Filter:=FDefaultFilter;
 
 
@@ -376,7 +470,7 @@ end;
 constructor TBGRASavePictureDialog.Create(TheOwner: TComponent);
 constructor TBGRASavePictureDialog.Create(TheOwner: TComponent);
 begin
 begin
   inherited Create(TheOwner);
   inherited Create(TheOwner);
-  FDefaultFilter := BuildBGRAImageWriterFilter+'|'+
+  FDefaultFilter := BGRARegisteredImageWriterFilter+'|'+
                     Format(rsAllFiles,[GetAllFilesMask, GetAllFilesMask,'']);
                     Format(rsAllFiles,[GetAllFilesMask, GetAllFilesMask,'']);
   Filter:=FDefaultFilter;
   Filter:=FDefaultFilter;
 
 

+ 67 - 13
bgraflashprogressbar.pas

@@ -23,6 +23,7 @@
              Added Graph Style and ShowDividers, Renamed MultiProgress properties
              Added Graph Style and ShowDividers, Renamed MultiProgress properties
              Added ShowBarAnimation
              Added ShowBarAnimation
     2025-02  Added use of Font.Color
     2025-02  Added use of Font.Color
+    2025-03  Added MarqueeWidthType
 ***************************** END CONTRIBUTOR(S) *****************************}
 ***************************** END CONTRIBUTOR(S) *****************************}
 unit BGRAFlashProgressBar;
 unit BGRAFlashProgressBar;
 
 
@@ -40,6 +41,7 @@ uses
 
 
 type
 type
   TBGRAPBarStyle = (pbstNormal, pbstMultiProgress, pbstMarquee, pbstTimer, pbstGraph);
   TBGRAPBarStyle = (pbstNormal, pbstMultiProgress, pbstMarquee, pbstTimer, pbstGraph);
+  TBGRAPBarMarqueeWidthType = (pbmwAuto, pbmwFixed, pbmwValue, pbmwValueSub); //, pbmwInc MaxM: maybe tomorrow when I have free time
   TBGRAPBarMarqueeDirection = (pbmdToRight, pbmdToLeft);
   TBGRAPBarMarqueeDirection = (pbmdToRight, pbmdToLeft);
   TBGRAPBarMarqueeSpeed = (pbmsSlow, pbmsMedium, pbmsFast);
   TBGRAPBarMarqueeSpeed = (pbmsSlow, pbmsMedium, pbmsFast);
 
 
@@ -73,6 +75,7 @@ type
     procedure SetGraphYLineAfter(AValue: String);
     procedure SetGraphYLineAfter(AValue: String);
     procedure SetGraphYLineCaption(AValue: String);
     procedure SetGraphYLineCaption(AValue: String);
     procedure SetGraphYLineDigits(AValue: Integer);
     procedure SetGraphYLineDigits(AValue: Integer);
+    procedure SetMarqueeWidthType(AValue: TBGRAPBarMarqueeWidthType);
     procedure SetMax(AValue: Integer);
     procedure SetMax(AValue: Integer);
     procedure SetMin(AValue: Integer);
     procedure SetMin(AValue: Integer);
     procedure SetPosition(AValue: Integer);
     procedure SetPosition(AValue: Integer);
@@ -110,6 +113,7 @@ type
     FGraphShowYDividers: Boolean;
     FGraphShowYDividers: Boolean;
     FBarColor,
     FBarColor,
     FBarColorSub: TColor;
     FBarColorSub: TColor;
+    FMarqueeWidthType: TBGRAPBarMarqueeWidthType;
     FMarqueeDirection: TBGRAPBarMarqueeDirection;
     FMarqueeDirection: TBGRAPBarMarqueeDirection;
     FMarqueeSpeed: TBGRAPBarMarqueeSpeed;
     FMarqueeSpeed: TBGRAPBarMarqueeSpeed;
     FMarqueeWidth,
     FMarqueeWidth,
@@ -159,6 +163,8 @@ type
 
 
     procedure TimerOnTimer(Sender: TObject);
     procedure TimerOnTimer(Sender: TObject);
 
 
+    procedure CalcMarqueeWidth;
+
   public
   public
     constructor Create(AOwner: TComponent); override;
     constructor Create(AOwner: TComponent); override;
     destructor Destroy; override;
     destructor Destroy; override;
@@ -226,10 +232,11 @@ type
     property ShowDividers: Boolean read FShowDividers write SetShowDividers default False;
     property ShowDividers: Boolean read FShowDividers write SetShowDividers default False;
     property ShowBarAnimation: Boolean read FShowBarAnimation write SetShowBarAnimation default False;
     property ShowBarAnimation: Boolean read FShowBarAnimation write SetShowBarAnimation default False;
     property Style: TBGRAPBarStyle read FStyle write SetStyle default pbstNormal;
     property Style: TBGRAPBarStyle read FStyle write SetStyle default pbstNormal;
-    property MarqueeWidth: Word read FMarqueeWidth write SetMarqueeWidth default 0;
+    property MarqueeWidthType: TBGRAPBarMarqueeWidthType read FMarqueeWidthType write SetMarqueeWidthType default pbmwAuto;
+    property MarqueeWidth: Word read FMarqueeWidth write SetMarqueeWidth default 95;
     property MarqueeSpeed: TBGRAPBarMarqueeSpeed read FMarqueeSpeed write SetMarqueeSpeed default pbmsMedium;
     property MarqueeSpeed: TBGRAPBarMarqueeSpeed read FMarqueeSpeed write SetMarqueeSpeed default pbmsMedium;
     property MarqueeDirection: TBGRAPBarMarqueeDirection read FMarqueeDirection write SetMarqueeDirection default pbmdToRight;
     property MarqueeDirection: TBGRAPBarMarqueeDirection read FMarqueeDirection write SetMarqueeDirection default pbmdToRight;
-    property MarqueeBounce: Word read FMarqueeBounce write SetMarqueeBounce;
+    property MarqueeBounce: Word read FMarqueeBounce write SetMarqueeBounce default 0;
 
 
     property TimerInterval: Cardinal read FTimerInterval write SetTimerInterval default 100;
     property TimerInterval: Cardinal read FTimerInterval write SetTimerInterval default 100;
     property TimerAutoRestart: Boolean read FTimerAutoRestart write FTimerAutoRestart default True;
     property TimerAutoRestart: Boolean read FTimerAutoRestart write FTimerAutoRestart default True;
@@ -269,6 +276,7 @@ const
   MARQUEE_TIMER_MED  = 20;
   MARQUEE_TIMER_MED  = 20;
   MARQUEE_TIMER_FAST = 10;
   MARQUEE_TIMER_FAST = 10;
   MARQUEE_INC = 2;
   MARQUEE_INC = 2;
+  MARQUEE_WIDTH_MIN = 10;
 
 
 {$IFDEF FPC}
 {$IFDEF FPC}
 procedure Register;
 procedure Register;
@@ -438,6 +446,17 @@ begin
   Invalidate;
   Invalidate;
 end;
 end;
 
 
+procedure TBGRAFlashProgressBar.SetMarqueeWidthType(AValue: TBGRAPBarMarqueeWidthType);
+begin
+  if FMarqueeWidthType=AValue then Exit;
+  FMarqueeWidthType:=AValue;
+
+  CalcMarqueeWidth;
+
+  if Assigned(FOnChange) then FOnChange(Self);
+  Invalidate;
+end;
+
 procedure TBGRAFlashProgressBar.SetMax(AValue: Integer);
 procedure TBGRAFlashProgressBar.SetMax(AValue: Integer);
 begin
 begin
   SetMaxValue(AValue);
   SetMaxValue(AValue);
@@ -515,13 +534,18 @@ end;
 procedure TBGRAFlashProgressBar.SetMarqueeWidth(AValue: Word);
 procedure TBGRAFlashProgressBar.SetMarqueeWidth(AValue: Word);
 begin
 begin
   if FMarqueeWidth=AValue then Exit;
   if FMarqueeWidth=AValue then Exit;
+
+  if (AValue > Width) then AValue:= Width;
+  if (AValue < MARQUEE_WIDTH_MIN) then AValue:= MARQUEE_WIDTH_MIN;
   FMarqueeWidth:= AValue;
   FMarqueeWidth:= AValue;
-  if (FMarqueeWidth = 0)
-  then rMarqueeWidth:= Width div 4
-  else rMarqueeWidth:= FMarqueeWidth;
 
 
-  if Assigned(FOnChange) then FOnChange(Self);
-  Invalidate;
+  if (FMarqueeWidthType = pbmwFixed) then
+  begin
+    rMarqueeWidth:= FMarqueeWidth;
+
+    if Assigned(FOnChange) then FOnChange(Self);
+    Invalidate;
+  end;
 end;
 end;
 
 
 procedure TBGRAFlashProgressBar.SetMaxValue(AValue: Double);
 procedure TBGRAFlashProgressBar.SetMaxValue(AValue: Double);
@@ -602,7 +626,7 @@ begin
 
 
         if (FMarqueeDirection = pbmdToRight)
         if (FMarqueeDirection = pbmdToRight)
         then marqueeLeft:= 2
         then marqueeLeft:= 2
-        else marqueeLeft:= -FMarqueeWidth;
+        else marqueeLeft:= -rMarqueeWidth;
 
 
         if FTimerAutoRestart and
         if FTimerAutoRestart and
            not(csLoading in ComponentState) and
            not(csLoading in ComponentState) and
@@ -727,6 +751,22 @@ begin
   end;
   end;
 end;
 end;
 
 
+procedure TBGRAFlashProgressBar.CalcMarqueeWidth;
+begin
+  Case FMarqueeWidthType of
+    pbmwAuto: rMarqueeWidth:= Width div 4;
+    pbmwFixed: rMarqueeWidth:= FMarqueeWidth;
+    pbmwValue: begin
+        rMarqueeWidth:= round((FValue - FMinValue) / (FMaxValue - FMinValue) * (Width-2));
+        if (rMarqueeWidth < MARQUEE_WIDTH_MIN) then rMarqueeWidth:= MARQUEE_WIDTH_MIN;
+    end;
+    pbmwValueSub: begin
+        rMarqueeWidth:= round((FValueSub - FMinValue) / (FMaxValue - FMinValue) * (Width-2));
+        if (rMarqueeWidth < MARQUEE_WIDTH_MIN) then rMarqueeWidth:= MARQUEE_WIDTH_MIN;
+    end;
+  end;
+end;
+
 {$hints off}
 {$hints off}
 class function TBGRAFlashProgressBar.GetControlClassDefaultSize: TSize;
 class function TBGRAFlashProgressBar.GetControlClassDefaultSize: TSize;
 begin
 begin
@@ -744,9 +784,7 @@ procedure TBGRAFlashProgressBar.DoOnResize;
 begin
 begin
   inherited DoOnResize;
   inherited DoOnResize;
 
 
-  if (FMarqueeWidth = 0)
-  then rMarqueeWidth:= Width div 4
-  else rMarqueeWidth:= FMarqueeWidth;
+  if (FMarqueeWidthType = pbmwAuto) then rMarqueeWidth:= Width div 4;
 end;
 end;
 
 
 {$hints on}
 {$hints on}
@@ -775,9 +813,11 @@ begin
       internalTimer.Enabled:= FShowBarAnimation;
       internalTimer.Enabled:= FShowBarAnimation;
     end;
     end;
     pbstMarquee: begin
     pbstMarquee: begin
+      CalcMarqueeWidth;
+
       if (FMarqueeDirection = pbmdToRight)
       if (FMarqueeDirection = pbmdToRight)
       then marqueeLeft:= 2
       then marqueeLeft:= 2
-      else marqueeLeft:= -FMarqueeWidth;
+      else marqueeLeft:= -rMarqueeWidth;
 
 
       if FTimerAutoRestart and not(csDesigning in ComponentState) then internalTimer.Enabled:= True;
       if FTimerAutoRestart and not(csDesigning in ComponentState) then internalTimer.Enabled:= True;
     end;
     end;
@@ -843,13 +883,15 @@ begin
   barAnimLeft:= 0;
   barAnimLeft:= 0;
 
 
   //Marquee
   //Marquee
-  FMarqueeWidth:= 0; //AutoWidth
+  FMarqueeWidthType:= pbmwAuto; //AutoWidth
   rMarqueeWidth:= 95; //PreferredWidth div 4
   rMarqueeWidth:= 95; //PreferredWidth div 4
+  FMarqueeWidth:= 95;
   FMarqueeSpeed:= pbmsMedium;
   FMarqueeSpeed:= pbmsMedium;
   FMarqueeDirection:= pbmdToRight;
   FMarqueeDirection:= pbmdToRight;
   marqueeCurMode:= pbmdToRight;
   marqueeCurMode:= pbmdToRight;
   marqueeLeft:= 0;
   marqueeLeft:= 0;
   marqueeRight:= 0;
   marqueeRight:= 0;
+  FMarqueeBounce:= 0;
   marqueeBouncing:= False;
   marqueeBouncing:= False;
 
 
   //Timer
   //Timer
@@ -1215,6 +1257,18 @@ begin
         else if FShowDividers then DrawDividers(False);
         else if FShowDividers then DrawDividers(False);
       end;
       end;
       pbstMarquee: begin
       pbstMarquee: begin
+        //Calculate new MarqueeWidth based on Values (only if type is Value related)
+        Case FMarqueeWidthType of
+          pbmwValue: begin
+              rMarqueeWidth:= round((FValue - FMinValue) / (FMaxValue - FMinValue) * (content.right - content.left));
+              if (rMarqueeWidth < MARQUEE_WIDTH_MIN) then rMarqueeWidth:= MARQUEE_WIDTH_MIN;
+          end;
+          pbmwValueSub: begin
+              rMarqueeWidth:= round((FValueSub - FMinValue) / (FMaxValue - FMinValue) * (content.right - content.left));
+              if (rMarqueeWidth < MARQUEE_WIDTH_MIN) then rMarqueeWidth:= MARQUEE_WIDTH_MIN;
+          end;
+        end;
+
         if (marqueeCurMode = pbmdToRight)
         if (marqueeCurMode = pbmdToRight)
         then begin
         then begin
                //check if the whole bar is out put it back to the beginning
                //check if the whole bar is out put it back to the beginning

+ 702 - 0
bgraformatui.lfm

@@ -0,0 +1,702 @@
+object BGRAFormatUIContainer: TBGRAFormatUIContainer
+  Left = 374
+  Height = 530
+  Top = 34
+  Width = 713
+  Caption = 'BGRAFormatUIContainer'
+  ClientHeight = 530
+  ClientWidth = 713
+  DesignTimePPI = 120
+  LCLVersion = '4.99.0.0'
+  OnCreate = FormCreate
+  object ifJpeg: TBCPanel
+    Left = 0
+    Height = 150
+    Top = 0
+    Width = 350
+    Background.Color = clBtnFace
+    Background.Gradient1.StartColor = clWhite
+    Background.Gradient1.EndColor = clBlack
+    Background.Gradient1.GradientType = gtLinear
+    Background.Gradient1.Point1XPercent = 0
+    Background.Gradient1.Point1YPercent = 0
+    Background.Gradient1.Point2XPercent = 0
+    Background.Gradient1.Point2YPercent = 100
+    Background.Gradient2.StartColor = clWhite
+    Background.Gradient2.EndColor = clBlack
+    Background.Gradient2.GradientType = gtLinear
+    Background.Gradient2.Point1XPercent = 0
+    Background.Gradient2.Point1YPercent = 0
+    Background.Gradient2.Point2XPercent = 0
+    Background.Gradient2.Point2YPercent = 100
+    Background.Gradient1EndPercent = 35
+    Background.Style = bbsColor
+    BevelInner = bvNone
+    BevelOuter = bvRaised
+    BevelWidth = 1
+    Border.Style = bboNone
+    Caption = 'jfJpeg'
+    FontEx.Color = clDefault
+    FontEx.FontQuality = fqSystemClearType
+    FontEx.Shadow = False
+    FontEx.ShadowRadius = 5
+    FontEx.ShadowOffsetX = 5
+    FontEx.ShadowOffsetY = 5
+    FontEx.Style = []
+    ParentBackground = False
+    Rounding.RoundX = 1
+    Rounding.RoundY = 1
+    TabOrder = 0
+    Visible = False
+    object ifJpeg_GrayScale: TCheckBox
+      Left = 16
+      Height = 24
+      Top = 8
+      Width = 90
+      Caption = 'Gray Scale'
+      TabOrder = 0
+    end
+    object ifJpeg_ProgressiveEncoding: TCheckBox
+      Left = 16
+      Height = 24
+      Top = 40
+      Width = 162
+      Caption = 'Progressive Encoding'
+      TabOrder = 1
+    end
+    object Label1: TLabel
+      Left = 18
+      Height = 20
+      Top = 74
+      Width = 137
+      Caption = 'Compression Quality'
+    end
+    object ifJpeg_CompressionQuality: TBCTrackbarUpdown
+      Left = 32
+      Height = 32
+      Top = 104
+      Width = 300
+      AllowNegativeValues = False
+      BarExponent = 1
+      Increment = 1
+      LongTimeInterval = 400
+      MinValue = 1
+      MaxValue = 100
+      Value = 75
+      ShortTimeInterval = 100
+      Background.Color = clWindow
+      Background.Gradient1.StartColor = clWhite
+      Background.Gradient1.EndColor = clBlack
+      Background.Gradient1.GradientType = gtLinear
+      Background.Gradient1.Point1XPercent = 0
+      Background.Gradient1.Point1YPercent = 0
+      Background.Gradient1.Point2XPercent = 0
+      Background.Gradient1.Point2YPercent = 100
+      Background.Gradient2.StartColor = clWhite
+      Background.Gradient2.EndColor = clBlack
+      Background.Gradient2.GradientType = gtLinear
+      Background.Gradient2.Point1XPercent = 0
+      Background.Gradient2.Point1YPercent = 0
+      Background.Gradient2.Point2XPercent = 0
+      Background.Gradient2.Point2YPercent = 100
+      Background.Gradient1EndPercent = 35
+      Background.Style = bbsColor
+      ButtonBackground.Gradient1.StartColor = clBtnShadow
+      ButtonBackground.Gradient1.EndColor = clBtnFace
+      ButtonBackground.Gradient1.GradientType = gtLinear
+      ButtonBackground.Gradient1.Point1XPercent = 0
+      ButtonBackground.Gradient1.Point1YPercent = -50
+      ButtonBackground.Gradient1.Point2XPercent = 0
+      ButtonBackground.Gradient1.Point2YPercent = 50
+      ButtonBackground.Gradient2.StartColor = clBtnFace
+      ButtonBackground.Gradient2.EndColor = clBtnShadow
+      ButtonBackground.Gradient2.GradientType = gtLinear
+      ButtonBackground.Gradient2.Point1XPercent = 0
+      ButtonBackground.Gradient2.Point1YPercent = 50
+      ButtonBackground.Gradient2.Point2XPercent = 0
+      ButtonBackground.Gradient2.Point2YPercent = 150
+      ButtonBackground.Gradient1EndPercent = 50
+      ButtonBackground.Style = bbsGradient
+      ButtonDownBackground.Color = clBtnShadow
+      ButtonDownBackground.Gradient1.StartColor = clWhite
+      ButtonDownBackground.Gradient1.EndColor = clBlack
+      ButtonDownBackground.Gradient1.GradientType = gtLinear
+      ButtonDownBackground.Gradient1.Point1XPercent = 0
+      ButtonDownBackground.Gradient1.Point1YPercent = 0
+      ButtonDownBackground.Gradient1.Point2XPercent = 0
+      ButtonDownBackground.Gradient1.Point2YPercent = 100
+      ButtonDownBackground.Gradient2.StartColor = clWhite
+      ButtonDownBackground.Gradient2.EndColor = clBlack
+      ButtonDownBackground.Gradient2.GradientType = gtLinear
+      ButtonDownBackground.Gradient2.Point1XPercent = 0
+      ButtonDownBackground.Gradient2.Point1YPercent = 0
+      ButtonDownBackground.Gradient2.Point2XPercent = 0
+      ButtonDownBackground.Gradient2.Point2YPercent = 100
+      ButtonDownBackground.Gradient1EndPercent = 35
+      ButtonDownBackground.Style = bbsColor
+      Border.Color = clWindowText
+      Border.Style = bboSolid
+      Rounding.RoundX = 1
+      Rounding.RoundY = 1
+      Font.Color = clWindowText
+      Font.Name = 'Arial'
+      HasTrackBar = True
+      ArrowColor = clBtnText
+      TabOrder = 2
+      TabStop = True
+      UseDockManager = False
+    end
+  end
+  object panelButtons: TPanel
+    Left = 0
+    Height = 50
+    Top = 480
+    Width = 713
+    Align = alBottom
+    BevelOuter = bvSpace
+    ClientHeight = 50
+    ClientWidth = 713
+    TabOrder = 1
+    object btCancel: TBitBtn
+      Left = 498
+      Height = 42
+      Top = 2
+      Width = 109
+      Anchors = [akRight, akBottom]
+      DefaultCaption = True
+      Kind = bkCancel
+      ModalResult = 2
+      TabOrder = 0
+    end
+    object btOk: TBitBtn
+      Left = 611
+      Height = 42
+      Top = 2
+      Width = 94
+      Anchors = [akRight, akBottom]
+      DefaultCaption = True
+      Kind = bkOK
+      ModalResult = 1
+      TabOrder = 1
+    end
+  end
+  object ifTiff: TBCPanel
+    Left = 352
+    Height = 150
+    Top = 0
+    Width = 350
+    Background.Color = clBtnFace
+    Background.Gradient1.StartColor = clWhite
+    Background.Gradient1.EndColor = clBlack
+    Background.Gradient1.GradientType = gtLinear
+    Background.Gradient1.Point1XPercent = 0
+    Background.Gradient1.Point1YPercent = 0
+    Background.Gradient1.Point2XPercent = 0
+    Background.Gradient1.Point2YPercent = 100
+    Background.Gradient2.StartColor = clWhite
+    Background.Gradient2.EndColor = clBlack
+    Background.Gradient2.GradientType = gtLinear
+    Background.Gradient2.Point1XPercent = 0
+    Background.Gradient2.Point1YPercent = 0
+    Background.Gradient2.Point2XPercent = 0
+    Background.Gradient2.Point2YPercent = 100
+    Background.Gradient1EndPercent = 35
+    Background.Style = bbsColor
+    BevelInner = bvNone
+    BevelOuter = bvRaised
+    BevelWidth = 1
+    Border.Style = bboNone
+    Caption = 'ifTiff'
+    FontEx.Color = clDefault
+    FontEx.FontQuality = fqSystemClearType
+    FontEx.Shadow = False
+    FontEx.ShadowRadius = 5
+    FontEx.ShadowOffsetX = 5
+    FontEx.ShadowOffsetY = 5
+    FontEx.Style = []
+    ParentBackground = False
+    Rounding.RoundX = 1
+    Rounding.RoundY = 1
+    TabOrder = 2
+    Visible = False
+    object ifTiff_SaveCMYKAsRGB: TCheckBox
+      Left = 16
+      Height = 24
+      Top = 40
+      Width = 147
+      Caption = 'Save CMYK As RGB'
+      TabOrder = 0
+    end
+    object ifTiff_PremultiplyRGB: TCheckBox
+      Left = 16
+      Height = 24
+      Top = 70
+      Width = 128
+      Caption = 'Premultiply RGB'
+      TabOrder = 1
+    end
+    object ifTiff_Compression: TCheckBox
+      Left = 16
+      Height = 24
+      Top = 8
+      Width = 203
+      Caption = 'Compression (Deflate ZLib)'
+      TabOrder = 2
+    end
+  end
+  object ifPng: TBCPanel
+    Left = 0
+    Height = 170
+    Top = 152
+    Width = 350
+    Background.Color = clBtnFace
+    Background.Gradient1.StartColor = clWhite
+    Background.Gradient1.EndColor = clBlack
+    Background.Gradient1.GradientType = gtLinear
+    Background.Gradient1.Point1XPercent = 0
+    Background.Gradient1.Point1YPercent = 0
+    Background.Gradient1.Point2XPercent = 0
+    Background.Gradient1.Point2YPercent = 100
+    Background.Gradient2.StartColor = clWhite
+    Background.Gradient2.EndColor = clBlack
+    Background.Gradient2.GradientType = gtLinear
+    Background.Gradient2.Point1XPercent = 0
+    Background.Gradient2.Point1YPercent = 0
+    Background.Gradient2.Point2XPercent = 0
+    Background.Gradient2.Point2YPercent = 100
+    Background.Gradient1EndPercent = 35
+    Background.Style = bbsColor
+    BevelInner = bvNone
+    BevelOuter = bvRaised
+    BevelWidth = 1
+    Border.Style = bboNone
+    Caption = 'jfPng'
+    FontEx.Color = clDefault
+    FontEx.FontQuality = fqSystemClearType
+    FontEx.Shadow = False
+    FontEx.ShadowRadius = 5
+    FontEx.ShadowOffsetX = 5
+    FontEx.ShadowOffsetY = 5
+    FontEx.Style = []
+    ParentBackground = False
+    Rounding.RoundX = 1
+    Rounding.RoundY = 1
+    TabOrder = 3
+    Visible = False
+    object ifPng_GrayScale: TCheckBox
+      Left = 16
+      Height = 24
+      Top = 8
+      Width = 90
+      Caption = 'Gray Scale'
+      TabOrder = 0
+    end
+    object ifPng_WordSized: TCheckBox
+      Left = 16
+      Height = 24
+      Top = 40
+      Width = 98
+      Caption = 'Word Sized'
+      TabOrder = 1
+    end
+    object Label2: TLabel
+      Left = 18
+      Height = 20
+      Top = 74
+      Width = 124
+      Caption = 'Compression Level'
+    end
+    object ifPng_CompressionLevel: TBCFluentSlider
+      Left = 32
+      Height = 40
+      Top = 96
+      Width = 300
+      MaxValue = 3
+      Value = 2
+      ShowTicks = True
+      TickFrequency = 1
+    end
+    object Label3: TLabel
+      Left = 28
+      Height = 20
+      Top = 135
+      Width = 36
+      Alignment = taCenter
+      Caption = 'None'
+    end
+    object Label4: TLabel
+      Left = 124
+      Height = 20
+      Top = 135
+      Width = 25
+      Alignment = taCenter
+      Caption = 'Fast'
+    end
+    object Label5: TLabel
+      Left = 204
+      Height = 20
+      Top = 136
+      Width = 49
+      Alignment = taCenter
+      Caption = 'Default'
+    end
+    object Label6: TLabel
+      Left = 304
+      Height = 20
+      Top = 136
+      Width = 28
+      Alignment = taCenter
+      Caption = 'Max'
+    end
+  end
+  object ifBmp: TBCPanel
+    Left = 352
+    Height = 110
+    Top = 152
+    Width = 350
+    Background.Color = clBtnFace
+    Background.Gradient1.StartColor = clWhite
+    Background.Gradient1.EndColor = clBlack
+    Background.Gradient1.GradientType = gtLinear
+    Background.Gradient1.Point1XPercent = 0
+    Background.Gradient1.Point1YPercent = 0
+    Background.Gradient1.Point2XPercent = 0
+    Background.Gradient1.Point2YPercent = 100
+    Background.Gradient2.StartColor = clWhite
+    Background.Gradient2.EndColor = clBlack
+    Background.Gradient2.GradientType = gtLinear
+    Background.Gradient2.Point1XPercent = 0
+    Background.Gradient2.Point1YPercent = 0
+    Background.Gradient2.Point2XPercent = 0
+    Background.Gradient2.Point2YPercent = 100
+    Background.Gradient1EndPercent = 35
+    Background.Style = bbsColor
+    BevelInner = bvNone
+    BevelOuter = bvRaised
+    BevelWidth = 1
+    Border.Style = bboNone
+    Caption = 'ifBmp'
+    FontEx.Color = clDefault
+    FontEx.FontQuality = fqSystemClearType
+    FontEx.Shadow = False
+    FontEx.ShadowRadius = 5
+    FontEx.ShadowOffsetX = 5
+    FontEx.ShadowOffsetY = 5
+    FontEx.Style = []
+    ParentBackground = False
+    Rounding.RoundX = 1
+    Rounding.RoundY = 1
+    TabOrder = 4
+    Visible = False
+    object ifBmp_RLECompress: TCheckBox
+      Left = 16
+      Height = 24
+      Top = 76
+      Width = 107
+      Caption = 'Compression'
+      TabOrder = 0
+    end
+    object ifBmp_BitsPerPixel: TComboBox
+      Tag = 1
+      Left = 112
+      Height = 28
+      Top = 8
+      Width = 220
+      ItemHeight = 20
+      Items.Strings = (
+        'Black & White'
+        '16 Colors'
+        '256 Colors/Gray Scale'
+        '15 bit'
+        '16 bit'
+        '24 bit'
+        '32 bit'
+      )
+      ReadOnly = True
+      Style = csDropDownList
+      TabOrder = 1
+      OnChange = ifBmp_BitsPerPixelChange
+    end
+    object Label7: TLabel
+      Left = 16
+      Height = 20
+      Top = 12
+      Width = 88
+      Alignment = taRightJustify
+      Caption = 'Color Depth :'
+    end
+    object ifBmp_GrayScale: TCheckBox
+      Left = 16
+      Height = 24
+      Top = 42
+      Width = 90
+      Caption = 'Gray Scale'
+      TabOrder = 2
+      OnChange = ifBmp_GrayScaleChange
+    end
+  end
+  object ifPcx: TBCPanel
+    Left = 352
+    Height = 40
+    Top = 264
+    Width = 350
+    Background.Color = clBtnFace
+    Background.Gradient1.StartColor = clWhite
+    Background.Gradient1.EndColor = clBlack
+    Background.Gradient1.GradientType = gtLinear
+    Background.Gradient1.Point1XPercent = 0
+    Background.Gradient1.Point1YPercent = 0
+    Background.Gradient1.Point2XPercent = 0
+    Background.Gradient1.Point2YPercent = 100
+    Background.Gradient2.StartColor = clWhite
+    Background.Gradient2.EndColor = clBlack
+    Background.Gradient2.GradientType = gtLinear
+    Background.Gradient2.Point1XPercent = 0
+    Background.Gradient2.Point1YPercent = 0
+    Background.Gradient2.Point2XPercent = 0
+    Background.Gradient2.Point2YPercent = 100
+    Background.Gradient1EndPercent = 35
+    Background.Style = bbsColor
+    BevelInner = bvNone
+    BevelOuter = bvRaised
+    BevelWidth = 1
+    Border.Style = bboNone
+    Caption = 'ifPcx'
+    FontEx.Color = clDefault
+    FontEx.FontQuality = fqSystemClearType
+    FontEx.Shadow = False
+    FontEx.ShadowRadius = 5
+    FontEx.ShadowOffsetX = 5
+    FontEx.ShadowOffsetY = 5
+    FontEx.Style = []
+    ParentBackground = False
+    Rounding.RoundX = 1
+    Rounding.RoundY = 1
+    TabOrder = 5
+    Visible = False
+    object ifPcx_Compressed: TCheckBox
+      Left = 16
+      Height = 24
+      Top = 8
+      Width = 107
+      Caption = 'Compression'
+      TabOrder = 0
+    end
+  end
+  object ifLazPaint: TBCPanel
+    Left = 0
+    Height = 150
+    Top = 328
+    Width = 350
+    Background.Color = clBtnFace
+    Background.Gradient1.StartColor = clWhite
+    Background.Gradient1.EndColor = clBlack
+    Background.Gradient1.GradientType = gtLinear
+    Background.Gradient1.Point1XPercent = 0
+    Background.Gradient1.Point1YPercent = 0
+    Background.Gradient1.Point2XPercent = 0
+    Background.Gradient1.Point2YPercent = 100
+    Background.Gradient2.StartColor = clWhite
+    Background.Gradient2.EndColor = clBlack
+    Background.Gradient2.GradientType = gtLinear
+    Background.Gradient2.Point1XPercent = 0
+    Background.Gradient2.Point1YPercent = 0
+    Background.Gradient2.Point2XPercent = 0
+    Background.Gradient2.Point2YPercent = 100
+    Background.Gradient1EndPercent = 35
+    Background.Style = bbsColor
+    BevelInner = bvNone
+    BevelOuter = bvRaised
+    BevelWidth = 1
+    Border.Style = bboNone
+    Caption = 'ifLazPaint'
+    FontEx.Color = clDefault
+    FontEx.FontQuality = fqSystemClearType
+    FontEx.Shadow = False
+    FontEx.ShadowRadius = 5
+    FontEx.ShadowOffsetX = 5
+    FontEx.ShadowOffsetY = 5
+    FontEx.Style = []
+    ParentBackground = False
+    Rounding.RoundX = 1
+    Rounding.RoundY = 1
+    TabOrder = 6
+    Visible = False
+    object ifLazPaint_IncludeThumbnail: TCheckBox
+      Left = 16
+      Height = 24
+      Top = 8
+      Width = 143
+      Caption = 'Include Thumbnail'
+      TabOrder = 0
+    end
+    object ifLazPaint_Caption: TEdit
+      Left = 80
+      Height = 28
+      Top = 112
+      Width = 252
+      TabOrder = 1
+    end
+    object Label8: TLabel
+      Left = 16
+      Height = 20
+      Top = 116
+      Width = 59
+      Alignment = taRightJustify
+      Caption = 'Caption :'
+    end
+    object ifLazPaint_Compression: TRadioGroup
+      Left = 16
+      Height = 65
+      Top = 40
+      Width = 316
+      AutoFill = True
+      Caption = 'Compression'
+      ChildSizing.LeftRightSpacing = 6
+      ChildSizing.EnlargeHorizontal = crsHomogenousChildResize
+      ChildSizing.EnlargeVertical = crsHomogenousChildResize
+      ChildSizing.ShrinkHorizontal = crsScaleChilds
+      ChildSizing.ShrinkVertical = crsScaleChilds
+      ChildSizing.Layout = cclLeftToRightThenTopToBottom
+      ChildSizing.ControlsPerLine = 2
+      ClientHeight = 40
+      ClientWidth = 312
+      Columns = 2
+      Items.Strings = (
+        'Z Stream'
+        'RLE'
+      )
+      TabOrder = 2
+    end
+  end
+  object ifWebP: TBCPanel
+    Left = 352
+    Height = 120
+    Top = 328
+    Width = 350
+    Background.Color = clBtnFace
+    Background.Gradient1.StartColor = clWhite
+    Background.Gradient1.EndColor = clBlack
+    Background.Gradient1.GradientType = gtLinear
+    Background.Gradient1.Point1XPercent = 0
+    Background.Gradient1.Point1YPercent = 0
+    Background.Gradient1.Point2XPercent = 0
+    Background.Gradient1.Point2YPercent = 100
+    Background.Gradient2.StartColor = clWhite
+    Background.Gradient2.EndColor = clBlack
+    Background.Gradient2.GradientType = gtLinear
+    Background.Gradient2.Point1XPercent = 0
+    Background.Gradient2.Point1YPercent = 0
+    Background.Gradient2.Point2XPercent = 0
+    Background.Gradient2.Point2YPercent = 100
+    Background.Gradient1EndPercent = 35
+    Background.Style = bbsColor
+    BevelInner = bvNone
+    BevelOuter = bvRaised
+    BevelWidth = 1
+    Border.Style = bboNone
+    Caption = 'ifWebP'
+    FontEx.Color = clDefault
+    FontEx.FontQuality = fqSystemClearType
+    FontEx.Shadow = False
+    FontEx.ShadowRadius = 5
+    FontEx.ShadowOffsetX = 5
+    FontEx.ShadowOffsetY = 5
+    FontEx.Style = []
+    ParentBackground = False
+    Rounding.RoundX = 1
+    Rounding.RoundY = 1
+    TabOrder = 7
+    Visible = False
+    object ifWebP_Lossless: TCheckBox
+      Left = 16
+      Height = 24
+      Top = 8
+      Width = 73
+      Caption = 'Lossless'
+      TabOrder = 0
+    end
+    object Label9: TLabel
+      Left = 18
+      Height = 20
+      Top = 42
+      Width = 137
+      Caption = 'Compression Quality'
+    end
+    object ifWebP_QualityPercent: TBCTrackbarUpdown
+      Left = 32
+      Height = 32
+      Top = 72
+      Width = 300
+      AllowNegativeValues = False
+      BarExponent = 1
+      Increment = 1
+      LongTimeInterval = 400
+      MinValue = 1
+      MaxValue = 100
+      Value = 75
+      ShortTimeInterval = 100
+      Background.Color = clWindow
+      Background.Gradient1.StartColor = clWhite
+      Background.Gradient1.EndColor = clBlack
+      Background.Gradient1.GradientType = gtLinear
+      Background.Gradient1.Point1XPercent = 0
+      Background.Gradient1.Point1YPercent = 0
+      Background.Gradient1.Point2XPercent = 0
+      Background.Gradient1.Point2YPercent = 100
+      Background.Gradient2.StartColor = clWhite
+      Background.Gradient2.EndColor = clBlack
+      Background.Gradient2.GradientType = gtLinear
+      Background.Gradient2.Point1XPercent = 0
+      Background.Gradient2.Point1YPercent = 0
+      Background.Gradient2.Point2XPercent = 0
+      Background.Gradient2.Point2YPercent = 100
+      Background.Gradient1EndPercent = 35
+      Background.Style = bbsColor
+      ButtonBackground.Gradient1.StartColor = clBtnShadow
+      ButtonBackground.Gradient1.EndColor = clBtnFace
+      ButtonBackground.Gradient1.GradientType = gtLinear
+      ButtonBackground.Gradient1.Point1XPercent = 0
+      ButtonBackground.Gradient1.Point1YPercent = -50
+      ButtonBackground.Gradient1.Point2XPercent = 0
+      ButtonBackground.Gradient1.Point2YPercent = 50
+      ButtonBackground.Gradient2.StartColor = clBtnFace
+      ButtonBackground.Gradient2.EndColor = clBtnShadow
+      ButtonBackground.Gradient2.GradientType = gtLinear
+      ButtonBackground.Gradient2.Point1XPercent = 0
+      ButtonBackground.Gradient2.Point1YPercent = 50
+      ButtonBackground.Gradient2.Point2XPercent = 0
+      ButtonBackground.Gradient2.Point2YPercent = 150
+      ButtonBackground.Gradient1EndPercent = 50
+      ButtonBackground.Style = bbsGradient
+      ButtonDownBackground.Color = clBtnShadow
+      ButtonDownBackground.Gradient1.StartColor = clWhite
+      ButtonDownBackground.Gradient1.EndColor = clBlack
+      ButtonDownBackground.Gradient1.GradientType = gtLinear
+      ButtonDownBackground.Gradient1.Point1XPercent = 0
+      ButtonDownBackground.Gradient1.Point1YPercent = 0
+      ButtonDownBackground.Gradient1.Point2XPercent = 0
+      ButtonDownBackground.Gradient1.Point2YPercent = 100
+      ButtonDownBackground.Gradient2.StartColor = clWhite
+      ButtonDownBackground.Gradient2.EndColor = clBlack
+      ButtonDownBackground.Gradient2.GradientType = gtLinear
+      ButtonDownBackground.Gradient2.Point1XPercent = 0
+      ButtonDownBackground.Gradient2.Point1YPercent = 0
+      ButtonDownBackground.Gradient2.Point2XPercent = 0
+      ButtonDownBackground.Gradient2.Point2YPercent = 100
+      ButtonDownBackground.Gradient1EndPercent = 35
+      ButtonDownBackground.Style = bbsColor
+      Border.Color = clWindowText
+      Border.Style = bboSolid
+      Rounding.RoundX = 1
+      Rounding.RoundY = 1
+      Font.Color = clWindowText
+      Font.Name = 'Arial'
+      HasTrackBar = True
+      ArrowColor = clBtnText
+      TabOrder = 1
+      TabStop = True
+      UseDockManager = False
+    end
+  end
+end

+ 674 - 0
bgraformatui.pas

@@ -0,0 +1,674 @@
+// SPDX-License-Identifier: LGPL-3.0-linking-exception
+{*******************************************************************************
+
+ (c) 2025 - Massimo Magnano
+
+********************************************************************************
+
+ Form that contains the various UI of the graphic formats inside panels.
+
+ When it is executed calling Execute ONLY the panel of the selected format will be
+ visible and the form will be resized accordingly.
+
+ Another way to use it is to call the GetUI method to take the panel of the
+ selected format, so that you can change its parent and use it in another form.
+ In this case the user is responsible for releasing the TBGRAFormatUIContainer class.
+}
+
+unit BGRAFormatUI;
+
+{$mode ObjFPC}{$H+}
+
+interface
+
+uses
+  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls, StdCtrls, ComCtrls, Buttons,
+  TypInfo, Rtti, FpImage,
+  BCPanel, BCTrackbarUpdown, BCFluentSlider,
+  BGRABitmapTypes;
+
+type
+  //To implement a new panel of a format, which I will probably do all of them myself :-) :
+  //
+  //The container panel (which must be a TBCPanel) must be named like the Format
+  //enum TBGRAImageFormat, so for example the panel for the Jpeg format will be named ifJpeg.
+  //The position at design time does not matter because the position is changed at runtime.
+  //
+  //If you want to use autofill to and from the UI
+  // - the Writer class must have the properties of interest declared as published
+  // - the names of the Panel sub controls must be panelname_propertyname
+  //
+  //   for example we have the ifJpeg Writer TBGRAWriterJPEG
+  //   published
+  //     property ProgressiveEncoding;
+  //     property GrayScale;
+  //     property CompressionQuality;
+  //
+  //   the corresponding UI will be
+  //     ifJpeg : TBCPanel
+  //       ifJpeg_ProgressiveEncoding: TCheckBox;
+  //       ifJpeg_GrayScale: TCheckBox;
+  //       ifJpeg_CompressionQuality: TBCTrackbarUpdown;
+  //
+  // - for controls that have a list of Items, like ComboBox or RadioGroup, there are 3 ways to use them:
+  //     Tag=0 the value will be taken/set directly from ItemIndex;
+  //     Tag=1 the value will be taken/set from the corresponding Objects[ItemIndex]
+  //           (it is the UI creator's responsibility to fill Objects in the Create event;
+  //     Tag=2 same as Tag=1 but Items are filled automatically with Enum Names;
+
+  { TBGRAFormatUIContainer }
+
+  TBGRAFormatUIContainer = class(TForm)
+    ifWebP: TBCPanel;
+    ifWebP_QualityPercent: TBCTrackbarUpdown;
+    ifWebP_Lossless: TCheckBox;
+    ifTiff_Compression: TCheckBox;
+    ifLazPaint_Caption: TEdit;
+    ifPcx: TBCPanel;
+    ifBmp_BitsPerPixel: TComboBox;
+    ifBmp_GrayScale: TCheckBox;
+    ifPcx_Compressed: TCheckBox;
+    ifPng_CompressionLevel: TBCFluentSlider;
+    ifPng: TBCPanel;
+    ifPng_GrayScale: TCheckBox;
+    ifPng_WordSized: TCheckBox;
+    ifBmp: TBCPanel;
+    ifBmp_RLECompress: TCheckBox;
+    ifLazPaint: TBCPanel;
+    ifTiff_SaveCMYKAsRGB: TCheckBox;
+    ifTiff_PremultiplyRGB: TCheckBox;
+    ifTiff: TBCPanel;
+    btCancel: TBitBtn;
+    btOk: TBitBtn;
+    ifJpeg_CompressionQuality: TBCTrackbarUpdown;
+    ifJpeg_GrayScale: TCheckBox;
+    ifJpeg: TBCPanel;
+    ifLazPaint_IncludeThumbnail: TCheckBox;
+    Label1: TLabel;
+    Label2: TLabel;
+    Label3: TLabel;
+    Label4: TLabel;
+    Label5: TLabel;
+    Label6: TLabel;
+    Label7: TLabel;
+    Label8: TLabel;
+    Label9: TLabel;
+    panelButtons: TPanel;
+    ifJpeg_ProgressiveEncoding: TCheckBox;
+    ifLazPaint_Compression: TRadioGroup;
+    procedure FormCreate(Sender: TObject);
+    procedure ifBmp_BitsPerPixelChange(Sender: TObject);
+    procedure ifBmp_GrayScaleChange(Sender: TObject);
+  private
+    curFormat: TBGRAImageFormat;
+    curWriter: TFPCustomImageWriter;
+    rPanelFormat: TBCPanel;
+
+    //some var for format specific UI
+    oldBmp_BitsPerPixel: Integer;
+
+    function AdjustPanels: Boolean;
+    function SelectPanel: TBCPanel;
+
+    function SetControlValue(const AValue: TValue; const AControl: TControl): Boolean;
+    function GetControlValue(var AValue: TValue; const AControl: TControl): Boolean;
+
+    //Copy Properties form TFPCustomImageWriter to UI
+    procedure GetWriterProperties;
+
+  public
+    class function Execute(const AFormat: TBGRAImageFormat;
+                           var AWriter: TFPCustomImageWriter): Boolean;
+
+    class function GetUI(const AFormat: TBGRAImageFormat;
+                         var AWriter: TFPCustomImageWriter;
+                         var APanel: TBCPanel): Boolean;
+
+    class function BuildSaveFormats(const AControl: TControl;
+                                    const ASelectFormat: TBGRAImageFormat=ifUnknown): Integer;
+
+    //Set TFPCustomImageWriter Properties from UI
+    procedure SetWriterProperties(var AWriter: TFPCustomImageWriter);
+
+    property PanelFormat: TBCPanel read rPanelFormat;
+  end;
+
+var
+  BGRAFormatUIContainer: TBGRAFormatUIContainer = nil;
+
+
+implementation
+
+{$R *.lfm}
+
+uses BCComboBox;
+
+const
+  BMP_BitsValidValues: array[0..6] of Integer = (1,4,8,15,16,24,32);
+
+type
+  TRttiPropertyArray = specialize TArray<TRttiProperty>;
+
+{ TBGRAFormatUIContainer }
+
+class function TBGRAFormatUIContainer.Execute(const AFormat: TBGRAImageFormat;
+                                              var AWriter: TFPCustomImageWriter): Boolean;
+begin
+  Result:= False;
+  if (AFormat = ifUnknown) or
+     ((AWriter = nil) and (DefaultBGRAImageWriter[AFormat] = nil))
+  then exit;
+
+  if (BGRAFormatUIContainer = nil)
+  then BGRAFormatUIContainer :=TBGRAFormatUIContainer.Create(nil);
+
+  if (BGRAFormatUIContainer <> nil) then
+  with BGRAFormatUIContainer do
+  try
+     if (AWriter = nil) then AWriter:= CreateBGRAImageWriter(AFormat, True);
+
+     curFormat:= AFormat;
+     curWriter:= AWriter;
+
+     AdjustPanels;
+     GetWriterProperties;
+
+     if (rPanelFormat <> nil) and (ShowModal = mrOk) then
+     begin
+       SetWriterProperties(AWriter);
+       Result:= True;
+     end;
+
+  finally
+     BGRAFormatUIContainer.Free; BGRAFormatUIContainer:= nil;
+  end;
+end;
+
+class function TBGRAFormatUIContainer.GetUI(const AFormat: TBGRAImageFormat;
+                                            var AWriter: TFPCustomImageWriter;
+                                            var APanel: TBCPanel): Boolean;
+begin
+  Result:= False;
+  if (AFormat = ifUnknown) or
+     ((AWriter = nil) and (DefaultBGRAImageWriter[AFormat] = nil))
+  then exit;
+
+  if (BGRAFormatUIContainer = nil)
+  then BGRAFormatUIContainer :=TBGRAFormatUIContainer.Create(nil);
+
+  if (BGRAFormatUIContainer <> nil) then
+  with BGRAFormatUIContainer do
+  try
+     if (AWriter = nil) then AWriter:= CreateBGRAImageWriter(AFormat, True);
+
+     curFormat:= AFormat;
+     curWriter:= AWriter;
+
+     APanel:= SelectPanel;
+     GetWriterProperties;
+
+  finally
+  end;
+end;
+
+class function TBGRAFormatUIContainer.BuildSaveFormats(const AControl: TControl;
+                                                       const ASelectFormat: TBGRAImageFormat): Integer;
+var
+  iFormat: TBGRAImageFormat;
+  aItems: TStrings;
+
+  procedure SetItemIndex(AValue: Integer);
+  begin
+    if (AControl is TComboBox)
+    then TComboBox(AControl).ItemIndex:= AValue
+    else
+    if (AControl is TBCComboBox)
+    then TBCComboBox(AControl).ItemIndex:= AValue
+    else
+    if (AControl is TRadioGroup)
+    then TRadioGroup(AControl).ItemIndex:= AValue;
+  end;
+
+begin
+  Result:= 0;
+
+  if (AControl is TComboBox)
+  then with TComboBox(AControl) do
+       begin
+         Clear;
+         aItems:= Items;
+       end
+  else
+  if (AControl is TBCComboBox)
+  then with TBCComboBox(AControl) do
+       begin
+         Clear;
+         aItems:= Items;
+       end
+  else
+  if (AControl is TRadioGroup)
+  then with TRadioGroup(AControl) do
+       begin
+         aItems:= Items;
+         aItems.Clear;
+       end
+  else exit;
+
+  for iFormat:=Low(TBGRAImageFormat) to High(TBGRAImageFormat) do
+    if (iFormat <> ifUnknown) and (DefaultBGRAImageWriter[iFormat] <> nil) then
+      aItems.AddObject(BGRAImageFormat[iFormat].TypeName+' (.'+SuggestImageExtension(iFormat)+')',
+                                TObject(PTRUInt(iFormat)));
+
+  Result:= aItems.Count;
+  if (Result > 0)
+  then if (ASelectFormat = ifUnknown)
+       then SetItemIndex(0)
+       else SetItemIndex(aItems.IndexOfObject(TObject(PTRUInt(ASelectFormat))));
+end;
+
+function TBGRAFormatUIContainer.SetControlValue(const AValue: TValue; const AControl: TControl): Boolean;
+var
+   minVal, maxVal, intVal,
+   iIndex: Integer;
+
+   function SetControlItems(AItems: TStrings): Boolean;
+   var
+      PS: PShortString;
+      i: Integer;
+
+   begin
+     Result:= False;
+
+     Case AControl.Tag of
+       0: begin
+            iIndex:= intVal;
+            Result:= True;
+       end;
+       1, -1: begin
+            iIndex:= AItems.IndexOfObject(TObject(PtrUInt(intVal)));
+            Result := (iIndex > -1);
+       end;
+       2: if (AValue.Kind = tkEnumeration)
+          then begin
+                 Result:= False;
+                 AControl.Tag:= -1; //Avoid Re-Fill already filled Items
+                 AItems.Clear;
+                 PS:= @AValue.TypeData^.NameList;
+                 for i:=AValue.TypeData^.MinValue to AValue.TypeData^.MaxValue do
+                 begin
+                   if PS=nil then break;
+
+                   AItems.AddObject(PS^, TObject(PtrUInt(i)));
+                   if (i = intVal) then
+                   begin
+                     iIndex:= intVal;
+                     Result:= True;
+                   end;
+                   PS:=PShortString(pointer(PS)+PByte(PS)^+1);
+                 end;
+                end
+           else begin
+                  iIndex:= AItems.IndexOfObject(TObject(PtrUInt(intVal)));
+                  Result := (iIndex > -1);
+                end;
+     end;
+   end;
+
+begin
+  Result:= False;
+
+  //If we are here the corresponding property is present
+  AControl.Visible:= True;
+
+  if AControl.Enabled then
+  try
+     {#to-do Set with TCheckGroup}
+     Case AValue.Kind of
+       tkInteger: intVal:= AValue.AsInteger;
+       tkEnumeration: begin
+         minVal:= AValue.TypeData^.MinValue;
+         maxVal:= AValue.TypeData^.MaxValue;
+         intVal:= AValue.AsOrdinal;
+       end;
+       tkFloat: intVal:= Round(AValue.AsExtended);
+     end;
+
+     //Types will be added as we use them,
+     //it is the responsibility of the UI creator not to put in crap like
+     //a checkbox that takes the value from a string, etc...
+
+     if (AControl is TEdit)
+     then TEdit(AControl).Caption:= AValue.AsString
+     else
+     if (AControl is TCheckBox)
+     then TCheckBox(AControl).Checked:= AValue.AsBoolean
+     else
+     if (AControl is TBCTrackbarUpdown)
+     then with TBCTrackbarUpdown(AControl) do
+          begin
+            if (AValue.Kind = tkEnumeration) then
+            begin
+              MinValue:= minVal;
+              MaxValue:= maxVal;
+            end;
+            Value:= intVal;
+          end
+     else
+     (*if (AControl is TTrackbar)
+     then with TTrackbar(AControl) do
+          begin
+            if (AValue.Kind = tkEnumeration) then
+            begin
+              Min:= minVal;
+              Max:= maxVal;
+            end;
+            Position:= intVal;
+          end
+     else*)
+     if (AControl is TComboBox)
+     then with TComboBox(AControl) do
+          begin
+            if SetControlItems(Items) then ItemIndex:= iIndex;
+          end
+     else
+     if (AControl is TRadioGroup)
+     then with TRadioGroup(AControl) do
+          begin
+            if SetControlItems(Items) then ItemIndex:= iIndex;
+          end
+     else
+     if (AControl is TBCFluentSlider)
+     then with TBCFluentSlider(AControl) do
+          begin
+            if (AValue.Kind = tkEnumeration) then
+            begin
+              MinValue:= minVal;
+              MaxValue:= maxVal;
+            end;
+            Value:= intVal;
+          end;
+
+    Result:= True;
+  except
+    Result:= False;
+  end;
+end;
+
+function TBGRAFormatUIContainer.GetControlValue(var AValue: TValue; const AControl: TControl): Boolean;
+begin
+  Result:= False;
+
+  if AControl.Visible and AControl.Enabled then
+  try
+     //Types will be added as we use them,
+     //it is the responsibility of the UI creator not to put in crap like
+     //a Boolean that takes the value from an Trackbar, etc...
+
+     {#to-do Set with TCheckGroup}
+
+     if (AControl is TEdit)
+     then AValue:= TEdit(AControl).Caption
+     else
+     if (AControl is TCheckBox)
+     then AValue:= TCheckBox(AControl).Checked
+     else
+     if (AControl is TBCTrackbarUpdown)
+     then AValue:= TBCTrackbarUpdown(AControl).Value
+     else
+     (*if (AControl is TTrackbar)
+     then AValue:= TTrackbar(AControl).Position
+     else*)
+     if (AControl is TComboBox)
+     then with TComboBox(AControl) do
+          begin
+            if (Tag = 0)
+            then AValue:= ItemIndex
+            else if (ItemIndex > -1) then AValue:= Integer(PtrUInt(Items.Objects[ItemIndex]));
+          end
+     else
+     if (AControl is TRadioGroup)
+     then with TRadioGroup(AControl) do
+          begin
+            if (Tag = 0)
+            then AValue:= ItemIndex
+            else if (ItemIndex > -1) then AValue:= Integer(PtrUInt(Items.Objects[ItemIndex]));
+          end
+     else
+     if (AControl is TBCFluentSlider)
+     then AValue:= TBCFluentSlider(AControl).Value;
+
+    Result:= True;
+  except
+    Result:= False;
+  end;
+end;
+
+//Set Writer Properties from UI
+procedure TBGRAFormatUIContainer.SetWriterProperties(var AWriter: TFPCustomImageWriter);
+var
+  LContext: TRttiContext;
+
+  procedure SetClassValues(const subPath: String; aInstance: TObject);
+  var
+    i: Integer;
+    LType: TRttiType;
+    PropList: TRttiPropertyArray;
+    aValue: TValue;
+    curControl: TControl;
+
+  begin
+    try
+       LType:= LContext.GetType(aInstance.ClassType);
+
+       //Read properties list
+       PropList := LType.GetProperties;
+
+       for i:= 0 to length(PropList)-1 do
+         if PropList[i].IsReadable and PropList[i].IsWritable then
+         begin
+           aValue:= PropList[i].GetValue(aInstance);
+
+           if aValue.IsObject
+           then begin
+                  //Call recursively passing the object
+                  if (aValue.AsObject <> nil)
+                  then SetClassValues(subPath+'_'+PropList[i].Name, aValue.AsObject);
+                end
+           else if not(aValue.Kind = tkMethod) then
+                begin
+                  //Find corresponding Control if any and Set Property value from it's Value
+                  curControl:= rPanelFormat.FindChildControl(subPath+'_'+PropList[i].Name);
+                  if (curControl <> nil) and
+                     GetControlValue(aValue, curControl)
+                  then PropList[i].SetValue(aInstance, aValue);
+                end;
+         end;
+
+    finally
+       PropList:=nil;
+    end;
+  end;
+
+begin
+  if (curWriter <> nil) and (rPanelFormat <> nil) then
+  try
+     LContext:= TRttiContext.Create;
+     SetClassValues(rPanelFormat.Name, curWriter);
+
+  finally
+    LContext.Free;
+  end;
+end;
+
+//Set UI Control Values from Writer Properties
+procedure TBGRAFormatUIContainer.GetWriterProperties;
+var
+  LContext: TRttiContext;
+
+  procedure GetClassValues(const subPath: String; aInstance: TObject);
+  var
+    i: Integer;
+    LType: TRttiType;
+    PropList: TRttiPropertyArray;
+    aValue: TValue;
+    curControl: TControl;
+
+  begin
+    try
+       LType:= LContext.GetType(aInstance.ClassType);
+
+       //Read properties list
+       PropList := LType.GetProperties;
+
+       for i:= 0 to length(PropList)-1 do
+         if PropList[i].IsReadable then
+         begin
+           aValue:= PropList[i].GetValue(aInstance);
+
+           if aValue.IsObject
+           then begin
+                  //Call recursively passing the object
+                  if (aValue.AsObject <> nil)
+                  then GetClassValues(subPath+'_'+PropList[i].Name, aValue.AsObject);
+                end
+           else if not(aValue.Kind = tkMethod) then
+                begin
+                  //Find corresponding Control if any and Set it's value
+                  curControl:= rPanelFormat.FindChildControl(subPath+'_'+PropList[i].Name);
+                  if (curControl <> nil) then SetControlValue(aValue, curControl);
+                end;
+         end;
+
+    finally
+       PropList:=nil;
+    end;
+  end;
+
+begin
+  if (curWriter <> nil) and (rPanelFormat <> nil) then
+  try
+     LContext:= TRttiContext.Create;
+     GetClassValues(rPanelFormat.Name, curWriter);
+
+  finally
+    LContext.Free;
+  end;
+end;
+
+procedure TBGRAFormatUIContainer.FormCreate(Sender: TObject);
+var
+   i: Integer;
+
+begin
+  //Bitmap Format
+  oldBmp_BitsPerPixel:= -1;
+
+  //Fill Bits x Pixels Objects Values
+  for i:=0 to ifBmp_BitsPerPixel.Items.Count-1 do
+    ifBmp_BitsPerPixel.Items.Objects[i]:= TObject(PtrUInt(BMP_BitsValidValues[i]));
+end;
+
+procedure TBGRAFormatUIContainer.ifBmp_GrayScaleChange(Sender: TObject);
+begin
+  if ifBmp_GrayScale.Checked
+  then begin
+         oldBmp_BitsPerPixel:= ifBmp_BitsPerPixel.ItemIndex;
+         ifBmp_BitsPerPixel.ItemIndex:= 2; //GrayScale
+       end
+  else if (oldBmp_BitsPerPixel > -1)
+       then ifBmp_BitsPerPixel.ItemIndex:= oldBmp_BitsPerPixel;
+
+  ifBmp_RLECompress.Enabled:= ifBmp_GrayScale.Checked;
+  ifBmp_BitsPerPixel.Enabled:= not(ifBmp_GrayScale.Checked);
+end;
+
+procedure TBGRAFormatUIContainer.ifBmp_BitsPerPixelChange(Sender: TObject);
+begin
+  ifBmp_RLECompress.Enabled:= (ifBmp_BitsPerPixel.ItemIndex in [1,2]);
+  ifBmp_GrayScale.Enabled:= (ifBmp_BitsPerPixel.ItemIndex = 2);
+end;
+
+function TBGRAFormatUIContainer.AdjustPanels: Boolean;
+var
+   pName: String;
+   curControl: TControl;
+   i: Integer;
+
+begin
+  rPanelFormat:= nil;
+  Result:= False;
+
+  pName:= GetEnumName(TypeInfo(TBGRAImageFormat), Integer(curFormat));
+
+  for i:=0 to ControlCount-1 do
+  begin
+    curControl:= Controls[i];
+
+    if (curControl <> nil) and
+       (curControl is TBCPanel) and
+       (curControl.Enabled) then
+    begin
+      if (CompareText(curControl.Name, pName) = 0) then
+      begin
+        rPanelFormat:= TBCPanel(curControl);
+        Result:= True;
+      end;
+
+      curControl.Visible:= False;
+    end;
+  end;
+
+  if Result then
+  begin
+    rPanelFormat.Top:= 0; rPanelFormat.Left:= 0;
+    rPanelFormat.BevelInner:= bvNone;
+    rPanelFormat.BevelOuter:= bvNone;
+    rPanelFormat.Caption:='';
+    Self.Width:= rPanelFormat.Width;
+    Self.Height:= rPanelFormat.Height+panelButtons.Height;
+
+    rPanelFormat.Visible:= True;
+  end;
+end;
+
+function TBGRAFormatUIContainer.SelectPanel: TBCPanel;
+var
+   pName: String;
+   curControl: TControl;
+   i: Integer;
+
+begin
+  rPanelFormat:= nil;
+  Result:= nil;
+
+  pName:= GetEnumName(TypeInfo(TBGRAImageFormat), Integer(curFormat));
+
+  //I use Components because when the Panels parent is changed they are removed from Controls
+  for i:=0 to ComponentCount-1 do
+    if (Components[i] is TControl) then
+    begin
+      curControl:= TControl(Components[i]);
+
+      if (curControl <> nil) and
+         (curControl is TBCPanel) and
+         (curControl.Enabled) and
+         (CompareText(curControl.Name, pName) = 0) then
+      begin
+        Result:= TBCPanel(curControl);
+        break;
+      end;
+    end;
+
+  if (Result <> nil) then
+  begin
+    Result.Top:= 0; Result.Left:= 0;
+    Result.BevelInner:= bvNone;
+    Result.BevelOuter:= bvNone;
+    Result.Caption:='';
+  end;
+
+  rPanelFormat:= Result;
+end;
+
+end.
+

+ 39 - 100
bgraimagemanipulation.pas

@@ -439,6 +439,7 @@ type
     procedure CalculatePreferredSize(var PreferredWidth, PreferredHeight: integer; WithThemeSpace: boolean); override;
     procedure CalculatePreferredSize(var PreferredWidth, PreferredHeight: integer; WithThemeSpace: boolean); override;
     procedure Loaded; override;
     procedure Loaded; override;
     procedure Paint; override;
     procedure Paint; override;
+    procedure ResizeVirtualScreen;
     procedure DoOnResize; override;
     procedure DoOnResize; override;
     procedure RenderBackground;
     procedure RenderBackground;
     procedure Render;
     procedure Render;
@@ -453,7 +454,6 @@ type
 
 
     constructor Create(AOwner: TComponent); override;
     constructor Create(AOwner: TComponent); override;
     destructor Destroy; override;
     destructor Destroy; override;
-    procedure Invalidate; override;
     function getAspectRatioFromImage(const Value: TBGRABitmap): string;
     function getAspectRatioFromImage(const Value: TBGRABitmap): string;
     function getResampledBitmap(ACropArea :TCropArea = Nil; ACopyProperties: Boolean=False) : TBGRABitmap;
     function getResampledBitmap(ACropArea :TCropArea = Nil; ACopyProperties: Boolean=False) : TBGRABitmap;
     function getBitmap(ACropArea :TCropArea = Nil; ACopyProperties: Boolean=False) : TBGRABitmap;
     function getBitmap(ACropArea :TCropArea = Nil; ACopyProperties: Boolean=False) : TBGRABitmap;
@@ -461,6 +461,9 @@ type
     procedure rotateLeft(ACopyProperties: Boolean=False);
     procedure rotateLeft(ACopyProperties: Boolean=False);
     procedure rotateRight(ACopyProperties: Boolean=False);
     procedure rotateRight(ACopyProperties: Boolean=False);
 
 
+    //Recreate Bitmap Render useful when use inplace filters directly in Bitmap
+    procedure RefreshBitmap;
+
     procedure tests;
     procedure tests;
 
 
     //Crop Areas Manipulation functions
     //Crop Areas Manipulation functions
@@ -542,7 +545,6 @@ procedure PixelXResolutionUnitConvert(var resX, resY:Single; fromRes, toRes:TRes
 implementation
 implementation
 
 
 uses
 uses
-  {$ifopt D+}LazLogger,{$endif}
   Math, ExtCtrls, BGRAUTF8, UniversalDrawer, BGRAWritePNG, FPWritePNM;
   Math, ExtCtrls, BGRAUTF8, UniversalDrawer, BGRAWritePNG, FPWritePNM;
 
 
 resourcestring
 resourcestring
@@ -673,18 +675,7 @@ end;
 
 
 procedure TCropArea.Render_Invalidate;
 procedure TCropArea.Render_Invalidate;
 begin
 begin
-  if not(fOwner.rCropAreas.loading) then
-  begin
-    {$ifopt D+}
-     DebugLn('  CropArea Render_Invalidate');
-    {$endif}
-
-    fOwner.Render_Invalidate;
-
-    {$ifopt D+}
-     DebugLn('  CropArea Render_Invalidate done');
-    {$endif}
-  end;
+  if not(fOwner.rCropAreas.loading) then fOwner.Render_Invalidate;
 end;
 end;
 
 
 procedure TCropArea.GetImageResolution(var resX, resY: Single; var resUnit: TResolutionUnit);
 procedure TCropArea.GetImageResolution(var resX, resY: Single; var resUnit: TResolutionUnit);
@@ -1440,6 +1431,8 @@ begin
   if (OwnerList<>nil) then
   if (OwnerList<>nil) then
   try
   try
     OwnerList.Move(OwnerList.IndexOf(Self), OwnerList.Count-1);
     OwnerList.Move(OwnerList.IndexOf(Self), OwnerList.Count-1);
+    Render_Invalidate;
+
   except
   except
   end;
   end;
 end;
 end;
@@ -1449,6 +1442,8 @@ begin
   if (OwnerList<>nil) then
   if (OwnerList<>nil) then
   try
   try
     OwnerList.Move(OwnerList.IndexOf(Self), 0);
     OwnerList.Move(OwnerList.IndexOf(Self), 0);
+    Render_Invalidate;
+
   except
   except
   end;
   end;
 end;
 end;
@@ -1463,6 +1458,9 @@ begin
     curIndex :=OwnerList.IndexOf(Self);
     curIndex :=OwnerList.IndexOf(Self);
     if (curIndex<OwnerList.Count-1)
     if (curIndex<OwnerList.Count-1)
     then OwnerList.Move(curIndex, curIndex+1);
     then OwnerList.Move(curIndex, curIndex+1);
+
+    Render_Invalidate;
+
   except
   except
   end;
   end;
 end;
 end;
@@ -1477,6 +1475,9 @@ begin
     curIndex :=OwnerList.IndexOf(Self);
     curIndex :=OwnerList.IndexOf(Self);
     if (curIndex>0)
     if (curIndex>0)
     then OwnerList.Move(curIndex, curIndex-1);
     then OwnerList.Move(curIndex, curIndex-1);
+
+    Render_Invalidate;
+
   except
   except
   end;
   end;
 end;
 end;
@@ -2757,10 +2758,6 @@ end;
 
 
 procedure TBGRAImageManipulation.Loaded;
 procedure TBGRAImageManipulation.Loaded;
 begin
 begin
-  {$ifopt D+}
-   DebugLn('Loaded '+BoolToStr(csLoading in ComponentState, True));
-  {$endif}
-
   inherited Loaded;
   inherited Loaded;
 
 
   if Self.Empty then
   if Self.Empty then
@@ -2768,14 +2765,6 @@ begin
     CreateEmptyImage;
     CreateEmptyImage;
     CreateResampledBitmap;
     CreateResampledBitmap;
   end;
   end;
-
-  // Force Render Struct
-//  RenderBackground;
-//  Render;
-
-  {$ifopt D+}
-   DebugLn('Loaded done');
-  {$endif}
 end;
 end;
 
 
  { ============================================================================ }
  { ============================================================================ }
@@ -2787,10 +2776,6 @@ var
    fGCD     :integer;
    fGCD     :integer;
 
 
 begin
 begin
-  {$ifopt D+}
-   DebugLn('Create');
-  {$endif}
-
   inherited Create(AOwner);
   inherited Create(AOwner);
 
 
   // Set default component values
   // Set default component values
@@ -2845,10 +2830,6 @@ begin
   rSelectedCropArea :=Nil;
   rSelectedCropArea :=Nil;
 
 
   fMouseCaught := False;
   fMouseCaught := False;
-
-  {$ifopt D+}
-   DebugLn('Create done');
-  {$endif}
 end;
 end;
 
 
 destructor TBGRAImageManipulation.Destroy;
 destructor TBGRAImageManipulation.Destroy;
@@ -2864,36 +2845,11 @@ begin
   inherited Destroy;
   inherited Destroy;
 end;
 end;
 
 
-procedure TBGRAImageManipulation.Invalidate;
-begin
-  {$ifopt D+}
-   DebugLn('Invalidate');
-  {$endif}
-
-  inherited Invalidate;
-
-  {$ifopt D+}
-   DebugLn('Invalidate done');
-  {$endif}
-end;
-
 procedure TBGRAImageManipulation.Paint;
 procedure TBGRAImageManipulation.Paint;
 begin
 begin
-  {$ifopt D+}
-   DebugLn('Paint');
-  {$endif}
-
   inherited Paint;
   inherited Paint;
 
 
-  {$ifopt D+}
-   DebugLn('Paint inherited done '+BoolToStr(fVirtualScreen<>nil, True)+' '+BoolToStr(Canvas<>nil, True));
-  {$endif}
-
   fVirtualScreen.Draw(Canvas, 0, 0, True);
   fVirtualScreen.Draw(Canvas, 0, 0, True);
-
-  {$ifopt D+}
-   DebugLn('Paint done');
-  {$endif}
 end;
 end;
 
 
 { This function repaint the background only when necessary to avoid unnecessary
 { This function repaint the background only when necessary to avoid unnecessary
@@ -2936,10 +2892,6 @@ var
   Border: TRect;
   Border: TRect;
   Grad: TBGRAGradientScanner;
   Grad: TBGRAGradientScanner;
 begin
 begin
-  {$ifopt D+}
-   DebugLn('RenderBackground');
-  {$endif}
-
   // Draw the outer bevel
   // Draw the outer bevel
   Border := Rect(0, 0, fVirtualScreen.Width, fVirtualScreen.Height);
   Border := Rect(0, 0, fVirtualScreen.Width, fVirtualScreen.Height);
 
 
@@ -2968,14 +2920,11 @@ begin
     cl3DLight, clBtnShadow);
     cl3DLight, clBtnShadow);
 
 
   DrawCheckers(fBackground, Border);
   DrawCheckers(fBackground, Border);
-
-  {$ifopt D+}
-   DebugLn('RenderBackground done');
-  {$endif}
 end;
 end;
 
 
 { Resize the component, recalculating the proportions }
 { Resize the component, recalculating the proportions }
-procedure TBGRAImageManipulation.DoOnResize;
+
+procedure TBGRAImageManipulation.ResizeVirtualScreen;
 
 
   function min(const Value: integer; const MinValue: integer): integer;
   function min(const Value: integer; const MinValue: integer): integer;
   begin
   begin
@@ -2990,10 +2939,6 @@ var
   curCropArea    :TCropArea;
   curCropArea    :TCropArea;
 
 
 begin
 begin
-  {$ifopt D+}
-   DebugLn('DoOnResize '+BoolToStr(fVirtualScreen<>nil, True));
-  {$endif}
-
   if (fVirtualScreen <> nil) then
   if (fVirtualScreen <> nil) then
   begin
   begin
     fVirtualScreen.SetSize(min(Self.Width, (fBorderSize * 2 + fAnchorSize + fMinWidth)),
     fVirtualScreen.SetSize(min(Self.Width, (fBorderSize * 2 + fAnchorSize + fMinWidth)),
@@ -3023,12 +2968,13 @@ begin
     RenderBackground;
     RenderBackground;
     Render;
     Render;
   end;
   end;
+end;
 
 
-  inherited DoOnResize;
+procedure TBGRAImageManipulation.DoOnResize;
+begin
+  ResizeVirtualScreen;
 
 
-  {$ifopt D+}
-   DebugLn('DoOnResize done');
-  {$endif}
+  inherited DoOnResize;
 end;
 end;
 
 
 { Function responsible for rendering the content of the component, including
 { Function responsible for rendering the content of the component, including
@@ -3047,10 +2993,6 @@ var
   TextS: TTextStyle;
   TextS: TTextStyle;
 
 
 begin
 begin
-  {$ifopt D+}
-   DebugLn('Render');
-  {$endif}
-
   // Draw background
   // Draw background
   fVirtualScreen.BlendImage(0, 0, fBackground, boLinearBlend);
   fVirtualScreen.BlendImage(0, 0, fBackground, boLinearBlend);
 
 
@@ -3236,10 +3178,6 @@ begin
     fVirtualScreen.BlendImage(WorkRect.Left, WorkRect.Top, Mask, boLinearBlend);
     fVirtualScreen.BlendImage(WorkRect.Left, WorkRect.Top, Mask, boLinearBlend);
     Mask.Free;
     Mask.Free;
   end;
   end;
-
-  {$ifopt D+}
-   DebugLn('Render done');
-  {$endif}
 end;
 end;
 
 
 procedure TBGRAImageManipulation.Render_Invalidate;
 procedure TBGRAImageManipulation.Render_Invalidate;
@@ -3407,6 +3345,12 @@ begin
   end;
   end;
 end;
 end;
 
 
+procedure TBGRAImageManipulation.RefreshBitmap;
+begin
+  ResizeVirtualScreen;
+  Invalidate;
+end;
+
 procedure TBGRAImageManipulation.tests;
 procedure TBGRAImageManipulation.tests;
 begin
 begin
   // Self.AutoSize:=False;
   // Self.AutoSize:=False;
@@ -3526,10 +3470,9 @@ var
    curCropAreaRect :TRectF;
    curCropAreaRect :TRectF;
    curCropArea :TCropArea;
    curCropArea :TCropArea;
    mWidth, mHeight:Single;
    mWidth, mHeight:Single;
-   xRatio, yRatio, resX :Single;
 
 
 begin
 begin
-  if Self.Empty and (rCropAreas.Count>0) then
+  if (rCropAreas.Count>0) then
   begin
   begin
      if ReduceLarger
      if ReduceLarger
      then begin
      then begin
@@ -3562,9 +3505,7 @@ begin
         then mHeight :=curCropAreaRect.Bottom;
         then mHeight :=curCropAreaRect.Bottom;
      end;
      end;
 
 
-     EmptyImage.ResolutionWidth :=mWidth;
-     EmptyImage.ResolutionHeight :=mHeight;
-     Resize;
+     SetEmptyImageSize(EmptyImage.ResolutionUnit, mWidth, mHeight);
   end;
   end;
 end;
 end;
 
 
@@ -3573,13 +3514,19 @@ begin
   SetEmptyImageSize(ruPixelsPerInch, 0, 0);
   SetEmptyImageSize(ruPixelsPerInch, 0, 0);
 end;
 end;
 
 
-procedure TBGRAImageManipulation.SetEmptyImageSize(AResolutionUnit: TResolutionUnit; AResolutionWidth,
-  AResolutionHeight: Single);
+procedure TBGRAImageManipulation.SetEmptyImageSize(AResolutionUnit: TResolutionUnit; AResolutionWidth, AResolutionHeight: Single);
 begin
 begin
   EmptyImage.ResolutionUnit:=AResolutionUnit;
   EmptyImage.ResolutionUnit:=AResolutionUnit;
   EmptyImage.rResolutionWidth:=AResolutionWidth;
   EmptyImage.rResolutionWidth:=AResolutionWidth;
   EmptyImage.rResolutionHeight:=AResolutionHeight;
   EmptyImage.rResolutionHeight:=AResolutionHeight;
-  Resize;
+
+  if Self.Empty then
+  begin
+    CreateEmptyImage;
+    CreateResampledBitmap;
+  end;
+
+  Render_Invalidate;
 end;
 end;
 
 
 procedure TBGRAImageManipulation.LoadFromFile(const AFilename: String);
 procedure TBGRAImageManipulation.LoadFromFile(const AFilename: String);
@@ -4136,10 +4083,6 @@ var
   end;
   end;
 
 
 begin
 begin
-  {$ifopt D+}
-   DebugLn('MouseMove');
-  {$endif}
-
   // Call the inherited MouseMove() procedure
   // Call the inherited MouseMove() procedure
   inherited MouseMove(Shift, X, Y);
   inherited MouseMove(Shift, X, Y);
 
 
@@ -4199,10 +4142,6 @@ begin
            Cursor :=ACursor;
            Cursor :=ACursor;
          end;
          end;
        end;
        end;
-
-  {$ifopt D+}
-   DebugLn('MouseMove done');
-  {$endif}
 end;
 end;
 
 
 procedure TBGRAImageManipulation.MouseUp(Button: TMouseButton;
 procedure TBGRAImageManipulation.MouseUp(Button: TMouseButton;

+ 1 - 1
bgrapascalscriptcomponent.lpk

@@ -11,7 +11,7 @@
         <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
         <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
       </SearchPaths>
       </SearchPaths>
     </CompilerOptions>
     </CompilerOptions>
-    <Version Major="9" Release="1" Build="7"/>
+    <Version Major="9" Release="2"/>
     <Files Count="3">
     <Files Count="3">
       <Item1>
       <Item1>
         <Filename Value="bgrapascalscript.pas"/>
         <Filename Value="bgrapascalscript.pas"/>

BIN
docs/img/SuperSpinner-V100.png


+ 1 - 1
dtthemedclock.pas

@@ -12,7 +12,7 @@
   (Compatibility with delphi VCL 11/2018)
   (Compatibility with delphi VCL 11/2018)
 
 
 ***************************** END CONTRIBUTOR(S) *****************************}
 ***************************** END CONTRIBUTOR(S) *****************************}
-unit dtthemedclock;
+unit DTThemedClock;
 
 
 {$I bgracontrols.inc}
 {$I bgracontrols.inc}
 
 

+ 1 - 1
dtthemedgauge.pas

@@ -13,7 +13,7 @@
   (Compatibility with delphi VCL 11/2018)
   (Compatibility with delphi VCL 11/2018)
 
 
 ***************************** END CONTRIBUTOR(S) *****************************}
 ***************************** END CONTRIBUTOR(S) *****************************}
-unit dtthemedgauge;
+unit DTThemedGauge;
 
 
 {$I bgracontrols.inc}
 {$I bgracontrols.inc}
 
 

+ 0 - 249
dtthemedgauge.pp

@@ -1,249 +0,0 @@
-// SPDX-License-Identifier: LGPL-3.0-linking-exception
-{
-  Part of BGRA Controls. Made by third party.
-  For detailed information see readme.txt
-
-  Site: https://sourceforge.net/p/bgra-controls/
-  Wiki: http://wiki.lazarus.freepascal.org/BGRAControls
-  Forum: http://forum.lazarus.freepascal.org/index.php/board,46.0.html
-}
-
-unit dtthemedgauge;
-
-{$mode objfpc}{$H+}
-
-interface
-
-uses
-  Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, DTAnalogCommon,
-  BGRABitmap, BGRABitmapTypes;
-
-type
-
-  { TDTCustomThemedGauge }
-
-  TDTCustomThemedGauge = class(TDTBaseAnalogDevice)
-  private
-    FPointerCapSettings: TDTPointerCapSettings;
-    FPointerSettings: TDTPointerSettings;
-    FScaleBitmap: TBGRABitmap;
-    FPointerBitmap: TBGRABitmap;
-    FPosition: integer;
-    procedure SetPointerCapSettings(AValue: TDTPointerCapSettings);
-    procedure SetPointerSettings(AValue: TDTPointerSettings);
-    procedure SetPosition(AValue: integer);
-    { Private declarations }
-  protected
-    { Protected declarations }
-    property PointerSettings: TDTPointerSettings read FPointerSettings write SetPointerSettings;
-    property PointerCapSettings: TDTPointerCapSettings read FPointerCapSettings write SetPointerCapSettings;
-    property Position: integer read FPosition write SetPosition;
-  public
-    { Public declarations }
-    constructor Create(AOwner: TComponent); override;
-    destructor Destroy; override;
-    procedure Paint; override;
-    procedure DrawScale;
-    procedure DrawPointer;
-  end;
-
-  { TDTThemedGauge }
-
-  TDTThemedGauge = class(TDTCustomThemedGauge)
-  private
-    { Private declarations }
-  protected
-    { Protected declarations }
-  public
-    { Public declarations }
-  published
-    { Published declarations }
-    property FaceSettings;
-    property ScaleSettings;
-    property PointerSettings;
-    property PointerCapSettings;
-    property Position;
-  end;
-
-procedure Register;
-
-implementation
-
-procedure Register;
-begin
-  //{$I icons\dtthemedgauge_icon.lrs}
-  RegisterComponents('BGRA Controls', [TDTThemedGauge]);
-end;
-
-{ TDTCustomThemedGauge }
-
-//procedure TDTCustomThemedGauge.SetNeedleSettings(AValue: TDTNeedleSettings);
-//begin
-//  if FNeedleSettings = AValue then
-//    Exit;
-//  FNeedleSettings := AValue;
-//
-//  DoChange(self);
-//end;
-//
-procedure TDTCustomThemedGauge.SetPointerCapSettings(AValue: TDTPointerCapSettings);
-begin
-  if FPointerCapSettings = AValue then
-    Exit;
-  FPointerCapSettings := AValue;
-
-  DoChange(self);
-end;
-
-procedure TDTCustomThemedGauge.SetPointerSettings(AValue: TDTPointerSettings);
-begin
-  if FPointerSettings = AValue then
-    Exit;
-  FPointerSettings := AValue;
-
-  DoChange(self);
-end;
-
-
-procedure TDTCustomThemedGauge.SetPosition(AValue: integer);
-begin
-  if FPosition = AValue then
-    Exit;
-  FPosition := AValue;
-
-  DoChange(self);
-end;
-
-constructor TDTCustomThemedGauge.Create(AOwner: TComponent);
-begin
-  inherited Create(AOwner);
-
-  FPointerSettings := TDTPointerSettings.Create;
-  FPointerSettings.OnChange := @DoChange;
-  FPointerSettings.Color := BGRA(255, 81, 81);
-
-  FPointerCapSettings := TDTPointerCapSettings.Create;
-  FPointerCapSettings.OnChange := @DoChange;
-
-  FScaleBitmap := TBGRABitmap.Create;
-  FPointerBitmap := TBGRABitmap.Create;
-
-end;
-
-destructor TDTCustomThemedGauge.Destroy;
-begin
-  FPointerCapSettings.OnChange:=nil;
-  FPointerCapSettings.Free;
-  FPointerSettings.OnChange:=nil;
-  FPointerSettings.Free;
-
-  FScaleBitmap.Free;
-  FPointerBitmap.Free;
-  inherited Destroy;
-end;
-
-procedure TDTCustomThemedGauge.Paint;
-begin
-  inherited Paint;
-  DrawScale;
-  DrawPointer;
-
-  FGaugeBitmap.BlendImage(0, 0, FScaleBitmap, boLinearBlend);
-  FGaugeBitmap.BlendImage(0, 0, FPointerBitmap, boLinearBlend);
-
-  FGaugeBitmap.Draw(Canvas, 0, 0, False);
-
-end;
-
-procedure TDTCustomThemedGauge.DrawScale;
-var
-  Origin: TDTOrigin;
-  r, i, n, x, y, xt, yt: integer;
-  j: single;
-begin
-
-  Origin := Initializebitmap(FScaleBitmap, Width, Height);
-
-  r := round(Origin.Radius * 0.85);
-
-  //j := (180 - ScaleSettings.Angle) / 2;
-  j := (180 - 270) / 2;
-
-  // Draw SubTicks
-  if ScaleSettings.EnableSubTicks then
-  begin
-
-    n := ScaleSettings.MainTickCount * ScaleSettings.SubTickCount;
-
-    for i := 0 to n do
-    begin
-      // Calculate draw from point
-      X := Origin.CenterPoint.x - Round(r * cos((j + i * 270 / n) * Pi / 180));
-      Y := Origin.CenterPoint.y - Round(r * sin((j + i * 270 / n) * Pi / 180));
-
-      // Calculate draw to point
-      Xt := Origin.CenterPoint.x - Round((r - ScaleSettings.LengthSubTick) * cos((j + i * 270 / n) * Pi / 180));
-      Yt := Origin.CenterPoint.y - Round((r - ScaleSettings.LengthSubTick) * sin((j + i * 270 / n) * Pi / 180));
-
-      FScaleBitmap.DrawLineAntialias(x, y, xt, yt, ScaleSettings.TickColor, ScaleSettings.ThicknessSubTick);
-
-    end;
-  end;
-
-  if ScaleSettings.EnableMainTicks then
-  begin
-
-    FScaleBitmap.FontName := ScaleSettings.TextFont;
-    FScaleBitmap.FontHeight := ScaleSettings.TextSize;
-    FScaleBitmap.FontQuality := fqFineAntialiasing;
-
-    n := ScaleSettings.MainTickCount;
-
-    for i := 0 to n do
-    begin
-      // Draw main ticks
-      // Calculate draw from point
-      X := Origin.CenterPoint.x - Round(r * cos((j + i * 270 / n) * Pi / 180));
-      Y := Origin.CenterPoint.y - Round(r * sin((j + i * 270 / n) * Pi / 180));
-
-      // Calculate draw to point
-      Xt := Origin.CenterPoint.x - Round((r - ScaleSettings.LengthMainTick) * cos((j + i * 270 / n) * Pi / 180));
-      Yt := Origin.CenterPoint.y - Round((r - ScaleSettings.LengthMainTick) * sin((j + i * 270 / n) * Pi / 180));
-
-      FScaleBitmap.DrawLineAntialias(x, y, xt, yt, ScaleSettings.TickColor, ScaleSettings.ThicknessMainTick);
-
-      if ScaleSettings.EnableScaleText then
-      begin
-        // Draw text for main ticks
-        Xt := Origin.CenterPoint.x - Round(ScaleSettings.TextRadius * cos((j + i * 270 / n) * Pi / 180));
-        Yt := Origin.CenterPoint.y - Round(ScaleSettings.TextRadius * sin((j + i * 270 / n) * Pi / 180));
-
-        FScaleBitmap.TextOut(Xt, Yt - (FScaleBitmap.FontHeight / 1.7), IntToStr(i * ScaleSettings.Maximum div ScaleSettings.MainTickCount), ScaleSettings.TextColor, taCenter);
-      end;
-    end;
-  end;
-end;
-
-procedure TDTCustomThemedGauge.DrawPointer;
-var
-  Origin: TDTOrigin;
-  {%H-}r, x, y: integer;
-  j: single;
-begin
-
-  Origin := Initializebitmap(FPointerBitmap, Width, Height);
-
-  r := round(Origin.Radius * 0.85);
-
-  j := (180 - 270) / 2;
-
-  X := origin.CenterPoint.x - Round(PointerSettings.Length * cos((j + Position * 270 / ScaleSettings.Maximum) * Pi / 180));
-  Y := origin.CenterPoint.y - Round(PointerSettings.Length * sin((j + Position * 270 / ScaleSettings.Maximum) * Pi / 180));
-
-  FPointerBitmap.DrawLineAntialias(origin.CenterPoint.y, origin.CenterPoint.y, x, y, PointerSettings.Color, PointerSettings.Thickness);
-
-  // Draw cap over needle
-  FPointerBitmap.EllipseAntialias(origin.CenterPoint.x, origin.CenterPoint.y, PointerCapSettings.Radius, PointerCapSettings.Radius, PointerCapSettings.EdgeColor, 2, ColorToBGRA(PointerCapSettings.FillColor));
-end;
-
-end.

+ 53 - 0
icons/superspinner.lrs

@@ -0,0 +1,53 @@
+LazarusResources.Add('tsuperspinner','PNG',[
+  #137'PNG'#13#10#26#10#0#0#0#13'IHDR'#0#0#0#24#0#0#0#24#8#3#0#0#0#215#169#205
+  +#202#0#0#3#0'PLTE'#255#255#255#0#0#0#2#2#2#3#3#3#4#4#4#5#5#5#6#6#6#7#7#7#8#8
+  +#8#9#9#9#10#10#10#11#11#11#12#12#12#13#13#13#14#14#14#15#15#15#16#16#16#17#17
+  +#17#18#18#18#19#19#19#20#20#20#21#21#21#22#22#22#23#23#23#24#24#24#25#25#25
+  +#26#26#26#27#27#27#28#28#28#29#29#29#30#30#30#31#31#31'   !!!"""###$$$%%%&&&'
+  +'''''''((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>'
+  +'???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWW'
+  +'XXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnoooppp'
+  +'qqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~'#127#127#127#128#128#128#129#129
+  +#129#130#130#130#131#131#131#132#132#132#133#133#133#134#134#134#135#135#135
+  +#136#136#136#137#137#137#138#138#138#139#139#139#140#140#140#141#141#141#142
+  +#142#142#143#143#143#144#144#144#145#145#145#146#146#146#147#147#147#148#148
+  +#148#149#149#149#150#150#150#151#151#151#152#152#152#153#153#153#154#154#154
+  +#155#155#155#156#156#156#157#157#157#158#158#158#159#159#159#160#160#160#161
+  +#161#161#162#162#162#163#163#163#164#164#164#165#165#165#166#166#166#167#167
+  +#167#168#168#168#169#169#169#170#170#170#171#171#171#172#172#172#173#173#173
+  +#174#174#174#175#175#175#176#176#176#177#177#177#178#178#178#179#179#179#180
+  +#180#180#181#181#181#182#182#182#183#183#183#184#184#184#185#185#185#186#186
+  +#186#187#187#187#188#188#188#189#189#189#190#190#190#191#191#191#192#192#192
+  +#193#193#193#194#194#194#195#195#195#196#196#196#197#197#197#198#198#198#199
+  +#199#199#200#200#200#201#201#201#202#202#202#203#203#203#204#204#204#205#205
+  +#205#206#206#206#207#207#207#208#208#208#209#209#209#210#210#210#211#211#211
+  +#212#212#212#213#213#213#214#214#214#215#215#215#216#216#216#217#217#217#218
+  +#218#218#219#219#219#220#220#220#221#221#221#222#222#222#223#223#223#224#224
+  +#224#225#225#225#226#226#226#227#227#227#228#228#228#229#229#229#230#230#230
+  +#231#231#231#232#232#232#233#233#233#234#234#234#235#235#235#236#236#236#237
+  +#237#237#238#238#238#239#239#239#240#240#240#241#241#241#242#242#242#243#243
+  +#243#244#244#244#245#245#245#246#246#246#247#247#247#248#248#248#249#249#249
+  +#250#250#250#251#251#251#252#252#252#253#253#253#254#254#254#255#255#255#149
+  +'q'#226'^'#0#0#0#1'tRNS'#0'@'#230#216'f'#0#0#0#1'bKGD'#255#165#7#242#197#0#0
+  +#0#9'pHYs'#0#2#207#188#0#2#207#188#1#135#151#213#247#0#0#1#182'IDAT('#207#133
+  +#146'K/'#3'a'#20#134'u'#22'm'#19#162'Z?@'#196#198']4'#169#223'bS'#137#216#147
+  +#16#183#141#149#127#192'B'#244#162#165#170'b'#162'S'#237#136'"'#8')'#162#243
+  +#205'M;'#230'k'#205#152#152'1*'#238#151'DB'#168#25#165#196#198'Y'#190'O'#206
+  +#251#157's'#222#175#164#228#223#178#153','#22#147#237#175#234'@'#236#253#129
+  +#249'P'#160#223#142'8~'#235#230#218#5'Y9'#15#6'r'#138#140#214#153#127't'#131
+  +'S'#21#1#224#189'n'#8#8#241#218'i'#248#214#145#246'4'#197#2#146#247#184'  33'
+  +'\'#145'T'#221#4#135#216'Cp'#228#243#242'$'#28#31#161'.'#170#155#10'F'#243#2
+  +#156#30#216'e'#153'd'#146#225#198'FyZ@'#11'-'#245#10#1#178'A'#140']'#143#199
+  +#183#168'8K'#3#226#172'Y'#215#173#189#10#0#128#163'c'#145'h'#20'[MQ'#128#4
+  +#202#128'E'#3'&'#159#10'!O'#175'Gp'#173#176#29#154#135#25'y'#198#168#129#178
+  +#232#162#219'3M'#172'Du'#16#217'$|'#158#201#229#181'R'#29#196#208'I'#151#143
+  +#136#235#29'1l'#139#240#186'&'#240'O`'#242'kV'#16'l'#224#225#165'p'#12#223
+  +#166#160'f'#21#208#173'*'#250#20'@&'#211'7'#167#185#189#132'r'#246#4#15#180
+  +#199#7#203#245#177#26#180'q'#147'/'#15#9#21'>K'#251'*'#145#210#198'm),'#184
+  +' pks'#239#249';N'#205#231#133'A'#20#30'/'#22#22'l'#170#190#218#232'N'#156'0'
+  +'o'#144#225#248#188#216#19#186#173'i'#248':n'#7#131#191'<'#138#175#203#217'{'
+  +#233'9{'#144#234'('#158#215#208'y)0'#130#232#158#146#4'*'#147#235'2'#252#4'b'
+  +'l'#12#203#146'2'#235'W%'#25'k4'#254#142#208#142#180#13#135'P44'#220#134#180
+  +#254#141#189#210'l'#181#154'+'#255#255'4'#31#25#151#245#196'f'#148'n'#243#0#0
+  +#0#0'IEND'#174'B`'#130
+]);

BIN
images/bgracontrols_images.res


+ 3 - 0
images/bgracontrols_images_list.txt

@@ -146,6 +146,9 @@ TBCLeaEngrave_200.png
 tsupergauge.png
 tsupergauge.png
 tsupergauge_150.png
 tsupergauge_150.png
 tsupergauge_200.png
 tsupergauge_200.png
+tsuperspinner.png
+tsuperspinner_150.png
+tsuperspinner_200.png
 tbcroundedimage.png
 tbcroundedimage.png
 tbcroundedimage_150.png
 tbcroundedimage_150.png
 tbcroundedimage_200.png
 tbcroundedimage_200.png

+ 21 - 0
images/svg/tsuperspinner.svg

@@ -0,0 +1,21 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!--Generator: Xara Designer (www.xara.com), SVG filter version: 6.7.0.0-->
+<svg fill="none" fill-rule="evenodd" stroke="black" stroke-width="0.501" stroke-linejoin="bevel" stroke-miterlimit="10" font-family="Times New Roman" font-size="16" style="font-variant-ligatures:none" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" version="1.1" overflow="visible" width="18pt" height="18pt" viewBox="0 -18 18 18">
+ <defs>
+	</defs>
+ <g id="Layer 1" transform="scale(1 -1)">
+  <ellipse cx="9" cy="8.999" rx="7.5" ry="7.5" fill="#cccccc" fill-rule="nonzero" stroke-linejoin="miter" stroke-width="0.75" stroke="#000000"/>
+  <ellipse rx="2.465" ry="2.465" transform="translate(8.907 9.093) rotate(136.512)" stroke="#b2b2b2" fill="#b2b2b2" stroke-width="0.475" stroke-linejoin="miter"/>
+  <rect x="-8.9995" y="-8.9995" width="17.999" height="17.999" rx="0" ry="0" transform="matrix(1 0 0 1 8.999 8.998)" fill-rule="nonzero" stroke-linejoin="miter" stroke-width="0.75" stroke="none"/>
+  <path d="M 9,12.763 L 9,15.422" fill="none" stroke="#666666" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round"/>
+  <path d="M 9,2.58 L 9,4.982" fill="none" stroke="#666666" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round"/>
+  <path d="M 12.764,9 L 15.045,9" fill="none" stroke="#666666" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round"/>
+  <path d="M 5.204,9 L 2.804,9" fill="none" stroke="#666666" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round"/>
+  <path d="M 11.859,6.156 L 13.497,4.73" fill="none" stroke="#666666" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round"/>
+  <path d="M 6.176,11.678 L 4.503,13.264" fill="none" stroke="#666666" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round"/>
+  <path d="M 4.625,4.625 L 6.322,6.177" fill="none" stroke="#666666" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round"/>
+  <path d="M 11.569,11.942 L 13.266,13.494" fill="none" stroke="#666666" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round"/>
+  <text xml:space="preserve" transform="translate(5.475 3.941) scale(1 -1)" kerning="auto" fill="#ffffff" font-size="5.189" stroke="none" stroke-width="0.162" stroke-linecap="round" stroke-linejoin="round" font-family="Clarendon"><tspan x="0" y="0">SS</tspan></text>
+ </g>
+</svg>

BIN
images/tsuperspinner.png


BIN
images/tsuperspinner_150.png


BIN
images/tsuperspinner_200.png


+ 0 - 80
mouseandkeyinput/carbonkeyinput.pas

@@ -1,80 +0,0 @@
-{ CarbonKeyInput
-
-  Copyright (C) 2008 Tom Gregorovic
-
-  This source 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 2 of the
-  License, or (at your option) any later version.
-
-  This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
-  even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-  General Public License for more details.
-
-  A copy of the GNU General Public License is available on the World Wide Web at
-  <http://www.gnu.org/copyleft/gpl.html>. You can also obtain it by writing to the Free Software
-  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-}
-unit CarbonKeyInput;
-
-{$mode objfpc}{$H+}
-
-interface
-
-uses
-  Classes, SysUtils, Controls, Forms,
-  MacOSAll, CarbonProc,
-  KeyInputIntf;
-  
-type
-
-  { TCarbonKeyInput }
-
-  TCarbonKeyInput = class(TKeyInput)
-  protected
-    procedure DoDown(Key: Word); override;
-    procedure DoUp(Key: Word); override;
-  end;
-  
-function InitializeKeyInput: TKeyInput;
-
-implementation
-
-uses
-  LCLType;
-
-function InitializeKeyInput: TKeyInput;
-begin
-  Result := TCarbonKeyInput.Create;
-end;
-
-procedure SendKeyInput(Key: Word; Down: Boolean);
-var
-  Char: Word;
-begin
-  Char := 0;
-  if Key in [VK_A .. VK_Z] then
-  begin
-    Char := Ord('A') + Key - VK_A;
-    Key := 0;
-  end;
-  if Key in [VK_0 .. VK_9] then
-  begin
-    Key := VK_NUMPAD0 + Key - VK_0;
-  end;
-  CGPostKeyboardEvent(Char, VirtualKeyCodeToMac(Key), Integer(Down));
-end;
-
-{ TCarbonKeyInput }
-
-procedure TCarbonKeyInput.DoDown(Key: Word);
-begin
-  SendKeyInput(Key, True);
-end;
-
-procedure TCarbonKeyInput.DoUp(Key: Word);
-begin
-  SendKeyInput(Key, False);
-end;
-
-end.
-

+ 0 - 72
mouseandkeyinput/carbonmouseinput.pas

@@ -1,72 +0,0 @@
-{ CarbonMouseInput
-
-  Copyright (C) 2008 Tom Gregorovic
-
-  This source 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 2 of the
-  License, or (at your option) any later version.
-
-  This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
-  even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-  General Public License for more details.
-
-  A copy of the GNU General Public License is available on the World Wide Web at
-  <http://www.gnu.org/copyleft/gpl.html>. You can also obtain it by writing to the Free Software
-  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-}
-unit CarbonMouseInput;
-
-{$mode objfpc}{$H+}
-
-interface
-
-uses
-  Classes, SysUtils, Controls, Forms,
-  MacOSAll, CarbonProc,
-  MouseInputIntf;
-  
-type
-
-  { TCarbonMouseInput }
-
-  TCarbonMouseInput = class(TMouseInput)
-  protected
-    procedure DoDown(Button: TMouseButton); override;
-    procedure DoMove(ScreenX, ScreenY: Integer); override;
-    procedure DoUp(Button: TMouseButton); override;
-  end;
-  
-function InitializeMouseInput: TMouseInput;
-
-
-implementation
-
-function InitializeMouseInput: TMouseInput;
-begin
-  Result := TCarbonMouseInput.Create;
-end;
-
-const
-  MouseButtonToCarbonButton: array [TMouseButton] of Integer =
-    (kCGMouseButtonLeft, kCGMouseButtonRight, kCGMouseButtonCenter,kCGMouseButtonLeft,kCGMouseButtonLeft);
-  
-
-{ TCarbonMouseInput }
-
-procedure TCarbonMouseInput.DoDown(Button: TMouseButton);
-begin
-  CGPostMouseEvent(PointToHIPoint(Mouse.CursorPos), 0, 1, 1, MouseButtonToCarbonButton[Button]);
-end;
-
-procedure TCarbonMouseInput.DoMove(ScreenX, ScreenY: Integer);
-begin
-  CGPostMouseEvent(GetHIPoint(ScreenX, ScreenY), 1, 1, 0, 0);
-end;
-
-procedure TCarbonMouseInput.DoUp(Button: TMouseButton);
-begin
-  CGPostMouseEvent(PointToHIPoint(Mouse.CursorPos), 0, 1, 0, MouseButtonToCarbonButton[Button]);
-end;
-
-end.
-

+ 0 - 92
mouseandkeyinput/keyinputintf.pas

@@ -1,92 +0,0 @@
-{ KeyInputIntf
-
-  Copyright (C) 2008 Tom Gregorovic
-
-  This source 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 2 of the
-  License, or (at your option) any later version.
-
-  This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
-  even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-  General Public License for more details.
-
-  A copy of the GNU General Public License is available on the World Wide Web at
-  <http://www.gnu.org/copyleft/gpl.html>. You can also obtain it by writing to the Free Software
-  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-}
-unit KeyInputIntf;
-
-{$IFDEF FPC}{$mode objfpc}{$H+}{$ENDIF}
-
-interface
-
-uses
-  Classes, SysUtils, {$IFDEF FPC}LCLType,{$ELSE}Types, windows, messages,{$ENDIF} Forms;
-
-type
-  { TKeyInput }
-
-  TKeyInput = class
-  protected
-    procedure DoDown(Key: Word); dynamic; abstract;
-    procedure DoUp(Key: Word); dynamic; abstract;
-  public
-    procedure Down(Key: Word);
-    procedure Up(Key: Word);
-
-    procedure Press(Key: Word);  overload;
-    procedure Press(StringValue : String);  overload;
-
-    procedure Apply(Shift: TShiftState);
-    procedure Unapply(Shift: TShiftState);
-  end;
-
-implementation
-
-{ TKeyInput }
-
-procedure TKeyInput.Down(Key: Word);
-begin  DoDown(Key);
-  Application.ProcessMessages;
-end;
-
-procedure TKeyInput.Up(Key: Word);
-begin
-  DoUp(Key);
-  Application.ProcessMessages;
-end;
-
-procedure TKeyInput.Press(Key: Word);
-begin
-  Down(Key);
-  Up(Key);
-end;
-
-procedure TKeyInput.Press(StringValue: String);
-var
-  i : Integer;
-begin
-  i :=1;
-  while (i <= Length(StringValue)) do
-    begin
-      Press(Ord(StringValue[i]));
-      Inc(i);
-    end;
-end;
-
-procedure TKeyInput.Apply(Shift: TShiftState);
-begin
-  if ssCtrl in Shift then Down(VK_CONTROL);
-  if ssAlt in Shift then Down(VK_MENU);
-  if ssShift in Shift then Down(VK_SHIFT);
-end;
-
-procedure TKeyInput.Unapply(Shift: TShiftState);
-begin
-  if ssShift in Shift then Up(VK_SHIFT);
-  if ssCtrl in Shift then Up(VK_CONTROL);
-  if ssAlt in Shift then Up(VK_MENU);
-end;
-
-end.
-

+ 0 - 88
mouseandkeyinput/lazmouseandkeyinput.lpk

@@ -1,88 +0,0 @@
-<?xml version="1.0"?>
-<CONFIG>
-  <Package Version="3">
-    <PathDelim Value="\"/>
-    <Name Value="LazMouseAndKeyInput"/>
-    <Author Value="Tom Gregorovic"/>
-    <CompilerOptions>
-      <Version Value="5"/>
-      <PathDelim Value="\"/>
-      <SearchPaths>
-        <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
-      </SearchPaths>
-      <CodeGeneration>
-        <Generate Value="Faster"/>
-      </CodeGeneration>
-      <Other>
-        <CompilerPath Value="$(CompPath)"/>
-      </Other>
-    </CompilerOptions>
-    <Description Value="Tool for cross-platform manipulation with mouse and key input.
-"/>
-    <License Value="GPL
-"/>
-    <Version Minor="1"/>
-    <Files Count="9">
-      <Item1>
-        <Filename Value="carbonkeyinput.pas"/>
-        <AddToUsesPkgSection Value="False"/>
-        <UnitName Value="CarbonKeyInput"/>
-      </Item1>
-      <Item2>
-        <Filename Value="carbonmouseinput.pas"/>
-        <AddToUsesPkgSection Value="False"/>
-        <UnitName Value="CarbonMouseInput"/>
-      </Item2>
-      <Item3>
-        <Filename Value="keyinputintf.pas"/>
-        <AddToUsesPkgSection Value="False"/>
-        <UnitName Value="KeyInputIntf"/>
-      </Item3>
-      <Item4>
-        <Filename Value="mouseandkeyinput.pas"/>
-        <UnitName Value="MouseAndKeyInput"/>
-      </Item4>
-      <Item5>
-        <Filename Value="mouseinputintf.pas"/>
-        <AddToUsesPkgSection Value="False"/>
-        <UnitName Value="MouseInputIntf"/>
-      </Item5>
-      <Item6>
-        <Filename Value="winkeyinput.pas"/>
-        <AddToUsesPkgSection Value="False"/>
-        <UnitName Value="WinKeyInput"/>
-      </Item6>
-      <Item7>
-        <Filename Value="winmouseinput.pas"/>
-        <AddToUsesPkgSection Value="False"/>
-        <UnitName Value="WinMouseInput"/>
-      </Item7>
-      <Item8>
-        <Filename Value="xkeyinput.pas"/>
-        <AddToUsesPkgSection Value="False"/>
-        <UnitName Value="XKeyInput"/>
-      </Item8>
-      <Item9>
-        <Filename Value="xmouseinput.pas"/>
-        <AddToUsesPkgSection Value="False"/>
-        <UnitName Value="XMouseInput"/>
-      </Item9>
-    </Files>
-    <RequiredPkgs Count="2">
-      <Item1>
-        <PackageName Value="LCL"/>
-      </Item1>
-      <Item2>
-        <PackageName Value="FCL"/>
-        <MinVersion Major="1" Valid="True"/>
-      </Item2>
-    </RequiredPkgs>
-    <UsageOptions>
-      <UnitPath Value="$(PkgOutDir)\"/>
-    </UsageOptions>
-    <PublishOptions>
-      <Version Value="2"/>
-      <IgnoreBinaries Value="False"/>
-    </PublishOptions>
-  </Package>
-</CONFIG>

+ 0 - 15
mouseandkeyinput/lazmouseandkeyinput.pas

@@ -1,15 +0,0 @@
-{ This file was automatically created by Lazarus. Do not edit!
-  This source is only used to compile and install the package.
- }
-
-unit LazMouseAndKeyInput;
-
-{$warn 5023 off : no warning about unused units}
-interface
-
-uses
-  MouseAndKeyInput;
-
-implementation
-
-end.

+ 0 - 66
mouseandkeyinput/mouseandkeyinput.pas

@@ -1,66 +0,0 @@
-{ MouseAndKeyInput
-
-  Copyright (C) 2008 Tom Gregorovic
-
-  This source 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 2 of the
-  License, or (at your option) any later version.
-
-  This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
-  even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-  General Public License for more details.
-
-  A copy of the GNU General Public License is available on the World Wide Web at
-  <http://www.gnu.org/copyleft/gpl.html>. You can also obtain it by writing to the Free Software
-  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-}
-unit MouseAndKeyInput;
-
-interface
-
-uses
-  MouseInputIntf,
-  KeyInputIntf,
-  {$IFDEF FPC}
-	  {$IFDEF WINDOWS}
-	  WinMouseInput,
-	  WinKeyInput,
-	  {$ENDIF}
-	  {$IFDEF UNIX}
-	    {$IFDEF LCLcarbon}
-	    CarbonMouseInput,
-	    CarbonKeyInput,
-	    {$ELSE}
-	    XMouseInput,
-	    XKeyInput,
-	    {$ENDIF}
-	  {$ENDIF}
-  {$ELSE}
-	  WinMouseInput,
-	  WinKeyInput,  
-  {$ENDIF}
-  Classes, SysUtils;
-
-var
-  MouseInput: TMouseInput;
-  KeyInput: TKeyInput;
-
-implementation
-
-
-
-initialization
-
-  // Create platform specific object for mouse input
-  MouseInput := InitializeMouseInput;
-
-  // Create platform specific object for key input
-  KeyInput := InitializeKeyInput;
-
-finalization
-
-  FreeAndNil(MouseInput);
-  FreeAndNil(KeyInput);
-
-
-end.

+ 0 - 284
mouseandkeyinput/mouseinputintf.pas

@@ -1,284 +0,0 @@
-{ MouseInputIntf
-
-  Copyright (C) 2008 Tom Gregorovic
-
-  This source 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 2 of the
-  License, or (at your option) any later version.
-
-  This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
-  even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-  General Public License for more details.
-
-  A copy of the GNU General Public License is available on the World Wide Web at
-  <http://www.gnu.org/copyleft/gpl.html>. You can also obtain it by writing to the Free Software
-  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-}
-unit MouseInputIntf;
-
-{$IFDEF FPC}{$mode objfpc}{$H+}{$ENDIF}
-interface
-
-uses
-  Classes, SysUtils, {$IFNDEF FPC}Types, windows,{$ENDIF} Controls, Forms;
-
-type
-  { TMouseInput }
-
-  TMouseInput = class
-  protected
-    procedure DoDown(Button: TMouseButton); dynamic; abstract;
-    procedure DoMove(ScreenX, ScreenY: Integer); dynamic; abstract;
-    procedure DoUp(Button: TMouseButton); dynamic; abstract;
-    procedure DoScrollUp; dynamic; abstract;
-    procedure DoScrollDown; dynamic; abstract;
-  public
-    procedure Down(Button: TMouseButton; Shift: TShiftState); overload;
-    procedure Down(Button: TMouseButton; Shift: TShiftState; Control: TControl; X, Y: Integer); overload;
-    procedure Down(Button: TMouseButton; Shift: TShiftState; ScreenX, ScreenY: Integer); overload;
-
-    procedure Move(Shift: TShiftState; Control: TControl; X, Y: Integer; Duration: Integer = 0); overload;
-    procedure MoveBy(Shift: TShiftState; DX, DY: Integer; Duration: Integer = 0); overload;
-    procedure Move(Shift: TShiftState; ScreenX, ScreenY: Integer; Duration: Integer); overload;
-    procedure Move(Shift: TShiftState; ScreenX, ScreenY: Integer); overload;
-
-    procedure ScrollUp(Shift: TShiftState); overload;
-    procedure ScrollUp(Shift: TShiftState; Control: TControl; X, Y: Integer); overload;
-    procedure ScrollUp(Shift: TShiftState; ScreenX, ScreenY: Integer); overload;
-    procedure ScrollDown(Shift: TShiftState); overload;
-    procedure ScrollDown(Shift: TShiftState; Control: TControl; X, Y: Integer); overload;
-    procedure ScrollDown(Shift: TShiftState; ScreenX, ScreenY: Integer); overload;
-
-    procedure Up(Button: TMouseButton; Shift: TShiftState); overload;
-    procedure Up(Button: TMouseButton; Shift: TShiftState; Control: TControl; X, Y: Integer); overload;
-    procedure Up(Button: TMouseButton; Shift: TShiftState; ScreenX, ScreenY: Integer); overload;
-
-    procedure Click(Button: TMouseButton; Shift: TShiftState); overload;
-    procedure Click(Button: TMouseButton; Shift: TShiftState; Control: TControl; X, Y: Integer); overload;
-    procedure Click(Button: TMouseButton; Shift: TShiftState; ScreenX, ScreenY: Integer); overload;
-
-    procedure DblClick(Button: TMouseButton; Shift: TShiftState); overload;
-    procedure DblClick(Button: TMouseButton; Shift: TShiftState; Control: TControl; X, Y: Integer); overload;
-    procedure DblClick(Button: TMouseButton; Shift: TShiftState; ScreenX, ScreenY: Integer); overload;
-  end;
-
-implementation
-
-uses
-  Math, MouseAndKeyInput;
-
-{ TMouseInput }
-
-procedure TMouseInput.Down(Button: TMouseButton; Shift: TShiftState);
-begin
-  KeyInput.Apply(Shift);
-  try
-    DoDown(Button);
-  finally
-    KeyInput.Unapply(Shift);
-  end;
-  Application.ProcessMessages;
-end;
-
-procedure TMouseInput.Down(Button: TMouseButton; Shift: TShiftState;
-  Control: TControl; X, Y: Integer);
-var
-  P: TPoint;
-begin
-  P := Control.ClientToScreen(Point(X, Y));
-  Down(Button, Shift, P.X, P.Y);
-end;
-
-procedure TMouseInput.Down(Button: TMouseButton; Shift: TShiftState;
-  ScreenX, ScreenY: Integer);
-begin
-  KeyInput.Apply(Shift);
-  try
-    DoMove(ScreenX, ScreenY);
-    DoDown(Button);
-  finally
-    KeyInput.Unapply(Shift);
-  end;
-end;
-
-procedure TMouseInput.Move(Shift: TShiftState; Control: TControl; X, Y: Integer; Duration: Integer = 0);
-var
-  P: TPoint;
-begin
-  P := Control.ClientToScreen(Point(X, Y));
-  Move(Shift, P.X, P.Y, Duration);
-end;
-
-procedure TMouseInput.MoveBy(Shift: TShiftState; DX, DY: Integer; Duration: Integer = 0);
-var
-  P: TPoint;
-begin
-  P := Mouse.CursorPos;
-  Move(Shift, P.X + DX, P.Y + DY, Duration);
-end;
-
-procedure TMouseInput.Move(Shift: TShiftState; ScreenX, ScreenY: Integer; Duration: Integer);
-const
-  Interval = 20; //ms
-var
-  TimeStep: Integer;
-  X, Y: Integer;
-  Start: TPoint;
-  S: LongWord;
-begin
-  Start := Mouse.CursorPos;
-
-  while Duration > 0 do
-  begin
-    TimeStep := Min(Interval, Duration);
-
-    S := {%H-}{$IFNDEF FPC}Windows.{$ENDIF}GetTickCount;
-    while {%H-}{$IFNDEF FPC}Windows.{$ENDIF}GetTickCount - S < TimeStep do Application.ProcessMessages;
-
-    X := Start.X + ((ScreenX - Start.X) * TimeStep) div Duration;
-    Y := Start.Y + ((ScreenY - Start.Y) * TimeStep) div Duration;
-    Move(Shift, X, Y);
-
-    Duration := Duration - TimeStep;
-    Start := Point(X, Y);
-  end;
-
-  Move(Shift, ScreenX, ScreenY);
-end;
-
-procedure TMouseInput.Move(Shift: TShiftState; ScreenX, ScreenY: Integer);
-begin
-  KeyInput.Apply(Shift);
-  try
-    DoMove(ScreenX, ScreenY);
-  finally
-    KeyInput.Unapply(Shift);
-  end;
-  Application.ProcessMessages;
-end;
-
-procedure TMouseInput.ScrollUp(Shift: TShiftState);
-begin
-  KeyInput.Apply(Shift);
-  try
-    DoScrollUp;
-  finally
-    KeyInput.Unapply(Shift);
-  end;
-  Application.ProcessMessages;
-end;
-
-procedure TMouseInput.ScrollUp(Shift: TShiftState; Control: TControl;
-  X, Y: Integer);
-var
-  P: TPoint;
-begin
-  P := Control.ClientToScreen(Point(X, Y));
-  ScrollUp(Shift, P.X, P.Y);
-end;
-
-procedure TMouseInput.ScrollUp(Shift: TShiftState; ScreenX, ScreenY: Integer);
-begin
-  Move(Shift, ScreenX, ScreenY);
-  ScrollUp(Shift);
-end;
-
-procedure TMouseInput.ScrollDown(Shift: TShiftState);
-begin
-  KeyInput.Apply(Shift);
-  try
-    DoScrollDown;
-  finally
-    KeyInput.Unapply(Shift);
-  end;
-  Application.ProcessMessages;
-end;
-
-procedure TMouseInput.ScrollDown(Shift: TShiftState; Control: TControl;
-  X, Y: Integer);
-var
-  P: TPoint;
-begin
-  P := Control.ClientToScreen(Point(X, Y));
-  ScrollDown(Shift, P.X, P.Y);
-end;
-
-procedure TMouseInput.ScrollDown(Shift: TShiftState; ScreenX, ScreenY: Integer);
-begin
-  Move(Shift, ScreenX, ScreenY);
-  ScrollDown(Shift);
-end;
-
-procedure TMouseInput.Up(Button: TMouseButton; Shift: TShiftState);
-begin
-  KeyInput.Apply(Shift);
-  try
-    DoUp(Button);
-  finally
-    KeyInput.Unapply(Shift);
-  end;
-  Application.ProcessMessages;
-end;
-
-procedure TMouseInput.Up(Button: TMouseButton; Shift: TShiftState;
-  Control: TControl; X, Y: Integer);
-var
-  P: TPoint;
-begin
-  P := Control.ClientToScreen(Point(X, Y));
-  Up(Button, Shift, P.X, P.Y);
-end;
-
-procedure TMouseInput.Up(Button: TMouseButton; Shift: TShiftState;
-  ScreenX, ScreenY: Integer);
-begin
-  Move(Shift, ScreenX, ScreenY);
-  Up(Button, Shift);
-end;
-
-procedure TMouseInput.Click(Button: TMouseButton; Shift: TShiftState);
-begin
-  Down(Button, Shift);
-  Up(Button, Shift);
-end;
-
-procedure TMouseInput.Click(Button: TMouseButton; Shift: TShiftState;
-  Control: TControl; X, Y: Integer);
-var
-  P: TPoint;
-begin
-  P := Control.ClientToScreen(Point(X, Y));
-  Click(Button, Shift, P.X, P.Y);
-end;
-
-procedure TMouseInput.Click(Button: TMouseButton; Shift: TShiftState;
-  ScreenX, ScreenY: Integer);
-begin
-  Move(Shift, ScreenX, ScreenY);
-  Click(Button, Shift);
-end;
-
-procedure TMouseInput.DblClick(Button: TMouseButton; Shift: TShiftState);
-begin
-  Click(Button, Shift);
-  Click(Button, Shift);
-end;
-
-procedure TMouseInput.DblClick(Button: TMouseButton; Shift: TShiftState;
-  Control: TControl; X, Y: Integer);
-var
-  P: TPoint;
-begin
-  P := Control.ClientToScreen(Point(X, Y));
-  DblClick(Button, Shift, P.X, P.Y);
-end;
-
-procedure TMouseInput.DblClick(Button: TMouseButton; Shift: TShiftState;
-  ScreenX, ScreenY: Integer);
-begin
-  Move(Shift, ScreenX, ScreenY);
-  DblClick(Button, Shift);
-end;
-
-end.
-

+ 0 - 21
mouseandkeyinput/readme.txt

@@ -1,21 +0,0 @@
-MouseAndKeyInput package is a tool for cross-platform manipulation with mouse and key input. You can move mouse cursor to specified location, send clicks and do key presses. It is suitable for GUI testing or program control demonstration.
-
-Author
-Tom Gregorovic
-
-License
-GPL
-
-Change Log
-* Version 0.1 
-
-Restrictions
-* it is not recommended calling mouse and key input directly from events like OnClick, use Application.QueueAsyncCall instead
-* do not forget to set back mouse button and key state after Down method with Up method 
-
- Carbon
- * pressing alpha chars is not supported 
-
- Gtk1/2
- * needs Xtst library
- * ALT key pressing is not supported 

+ 0 - 72
mouseandkeyinput/winkeyinput.pas

@@ -1,72 +0,0 @@
-{ WinKeyInput
-
-  Copyright (C) 2008 Tom Gregorovic
-
-  This source 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 2 of the
-  License, or (at your option) any later version.
-
-  This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
-  even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-  General Public License for more details.
-
-  A copy of the GNU General Public License is available on the World Wide Web at
-  <http://www.gnu.org/copyleft/gpl.html>. You can also obtain it by writing to the Free Software
-  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-}
-unit WinKeyInput;
-
-{$IFDEF FPC}{$mode objfpc}{$H+}{$ENDIF}
-interface
-
-uses
-  Classes, SysUtils, Controls, Forms,
-  Windows, {$IFDEF FPC}JwaWinUser,{$ENDIF}
-  KeyInputIntf;
-
-type
-
-  { TWinKeyInput }
-
-  TWinKeyInput = class(TKeyInput)
-  protected
-    procedure DoDown(Key: Word); override;
-    procedure DoUp(Key: Word); override;
-  end;
-
-function InitializeKeyInput: TKeyInput;
-
-implementation
-
-function InitializeKeyInput: TKeyInput;
-begin
-  Result := TWinKeyInput.Create;
-end;
-
-procedure SendKeyInput(Flag: DWORD; Key: Word);
-var
-  Input: TInput;
-begin
-  FillChar({%H-}Input, SizeOf(Input), 0);
-  Input.{$IFDEF FPC}type_{$ELSE}Itype{$ENDIF} := INPUT_KEYBOARD;
-  Input.ki.dwFlags := Flag;
-  Input.ki.wVk := Key;
-
-  SendInput(1, {$IFDEF FPC}@{$ENDIF}Input, SizeOf(Input));
-end;
-
-
-{ TWinKeyInput }
-
-procedure TWinKeyInput.DoDown(Key: Word);
-begin
-  SendKeyInput(0, Key);
-end;
-
-procedure TWinKeyInput.DoUp(Key: Word);
-begin
-  SendKeyInput(KEYEVENTF_KEYUP, Key);
-end;
-
-end.
-

+ 0 - 127
mouseandkeyinput/winmouseinput.pas

@@ -1,127 +0,0 @@
-{ WinMouseInput
-
-  Copyright (C) 2008 Tom Gregorovic
-
-  This source 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 2 of the
-  License, or (at your option) any later version.
-
-  This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
-  even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-  General Public License for more details.
-
-  A copy of the GNU General Public License is available on the World Wide Web at
-  <http://www.gnu.org/copyleft/gpl.html>. You can also obtain it by writing to the Free Software
-  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-}
-unit WinMouseInput;
-
-{$IFDEF FPC}{$mode objfpc}{$H+}{$ENDIF}
-
-interface
-
-uses
-  Classes, SysUtils, Controls, Forms,
-  Windows, {$IFDEF FPC}JwaWinUser,{$ENDIF}
-  MouseInputIntf;
-
-type
-
-  { TWinMouseInput }
-
-  TWinMouseInput = class(TMouseInput)
-  protected
-    procedure DoDown(Button: TMouseButton); override;
-    procedure DoMove(ScreenX, ScreenY: Integer); override;
-    procedure DoUp(Button: TMouseButton); override;
-    procedure DoScrollUp; override;
-    procedure DoScrollDown; override;
-  end;
-
-function InitializeMouseInput: TMouseInput;
-
-implementation
-
-function InitializeMouseInput: TMouseInput;
-begin
-  Result := TWinMouseInput.Create;
-end;
-
-procedure SendMouseInput(Flag: DWORD; MouseData: DWORD = 0); overload;
-var
-  Input: TInput;
-begin
-{$IFDEF VER2_6}
-  FillChar(Input, SizeOf(Input), 0);
-{$ELSE}
-  Input := Default(TInput);
-{$ENDIF}
-  Input.mi.mouseData := MouseData;
-  Input.{$IFDEF FPC}type_{$ELSE}Itype{$ENDIF} := INPUT_MOUSE;
-  Input.mi.dwFlags := Flag;
-
-  SendInput(1, {$IFDEF FPC}@{$ENDIF}Input, SizeOf(Input));
-end;
-
-procedure SendMouseInput(Flag: DWORD; X, Y: Integer); overload;
-var
-  Input: TInput;
-begin
-{$IFDEF VER2_6}
-  FillChar(Input, SizeOf(Input), 0);
-{$ELSE}
-  Input := Default(TInput);
-{$ENDIF}
-  Input.{$IFDEF FPC}type_{$ELSE}Itype{$ENDIF} := INPUT_MOUSE;
-  Input.mi.dx := MulDiv(X, 65535, Screen.Width - 1); // screen horizontal coordinates: 0 - 65535
-  Input.mi.dy := MulDiv(Y, 65535, Screen.Height - 1); // screen vertical coordinates: 0 - 65535
-  Input.mi.dwFlags := Flag or MOUSEEVENTF_ABSOLUTE;
-
-  SendInput(1, {$IFDEF FPC}@{$ENDIF}Input, SizeOf(Input));
-end;
-
-{ TWinMouseInput }
-
-procedure TWinMouseInput.DoDown(Button: TMouseButton);
-var
-  Flag: DWORD;
-begin
-  case Button of
-    mbRight: Flag := MOUSEEVENTF_RIGHTDOWN;
-    mbMiddle: Flag := MOUSEEVENTF_MIDDLEDOWN;
-  else
-    Flag := MOUSEEVENTF_LEFTDOWN;
-  end;
-  SendMouseInput(Flag);
-end;
-
-procedure TWinMouseInput.DoMove(ScreenX, ScreenY: Integer);
-begin
-  SendMouseInput(MOUSEEVENTF_MOVE, ScreenX, ScreenY);
-end;
-
-procedure TWinMouseInput.DoUp(Button: TMouseButton);
-var
-  Flag: DWORD;
-begin
-  case Button of
-    mbRight: Flag := MOUSEEVENTF_RIGHTUP;
-    mbMiddle: Flag := MOUSEEVENTF_MIDDLEUP;
-  else
-    Flag := MOUSEEVENTF_LEFTUP;
-  end;
-  SendMouseInput(Flag);
-end;
-
-procedure TWinMouseInput.DoScrollUp;
-begin
-  SendMouseInput(MOUSEEVENTF_WHEEL, WHEEL_DELTA);
-end;
-
-procedure TWinMouseInput.DoScrollDown;
-begin
-  SendMouseInput(MOUSEEVENTF_WHEEL, DWORD(-WHEEL_DELTA));
-end;
-
-end.
-

+ 0 - 198
mouseandkeyinput/xkeyinput.pas

@@ -1,198 +0,0 @@
-{ XKeyInput
-
-  Copyright (C) 2008 Tom Gregorovic
-
-  This source 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 2 of the
-  License, or (at your option) any later version.
-
-  This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
-  even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-  General Public License for more details.
-
-  A copy of the GNU General Public License is available on the World Wide Web at
-  <http://www.gnu.org/copyleft/gpl.html>. You can also obtain it by writing to the Free Software
-  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-}
-unit XKeyInput;
-
-{$mode objfpc}{$H+}
-{$linklib Xtst}
-
-interface
-
-uses
-  Classes, SysUtils, Controls, Forms,
-  X, XLib, KeySym,
-  KeyInputIntf;
-  
-type
-
-  { TXKeyInput }
-
-  TXKeyInput = class(TKeyInput)
-  protected
-    procedure DoDown(Key: Word); override;
-    procedure DoUp(Key: Word); override;
-  end;
-  
-function InitializeKeyInput: TKeyInput;
-
-function XTestFakeKeyEvent(dpy: PDisplay; keycode: dword; is_press: Boolean32;
-  delay: dword): longint; cdecl; external;
-
-implementation
-
-uses LCLType;
-
-function InitializeKeyInput: TKeyInput;
-begin
-  Result := TXKeyInput.Create;
-end;
-
-function VirtualKeyToXKeySym(Key: Word): TKeySym;
-begin
-  case Key of
-    VK_BACK: Result := XK_BackSpace;
-    VK_TAB: Result := XK_Tab;
-    VK_CLEAR: Result := XK_Clear;
-    VK_RETURN: Result := XK_Return;
-    VK_SHIFT: Result := XK_Shift_L;
-    VK_CONTROL: Result := XK_Control_L;
-    VK_MENU: Result := XK_VoidSymbol; // alt key crashes app, XK_Alt_R;
-    VK_CAPITAL: Result := XK_Caps_Lock;
-
-    VK_ESCAPE: Result := XK_Escape;
-    VK_SPACE: Result := XK_space;
-    VK_PRIOR: Result := XK_Prior;
-    VK_NEXT: Result := XK_Next;
-    VK_END: Result := XK_End;
-    VK_HOME: Result := XK_Home;
-    VK_LEFT: Result := XK_Left;
-    VK_UP: Result := XK_Up;
-    VK_RIGHT: Result := XK_Right;
-    VK_DOWN: Result := XK_Down;
-    VK_SELECT: Result := XK_Select;
-    VK_PRINT: Result := XK_Print;
-    VK_EXECUTE: Result := XK_Execute;
-
-    VK_INSERT: Result := XK_Insert;
-    VK_DELETE: Result := XK_Delete;
-    VK_HELP: Result := XK_Help;
-    VK_0: Result := XK_0;
-    VK_1: Result := XK_1;
-    VK_2: Result := XK_2;
-    VK_3: Result := XK_3;
-    VK_4: Result := XK_4;
-    VK_5: Result := XK_5;
-    VK_6: Result := XK_6;
-    VK_7: Result := XK_7;
-    VK_8: Result := XK_8;
-    VK_9: Result := XK_9;
-
-    VK_A: Result := XK_a;
-    VK_B: Result := XK_b;
-    VK_C: Result := XK_c;
-    VK_D: Result := XK_d;
-    VK_E: Result := XK_e;
-    VK_F: Result := XK_f;
-    VK_G: Result := XK_g;
-    VK_H: Result := XK_h;
-    VK_I: Result := XK_i;
-    VK_J: Result := XK_j;
-    VK_K: Result := XK_k;
-    VK_L: Result := XK_l;
-    VK_M: Result := XK_m;
-    VK_N: Result := XK_n;
-    VK_O: Result := XK_o;
-    VK_P: Result := XK_p;
-    VK_Q: Result := XK_q;
-    VK_R: Result := XK_r;
-    VK_S: Result := XK_s;
-    VK_T: Result := XK_t;
-    VK_U: Result := XK_u;
-    VK_V: Result := XK_v;
-    VK_W: Result := XK_w;
-    VK_X: Result := XK_x;
-    VK_Y: Result := XK_y;
-    VK_Z: Result := XK_z;
-
-    VK_NUMPAD0: Result := XK_KP_0;
-    VK_NUMPAD1: Result := XK_KP_1;
-    VK_NUMPAD2: Result := XK_KP_2;
-    VK_NUMPAD3: Result := XK_KP_3;
-    VK_NUMPAD4: Result := XK_KP_4;
-    VK_NUMPAD5: Result := XK_KP_5;
-    VK_NUMPAD6: Result := XK_KP_6;
-    VK_NUMPAD7: Result := XK_KP_7;
-    VK_NUMPAD8: Result := XK_KP_8;
-    VK_NUMPAD9: Result := XK_KP_9;
-    VK_MULTIPLY: Result := XK_KP_Multiply;
-    VK_ADD: Result := XK_KP_Add;
-    VK_SEPARATOR: Result := XK_KP_Separator;
-    VK_SUBTRACT: Result := XK_KP_Subtract;
-    VK_DECIMAL: Result := XK_KP_Decimal;
-    VK_DIVIDE: Result := XK_KP_Divide;
-    VK_F1: Result := XK_F1;
-    VK_F2: Result := XK_F2;
-    VK_F3: Result := XK_F3;
-    VK_F4: Result := XK_F4;
-    VK_F5: Result := XK_F5;
-    VK_F6: Result := XK_F6;
-    VK_F7: Result := XK_F7;
-    VK_F8: Result := XK_F8;
-    VK_F9: Result := XK_F9;
-    VK_F10: Result := XK_F10;
-    VK_F11: Result := XK_F11;
-    VK_F12: Result := XK_F12;
-    VK_F13: Result := XK_F13;
-    VK_F14: Result := XK_F14;
-    VK_F15: Result := XK_F15;
-    VK_F16: Result := XK_F16;
-    VK_F17: Result := XK_F17;
-    VK_F18: Result := XK_F18;
-    VK_F19: Result := XK_F19;
-    VK_F20: Result := XK_F20;
-    VK_F21: Result := XK_F21;
-    VK_F22: Result := XK_F22;
-    VK_F23: Result := XK_F23;
-    VK_F24: Result := XK_F24;
-    VK_NUMLOCK: Result := XK_Num_Lock;
-    VK_SCROLL: Result := XK_Scroll_Lock;
-  else
-    Result := XK_VoidSymbol;
-  end;
-end;
-
-{ TXKeyInput }
-
-procedure TXKeyInput.DoDown(Key: Word);
-var
-  Display: PDisplay;
-  KeySym: TKeySym;
-begin
-  KeySym := VirtualKeyToXKeySym(Key);
-  if KeySym = XK_VoidSymbol then Exit;
-  
-  Display := XOpenDisplay(nil);
-  XTestFakeKeyEvent(Display, XKeysymToKeycode(Display, KeySym), True, 0);
-  XFlush(Display);
-  XCloseDisplay(Display);
-end;
-
-procedure TXKeyInput.DoUp(Key: Word);
-var
-  Display: PDisplay;
-  KeySym: TKeySym;
-begin
-  KeySym := VirtualKeyToXKeySym(Key);
-  if KeySym = XK_VoidSymbol then Exit;
-  
-  Display := XOpenDisplay(nil);
-  XTestFakeKeyEvent(Display, XKeysymToKeycode(Display, KeySym), False, 0);
-  XFlush(Display);
-  XCloseDisplay(Display);
-end;
-
-end.
-

+ 0 - 114
mouseandkeyinput/xmouseinput.pas

@@ -1,114 +0,0 @@
-{ XMouseInput
-
-  Copyright (C) 2008 Tom Gregorovic
-
-  This source 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 2 of the
-  License, or (at your option) any later version.
-
-  This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
-  even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-  General Public License for more details.
-
-  A copy of the GNU General Public License is available on the World Wide Web at
-  <http://www.gnu.org/copyleft/gpl.html>. You can also obtain it by writing to the Free Software
-  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-}
-unit XMouseInput;
-
-{$mode objfpc}{$H+}
-{$linklib Xtst}
-
-interface
-
-uses
-  Classes, SysUtils, Controls, Forms,
-  XLib, MouseInputIntf;
-  
-type
-
-  { TXMouseInput }
-
-  TXMouseInput = class(TMouseInput)
-  protected
-    procedure DoDown(Button: TMouseButton); override;
-    procedure DoMove(ScreenX, ScreenY: Integer); override;
-    procedure DoUp(Button: TMouseButton); override;
-    procedure DoScrollUp; override;
-    procedure DoScrollDown; override;
-  end;
-  
-function InitializeMouseInput: TMouseInput;
-
-function XTestFakeButtonEvent(dpy: PDisplay; button: dword; is_press: Boolean;
-  delay: dword): longint; cdecl; external;
-
-function XTestFakeMotionEvent(dpy: PDisplay; screen: longint; x: longint; y: longint;
-  delay: dword): longint; cdecl; external;
-
-implementation
-
-function InitializeMouseInput: TMouseInput;
-begin
-  Result := TXMouseInput.Create;
-end;
-
-const
-  MouseButtonToXButton: array [TMouseButton] of Integer = (1, 3, 2, 4, 5);
-
-{ TXMouseInput }
-
-procedure TXMouseInput.DoDown(Button: TMouseButton);
-var
-  Display: PDisplay;
-begin
-  Display := XOpenDisplay(nil);
-  XTestFakeButtonEvent(Display, MouseButtonToXButton[Button], True, 0);
-  XFlush(Display);
-  XCloseDisplay(Display);
-end;
-
-procedure TXMouseInput.DoMove(ScreenX, ScreenY: Integer);
-var
-  Display: PDisplay;
-begin
-  Display := XOpenDisplay(nil);
-  XTestFakeMotionEvent(Display, 0, ScreenX, ScreenY, 0);
-  XFlush(Display);
-  XCloseDisplay(Display);
-end;
-
-procedure TXMouseInput.DoUp(Button: TMouseButton);
-var
-  Display: PDisplay;
-begin
-  Display := XOpenDisplay(nil);
-  XTestFakeButtonEvent(Display, MouseButtonToXButton[Button], False, 0);
-  XFlush(Display);
-  XCloseDisplay(Display);
-end;
-
-procedure TXMouseInput.DoScrollUp;
-var
-  Display: PDisplay;
-begin
-  Display := XOpenDisplay(nil);
-  XTestFakeButtonEvent(Display, 4, True, 0);
-  XTestFakeButtonEvent(Display, 4, False, 0);
-  XFlush(Display);
-  XCloseDisplay(Display);
-end;
-
-procedure TXMouseInput.DoScrollDown;
-var
-  Display: PDisplay;
-begin
-  Display := XOpenDisplay(nil);
-  XTestFakeButtonEvent(Display, 5, True, 0);
-  XTestFakeButtonEvent(Display, 5, False, 0);
-  XFlush(Display);
-  XCloseDisplay(Display);
-end;
-
-end.
-

+ 1 - 1
supergauge.pas

@@ -17,7 +17,7 @@
   Needed to split off as changes broke compatibility badly
   Needed to split off as changes broke compatibility badly
 
 
 ***************************** END CONTRIBUTOR(S) *****************************}
 ***************************** END CONTRIBUTOR(S) *****************************}
-unit supergauge;
+unit SuperGauge;
 
 
 {$I bgracontrols.inc}
 {$I bgracontrols.inc}
 
 

+ 1 - 1
supergaugecommon.pas

@@ -17,7 +17,7 @@
   Needed to split off as changes broke compatibility badly
   Needed to split off as changes broke compatibility badly
 
 
 ***************************** END CONTRIBUTOR(S) *****************************}
 ***************************** END CONTRIBUTOR(S) *****************************}
-unit supergaugecommon;
+unit SuperGaugeCommon;
 
 
 {$I bgracontrols.inc}
 {$I bgracontrols.inc}
 
 

+ 1656 - 0
superspinner.pas

@@ -0,0 +1,1656 @@
+// SPDX-License-Identifier: LGPL-3.0-linking-exception
+{
+  The BGRASpinner is basically a spinner that just spins like an encoder pulse
+  wheel. You can set many specific details to render and operate this control.
+
+  Remeber you capture the pulses as the knob spins, it does retain the angular
+  position, but typically that is not really used. Events for key operations
+  including movement, wrapping, etc.
+
+  In addition specific events for clicking on the center button (if enabled) or
+  the spinner area if desired.
+
+  When using these controls it best to have the form scaling set to FALSE, since
+  some aspects of the compoent reference SIZE of the client area and scaling
+  will update your sizes of Width and Height, good luck.
+
+  Lastly the Resolution of the spinner can be controled, but since angular control
+  or trying to set it for all conditions became a problem, the settings are
+  from Higest to Lowest. Highest being maximum resolution of the mouse movements
+  and Lowest makes it more like an old iPod with larger movements (clicks).
+}
+{******************************* CONTRIBUTOR(S) ******************************
+- Sandy Ganz | [email protected]
+  02/20/2025 - Begat conversion from BGRASpinner, loads of changes to support
+               the way a Spinner works, new events and props. Updated code style
+               to be more similar to SuperGauge.
+***************************** END CONTRIBUTOR(S) *****************************}
+
+unit SuperSpinner;
+
+{$I bgracontrols.inc}
+
+interface
+
+uses
+  Classes, SysUtils, {$IFDEF FPC}LResources,{$ENDIF} Forms, Controls, Graphics,
+  {$IFNDEF FPC}BGRAGraphics, GraphType, FPImage, {$ENDIF}
+  BCBaseCtrls, BGRAGradients, BGRABitmap, BGRABitmapTypes, SuperSpinnerCommon;
+
+const
+  VERSIONSTR = '1.00';          // spinner version
+  WHEEL_SPEED_FACTOR = 0.005;   // used to calculate mouse wheel speed
+
+  RESOLUTION_HIGHEST = 1;       // used for setting spinners resolution
+  RESOLUTION_HIGH = 2;          // Keeps the number of position somewhat hidden
+  RESOLUTION_HIGH_MEDIUM = 3;
+  RESOLUTION_MEDIUM = 4;
+  RESOLUTION_MEDIUM_LOW = 5;
+  RESOLUTION_LOW = 10;
+  RESOLUTION_LOWEST = 20;
+
+type
+  TSSHitType = (shtNone, shtCap, shtKnob); // for sub component hit test
+  TSSResolution = (srHighest, srHigh, srHighMedium, srMedium, srMediumLow, srLow, srLowest);
+
+  TSSpinnerPosChangedEvent = procedure(Sender: TObject; Shift: TShiftState; Value: single; MoveDir : TSSDirection) of object;
+  TSSpinnerCapClickEvent = procedure(Sender: TObject; Button: TMouseButton; Shift: TShiftState) of object;
+  TSSpinnerKnobClickEvent = procedure(Sender: TObject; Button: TMouseButton; Shift: TShiftState) of object;
+  TSSpinnerWrappedEvent = procedure(Sender: TObject; Shift: TShiftState; OldAngle, NewAngle: single; MoveDir : TSSDirection) of object;
+  TSSpinnerCapEnterEvent = procedure(Sender: TObject; Shift: TShiftState; X,Y: Integer) of object;
+  TSSpinnerCapLeaveEvent = procedure(Sender: TObject; Shift: TShiftState; X,Y: Integer) of object;
+  TSSpinnerKnobEnterEvent = procedure(Sender: TObject; Shift: TShiftState;X,Y: Integer) of object;
+  TSSpinnerKnobLeaveEvent = procedure(Sender: TObject; Shift: TShiftState; X,Y: Integer) of object;
+
+  TResolveSizes = Record
+    MinRadius: integer;
+    MinWH: integer;
+    FrameBorderWidth: integer;
+    CapRadius: integer;
+    CapEdgeThickness: integer;
+    PositionRadius: integer;
+    PositionMargin: integer;
+    PositionCenterMargin: integer;
+    PositionLineWidth: integer;
+    KnobEdgeThickness: integer;
+
+    // add anything here that might need autosize
+    // also initialize these in the constructor
+  end;
+
+  { TCustomSuperSpinner }
+
+  TCustomSuperSpinner = class(TBGRAGraphicCtrl)
+  private
+    { Private declarations }
+    FDirty: boolean;
+
+    // Settings
+
+    FAutoScale: boolean;
+    FResolvedSizes: TResolveSizes;
+    FPositionSettings: TSSPositionSettings;
+    FCapSettings: TSSCapSettings;
+    FFrameSettings: TSSFrameSettings;
+    FKnobSettings: TSSKnobSettings;
+    FMouseDownAnglePos: single;
+    FMouseDownExistingPos : single;
+    FCapMouseDown: boolean;
+    FKnobMouseDown: boolean;
+    FInCap: boolean;
+    FInKnob: boolean;
+    FSpinnerBmp: TBGRABitmap;  // Main assembled image
+    FFrameBmp: TBGRABitmap;    // Draws just the frame
+    FKnobBmp: TBGRABitmap;     // Draws just the knob
+    FCapBmp: TBGRABitmap;      // Draws just the cap that sits in the middle of the knob
+    FPositionBmp: TBGRABitmap; // Draws just the position (lines, finger hole)
+    FAngularPos: single;       // In RADIANS
+    FCWSkipCounter: integer;
+    FCCWSkipCounter: integer;
+    FSpinnerResolution: TSSResolution;
+    FSpinnerResolutionCount: integer;
+    FSettingAngularPos: boolean;
+    FPositionSnap: boolean;
+    FOnSpinnerPosChange: TSSpinnerPosChangedEvent;
+    FOnCapClick: TSSpinnerCapClickEvent;
+    FOnKnobClick: TSSpinnerKnobClickEvent;
+    FOnWrapped: TSSpinnerWrappedEvent;
+    FOnMouseCapEnter: TSSpinnerCapEnterEvent;
+    FOnMouseCapLeave: TSSpinnerCapLeaveEvent;
+    FOnMouseKnobEnter: TSSpinnerKnobEnterEvent;
+    FOnMouseKnobLeave: TSSpinnerKnobLeaveEvent;
+    FLocked: boolean;     // Keeps Mouse from doing most things
+    FWheelSpeed: byte;    // 0 : no wheel, 1 slowest, 255 fastest
+    FMinRadius: integer;  // Computed minimum dimension for radius of spinner including Margin
+
+    function GetAngle: single;
+    function RadPosToDeg(RadPos: single): single;
+    function DegPosToAngular(DegPos: single): single;
+    procedure SetAngle(AValue: single);
+    procedure SetPositionSnap(const AValue: boolean);
+    function CalcAngularPos(X, Y: integer) : single;
+    procedure UpdateAngularPos(Shift: TShiftState; AngularPos: single);
+    function CapHitTest(X, Y: integer): boolean;
+    function KnobHitTest(X, Y: integer): boolean;
+    function HitTest(X, Y: integer): TSSHitType;
+    procedure SetAutoScale(AValue: boolean);
+    procedure SetWheelSpeed(AValue: byte);
+    procedure SetLocked(AValue: boolean);
+    procedure SetPositionSettings(AValue: TSSPositionSettings);
+    procedure SetCapSettings(AValue: TSSCapSettings);
+    procedure SetFrameSettings(AValue: TSSFrameSettings);
+    procedure SetKnobSettings(AValue: TSSKnobSettings);
+    procedure SetResolution(const AValue: TSSResolution);
+
+  protected
+    { Protected declarations }
+
+    class function GetControlClassDefaultSize: TSize; override;
+    procedure DoChange({%H-}Sender: TObject);
+    procedure DoSetBounds(ALeft, ATop, AWidth, AHeight: Integer); override;
+    procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: integer); override;
+    procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: integer); override;
+    procedure MouseMove(Shift: TShiftState; X, Y: integer); override;
+    function GetMinSize: integer;
+    procedure ResolveSizes;
+    procedure Paint; override;
+    procedure DrawFrame;
+    procedure DrawKnob;
+    procedure DrawCap;
+    procedure DrawPosition;
+
+    function DoMouseWheel(Shift: TShiftState; WheelDelta: integer; MousePos: TPoint): boolean; override;
+    procedure MouseWheelPos({%H-}Shift: TShiftState; WheelDelta: integer); virtual;
+
+  public
+    { Public declarations }
+
+    constructor Create(AOwner: TComponent); override;
+    destructor Destroy; override;
+
+  public
+    { Streaming }
+
+    {$IFDEF FPC}
+    procedure SaveToFile(AFileName: string);
+    procedure LoadFromFile(AFileName: string);
+    {$ENDIF}
+    procedure OnFindClass({%H-}Reader: TReader; const AClassName: string; var ComponentClass: TComponentClass);
+    procedure Bump(Direction: TSSDirection; Degrees: single);
+    procedure Spin(Direction: TSSDirection; Degrees: single; Count: integer; ProcessMessages: Boolean = True);
+
+  published
+    { Published declarations }
+
+    property AutoScale: boolean read FAutoScale write SetAutoScale default False;
+    property PositionSettings: TSSPositionSettings read FPositionSettings write SetPositionSettings;
+    property CapSettings: TSSCapSettings read FCapSettings write SetCapSettings;
+    property FrameSettings: TSSFrameSettings read FFrameSettings write SetFrameSettings;
+    property KnobSettings: TSSKnobSettings read FKnobSettings write SetKnobSettings;
+    property PositionSnap: boolean read FPositionSnap write SetPositionSnap default False;
+    property Angle: single read GetAngle write SetAngle nodefault;
+    property SpinResolution: TSSResolution read FSpinnerResolution write SetResolution default srHighest;
+    property WheelSpeed: byte read FWheelSpeed write SetWheelSpeed default 0;
+    property Locked: boolean read FLocked write SetLocked default False; // TODO : Check if we need to cancel mouse movement, etc
+    property OnPosChanged: TSSpinnerPosChangedEvent read FOnSpinnerPosChange write FOnSpinnerPosChange;
+    property OnCapClick: TSSpinnerCapClickEvent read FOnCapClick write FOnCapClick;
+    property OnKnobClick: TSSpinnerKnobClickEvent read FOnKnobClick write FOnKnobClick;
+    property OnWrapped: TSSpinnerWrappedEvent read FOnWrapped write FOnWrapped;
+    property OnMouseCapEnter: TSSpinnerCapEnterEvent read FOnMouseCapEnter write FOnMouseCapEnter;
+    property OnMouseCapLeave: TSSpinnerCapLeaveEvent read FOnMouseCapLeave write FOnMouseCapLeave;
+    property OnMouseKnobEnter: TSSpinnerKnobEnterEvent read FOnMouseKnobEnter write FOnMouseKnobEnter;
+    property OnMouseKnobLeave: TSSpinnerKnobLeaveEvent read FOnMouseKnobLeave write FOnMouseKnobLeave;
+    property OnMouseWheel;
+    property OnClick;
+    property OnDblClick;
+    property OnMouseDown;
+    property OnMouseUp;
+    property OnMouseMove;
+    property OnMouseEnter;
+    property OnMouseLeave;
+    property Visible;
+  end;
+
+  { TSuperSpinner }
+
+  TSuperSpinner = class(TCustomSuperSpinner)
+  private
+    { Private declarations }
+  protected
+    { Protected declarations }
+  public
+    { Public declarations }
+  published
+    { Published declarations }
+
+    property Anchors;
+    property Color default clNone;
+    property Hint;
+    property ShowHint;
+  end;
+
+  {$IFDEF FPC}
+        procedure Register;
+  {$ENDIF}
+
+implementation
+
+uses Math;
+
+{$IFDEF FPC}
+procedure Register;
+begin
+  RegisterComponents('BGRA Controls', [TSuperSpinner]);
+end;
+{$ENDIF}
+
+{ TCustomSuperSpinner }
+
+constructor TCustomSuperSpinner.Create(AOwner: TComponent);
+begin
+  inherited Create(AOwner);
+
+  // remember if form is scaled CX, CY values will be too!
+  // this may not do anything!!!
+
+  with GetControlClassDefaultSize do
+    SetInitialBounds(0, 0, CX, CY);
+
+  // Position Settings
+
+  FPositionSettings := TSSPositionSettings.Create;
+  FPositionSettings.OnChange := DoChange;
+
+  // Spinner Cap Settings
+
+  FCapSettings := TSSCapSettings.Create;
+  FCapSettings.OnChange := DoChange;
+
+  // Frame Settings
+
+  FFrameSettings := TSSFrameSettings.Create;
+  FFrameSettings.OnChange := DoChange;
+
+  // Knob Settings
+
+  FKnobSettings := TSSKnobSettings.Create;
+  FKnobSettings.OnChange := DoChange;
+
+  // Bitmaps
+
+  FFrameBmp := TBGRABitmap.Create;
+  FKnobBmp := TBGRABitmap.Create;
+  FCapBmp := TBGRABitmap.Create;
+  FPositionBmp := TBGRABitmap.Create;
+  FSpinnerBmp := TBGRABitmap.Create;
+
+  // General Inits
+
+  FOnSpinnerPosChange := nil;
+  FOnCapClick := nil;
+  FOnKnobClick := nil;
+  FOnWrapped := nil;
+  FPositionSnap := False;
+  FWheelSpeed := 0;        // 0, no wheel, 1 slowest, 255 fastest
+  FLocked := False;
+  FMouseDownAnglePos := 0;
+  FMouseDownExistingPos := 0;
+  FCapMouseDown := False;
+  FKnobMouseDown := False;
+  FSettingAngularPos := False;
+  FInCap := False;
+  Color := clNone;
+  FCWSkipCounter := 0;
+  FCCWSkipCounter := 0;
+  FSpinnerResolutionCount := RESOLUTION_HIGHEST;  // how many clicks it takes to make a revolution
+  SetAngle(0);      // Does NOT call any events
+  FMinRadius := 0;  // Can't know just yet
+
+  // set up baseline values from the defaults, good starting point any-a-ways
+
+  FResolvedSizes.MinRadius := 0;  // can't know MinRadius or MinWH yet, not resolved
+  FResolvedSizes.MinWH := 0;
+  FResolvedSizes.FrameBorderWidth := FFrameSettings.BorderWidth;
+  FResolvedSizes.CapRadius := FCapSettings.Radius;
+  FResolvedSizes.CapEdgeThickness := FCapSettings.EdgeThickness;
+  FResolvedSizes.PositionRadius := FPositionSettings.Radius;
+  FResolvedSizes.PositionMargin := FPositionSettings.Margin;
+  FResolvedSizes.PositionCenterMargin := FPositionSettings.CenterMargin;
+  FResolvedSizes.PositionLineWidth := FPositionSettings.LineWidth;
+  FResolvedSizes.KnobEdgeThickness := FKnobSettings.EdgeThickness;
+
+  FDirty := True;   // Always force initial paint/draw on everything!
+end;
+
+destructor TCustomSuperSpinner.Destroy;
+begin
+  // Free up the bitmaps
+
+  FSpinnerBmp.Free;
+  FPositionBmp.Free;
+  FFrameBmp.Free;
+  FKnobBmp.Free;
+  FCapBmp.Free;
+
+  // Handlers (May not be needed, but good idea)
+
+  FOnSpinnerPosChange := nil;
+  FOnCapClick := nil;
+  FOnKnobClick := nil;
+  FOnWrapped := nil;
+
+  // Position Settings
+
+  FPositionSettings.OnChange := nil;
+  FPositionSettings.Free;
+
+  // Cap Settings
+
+  FCapSettings.OnChange := nil;
+  FCapSettings.Free;
+
+  // Frame Settings
+
+  FFrameSettings.OnChange := nil;
+  FFrameSettings.Free;
+
+  // Knob Settings
+
+  FKnobSettings.OnChange := nil;
+  FKnobSettings.Free;
+
+  inherited Destroy;
+end;
+
+// Override the base class which has a rectangular dimension
+
+class function TCustomSuperSpinner.GetControlClassDefaultSize: TSize;
+begin
+  // Note the preferred size for the control is 150x150, however in highdpi modes
+  // on windows (maybe others) the control is scaled since the by default the forms
+  // scale will affect the actual value on creation. So as an example, Windows 11,
+  // 4k monitor, 150% scaling (windows settings), will cause the component to be
+  // created and rendered with the size of 150x150. So these numbers get scaled
+  // UP in this instance. If you run the scaling on Windows 11 at 100%, settings
+  // after LCL does it's business is 100x100. This is tricky since some spinner
+  // setting are NOT referenced by the size of the component but by pixels. So
+  // the Cap for example is in non-scaled pixels, lines for the position is
+  // based on component width so kinda' works OK, but not the cap. I remember
+  // when pixels were just pixels...
+
+  Result.CX := 100;
+  Result.CY := 100;
+end;
+
+function TCustomSuperSpinner.GetMinSize: integer;
+begin
+  // Take the smallest width or height so we can use for max size spinner
+
+  if ClientWidth < ClientHeight then
+    Exit(ClientWidth)
+  else
+    Exit(ClientHeight);
+end;
+
+procedure TCustomSuperSpinner.ResolveSizes;
+var
+  scale: single;
+
+begin
+  // Compute the size of the drawing elements of the spinner based
+  // on the FMinRadius size. If AutoScale is enabled for the control
+  // will calculate the drawing elements needed. If not will return
+  // the correct properties so all the testing for the AutoScale
+  // option setting is done here.
+
+  // Drawing sized based on proportions of the DEFAULT component values and
+  // may not always look right based on settings of cap, position, borders, etc.
+
+  // Get the minimum size for the drawing of the spinner
+
+  // Todo : Not sure if FMinRadius belongs here...
+
+  FResolvedSizes.MinWH := GetMinSize;
+  FResolvedSizes.MinRadius := FResolvedSizes.MinWH div 2;
+  FMinRadius := FResolvedSizes.MinRadius;
+  scale := FResolvedSizes.MinWH / 150.0;
+
+  if FAutoScale then
+  begin
+    // AutoScale based on 150x150 spinner size. Computes ratios from that to any size
+    // Will it always look good? Hard to say, but can use break points on sizes to
+    // also help with some edge cases like tiny and large if needed
+
+    FResolvedSizes.FrameBorderWidth := Round(FFrameSettings.BorderWidth * scale);
+    FResolvedSizes.CapRadius := Round(FCapSettings.Radius * scale);
+    FResolvedSizes.CapEdgeThickness := Round(FCapSettings.EdgeThickness * scale);
+    FResolvedSizes.PositionRadius := Round(FPositionSettings.Radius * scale);
+    FResolvedSizes.PositionMargin := Round(FPositionSettings.Margin * scale);
+    FResolvedSizes.PositionCenterMargin := Round(FPositionSettings.CenterMargin * scale);
+    FResolvedSizes.PositionLineWidth := Round(FPositionSettings.LineWidth * scale);
+    FResolvedSizes.KnobEdgeThickness := Round(FKnobSettings.EdgeThickness * scale);
+  end
+    else
+      begin
+        // Easy, not scaling
+
+        FResolvedSizes.FrameBorderWidth := FFrameSettings.BorderWidth;
+        FResolvedSizes.CapRadius := FCapSettings.Radius;
+        FResolvedSizes.CapEdgeThickness := FCapSettings.EdgeThickness;
+        FResolvedSizes.PositionRadius := FPositionSettings.Radius;
+        FResolvedSizes.PositionMargin := FPositionSettings.Margin;
+        FResolvedSizes.PositionCenterMargin := FPositionSettings.CenterMargin;
+        FResolvedSizes.PositionLineWidth := FPositionSettings.LineWidth;
+        FResolvedSizes.KnobEdgeThickness := FKnobSettings.EdgeThickness
+      end;
+end;
+
+procedure TCustomSuperSpinner.SetAutoScale(AValue: boolean);
+begin
+  if FAutoScale = AValue then
+    Exit;
+
+  FAutoScale := AValue;
+  FDirty := True;
+  DoChange(self);
+end;
+
+procedure TCustomSuperSpinner.SetPositionSettings(AValue: TSSPositionSettings);
+begin
+  if FPositionSettings = AValue then
+    Exit;
+
+  FPositionSettings := AValue;
+  FPositionSettings.Dirty := True;
+  DoChange(self);
+end;
+
+procedure TCustomSuperSpinner.SetCapSettings(AValue: TSSCapSettings);
+begin
+  if FCapSettings = AValue then
+    Exit;
+
+  FCapSettings := AValue;
+  FCapSettings.Dirty := True;
+  DoChange(self);
+end;
+
+procedure TCustomSuperSpinner.SetFrameSettings(AValue: TSSFrameSettings);
+begin
+  if FFrameSettings = AValue then
+    Exit;
+
+  FFrameSettings := AValue;
+  FFrameSettings.Dirty := True;
+  DoChange(self);
+end;
+
+procedure TCustomSuperSpinner.SetKnobSettings(AValue: TSSKnobSettings);
+begin
+  if FKnobSettings = AValue then
+    Exit;
+
+  FKnobSettings := AValue;
+  FKnobSettings.Dirty := True;
+  DoChange(self);
+end;
+
+procedure TCustomSuperSpinner.DoChange(Sender: TObject);
+begin
+  Invalidate;
+end;
+
+// Handler to force redraw when in design mode
+
+procedure TCustomSuperSpinner.DoSetBounds(ALeft, ATop, AWidth, AHeight: Integer);
+begin
+  inherited;
+  FDirty := true; // Called on Resize of component
+end;
+
+function TCustomSuperSpinner.RadPosToDeg(RadPos: single): single;
+begin
+  // helper to convert AnglePos in radians to degrees, wraps as needed
+
+  Result := RadPos * 180 / Pi;
+
+  if Result < 0 then
+    Result := Result + 360;
+
+  Result := 270 - Result;  // adjusts for screen coords
+
+  if Result < 0 then
+    Result := Result + 360;
+
+  if Result > 360 then
+    Result := Result - 360;
+
+end;
+
+function TCustomSuperSpinner.DegPosToAngular(DegPos: single): single;
+begin
+  // helper to convert Angle in degrees to radians, wraps as needed
+  // 3 * pi/2 = 270 degrees, degs to radians = degs * pi/180
+
+  Result := 3 * Pi / 2 - DegPos * Pi / 180;
+
+  if Result > Pi then
+    Result := Result - (2 * Pi);
+
+  if Result < -Pi then
+    Result := Result + (2 * Pi);
+end;
+
+procedure TCustomSuperSpinner.SetWheelSpeed(AValue: byte);
+begin
+  // Sets the mouse wheel speed
+
+  FWheelSpeed := AValue;
+end;
+
+procedure TCustomSuperSpinner.SetLocked(AValue: boolean);
+begin
+  // If we are locking, this may cause some issues as we are disabling
+  // some of the mouse control. So reset back to a clean state if needed
+
+  if AValue = FLocked then
+    Exit;
+
+  FLocked := AValue;
+
+  // if we are not locked now we should reset stuff to a clean state.
+  // this MIGHT be needed if the lock happens while clicking or moving
+  // in the spinner. The user should be starting fresh if this happens
+  // (Or so I think)
+
+  if not FLocked then
+    begin
+
+      // Reset Skip Counters
+
+      FCWSkipCounter := 0;
+      FCCWSkipCounter := 0;
+
+      // If mouse was down in cap or knob reset too, we lose that tracking
+
+      FCapMouseDown := False;
+      FKnobMouseDown := False;
+      FInCap := False;
+      FInKnob := False;
+
+      // Finally stop any mouse tracking
+
+      FSettingAngularPos := False;
+
+    end;
+end;
+
+procedure TCustomSuperSpinner.SetAngle(AValue: single);
+begin
+  // Sets the angle (in Degrees) of the Knobs position. This
+  // will NOT call the OnPosChange event, and not affected by
+  // the spinners resolution
+
+  if DegPosToAngular(AValue) = FAngularPos then
+    Exit;
+
+  FAngularPos := DegPosToAngular(AValue);
+  DoChange(self);
+end;
+
+function TCustomSuperSpinner.GetAngle: single;
+begin
+  Result := RadPosToDeg(FAngularPos);
+end;
+
+// Sets if the spinner position should snap to the mouse when clicked
+// otherwise will allow the mouse to spin the knob without first 'snapping'
+// to the mouse down position
+
+procedure TCustomSuperSpinner.SetPositionSnap(const AValue: boolean);
+begin
+  if FPositionSnap = AValue then
+    exit;
+
+  FPositionSnap := AValue;
+  DoChange(self);
+end;
+
+procedure TCustomSuperSpinner.SetResolution(const AValue: TSSResolution);
+begin
+  if AValue = FSpinnerResolution then
+    Exit;
+
+  FSpinnerResolution := AValue;
+
+  // In general It's best to have it at srHighest. If you want it more like
+  // an old iPod spinner try Low or Lowest. These are essentially
+  // messing with the number of clicks per revolution, but I decided
+  // not to try to calculate an exact value so these are just abstracting
+  // that
+
+  case AValue of
+    srHighest: FSpinnerResolutionCount := RESOLUTION_HIGHEST;
+    srHigh: FSpinnerResolutionCount := RESOLUTION_HIGH;
+    srHighMedium: FSpinnerResolutionCount := RESOLUTION_HIGH_MEDIUM;
+    srMedium: FSpinnerResolutionCount :=RESOLUTION_MEDIUM;
+    srMediumLow: FSpinnerResolutionCount := RESOLUTION_MEDIUM_LOW;
+    srLow: FSpinnerResolutionCount := RESOLUTION_LOW;
+    srLowest: FSpinnerResolutionCount := RESOLUTION_LOWEST;
+  end;
+end;
+
+function TCustomSuperSpinner.CalcAngularPos(X, Y: integer) : single;
+begin
+  // returns -pi to pi based on the XY of the mouse in the client box
+
+  Result := ArcTan2(-1 * (Y - ClientHeight / 2) / ClientHeight, (X - ClientWidth / 2) / ClientWidth);
+end;
+
+procedure TCustomSuperSpinner.Bump(Direction: TSSDirection; Degrees: single);
+var
+  Offset: single;
+
+begin
+  if (Degrees < 0) or (Degrees > 359.99999) then
+    Exit;
+
+  Offset := GetAngle();
+
+  if Direction = sdCW then
+    Offset := Offset + Degrees
+  else
+    Offset := Offset - Degrees;
+
+  // Force move, since UpdateAngularPos() PRE-Increments the Skip counters we
+  // Must be one less or this trick won't work
+  //
+  // Since we may be forcing a specific degree move here, it can
+  // shift the position of the spinner to an off increment angle than
+  // the mouse is moving since that angle to bump to is arbitrary.
+  // In general Spin and Bump are not great to use for this reason unless needed.
+
+  // Must invalidate both as we don't know the current direction it's moving
+  // so one will get reset, the other will trigger, so always works.
+
+  FCWSkipCounter := FSpinnerResolutionCount - 1;
+  FCCWSkipCounter := FCWSkipCounter;
+
+  UpdateAngularPos([], DegPosToAngular(Offset));
+end;
+
+procedure TCustomSuperSpinner.Spin(Direction: TSSDirection; Degrees: single; Count: integer; ProcessMessages: Boolean = True);
+var
+  i, processRate: integer;
+
+begin
+  // This is something that likely should not be used more so then bump. It is easy to animate
+  // a movement to a number of events triggered. This is tricky as you need to
+  // call ProcessMessages or the update of the spinner will/could show up just
+  // at the finish point since it will just do it fast if no movement will be shown.
+  // Some tricky-ness can be done, for example if you want to do a Count of 100 at
+  // 1 Degree per, that will be quickly animated, if you want to slow it down
+  // you can try 0.1 Degrees per, and 1000 for the Count and only process
+  // 1 out of 10 movement events to make it the same, the spinner will go slower
+  // as it's rendering at a higher resolution, this is a hack for sure.
+
+  // Degrees will be validated in Bump()
+
+  if (Count < 1) then
+    Exit;
+
+  // Super Hack
+  //
+  // Try to keep fast for fine moves or moves with a lot of steps so looks nice
+  // Tries to keep down calls to ProcessMessages, but Still update the display
+  // As the Count goes up or the Degree granularity goes up (smaller Degree) the
+  // processRate is smaller to have more screen updated UNLESS the count is
+  // just too large, and then it slows down a lot. This is all testing
+  // on a fast machine, fast video, Low or high res, lower speed CPU or Video
+  // would totally impact this code.
+  //
+  // SUPER HACK
+
+  if (Degrees < 1.0) or (Count < 25) then
+    processRate := 2  // process a lot of screen updates
+  else
+    processRate := 4; // Less
+
+  // If we have a lot of resolution can turn down the process rate a lot
+
+  if (Count / Degrees) > 500 then
+    processRate := 16;  // A lot less since movement is very small, not worth a lot of updates
+
+  for i := 0 to Count - 1  do
+  begin
+
+    // Bump will call the event handler for movement for each
+
+    Bump(Direction, Degrees);
+
+    // Call ProcessMessages at a slower rate for small Degrees or large Count
+    // Not sure if their is a better way to move and update the visuals. This
+    // may not be needed IF the PosChanged event handler actually does a lot of
+    // stuff, but I think (on Windows) the drawing of the spinners are all
+    // coalesced until the message loop is caught up and only the last update
+    // to the screen is seen. Application. ProcessMessages an optional call
+    // and can let the handler deal with it as needed.
+
+    if (i mod processRate = 0) and ProcessMessages then
+      Application.ProcessMessages;
+  end;
+end;
+
+procedure TCustomSuperSpinner.UpdateAngularPos(Shift: TShiftState; AngularPos: single);
+var
+  Direction: TSSDirection;
+  currAngle, newAngle: single;
+
+begin
+  // AngularPos is in Rads, Wrap range if needed (Radians wrap)
+
+  if AngularPos > Pi then
+    AngularPos := AngularPos - (2 * Pi);
+
+  if AngularPos < -Pi then
+    AngularPos := AngularPos + (2 * Pi);
+
+  // See which direction we are going, check start (Current)
+  // is less than the new. This will give us the direction
+  // This works EXCEPT at wrap around from 359 to 0 and 0 to 359
+  // so either bring in the X, Y and do it sector by sector or
+  // hack and say that if in the lower 2 sectors and track around
+  // that. The 270 is a big delta, and unlikely, so unless a very large
+  // update it works great. Remember that setting the position by
+  // Angle does NOT cause the handler to be called ONLY this update method.
+
+  currAngle := GetAngle();  // Degs
+  newAngle := RadPosToDeg(AngularPos); // Degs
+
+  // need this for skipping first
+
+  if newAngle - currAngle > 270 then // crossing CCW over 359 to 0
+    Direction := sdCCw
+  else
+      if currAngle - newAngle > 270 then // crossing CW over 0 to 359
+        Direction := sdCW
+      else
+          if currAngle < newAngle then
+            Direction := sdCW
+          else
+            Direction := sdCCW;
+
+  // Must take into account direction changes so we can
+  // have fresh counts in the correct direction or it
+  // would have an inconsistant value if moving back and forth!
+
+  if Direction = sdCW then
+  begin
+    Inc(FCWSkipCounter);
+    FCCWSkipCounter := 0;
+  end
+  else
+    begin
+      Inc(FCCWSkipCounter);
+      FCWSkipCounter := 0;
+    end;
+
+  // 1 is never skip since we pre-inc the numbers above, 2 is skip every other and so on
+
+  if (FCWSkipCounter = FSpinnerResolutionCount) or (FCCWSkipCounter = FSpinnerResolutionCount) then
+  begin
+
+    // We are moving, so can reset BOTH, and set the new position, then update
+
+    FCWSkipCounter := 0;
+    FCCWSkipCounter := 0;
+
+    // Need to check wrap here before we update the positions
+
+    if newAngle - currAngle > 270 then // crossing CCW over 359 to 0
+    begin
+      if Assigned(FOnWrapped) then
+        FOnWrapped(Self, Shift, currAngle, newAngle, sdCCW);
+    end
+      else
+        if currAngle - newAngle > 270 then // crossing CW over 0 to 359
+        begin
+          if Assigned(FOnWrapped) then
+            FOnWrapped(Self, Shift, currAngle, newAngle, sdCW);
+        end;
+
+    FAngularPos := AngularPos;
+
+    if Assigned(FOnSpinnerPosChange) then
+      FOnSpinnerPosChange(Self, Shift, Angle, Direction);
+
+    DoChange(self);
+  end;
+end;
+
+procedure TCustomSuperSpinner.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: integer);
+var
+  hitIn: TSSHitType;
+
+begin
+  inherited MouseDown(Button, Shift, X, Y);
+
+  if FLocked then
+    Exit;
+
+  // See if anything clicked on the cap, and then the knob
+  // If we do have a Cap hit and it's enabled, then we
+  // do not need to check the Knob, as it can't be in it!
+
+  // HEADS UP : If the knob porting is not clicked on, ie, blank client area,
+  // the border, the cap (and future stuff) the state of FSettingAngularPos
+  // will NOT change. Mouse movement once started does NOT look at any boundries
+
+  hitIn := HitTest(X, Y);
+
+  if hitIn = shtCap then
+    FCapMouseDown := True
+  else
+    if hitIn = shtKnob then
+      FKnobMouseDown := True;
+
+  // if user has pressed the left mouse button, then start tracking
+  // skip any movement if mouse down in the cap (button enabled)
+
+  if (Button = mbLeft) and (not FCapMouseDown) and (FKnobMouseDown) then
+  begin
+    FSettingAngularPos := True; // start of dragging the spinner, update the state
+
+    // save the angle of the mouse down, this will later
+    // be used to offset to the current position with existing angle
+    // to allow the user to grab anywhere on the knob and spin
+
+    FMouseDownAnglePos := CalcAngularPos(X, Y);
+
+    if FPositionSnap then
+    begin
+
+      // If we have position snap enabled, when the mouse clicks on it, will spin
+      // the spinners angle to it, position to it, but will NOT update anything
+      // else or call the handler for movement
+
+      FAngularPos := FMouseDownAnglePos;
+      DoChange(self);
+    end;
+
+    FMouseDownExistingPos := FAngularPos; // after update always set this
+  end;
+end;
+
+procedure TCustomSuperSpinner.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: integer);
+var
+  hitIn: TSSHitType;
+
+begin
+  inherited MouseUp(Button, Shift, X, Y);
+
+  if FLocked then
+    Exit;
+
+  if Button = mbLeft then
+    FSettingAngularPos := False;  // Change state to not moving
+
+  // can have different mouse buttons click on the cap, catch then all I guess
+  // let the OnClicks sort it out if needed
+
+  // see if we are still in the cap, if so call back as this is a OnClick
+  // style event. Always clear the state in anycase as a left mouse up
+  // should stop tracking the down events
+
+  hitIn := HitTest(X, Y);
+
+  if FCapMouseDown and (hitIn = shtCap) then
+  begin
+    if Assigned(FOnCapClick) then
+      FOnCapClick(Self, Button, Shift);
+  end
+    else
+      if FKnobMouseDown and (hitIn = shtKnob) then
+      begin
+        if Assigned(FOnKnobClick) then
+          FOnKnobClick(Self, Button, Shift);
+      end;
+
+  FCapMouseDown := False; // wipes all potential mouse downs
+  FKnobMouseDown := False;
+end;
+
+procedure TCustomSuperSpinner.MouseMove(Shift: TShiftState; X, Y: integer);
+var
+  hitIn: TSSHitType;
+
+begin
+  inherited MouseMove(Shift, X, Y);
+
+  if FLocked then
+    Exit;
+
+  // being nice, here is a specific event for the cap/knob enter/exit
+  // can do some nice stuff with it like highlight when over them
+
+  hitIn := HitTest(X, Y);
+
+  // See what's cooking with the cap first. If cap disabled always False
+
+  if FInCap <> (hitIn = shtCap) then
+  begin
+    if FInCap then
+    begin
+      // we are in the cap, then exiting
+
+      FInCap := False;
+      if Assigned(FOnMouseCapLeave) then
+          FOnMouseCapLeave(Self, Shift, X, Y);
+    end
+    else
+      begin
+        // Out of the cap, then entering
+
+        FInCap := True;
+        if Assigned(FOnMouseCapEnter) then
+            FOnMouseCapEnter(Self, Shift, X, Y);
+      end;
+  end;
+
+  // now the Knob part
+
+  if FInKnob <> (hitIn = shtKnob) then
+  begin
+    if FInKnob then
+    begin
+      // we are in the cap, then exiting
+
+      FInKnob := False;
+      if Assigned(FOnMouseKnobLeave) then
+          FOnMouseKnobLeave(Self, Shift, X, Y);
+    end
+    else
+      begin
+        // Out of the cap, then entering
+
+        FInKnob := True;
+        if Assigned(FOnMouseKnobEnter) then
+            FOnMouseKnobEnter(Self, Shift, X, Y);
+      end;
+
+  end;
+
+  if FSettingAngularPos then
+  begin
+
+    // Move the spinner, takes into account the MouseDown values
+    // to either snap the wheels angle to the mouse or ignore. This
+    // is all done in MouseDown based on the PositionSnap setting.
+
+    FKnobMouseDown := False; // Cancel KnobMouseDown so we don't allow click if moving
+    UpdateAngularPos(Shift, FMouseDownExistingPos + CalcAngularPos(X, Y) - FMouseDownAnglePos);
+  end;
+end;
+
+function TCustomSuperSpinner.CapHitTest(X, Y: integer) : boolean;
+begin
+  // Easy check, if mouse distance from center of client is
+  // within center radius (also at center of client) we can
+  // hit test the cap circle
+
+  // see if we need to even do anything, also prevents error if radius is 0
+
+  if FCapSettings.Style = csNone then   // safe-tee
+    Exit(False);
+
+  // If the distance of the mouse to center is less than the radius of the cap
+  // and the edge we are in the cap, remember dealing with the RADIUS not Diameter
+  // The center of the measure is not 0,0 but half the the min size. So if the
+  // size of the MinRadius is 75, the line is measured from 75,75 to the Mouse
+  // X,Y which is in terms of the client area. Tricky but works. Similar for
+  // the Knob. We need to use the client sizes for width and height here to get the center!
+
+Result := Sqrt(((FSpinnerBmp.Width div 2 - X)** 2 + (FSpinnerBmp.Height div 2 - Y)** 2))
+      <= (FResolvedSizes.CapRadius + FResolvedSizes.CapEdgeThickness - 1);
+end;
+
+function TCustomSuperSpinner.KnobHitTest(X, Y: integer) : boolean;
+begin
+  // if are using the cap as a button, and it's a hit,
+  // get out, we don't count that as a knob hit as
+  // it's excluded in this case, so a bit slower to call this first
+  // but what can you do unless you want to do more math below...
+
+  if CapHitTest(X, Y) then
+   Exit(False);
+
+  // Get the current Radius of the knob, GetMinRadius returns the smaller of
+  // width/height of the client and less the frame width.
+
+  // Todo : May just use FMinRadius as it must be calculated if we had
+  // a paint event done. So might not need to recompute
+  // shortRadius := FMinRadius - FFrameSettings.BorderWidth;
+
+
+  // Test if the distance from the mouse to the center is less then the short radius
+  // we are in the knob. Remeber we tested for cap and if in that we are not here
+  // as the radius must be longer then the cap! We need to use the client sizes
+  // for width and height here to get the center!
+
+    Result := Sqrt(((FSpinnerBmp.Width div 2 - X)** 2 + (FSpinnerBmp.Height div 2 - Y)** 2))
+          <= (FResolvedSizes.MinRadius - FResolvedSizes.FrameBorderWidth);
+end;
+
+// This done for future expansion of sub items in a spinner, for now simple
+
+function TCustomSuperSpinner.HitTest(X, Y: integer) : TSSHitType;
+begin
+  // if are using the cap as a button, and it's a hit all done, only one
+  // can be hit at a time
+
+  if CapHitTest(X, Y) then
+    Exit(shtCap);
+
+  // now on with the knob
+
+  if KnobHitTest(X, Y) then
+    Exit(shtknob);
+
+  Result := shtNone;
+end;
+
+procedure TCustomSuperSpinner.Paint;
+var
+  offsetX, offsetY: integer;
+
+begin
+  if (ClientWidth = 0) or (ClientHeight = 0) then
+    exit;
+
+  // Generally all objects should draw in the center of the client area, so
+  // the spinner is ALWAYS square. IF the initializebitmap routine is used
+  // it also has min size for width and height, but you still need to div/2
+  // to get the radius, this can save a bit of calcs in loops if it can be used.
+
+  // ResolveSizes calculates a bunch of sizes for the component based on the
+  // setting of the AutoScale. It MUST be called prior to paint so all needed
+  // sizes and dimensions for drawing the elements are resolved!
+
+  ResolveSizes;
+
+  // IF the component is resized OR moved (this is safer) we
+  // need to make sure EVERYTHING redraws. The base class will
+  // also do it's own thing to invalidate and redraw it all.
+
+  if FDirty then
+  begin
+    FPositionSettings.Dirty := True;  // sjg - this is ALWAYS dirty for drawing
+    FCapSettings.Dirty := True;
+    FFrameSettings.Dirty := True;
+    FKnobSettings.Dirty := True;
+
+    FDirty := False;  // everything here marked, so can reset
+  end;
+
+  // no cost on SetSize if same sizes!
+
+  FSpinnerBmp.SetSize(Width, Height);
+
+  // If the spinner color is clNone then we start with a transparent background,
+  // Otherwise we start with the users color.
+
+  if Color = clNone then
+    FSpinnerBmp.Fill(BGRA(0, 0, 0, 0))  // fill transparent
+  else
+    FSpinnerBmp.Fill(ColorToBGRA(Color, 255));  // fill solid color
+
+  // If the frame changes we must dirty the knob as the frame
+  // changes could impact size of the knob
+
+  if FFrameSettings.Dirty then
+    FKnobSettings.Dirty := True;
+
+  DrawFrame;
+  FSpinnerBmp.BlendImage(0, 0, FFrameBmp, boLinearBlend);
+
+  DrawKnob;
+  offsetX := FSpinnerBmp.Width div 2  - FKnobBmp.Width div 2;
+  offsetY := FSpinnerBmp.Height div 2 - FKnobBmp.Height div 2;
+  FSpinnerBmp.BlendImage(offsetX, offsetY, FKnobBmp, boLinearBlend);
+
+  // Position is most always rendered and drawn. Could optimize
+  // by drawing the position and moving around the spinnerbmp
+  // by getting the correct position. Left as an exercise for the
+  // coder...
+
+  DrawPosition;
+  FSpinnerBmp.BlendImage(0, 0, FPositionBmp, boLinearBlend);
+
+  // Draw Cap last as it can be a nice look over lines if needed
+
+  if FCapSettings.Style <> csNone then
+  begin
+    DrawCap;
+    offsetX := FSpinnerBmp.Width div 2  - FCapBmp.Width div 2;
+    offsetY := FSpinnerBmp.Height div 2 - FCapBmp.Height div 2;
+    FSpinnerBmp.BlendImage(offsetX, offsetY, FCapBmp, boLinearBlend);
+  end;
+
+  // draw other stuff as needed here before the canvas draw
+
+  FSpinnerBmp.Draw(Canvas, 0, 0, False);
+end;
+
+procedure TCustomSuperSpinner.DrawFrame;
+var
+  Origin: TSSOrigin;
+  r: integer;
+
+begin
+  if not FFrameSettings.Dirty then
+    Exit;
+
+  FFrameSettings.Dirty := False;
+
+  // Origin has the correct Max size the radius can be!
+
+  Origin := Initializebitmap(FFrameBmp, Width, Height);
+
+  // skip doing anything further if border is 0
+
+  if FResolvedSizes.FrameBorderWidth < 1 then
+    Exit;
+
+  // Get the radius of the frame, less border so we can fit
+
+  r := FResolvedSizes.MinRadius - FResolvedSizes.FrameBorderWidth div 2 - 1;
+
+  // Draw thin antialiased border to smooth against background
+
+  FFrameBmp.EllipseAntialias(Origin.CenterPoint.x, Origin.CenterPoint.y,
+    r, r,
+    FFrameSettings.BorderColor,
+    FResolvedSizes.FrameBorderWidth);
+end;
+
+procedure TCustomSuperSpinner.DrawKnob;
+var
+  xy: integer;
+  h: single;
+  d2: single;
+  v: TPointF;
+  p: PBGRAPixel;
+  Center: TPointF;
+  yb: integer;
+  xb: integer;
+  mask: TBGRABitmap;
+  Map: TBGRABitmap;
+
+begin
+  if not FKnobSettings.Dirty then
+    Exit;
+
+  FKnobSettings.Dirty := False;
+
+  // set the knob size less the radius
+
+  xy := FResolvedSizes.MinWH;
+
+  FKnobBmp.SetSize(xy, xy);
+
+  // Clear bitmap to transparent
+
+  FKnobBmp.Fill(BGRA(0, 0, 0, 0));
+  Center := PointF(xy / 2, xy / 2);
+
+  case FKnobSettings.Style of
+  ssFlat:
+    begin // draw flat knob
+
+      // This will draw it filled with an edge, must remove both
+      // the knob's edge and the frames edge thickness to get the right size
+
+        FKnobBmp.EllipseAntialias(Center.x, Center.y,
+        FResolvedSizes.MinRadius - FResolvedSizes.FrameBorderWidth - FResolvedSizes.KnobEdgeThickness div 2,
+        FResolvedSizes.MinRadius - FResolvedSizes.FrameBorderWidth - FResolvedSizes.KnobEdgeThickness div 2,
+          FKnobSettings.EdgeColor,
+          FResolvedSizes.KnobEdgeThickness,
+          FKnobSettings.FillColor);
+    end;
+
+  ssShaded:
+    begin   // shaded knob
+
+      FKnobBmp.FillEllipseLinearColorAntialias(Center.x, Center.y,
+        FResolvedSizes.MinRadius - FResolvedSizes.FrameBorderWidth,
+        FResolvedSizes.MinRadius - FResolvedSizes.FrameBorderWidth,
+        FKnobSettings.EdgeColor,
+        FKnobSettings.FillColor);
+
+      FKnobBmp.EllipseAntialias(Center.x, Center.y,
+        FResolvedSizes.MinRadius - FResolvedSizes.FrameBorderWidth - FResolvedSizes.KnobEdgeThickness div 2,
+        FResolvedSizes.MinRadius - FResolvedSizes.FrameBorderWidth - FResolvedSizes.KnobEdgeThickness div 2,
+        FKnobSettings.EdgeColor,
+        FResolvedSizes.KnobEdgeThickness);
+    end;
+
+  ssPhong:
+    begin // Phong shaded knob
+
+      // compute spinner height map
+
+      Map := TBGRABitmap.Create(xy, xy);
+
+      for yb := 0 to xy - 1 do
+      begin
+        p := map.ScanLine[yb];
+        for xb := 0 to xy - 1 do
+        begin
+
+          // compute vector between center and current pixel
+
+          v := PointF(xb, yb) - Center;
+
+          // scale down to unit circle (with 1 pixel margin for soft border)
+
+          v.x := v.x / (xy / 2 + 1);
+          v.y := v.y / (xy / 2 + 1);
+
+          // compute squared distance with scalar product
+
+          d2 := v {$if FPC_FULLVERSION < 30203}*{$ELSE}**{$ENDIF} v;
+
+          // interpolate as quadratic curve and apply power function
+
+          if d2 > 1 then
+            h := 0
+          else
+            h := power(1 - d2, FKnobSettings.CurveExponent);
+          p^ := MapHeightToBGRA(h, 255);
+          Inc(p);
+        end;
+      end;
+
+    mask := TBGRABitmap.Create(xy, xy, BGRABlack);
+
+    // Adjust Size for frame AND knob edge. Note this is a FILL so no div 2
+
+    Mask.FillEllipseAntialias(Center.x, Center.y,
+     FResolvedSizes.MinRadius - FResolvedSizes.FrameBorderWidth - FResolvedSizes.KnobEdgeThickness +1,
+     FResolvedSizes.MinRadius - FResolvedSizes.FrameBorderWidth - FResolvedSizes.KnobEdgeThickness +1,
+     BGRAWhite);
+
+      map.ApplyMask(mask);
+      Mask.Free;
+
+      KnobSettings.FPhong.Draw(FKnobBmp, Map, 30, 0, 0, FKnobSettings.FillColor);
+      Map.Free;
+
+    // Fill the edge now
+
+    FKnobBmp.EllipseAntialias(Center.x, Center.y,
+      FResolvedSizes.MinRadius - FResolvedSizes.FrameBorderWidth - FResolvedSizes.KnobEdgeThickness div 2,
+      FResolvedSizes.MinRadius - FResolvedSizes.FrameBorderWidth - FResolvedSizes.KnobEdgeThickness div 2,
+         FKnobSettings.EdgeColor,
+        FResolvedSizes.KnobEdgeThickness);
+    end;
+  end;
+end;
+
+procedure TCustomSuperSpinner.DrawCap;
+var
+  Origin: TSSOrigin;
+  sizeWH : integer;
+  pCapEdge : integer;
+  xy: integer;
+  xyFDiv2: single;
+  h: single;
+  d2: single;
+  v: TPointF;
+  p: PBGRAPixel;
+  Center: TPointF;
+  yb: integer;
+  xb: integer;
+  mask: TBGRABitmap;
+  Map: TBGRABitmap;
+
+begin
+  // skip drawing if nothing changed
+
+  if not FCapSettings.Dirty then
+    Exit;
+
+  FCapSettings.Dirty := False;
+
+  // drawing is the size of the cap, not of the entire knob!
+
+  sizeWH := (FResolvedSizes.CapRadius + FResolvedSizes.CapEdgeThickness) * 2 + 2;
+  Origin := Initializebitmap(FCapBmp, SizeWH, SizeWH);
+
+  // can skip drawing if nothing to draw, but still needed to init the bmp
+
+  if FCapSettings.Style = csNone then
+    Exit;
+
+  pCapEdge := FResolvedSizes.CapRadius + FResolvedSizes.CapEdgeThickness div 2;
+
+  case FCapSettings.Style of
+
+    csFlat:
+      begin
+
+        // Draw the flat cap, but make sure size is similar to the shaded below or will be odd
+
+        FCapBmp.EllipseAntialias(Origin.CenterPoint.x, Origin.CenterPoint.y,
+          pCapEdge,
+          pCapEdge,
+          FCapSettings.EdgeColor,
+          FResolvedSizes.CapEdgeThickness,
+          FCapSettings.FillColor);
+      end;
+
+    csShaded:
+      begin
+
+        // Regular shading
+
+         FCapBmp.FillEllipseLinearColorAntialias(Origin.CenterPoint.x, Origin.CenterPoint.y,
+           pCapEdge,
+           pCapEdge,
+           FCapSettings.FillColor,
+           FCapSettings.EdgeColor);
+
+         // draw edge since the shading is backwards ending on fill color not Edge
+
+         FCapBmp.EllipseAntialias(Origin.CenterPoint.x, Origin.CenterPoint.y,
+           pCapEdge,
+           pCapEdge,
+           FCapSettings.EdgeColor,
+           FResolvedSizes.CapEdgeThickness);
+      end;
+
+    csPhong:
+      begin
+
+        // Phong shaded cap
+
+        // Draw a flat radius around the cap if set, must be alpha 0 or will not
+        // be an outline. Draw First, fixes some issues with Phong drawing
+
+        xy := FResolvedSizes.CapRadius * 2 ;
+        xyFDiv2 := FResolvedSizes.CapRadius;
+
+        if xy = 0 then
+          Exit;
+
+         if FResolvedSizes.CapEdgeThickness > 0 then
+           FCapBmp.EllipseAntialias(Origin.CenterPoint.x, Origin.CenterPoint.y,
+             pCapEdge - 1,  // suck in a little to make sure we are under it all
+             pCapEdge - 1,
+             FCapSettings.EdgeColor,
+             FResolvedSizes.CapEdgeThickness);
+
+          // compute knob height map
+
+          Center := PointF(xyFDiv2 , xyFDiv2);
+          Map := TBGRABitmap.Create(xy, xy);
+
+          for yb := 0 to xy - 1 do
+          begin
+            p := map.ScanLine[yb];
+            for xb := 0 to xy - 1 do
+            begin
+
+              // compute vector between center and current pixel
+
+              v := PointF(xb, yb) - Center;
+
+              // scale down to unit circle (with 1 pixel margin for soft border)
+
+              v.x := v.x / (xyFDiv2 + 1);
+              v.y := v.y / (xyFDiv2 + 1);
+
+              // compute squared distance with scalar product
+
+              d2 := v {$if FPC_FULLVERSION < 30203}*{$ELSE}**{$ENDIF} v;
+
+              // interpolate as quadratic curve and apply power function
+
+              if d2 > 1 then
+                h := 0
+              else
+                h := power(1 - d2, FCapSettings.CurveExponent);
+              p^ := MapHeightToBGRA(h, 255);
+              Inc(p);
+            end;
+          end;
+
+          // mask image round with and antialiased border
+
+          mask := TBGRABitmap.Create(xy, xy, BGRABlack);
+
+          // Shrink the size by one as the antialias gets chopped on the right edge
+          // if the image is full size. Looks nicer too.
+
+          Mask.FillEllipseAntialias(Center.x, Center.y, xyFDiv2 - 1, xyFDiv2 - 1 , BGRAWhite);
+          map.ApplyMask(mask);
+          Mask.Free;
+
+          // now draw it all
+
+          FCapSettings.FPhong.Draw(FCapBmp, Map, 30,
+                  Origin.CenterPoint.x - xy div 2, Origin.CenterPoint.y - xy div 2,
+                  FCapSettings.FillColor);
+          Map.Free;
+      end;
+
+    csOutline:
+      begin
+
+        // Just an outline
+
+        if FResolvedSizes.CapEdgeThickness > 0 then
+          FCapBmp.EllipseAntialias(Origin.CenterPoint.x, Origin.CenterPoint.y,
+            pCapEdge,
+            pCapEdge,
+            FCapSettings.EdgeColor,
+            FResolvedSizes.CapEdgeThickness);
+      end;
+  end;
+end;
+
+procedure TCustomSuperSpinner.DrawPosition;
+var
+  Center, Pos: TPointF;
+  PosColor: TBGRAPixel;
+  PosLen, x,y,xt,yt: single;
+  i, n : integer;
+
+begin
+  // Note this is mostly always be dirty, if the knob moves or a setting
+  // changes it's dirty so always, no need to currently check dirty flag
+
+  // Do some magic since we can adjust opacity with an additional property
+  // This sometimes draws different color in design vs. runtime BGRA issue??
+
+  PosColor := ColorToBGRA(ColorToRGB(FPositionSettings.FillColor), FPositionSettings.Opacity);
+
+  // set up positions for position indicator, use ResolvedSizes!
+  // Pos.X and Pos.Y should be both based on the minimum sized dimension
+
+  Center := PointF(ClientWidth / 2, ClientHeight / 2);
+  Pos.X := Cos(FAngularPos) * (FResolvedSizes.MinWH / 2);
+  Pos.Y := -Sin(FAngularPos) * (FResolvedSizes.MinWH / 2);
+
+  PosLen := VectLen(Pos);
+  Pos := Pos * ((PosLen - FResolvedSizes.PositionMargin - FResolvedSizes.PositionRadius) / PosLen);
+  Pos := Center + Pos;
+
+  // Size and Clear bitmap to transparent, keep full size bitmap
+
+  FPositionBmp.SetSize(ClientWidth, ClientHeight);
+  FPositionBmp.Fill(BGRA(0, 0, 0, 0));
+
+  case PositionSettings.Style of
+    psFilledCircle:
+      begin
+        FPositionBmp.FillEllipseAntialias(Pos.X, Pos.Y,
+            FResolvedSizes.PositionRadius, FResolvedSizes.PositionRadius,
+            PosColor);
+      end;
+
+    psHollowCircle:
+      begin
+        FPositionBmp.EllipseAntialias(Pos.X, Pos.Y,
+            FResolvedSizes.PositionRadius, FResolvedSizes.PositionRadius,
+            PosColor, FPositionSettings.LineWidth);
+      end;
+    psShaded:
+      begin
+        // Regular shading similar to Cap
+
+         FPositionBmp.FillEllipseLinearColorAntialias(Pos.X, Pos.Y,
+         FResolvedSizes.PositionRadius, FResolvedSizes.PositionRadius,
+           Poscolor,
+           FPositionSettings.EdgeColor);
+      end;
+
+    psIndentCircle:
+      begin
+        // hack to give some indented depth, Doing colors
+        // backwards to make it look nicer.
+
+        FPositionBmp.FillEllipseLinearColorAntialias(Pos.X, Pos.Y,
+            FResolvedSizes.PositionRadius, FResolvedSizes.PositionRadius,
+             PosColor, FKnobSettings.EdgeColor);
+
+        FPositionBmp.EllipseAntialias(Pos.X, Pos.Y,
+            FResolvedSizes.PositionRadius, FResolvedSizes.PositionRadius,
+             PosColor, 1);
+      end;
+
+    psLines:
+      begin
+        FPositionBmp.LineCap := pecRound; // ensure correct cap mode
+        n := FPositionSettings.LineCount;
+
+        // Skip if number of lines is 0
+
+        if n > 0 then
+          for i := 0 to n - 1 do
+          begin
+            // Center Point
+
+            x := Center.x - FResolvedSizes.PositionCenterMargin * cos((i * 360 / n) * Pi / 180 - FAngularPos - PI);
+            y := Center.y - FResolvedSizes.PositionCenterMargin * sin((i * 360 / n) * Pi / 180 - FAngularPos - PI);
+
+            // Draw to Outer Point
+
+            xt := Center.x - (FResolvedSizes.MinRadius - FResolvedSizes.PositionMargin) * cos((i * 360 / n) * Pi / 180 - FAngularPos - PI);
+            yt := Center.y - (FResolvedSizes.MinRadius - FResolvedSizes.PositionMargin)* sin((i * 360 / n) * Pi / 180 - FAngularPos - PI);
+
+            FPositionBmp.DrawLineAntialias(x, y, xt, yt, PosColor, FResolvedSizes.PositionLineWidth);
+          end;
+    end;
+  end;
+
+  // Draw outer circle border if desired, only for circle types
+
+  if (FPositionSettings.EdgeThickness > 0) and (FPositionSettings.Style <> psLines)
+        and (FPositionSettings.Style <> psNone) then
+  begin
+    FPositionBmp.EllipseAntialias(Pos.X, Pos.Y,
+          FResolvedSizes.PositionRadius + FPositionSettings.EdgeThickness div 2,
+          FResolvedSizes.PositionRadius + FPositionSettings.EdgeThickness div 2,
+          FPositionSettings.EdgeColor, FPositionSettings.EdgeThickness);
+  end;
+end;
+
+{$IFDEF FPC}
+procedure TCustomSuperSpinner.SaveToFile(AFileName: string);
+var
+  AStream: TMemoryStream;
+
+begin
+  AStream := TMemoryStream.Create;
+  try
+    WriteComponentAsTextToStream(AStream, Self);
+    AStream.SaveToFile(AFileName);
+  finally
+    AStream.Free;
+  end;
+end;
+
+procedure TCustomSuperSpinner.LoadFromFile(AFileName: string);
+var
+  AStream: TMemoryStream;
+
+begin
+  AStream := TMemoryStream.Create;
+  try
+    AStream.LoadFromFile(AFileName);
+    ReadComponentFromTextStream(AStream, TComponent(Self), OnFindClass);
+  finally
+    AStream.Free;
+  end;
+end;
+{$ENDIF}
+
+procedure TCustomSuperSpinner.OnFindClass(Reader: TReader; const AClassName: string;
+  var ComponentClass: TComponentClass);
+begin
+
+  if CompareText(AClassName, 'TCustomSuperSpinner') = 0 then
+    ComponentClass := TCustomSuperSpinner;
+end;
+
+function TCustomSuperSpinner.DoMouseWheel(Shift: TShiftState; WheelDelta: integer;
+  MousePos: TPoint): boolean;
+begin
+
+  Result := inherited DoMouseWheel(Shift, WheelDelta, MousePos);
+  MouseWheelPos(Shift, WheelDelta);
+end;
+
+procedure TCustomSuperSpinner.MouseWheelPos(Shift: TShiftState; WheelDelta: integer);
+var
+  newValue: single;
+
+begin
+  if FLocked then
+    Exit;
+
+  // WheelSpeed is a Base Value and a factor to slow or speed up the wheel affect.
+  // FWheelSpeed = 0 then no wheel, 1 slowest movement, 255 fastest movement
+  // Wheel speed still just does one step no matter what the wheel angle is set to
+  // so the WheelSpeed just really adjust the look of how fast the knob spins
+
+  if FWheelSpeed > 0 then
+  begin
+
+    // WheelDelta should just catch direction, negative or positive
+    // not sure if 0 is ever possible????
+
+    if WheelDelta >= 0 then
+      newValue := -1.0
+    else
+      newValue := 1.0;
+
+    // Must invalidate both as we don't know the current direction it's moving
+    // so one will get reset, the other will trigger, so always works.
+    // This is used in UpdateAngularPos to help with direction changes
+
+    FCWSkipCounter := FSpinnerResolutionCount - 1;
+    FCCWSkipCounter := FCWSkipCounter;
+
+    // Scale the Wheel rate so 1-255 will give good dynamic range of really slow to really fast
+
+    // TIP : To make the mouse movement sorta' match the Resolution you can change
+    // the Wheel speed to make it more closely match if resolution is not the highest
+
+    UpdateAngularPos(Shift, FAngularPos + WHEEL_SPEED_FACTOR * newValue * FWheelSpeed);
+
+  end; // wheel speed enabled
+end;
+
+end.

+ 628 - 0
superspinnercommon.pas

@@ -0,0 +1,628 @@
+// SPDX-License-Identifier: LGPL-3.0-linking-exception
+{
+  Part of BGRA Controls. Made by third party.
+  For detailed information see readme.txt
+
+  Site: https://sourceforge.net/p/bgra-controls/
+  Wiki: http://wiki.lazarus.freepascal.org/BGRAControls
+  Forum: http://forum.lazarus.freepascal.org/index.php/board,46.0.html
+
+}
+{******************************* CONTRIBUTOR(S) ******************************
+- Sandy Ganz | [email protected]
+  Evolved from BGRAKnob and SuperGauge, changed style to be more inline with
+  SuperGauge settings and related. Mostly support classes
+***************************** END CONTRIBUTOR(S) *****************************}
+unit SuperSpinnerCommon;
+
+{$I bgracontrols.inc}
+
+interface
+uses
+  Classes, SysUtils, {$IFDEF FPC}LResources,{$ELSE}Types, {$ENDIF} Forms, Controls, Graphics, Dialogs,
+  BGRABitmap, BGRABitmapTypes, BGRAGradients;
+
+type
+  TSSPositionStyle = (psNone, psFilledCircle, psHollowCircle, psShaded, psIndentCircle, psLines);
+  TSSStyle = (ssFlat, ssShaded, ssPhong);
+  TSSCapStyle = (csNone, csFlat, csShaded, csPhong, csOutline);
+  TSSDirection = (sdCW, sdCCW);
+
+  { TSSOrigin }
+
+  TSSOrigin = packed record
+    CenterPoint: TPoint;
+    Radius: integer;
+  end;
+
+  { TSSFrameSettings }
+
+  TSSFrameSettings = class(TPersistent)
+  private
+    FBorderColor: TColor;
+    FBorderWidth: integer;
+    FOnChange: TNotifyEvent;
+    FDirty: boolean;
+
+    procedure SetBorderWidth(AValue: integer);
+    procedure SetBorderColor(AValue: TColor);
+    procedure SetOnChange(AValue: TNotifyEvent);
+    procedure DirtyOnChange;
+  protected
+  public
+    constructor Create;
+    destructor Destroy; override;
+    property OnChange: TNotifyEvent read FOnChange write SetOnChange;
+    property Dirty: boolean read FDirty write FDirty;
+
+  published
+    property BorderWidth: integer read FBorderWidth write SetBorderWidth default 5;
+    property BorderColor: TColor read FBorderColor write SetBorderColor default clGray;
+  end;
+
+  { TSSPositionSettings }
+
+  TSSPositionSettings = class(TPersistent)
+  private
+    FEdgeColor: TColor;
+    FEdgeThickness: integer;
+    FFillColor: TColor;
+    FStyle:TSSPositionStyle;
+    FMargin: integer;
+    FCenterMargin: integer;
+    FLineWidth: integer;  // total width of position
+    FLineCount: integer;  // Number of lines to be draw
+    FRadius: integer;
+    FOpacity: byte;
+    FDirty: boolean;
+    FOnChange: TNotifyEvent;
+
+    procedure SetEdgeColor(AValue: TColor);
+    procedure SetEdgeThickness(AValue: integer);
+    procedure SetColor(AValue: TColor);
+    procedure SetStyle(const AValue: TSSPositionStyle);
+    procedure SetMargin(const AValue: integer);
+    procedure SetCenterMargin(const AValue: integer);
+    procedure SetLineWidth(const AValue: integer);
+    procedure SetLineCount(const AValue: integer);
+    procedure SetRadius(const AValue: integer);
+    procedure SetOpacity(const AValue: byte);
+    procedure SetOnChange(AValue: TNotifyEvent);
+    procedure DirtyOnChange;
+
+  protected
+  public
+
+    property Dirty: boolean read FDirty write FDirty;
+    constructor Create;
+    destructor Destroy; override;
+    property OnChange: TNotifyEvent read FOnChange write SetOnChange;
+
+  published
+    property FillColor: TColor read FFillColor write SetColor default clBlack;
+    property Style: TSSPositionStyle read FStyle write SetStyle default psLines;
+    property Margin: integer read FMargin write SetMargin default 15;
+    property CenterMargin: integer read FCenterMargin write SetCenterMargin default 20;
+    property LineWidth: integer read FLineWidth write SetLineWidth default 4;
+    property LineCount: integer read FLineCount write SetLineCount default 10;
+    property Radius: integer read FRadius write SetRadius default 20;
+    property Opacity: byte  read FOpacity write SetOpacity default 192;
+    property EdgeColor: TColor read FEdgeColor write SetEdgeColor default clGray;
+    property EdgeThickness: integer read FEdgeThickness write SetEdgeThickness default 2;
+  end;
+
+  { TSSCapSettings }
+
+  TSSCapSettings = class(TPersistent)
+    private
+      FEdgeColor: TColor;
+      FEdgeThickness: integer;
+      FFillColor: TColor;
+      FOnChange: TNotifyEvent;
+      FRadius: integer;
+      FCurveExponent: single;
+      FStyle: TSSCapStyle;
+      FDirty: boolean;
+
+      procedure SetEdgeColor(AValue: TColor);
+      procedure SetEdgeThickness(AValue: integer);
+      procedure SetFillColor(AValue: TColor);
+      procedure SetOnChange(AValue: TNotifyEvent);
+      procedure SetRadius(AValue: integer);
+      procedure SetLightIntensity(const AValue: integer);
+      function GetLightIntensity: integer;
+      procedure SetCurveExponent(const AValue: single);
+      procedure SetStyle(const AValue: TSSCapStyle);
+      procedure DirtyOnChange;
+
+    protected
+
+    public
+      FPhong: TPhongShading;
+      property Dirty: boolean read FDirty write FDirty;
+      constructor Create;
+      destructor Destroy; override;
+      property OnChange: TNotifyEvent read FOnChange write SetOnChange;
+
+    published
+      property EdgeColor: TColor read FEdgeColor write SetEdgeColor default clGray;
+      property FillColor: TColor read FFillColor write SetFillColor default clWhite;
+      property Radius: integer read FRadius write SetRadius default 20;
+      property EdgeThickness: integer read FEdgeThickness write SetEdgeThickness default 2;
+      property LightIntensity: integer read GetLightIntensity write SetLightIntensity default 300;
+      property CurveExponent: single read FCurveExponent write SetCurveExponent default 0.05;
+      property Style: TSSCapStyle read FStyle write SetStyle default csPhong;
+
+    end;
+
+    { TSSKnobSettings }
+
+    TSSKnobSettings = class(TPersistent)
+      private
+        FEdgeColor: TColor;
+        FEdgeThickness: integer;
+        FFillColor: TColor;
+        FOnChange: TNotifyEvent;
+        FCurveExponent: single;
+        FStyle: TSSStyle;
+        FDirty: boolean;
+
+        procedure SetEdgeColor(AValue: TColor);
+        procedure SetEdgeThickness(AValue: integer);
+        procedure SetFillColor(AValue: TColor);
+        procedure SetOnChange(AValue: TNotifyEvent);
+        procedure SetLightIntensity(const AValue: integer);
+        function GetLightIntensity: integer;
+        procedure SetCurveExponent(const AValue: single);
+        procedure SetStyle(const AValue: TSSStyle);
+        procedure DirtyOnChange;
+
+      protected
+
+      public
+        FPhong: TPhongShading;
+        property Dirty: boolean read FDirty write FDirty;
+        constructor Create;
+        destructor Destroy; override;
+        property OnChange: TNotifyEvent read FOnChange write SetOnChange;
+
+      published
+        property EdgeColor: TColor read FEdgeColor write SetEdgeColor default clMedGray;
+        property FillColor: TColor read FFillColor write SetFillColor default clWhite;
+        property EdgeThickness: integer read FEdgeThickness write SetEdgeThickness default 2;
+        property LightIntensity: integer read GetLightIntensity write SetLightIntensity default 300;
+        property CurveExponent: single read FCurveExponent write SetCurveExponent default 0.05;
+        property Style: TSSStyle read FStyle write SetStyle default ssPhong;
+      end;
+
+  function Initializebitmap(var Bitmap: TBGRABitmap; Width, Height: integer): TSSOrigin;
+
+implementation
+
+// Helper for all bitmap setup
+
+function Initializebitmap(var Bitmap: TBGRABitmap; Width, Height: integer): TSSOrigin;
+begin
+  Bitmap.SetSize(Width, Height);
+
+  // Clear bitmap to transparent
+
+  BitMap.Fill(BGRA(0, 0, 0, 0));
+
+  // Get origin information
+
+  Result.CenterPoint.x := Width div 2;
+  Result.CenterPoint.y := Height div 2;
+
+  // Take the smallest so radius will always fit
+
+  if Result.CenterPoint.x < Result.CenterPoint.y then
+    Result.Radius := Result.CenterPoint.x
+  else
+    Result.Radius := Result.CenterPoint.y;
+end;
+
+{ TSSFrameSettings }
+
+constructor TSSFrameSettings.Create;
+begin
+
+  FBorderColor := clGray;
+  FBorderWidth := 5;
+  FDirty := True;
+end;
+
+destructor TSSFrameSettings.Destroy;
+begin
+  inherited Destroy;
+end;
+
+procedure TSSFrameSettings.SetBorderWidth(AValue: integer);
+begin
+  if (FBorderWidth = AValue) or (AValue < 0) then
+    Exit;
+
+  FBorderWidth := AValue;
+  DirtyOnChange;
+end;
+
+
+procedure TSSFrameSettings.SetBorderColor(AValue: TColor);
+begin
+  if FBorderColor = AValue then
+    Exit;
+
+  FBorderColor := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSSFrameSettings.SetOnChange(AValue: TNotifyEvent);
+begin
+  FOnChange := AValue;
+
+  if Assigned(FOnChange) then
+    FOnChange(Self);
+end;
+
+procedure TSSFrameSettings.DirtyOnChange;
+begin
+  FDirty := True;   // if we get here a prop must have changed, mark dirty
+
+  if Assigned(FOnChange) then
+    FOnChange(Self);
+end;
+
+{ TSSPositionSettings }
+
+constructor TSSPositionSettings.Create;
+begin
+  FOpacity := 192;
+  FStyle := psLines;
+  FEdgeColor := clGray;
+  FFillColor := clBlack;
+  FMargin := 15;
+  FCenterMargin := 40;
+  FLineWidth := 4;
+  FLineCount := 10;
+  FRadius := 20;
+  FEdgeThickness := 2;
+  FDirty := True;
+end;
+
+destructor TSSPositionSettings.Destroy;
+begin
+  inherited Destroy;
+end;
+procedure TSSPositionSettings.SetStyle(const AValue: TSSPositionStyle);
+begin
+  if FStyle = AValue then
+    Exit;
+
+  FStyle := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSSPositionSettings.SetOpacity(const AValue: byte);
+begin
+  if FOpacity = AValue then
+    Exit;
+
+  FOpacity := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSSPositionSettings.SetEdgeColor(AValue: TColor);
+begin
+  if FEdgeColor = AValue then
+    Exit;
+
+  FEdgeColor := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSSPositionSettings.SetEdgeThickness(AValue: integer);
+begin
+  if (FEdgeThickness = AValue) or (AValue < 0) then
+    Exit;
+
+  FEdgeThickness := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSSPositionSettings.SetColor(AValue: TColor);
+begin
+  if FFillColor = AValue then
+    Exit;
+
+  FFillColor := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSSPositionSettings.SetOnChange(AValue: TNotifyEvent);
+begin
+  FOnChange := AValue;
+
+  // no dirty needed possibly, call directly
+
+  if Assigned(FOnChange) then
+    FOnChange(Self);
+end;
+
+// Diameter of the the spinner circle, ignored for lines
+procedure TSSPositionSettings.SetRadius(const AValue: integer);
+begin
+  if FRadius = AValue then
+    Exit;
+
+  FRadius := AValue;
+  DirtyOnChange;
+end;
+
+// Line width for hollow circle, and lines types. Ignored for others
+procedure TSSPositionSettings.SetLineWidth(const AValue: integer);
+begin
+  if FLineWidth = AValue then
+    Exit;
+
+  FLineWidth := AValue;
+  DirtyOnChange;
+end;
+
+// Line count, for lines, Ignored for others
+procedure TSSPositionSettings.SetLineCount(const AValue: integer);
+begin
+  if FLineCount = AValue then
+    Exit;
+
+  FLineCount := AValue;
+  DirtyOnChange;
+end;
+
+// Offset from the edge of the knob
+procedure TSSPositionSettings.SetMargin(const AValue: integer);
+begin
+  if FMargin = AValue then
+    Exit;
+
+  FMargin := AValue;
+  DirtyOnChange;
+end;
+
+// Offset from the center of the knob
+procedure TSSPositionSettings.SetCenterMargin(const AValue: integer);
+begin
+  if FCenterMargin = AValue then
+    Exit;
+
+  FCenterMargin := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSSPositionSettings.DirtyOnChange;
+begin
+  FDirty := True;   // if we get here some props must have changed, mark dirty
+
+  if Assigned(FOnChange) then
+    FOnChange(Self);
+end;
+
+{ TSSSpinnerCapSettings }
+
+constructor TSSCapSettings.Create;
+begin
+  // create a phong shader, will need to delete on clean up
+
+  FPhong := TPhongShading.Create;
+  FPhong.LightPositionZ := 100;
+  FPhong.LightSourceIntensity := 300;
+  FPhong.NegativeDiffusionFactor := 0.8;
+  FPhong.AmbientFactor := 0.5;
+  FPhong.DiffusionFactor := 0.6;
+  FCurveExponent := 0.05;
+  FStyle := csPhong;
+  FEdgeColor := clGray;
+  FFillColor := clWhite;
+  FRadius := 20;
+  FEdgeThickness := 2;
+
+  FDirty := True;
+end;
+
+destructor TSSCapSettings.Destroy;
+begin
+  FPhong.Free;
+  inherited Destroy;
+end;
+
+procedure TSSCapSettings.SetStyle(const AValue: TSSCapStyle);
+begin
+  if FStyle = AValue then
+    Exit;
+
+  FStyle := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSSCapSettings.SetLightIntensity(const AValue: integer);
+begin
+  if AValue = FPhong.LightSourceIntensity then
+    Exit;
+
+  FPhong.LightSourceIntensity := AValue;
+  DirtyOnChange;
+end;
+
+function TSSCapSettings.GetLightIntensity: integer;
+begin
+  Result := round(FPhong.LightSourceIntensity);
+end;
+
+procedure TSSCapSettings.SetCurveExponent(const AValue: single);
+begin
+  if FCurveExponent = AValue then
+    Exit;
+
+  FCurveExponent := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSSCapSettings.SetEdgeColor(AValue: TColor);
+begin
+  if FEdgeColor = AValue then
+    Exit;
+
+  FEdgeColor := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSSCapSettings.SetEdgeThickness(AValue: integer);
+begin
+  if (FEdgeThickness = AValue) or (AValue < 0) then
+    Exit;
+
+  FEdgeThickness := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSSCapSettings.SetFillColor(AValue: TColor);
+begin
+  if FFillColor = AValue then
+    Exit;
+
+  FFillColor := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSSCapSettings.SetOnChange(AValue: TNotifyEvent);
+begin
+  FOnChange := AValue;
+
+  // no dirty needed possibly, call directly
+
+  if Assigned(FOnChange) then
+    FOnChange(Self);
+end;
+
+procedure TSSCapSettings.SetRadius(AValue: integer);
+begin
+  if FRadius = AValue then
+    Exit;
+
+  FRadius := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSSCapSettings.DirtyOnChange;
+begin
+  FDirty := True;   // if we get here some props must have changed, mark dirty
+
+  if Assigned(FOnChange) then
+    FOnChange(Self);
+end;
+
+{ TSSKnobSettings }
+
+constructor TSSKnobSettings.Create;
+begin
+  // create a phong shader, will need to delete on clean up
+
+  FPhong := TPhongShading.Create;
+  FPhong.LightPositionZ := 100;
+  FPhong.LightSourceIntensity := 300;
+  FPhong.NegativeDiffusionFactor := 0.8;
+  FPhong.AmbientFactor := 0.5;
+  FPhong.DiffusionFactor := 0.6;
+
+  FCurveExponent := 0.2;
+  FStyle := ssPhong;
+  FEdgeColor := clMedGray;
+  FFillColor := clWhite;
+  FEdgeThickness := 2;
+  FDirty := True;
+end;
+
+destructor TSSKnobSettings.Destroy;
+begin
+  FPhong.Free;
+  inherited Destroy;
+end;
+
+procedure TSSKnobSettings.SetStyle(const AValue: TSSStyle);
+begin
+  if FStyle = AValue then
+    Exit;
+
+  FStyle := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSSKnobSettings.SetLightIntensity(const AValue: integer);
+begin
+  if AValue = FPhong.LightSourceIntensity then
+    Exit;
+
+  FPhong.LightSourceIntensity := AValue;
+  DirtyOnChange;
+end;
+
+function TSSKnobSettings.GetLightIntensity: integer;
+begin
+  Result := round(FPhong.LightSourceIntensity);
+end;
+
+procedure TSSKnobSettings.SetCurveExponent(const AValue: single);
+begin
+  if FCurveExponent = AValue then
+    Exit;
+
+  FCurveExponent := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSSKnobSettings.SetEdgeColor(AValue: TColor);
+begin
+  if FEdgeColor = AValue then
+    Exit;
+
+  FEdgeColor := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSSKnobSettings.SetEdgeThickness(AValue: integer);
+begin
+  if (FEdgeThickness = AValue) or (AValue < 0) then
+    Exit;
+
+  FEdgeThickness := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSSKnobSettings.SetFillColor(AValue: TColor);
+begin
+  if FFillColor = AValue then
+    Exit;
+
+  FFillColor := AValue;
+  DirtyOnChange;
+end;
+
+procedure TSSKnobSettings.SetOnChange(AValue: TNotifyEvent);
+begin
+  FOnChange := AValue;
+
+  // no dirty needed possibly, call directly
+
+  if Assigned(FOnChange) then
+    FOnChange(Self);
+end;
+
+procedure TSSKnobSettings.DirtyOnChange;
+begin
+  FDirty := True;   // if we get here some props must have changed, mark dirty
+
+  if Assigned(FOnChange) then
+    FOnChange(Self);
+end;
+
+end.
+

+ 1 - 1
test/test_analog_controls/umain.pas

@@ -9,7 +9,7 @@ uses
   ComCtrls, ExtCtrls,
   ComCtrls, ExtCtrls,
 
 
   // DT Controls
   // DT Controls
-  dtthemedgauge, dtthemedclock, DTAnalogClock, DTAnalogGauge;
+  DTThemedGauge, DTThemedClock, DTAnalogClock, DTAnalogGauge;
 
 
 type
 type
 
 

+ 12 - 4
test/test_bccombobox/umain.lfm

@@ -7,7 +7,7 @@ object Form1: TForm1
   ClientHeight = 174
   ClientHeight = 174
   ClientWidth = 320
   ClientWidth = 320
   OnCreate = FormCreate
   OnCreate = FormCreate
-  LCLVersion = '3.2.0.0'
+  LCLVersion = '3.8.0.0'
   object BCComboBox1: TBCComboBox
   object BCComboBox1: TBCComboBox
     Left = 8
     Left = 8
     Height = 39
     Height = 39
@@ -21,7 +21,6 @@ object Form1: TForm1
     ItemIndex = 0
     ItemIndex = 0
     ArrowSize = 8
     ArrowSize = 8
     ArrowWidth = 16
     ArrowWidth = 16
-    FocusBorderOpacity = 0
     DropDownBorderColor = 4194304
     DropDownBorderColor = 4194304
     DropDownColor = 8405056
     DropDownColor = 8405056
     DropDownFontColor = 16770790
     DropDownFontColor = 16770790
@@ -177,6 +176,15 @@ object Form1: TForm1
     TabOrder = 5
     TabOrder = 5
     OnClick = Button1Click
     OnClick = Button1Click
   end
   end
+  object CheckOnSameForm: TCheckBox
+    Left = 136
+    Height = 20
+    Top = 82
+    Width = 151
+    Caption = 'Dropdown on same form'
+    TabOrder = 6
+    OnChange = CheckOnSameFormChange
+  end
   object BGRAColorTheme1: TBGRAColorTheme
   object BGRAColorTheme1: TBGRAColorTheme
     ColorNormal = clBlack
     ColorNormal = clBlack
     ColorHover = 24576
     ColorHover = 24576
@@ -184,7 +192,7 @@ object Form1: TForm1
     ColorDisabled = clGray
     ColorDisabled = clGray
     ColorFocused = clMoneyGreen
     ColorFocused = clMoneyGreen
     ColorText = clBlack
     ColorText = clBlack
-    Left = 192
-    Top = 72
+    Left = 160
+    Top = 104
   end
   end
 end
 end

+ 7 - 0
test/test_bccombobox/umain.pas

@@ -16,6 +16,7 @@ type
     BCComboBox1: TBCComboBox;
     BCComboBox1: TBCComboBox;
     BGRAColorTheme1: TBGRAColorTheme;
     BGRAColorTheme1: TBGRAColorTheme;
     Button1: TButton;
     Button1: TButton;
+    CheckOnSameForm: TCheckBox;
     Label1: TLabel;
     Label1: TLabel;
     Label2: TLabel;
     Label2: TLabel;
     RadioCustom: TBGRAThemeRadioButton;
     RadioCustom: TBGRAThemeRadioButton;
@@ -24,6 +25,7 @@ type
     RadioDefault: TBGRAThemeRadioButton;
     RadioDefault: TBGRAThemeRadioButton;
     procedure BCComboBox1Change(Sender: TObject);
     procedure BCComboBox1Change(Sender: TObject);
     procedure Button1Click(Sender: TObject);
     procedure Button1Click(Sender: TObject);
+    procedure CheckOnSameFormChange(Sender: TObject);
     procedure FormCreate(Sender: TObject);
     procedure FormCreate(Sender: TObject);
     procedure RadioButtonChange(Sender: TObject);
     procedure RadioButtonChange(Sender: TObject);
   private
   private
@@ -77,6 +79,11 @@ begin
   Close;
   Close;
 end;
 end;
 
 
+procedure TForm1.CheckOnSameFormChange(Sender: TObject);
+begin
+  BCComboBox1.DropDownOnSameForm:= not BCComboBox1.DropDownOnSameForm;
+end;
+
 procedure TForm1.OnBCComboBoxDrawItem(Control: TWinControl; Index: integer;
 procedure TForm1.OnBCComboBoxDrawItem(Control: TWinControl; Index: integer;
   ARect: TRect; State: TOwnerDrawState);
   ARect: TRect; State: TOwnerDrawState);
 var
 var

+ 0 - 1
test/test_bckeyboard/test_bckeyboard.lpi

@@ -134,7 +134,6 @@
     </Target>
     </Target>
     <SearchPaths>
     <SearchPaths>
       <IncludeFiles Value="$(ProjOutDir)"/>
       <IncludeFiles Value="$(ProjOutDir)"/>
-      <OtherUnitFiles Value="..\..;..\..\lcl"/>
       <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
       <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
     </SearchPaths>
     </SearchPaths>
     <Linking>
     <Linking>

+ 79 - 79
test/test_bcroundedimage_pictdialogs/test_bcroundedimage_pictdialogs_main.lfm

@@ -1,17 +1,17 @@
 object Form1: TForm1
 object Form1: TForm1
   Left = 334
   Left = 334
-  Height = 328
+  Height = 410
   Top = 229
   Top = 229
-  Width = 510
+  Width = 638
   Caption = 'Form1'
   Caption = 'Form1'
-  ClientHeight = 328
-  ClientWidth = 510
-  LCLVersion = '4.99.0.0'
+  ClientHeight = 410
+  ClientWidth = 638
+  DesignTimePPI = 120
   object rgStyle: TRadioGroup
   object rgStyle: TRadioGroup
-    Left = 320
-    Height = 80
-    Top = 136
-    Width = 168
+    Left = 400
+    Height = 100
+    Top = 170
+    Width = 210
     AutoFill = True
     AutoFill = True
     Caption = 'Style'
     Caption = 'Style'
     ChildSizing.LeftRightSpacing = 6
     ChildSizing.LeftRightSpacing = 6
@@ -21,8 +21,8 @@ object Form1: TForm1
     ChildSizing.ShrinkVertical = crsScaleChilds
     ChildSizing.ShrinkVertical = crsScaleChilds
     ChildSizing.Layout = cclLeftToRightThenTopToBottom
     ChildSizing.Layout = cclLeftToRightThenTopToBottom
     ChildSizing.ControlsPerLine = 1
     ChildSizing.ControlsPerLine = 1
-    ClientHeight = 60
-    ClientWidth = 164
+    ClientHeight = 75
+    ClientWidth = 206
     ItemIndex = 2
     ItemIndex = 2
     Items.Strings = (
     Items.Strings = (
       'Circle'
       'Circle'
@@ -34,53 +34,53 @@ object Form1: TForm1
     OnClick = rgStyleClick
     OnClick = rgStyleClick
   end
   end
   object Label1: TLabel
   object Label1: TLabel
-    Left = 320
-    Height = 15
-    Top = 219
-    Width = 55
+    Left = 400
+    Height = 20
+    Top = 274
+    Width = 67
     Caption = 'Rounding:'
     Caption = 'Rounding:'
   end
   end
   object edRounding: TFloatSpinEdit
   object edRounding: TFloatSpinEdit
-    Left = 384
-    Height = 23
-    Top = 216
-    Width = 56
+    Left = 480
+    Height = 28
+    Top = 270
+    Width = 70
     MaxValue = 100
     MaxValue = 100
     TabOrder = 1
     TabOrder = 1
     Value = 10
     Value = 10
     OnChange = edRoundingChange
     OnChange = edRoundingChange
   end
   end
   object btLoad: TButton
   object btLoad: TButton
-    Left = 320
-    Height = 25
-    Top = 248
-    Width = 80
+    Left = 400
+    Height = 31
+    Top = 310
+    Width = 100
     Caption = 'Load (BGRA)'
     Caption = 'Load (BGRA)'
     TabOrder = 2
     TabOrder = 2
     OnClick = btLoadClick
     OnClick = btLoadClick
   end
   end
   object cbProportional: TCheckBox
   object cbProportional: TCheckBox
-    Left = 320
-    Height = 19
-    Top = 32
-    Width = 84
+    Left = 400
+    Height = 24
+    Top = 40
+    Width = 104
     Caption = 'Proportional'
     Caption = 'Proportional'
     TabOrder = 3
     TabOrder = 3
     OnChange = cbProportionalChange
     OnChange = cbProportionalChange
   end
   end
   object Panel1: TPanel
   object Panel1: TPanel
-    Left = 8
-    Height = 302
-    Top = 8
-    Width = 302
-    ClientHeight = 302
-    ClientWidth = 302
+    Left = 10
+    Height = 378
+    Top = 10
+    Width = 378
+    ClientHeight = 378
+    ClientWidth = 378
     TabOrder = 4
     TabOrder = 4
     object BCRoundedImage1: TBCRoundedImage
     object BCRoundedImage1: TBCRoundedImage
       Left = 0
       Left = 0
-      Height = 300
+      Height = 375
       Top = 0
       Top = 0
-      Width = 300
+      Width = 375
       Style = isSquare
       Style = isSquare
       BorderStyle = []
       BorderStyle = []
       Rounding = 10
       Rounding = 10
@@ -90,10 +90,10 @@ object Form1: TForm1
     end
     end
   end
   end
   object rgAlign: TRadioGroup
   object rgAlign: TRadioGroup
-    Left = 320
-    Height = 80
-    Top = 56
-    Width = 79
+    Left = 400
+    Height = 100
+    Top = 70
+    Width = 99
     AutoFill = True
     AutoFill = True
     Caption = 'Align'
     Caption = 'Align'
     ChildSizing.LeftRightSpacing = 6
     ChildSizing.LeftRightSpacing = 6
@@ -103,8 +103,8 @@ object Form1: TForm1
     ChildSizing.ShrinkVertical = crsScaleChilds
     ChildSizing.ShrinkVertical = crsScaleChilds
     ChildSizing.Layout = cclLeftToRightThenTopToBottom
     ChildSizing.Layout = cclLeftToRightThenTopToBottom
     ChildSizing.ControlsPerLine = 1
     ChildSizing.ControlsPerLine = 1
-    ClientHeight = 60
-    ClientWidth = 75
+    ClientHeight = 75
+    ClientWidth = 95
     ItemIndex = 2
     ItemIndex = 2
     Items.Strings = (
     Items.Strings = (
       'Left'
       'Left'
@@ -115,10 +115,10 @@ object Form1: TForm1
     OnClick = rgAlignClick
     OnClick = rgAlignClick
   end
   end
   object rgAlignV: TRadioGroup
   object rgAlignV: TRadioGroup
-    Left = 400
-    Height = 81
-    Top = 56
-    Width = 79
+    Left = 500
+    Height = 101
+    Top = 70
+    Width = 99
     AutoFill = True
     AutoFill = True
     Caption = 'Align Vert'
     Caption = 'Align Vert'
     ChildSizing.LeftRightSpacing = 6
     ChildSizing.LeftRightSpacing = 6
@@ -128,8 +128,8 @@ object Form1: TForm1
     ChildSizing.ShrinkVertical = crsScaleChilds
     ChildSizing.ShrinkVertical = crsScaleChilds
     ChildSizing.Layout = cclLeftToRightThenTopToBottom
     ChildSizing.Layout = cclLeftToRightThenTopToBottom
     ChildSizing.ControlsPerLine = 1
     ChildSizing.ControlsPerLine = 1
-    ClientHeight = 61
-    ClientWidth = 75
+    ClientHeight = 76
+    ClientWidth = 95
     ItemIndex = 1
     ItemIndex = 1
     Items.Strings = (
     Items.Strings = (
       'Top'
       'Top'
@@ -140,10 +140,10 @@ object Form1: TForm1
     OnClick = rgAlignVClick
     OnClick = rgAlignVClick
   end
   end
   object cbStretch: TCheckBox
   object cbStretch: TCheckBox
-    Left = 320
-    Height = 19
-    Top = 8
-    Width = 55
+    Left = 400
+    Height = 24
+    Top = 10
+    Width = 67
     Caption = 'Stretch'
     Caption = 'Stretch'
     Checked = True
     Checked = True
     State = cbChecked
     State = cbChecked
@@ -151,62 +151,62 @@ object Form1: TForm1
     OnChange = cbStretchChange
     OnChange = cbStretchChange
   end
   end
   object btLoadT: TButton
   object btLoadT: TButton
-    Left = 408
-    Height = 25
-    Top = 248
-    Width = 96
+    Left = 510
+    Height = 31
+    Top = 310
+    Width = 120
     Caption = 'Load (TPicture)'
     Caption = 'Load (TPicture)'
     TabOrder = 8
     TabOrder = 8
     OnClick = btLoadTClick
     OnClick = btLoadTClick
   end
   end
   object btSavePictBGRA: TButton
   object btSavePictBGRA: TButton
-    Left = 320
-    Height = 25
-    Top = 277
-    Width = 80
+    Left = 400
+    Height = 31
+    Top = 346
+    Width = 100
     Caption = 'Save (BGRA)'
     Caption = 'Save (BGRA)'
     TabOrder = 9
     TabOrder = 9
     OnClick = btSavePictBGRAClick
     OnClick = btSavePictBGRAClick
   end
   end
   object btSavePict: TButton
   object btSavePict: TButton
-    Left = 408
-    Height = 25
-    Top = 277
-    Width = 96
+    Left = 510
+    Height = 31
+    Top = 346
+    Width = 120
     Caption = 'Save (TPicture)'
     Caption = 'Save (TPicture)'
     TabOrder = 10
     TabOrder = 10
     OnClick = btSavePictClick
     OnClick = btSavePictClick
   end
   end
   object lbDetails: TLabel
   object lbDetails: TLabel
-    Left = 320
-    Height = 15
-    Top = 304
-    Width = 36
+    Left = 400
+    Height = 20
+    Top = 380
+    Width = 45
     Caption = 'image:'
     Caption = 'image:'
   end
   end
   object Button1: TButton
   object Button1: TButton
-    Left = 448
-    Height = 25
-    Top = 216
-    Width = 40
+    Left = 560
+    Height = 31
+    Top = 270
+    Width = 50
     Caption = 'test'
     Caption = 'test'
     TabOrder = 11
     TabOrder = 11
     OnClick = Button1Click
     OnClick = Button1Click
   end
   end
   object openPict: TOpenPictureDialog
   object openPict: TOpenPictureDialog
-    Left = 72
-    Top = 136
+    Left = 90
+    Top = 170
   end
   end
   object savePict: TSavePictureDialog
   object savePict: TSavePictureDialog
-    Left = 192
-    Top = 136
+    Left = 240
+    Top = 170
   end
   end
   object openPictBGRA: TBGRAOpenPictureDialog
   object openPictBGRA: TBGRAOpenPictureDialog
-    Left = 72
-    Top = 56
+    Left = 90
+    Top = 70
   end
   end
   object savePictBGRA: TBGRASavePictureDialog
   object savePictBGRA: TBGRASavePictureDialog
-    Left = 192
-    Top = 56
+    Left = 240
+    Top = 70
   end
   end
 end
 end

+ 2 - 1
test/test_bcroundedimage_pictdialogs/test_bcroundedimage_pictdialogs_main.pas

@@ -109,7 +109,8 @@ var
    t, t2: String;
    t, t2: String;
 
 
 begin
 begin
-  BuildBGRAFilterStrings(True, t, t2);
+  t:= BGRARegisteredImageReaderFilter;
+  t2:= BGRARegisteredImageWriterFilter;
 end;
 end;
 
 
 procedure TForm1.cbProportionalChange(Sender: TObject);
 procedure TForm1.cbProportionalChange(Sender: TObject);

+ 2 - 2
test/test_bgraknob/unit1.lfm

@@ -1,7 +1,7 @@
 object Form1: TForm1
 object Form1: TForm1
-  Left = 592
+  Left = 577
   Height = 503
   Height = 503
-  Top = 516
+  Top = 189
   Width = 1269
   Width = 1269
   Caption = 'BGRAKnob Test'
   Caption = 'BGRAKnob Test'
   ClientHeight = 503
   ClientHeight = 503

BIN
test/test_formatui/test_formatui.ico


+ 134 - 0
test/test_formatui/test_formatui.lpi

@@ -0,0 +1,134 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<CONFIG>
+  <ProjectOptions>
+    <Version Value="12"/>
+    <PathDelim Value="\"/>
+    <General>
+      <SessionStorage Value="InProjectDir"/>
+      <Title Value="test_formatui"/>
+      <Scaled Value="True"/>
+      <ResourceType Value="res"/>
+      <UseXPManifest Value="True"/>
+      <XPManifest>
+        <DpiAware Value="True"/>
+      </XPManifest>
+      <Icon Value="0"/>
+    </General>
+    <BuildModes>
+      <Item Name="Debug" Default="True"/>
+      <Item Name="Release">
+        <CompilerOptions>
+          <Version Value="11"/>
+          <PathDelim Value="\"/>
+          <Target>
+            <Filename Value="bin\$(TargetCPU)-$(TargetOS)\test_formatui"/>
+          </Target>
+          <SearchPaths>
+            <IncludeFiles Value="$(ProjOutDir)"/>
+            <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
+          </SearchPaths>
+          <CodeGeneration>
+            <SmartLinkUnit Value="True"/>
+            <Optimizations>
+              <OptimizationLevel Value="3"/>
+            </Optimizations>
+          </CodeGeneration>
+          <Linking>
+            <Debugging>
+              <GenerateDebugInfo Value="False"/>
+              <RunWithoutDebug Value="True"/>
+              <StripSymbols Value="True"/>
+            </Debugging>
+            <LinkSmart Value="True"/>
+            <Options>
+              <Win32>
+                <GraphicApplication Value="True"/>
+              </Win32>
+            </Options>
+          </Linking>
+        </CompilerOptions>
+      </Item>
+    </BuildModes>
+    <PublishOptions>
+      <Version Value="2"/>
+      <UseFileFilters Value="True"/>
+    </PublishOptions>
+    <RunParams>
+      <FormatVersion Value="2"/>
+    </RunParams>
+    <RequiredPackages>
+      <Item>
+        <PackageName Value="bgracontrols"/>
+      </Item>
+      <Item>
+        <PackageName Value="BGRABitmapPack"/>
+      </Item>
+      <Item>
+        <PackageName Value="LCL"/>
+      </Item>
+    </RequiredPackages>
+    <Units>
+      <Unit>
+        <Filename Value="test_formatui.lpr"/>
+        <IsPartOfProject Value="True"/>
+      </Unit>
+      <Unit>
+        <Filename Value="test_formatui_main.pas"/>
+        <IsPartOfProject Value="True"/>
+        <ComponentName Value="SaveFile_Settings"/>
+        <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
+      </Unit>
+    </Units>
+  </ProjectOptions>
+  <CompilerOptions>
+    <Version Value="11"/>
+    <PathDelim Value="\"/>
+    <Target>
+      <Filename Value="bin\$(TargetCPU)-$(TargetOS)\test_formatui"/>
+    </Target>
+    <SearchPaths>
+      <IncludeFiles Value="$(ProjOutDir)"/>
+      <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
+    </SearchPaths>
+    <Parsing>
+      <SyntaxOptions>
+        <IncludeAssertionCode Value="True"/>
+      </SyntaxOptions>
+    </Parsing>
+    <CodeGeneration>
+      <Checks>
+        <IOChecks Value="True"/>
+        <RangeChecks Value="True"/>
+        <OverflowChecks Value="True"/>
+        <StackChecks Value="True"/>
+      </Checks>
+      <VerifyObjMethodCallValidity Value="True"/>
+    </CodeGeneration>
+    <Linking>
+      <Debugging>
+        <DebugInfoType Value="dsDwarf3"/>
+        <UseHeaptrc Value="True"/>
+        <TrashVariables Value="True"/>
+      </Debugging>
+      <Options>
+        <Win32>
+          <GraphicApplication Value="True"/>
+        </Win32>
+      </Options>
+    </Linking>
+  </CompilerOptions>
+  <Debugging>
+    <Exceptions>
+      <Item>
+        <Name Value="EAbort"/>
+      </Item>
+      <Item>
+        <Name Value="ECodetoolError"/>
+      </Item>
+      <Item>
+        <Name Value="EFOpenError"/>
+      </Item>
+    </Exceptions>
+  </Debugging>
+</CONFIG>

+ 27 - 0
test/test_formatui/test_formatui.lpr

@@ -0,0 +1,27 @@
+program test_formatui;
+
+{$mode objfpc}{$H+}
+
+uses
+  {$IFDEF UNIX}
+  cthreads,
+  {$ENDIF}
+  {$IFDEF HASAMIGA}
+  athreads,
+  {$ENDIF}
+  Interfaces, // this includes the LCL widgetset
+  Forms, test_formatui_main;
+
+{$R *.res}
+
+begin
+  RequireDerivedFormResource:=True;
+  Application.Scaled:=True;
+  {$PUSH}{$WARN 5044 OFF}
+  Application.MainFormOnTaskbar:=True;
+  {$POP}
+  Application.Initialize;
+  Application.CreateForm(TSaveFile_Settings, SaveFile_Settings);
+  Application.Run;
+end.
+

+ 359 - 0
test/test_formatui/test_formatui_main.lfm

@@ -0,0 +1,359 @@
+object SaveFile_Settings: TSaveFile_Settings
+  Left = 416
+  Height = 450
+  Top = 239
+  Width = 780
+  BorderIcons = [biSystemMenu]
+  BorderStyle = bsSingle
+  Caption = 'Save as Files Settings'
+  ClientHeight = 450
+  ClientWidth = 780
+  DesignTimePPI = 120
+  Position = poScreenCenter
+  LCLVersion = '4.99.0.0'
+  OnClose = FormClose
+  OnShow = FormShow
+  object panelWrite: TBCPanel
+    Left = 374
+    Height = 450
+    Top = 0
+    Width = 406
+    Align = alRight
+    Background.Color = clBtnFace
+    Background.Gradient1.StartColor = clWhite
+    Background.Gradient1.EndColor = clBlack
+    Background.Gradient1.GradientType = gtLinear
+    Background.Gradient1.Point1XPercent = 0
+    Background.Gradient1.Point1YPercent = 0
+    Background.Gradient1.Point2XPercent = 0
+    Background.Gradient1.Point2YPercent = 100
+    Background.Gradient2.StartColor = clWhite
+    Background.Gradient2.EndColor = clBlack
+    Background.Gradient2.GradientType = gtLinear
+    Background.Gradient2.Point1XPercent = 0
+    Background.Gradient2.Point1YPercent = 0
+    Background.Gradient2.Point2XPercent = 0
+    Background.Gradient2.Point2YPercent = 100
+    Background.Gradient1EndPercent = 35
+    Background.Style = bbsColor
+    BevelInner = bvLowered
+    BevelOuter = bvRaised
+    BevelWidth = 1
+    Border.Style = bboNone
+    FontEx.Color = clDefault
+    FontEx.FontQuality = fqSystemClearType
+    FontEx.Shadow = False
+    FontEx.ShadowRadius = 5
+    FontEx.ShadowOffsetX = 5
+    FontEx.ShadowOffsetY = 5
+    FontEx.Style = []
+    ParentBackground = False
+    Rounding.RoundX = 1
+    Rounding.RoundY = 1
+    TabOrder = 0
+    object Label6: TLabel
+      Left = 45
+      Height = 20
+      Top = 48
+      Width = 49
+      Alignment = taRightJustify
+      Caption = 'Folder :'
+    end
+    object dirDestination: TDirectoryEdit
+      Left = 100
+      Height = 28
+      Top = 48
+      Width = 300
+      ShowHidden = False
+      ButtonWidth = 29
+      NumGlyphs = 1
+      MaxLength = 0
+      TabOrder = 0
+      OnChange = dirDestinationChange
+    end
+    object cbSaveFormat: TComboBox
+      Left = 100
+      Height = 28
+      Top = 112
+      Width = 300
+      ItemHeight = 20
+      Style = csDropDownList
+      TabOrder = 1
+      OnChange = cbSaveFormatChange
+    end
+    object btSave: TBCButton
+      Left = 4
+      Height = 41
+      Top = 1
+      Width = 154
+      StateClicked.Background.Gradient1.StartColor = 8404992
+      StateClicked.Background.Gradient1.EndColor = 4194304
+      StateClicked.Background.Gradient1.GradientType = gtRadial
+      StateClicked.Background.Gradient1.Point1XPercent = 50
+      StateClicked.Background.Gradient1.Point1YPercent = 100
+      StateClicked.Background.Gradient1.Point2XPercent = 0
+      StateClicked.Background.Gradient1.Point2YPercent = 0
+      StateClicked.Background.Gradient2.StartColor = clWhite
+      StateClicked.Background.Gradient2.EndColor = clBlack
+      StateClicked.Background.Gradient2.GradientType = gtLinear
+      StateClicked.Background.Gradient2.Point1XPercent = 0
+      StateClicked.Background.Gradient2.Point1YPercent = 0
+      StateClicked.Background.Gradient2.Point2XPercent = 0
+      StateClicked.Background.Gradient2.Point2YPercent = 100
+      StateClicked.Background.Gradient1EndPercent = 100
+      StateClicked.Background.Style = bbsGradient
+      StateClicked.Border.Style = bboNone
+      StateClicked.FontEx.Color = 16770790
+      StateClicked.FontEx.FontQuality = fqSystemClearType
+      StateClicked.FontEx.Shadow = True
+      StateClicked.FontEx.ShadowRadius = 2
+      StateClicked.FontEx.ShadowOffsetX = 1
+      StateClicked.FontEx.ShadowOffsetY = 1
+      StateClicked.FontEx.Style = [fsBold]
+      StateHover.Background.Gradient1.StartColor = 16744448
+      StateHover.Background.Gradient1.EndColor = 8404992
+      StateHover.Background.Gradient1.GradientType = gtRadial
+      StateHover.Background.Gradient1.Point1XPercent = 50
+      StateHover.Background.Gradient1.Point1YPercent = 100
+      StateHover.Background.Gradient1.Point2XPercent = 0
+      StateHover.Background.Gradient1.Point2YPercent = 0
+      StateHover.Background.Gradient2.StartColor = clWhite
+      StateHover.Background.Gradient2.EndColor = clBlack
+      StateHover.Background.Gradient2.GradientType = gtLinear
+      StateHover.Background.Gradient2.Point1XPercent = 0
+      StateHover.Background.Gradient2.Point1YPercent = 0
+      StateHover.Background.Gradient2.Point2XPercent = 0
+      StateHover.Background.Gradient2.Point2YPercent = 100
+      StateHover.Background.Gradient1EndPercent = 100
+      StateHover.Background.Style = bbsGradient
+      StateHover.Border.Style = bboNone
+      StateHover.FontEx.Color = clWhite
+      StateHover.FontEx.FontQuality = fqSystemClearType
+      StateHover.FontEx.Shadow = True
+      StateHover.FontEx.ShadowRadius = 2
+      StateHover.FontEx.ShadowOffsetX = 1
+      StateHover.FontEx.ShadowOffsetY = 1
+      StateHover.FontEx.Style = [fsBold]
+      StateNormal.Background.Gradient1.StartColor = 4194304
+      StateNormal.Background.Gradient1.EndColor = 8405056
+      StateNormal.Background.Gradient1.GradientType = gtLinear
+      StateNormal.Background.Gradient1.Point1XPercent = 0
+      StateNormal.Background.Gradient1.Point1YPercent = 0
+      StateNormal.Background.Gradient1.Point2XPercent = 0
+      StateNormal.Background.Gradient1.Point2YPercent = 100
+      StateNormal.Background.Gradient2.StartColor = 8405056
+      StateNormal.Background.Gradient2.EndColor = 4194304
+      StateNormal.Background.Gradient2.GradientType = gtRadial
+      StateNormal.Background.Gradient2.Point1XPercent = 50
+      StateNormal.Background.Gradient2.Point1YPercent = 100
+      StateNormal.Background.Gradient2.Point2XPercent = 0
+      StateNormal.Background.Gradient2.Point2YPercent = 0
+      StateNormal.Background.Gradient1EndPercent = 60
+      StateNormal.Background.Style = bbsGradient
+      StateNormal.Border.Style = bboNone
+      StateNormal.FontEx.Color = 16770790
+      StateNormal.FontEx.FontQuality = fqSystemClearType
+      StateNormal.FontEx.Shadow = True
+      StateNormal.FontEx.ShadowRadius = 2
+      StateNormal.FontEx.ShadowOffsetX = 1
+      StateNormal.FontEx.ShadowOffsetY = 1
+      StateNormal.FontEx.Style = [fsBold]
+      Caption = 'Save Image'
+      Color = clNone
+      DropDownWidth = 16
+      DropDownArrowSize = 8
+      GlobalOpacity = 255
+      OnClick = btSaveClick
+      ParentColor = False
+      Rounding.RoundX = 12
+      Rounding.RoundY = 12
+      RoundingDropDown.RoundX = 1
+      RoundingDropDown.RoundY = 1
+      TextApplyGlobalOpacity = False
+      MemoryUsage = bmuHigh
+    end
+    object BCLabel9: TLabel
+      Left = 40
+      Height = 20
+      Top = 114
+      Width = 54
+      Alignment = taRightJustify
+      Caption = 'Format :'
+    end
+    object BCLabel10: TLabel
+      Left = 27
+      Height = 20
+      Top = 80
+      Width = 67
+      Alignment = taRightJustify
+      Caption = 'Filename :'
+    end
+    object edFileName: TEdit
+      Left = 100
+      Height = 28
+      Top = 79
+      Width = 250
+      TabOrder = 2
+      Text = 'test_format'
+    end
+    object lbExt: TLabel
+      Left = 354
+      Height = 20
+      Top = 87
+      Width = 25
+      Caption = '.jpg'
+    end
+  end
+  object panelRead: TBCPanel
+    Left = 0
+    Height = 450
+    Top = 0
+    Width = 375
+    Align = alLeft
+    Background.Color = clBtnFace
+    Background.Gradient1.StartColor = clWhite
+    Background.Gradient1.EndColor = clBlack
+    Background.Gradient1.GradientType = gtLinear
+    Background.Gradient1.Point1XPercent = 0
+    Background.Gradient1.Point1YPercent = 0
+    Background.Gradient1.Point2XPercent = 0
+    Background.Gradient1.Point2YPercent = 100
+    Background.Gradient2.StartColor = clWhite
+    Background.Gradient2.EndColor = clBlack
+    Background.Gradient2.GradientType = gtLinear
+    Background.Gradient2.Point1XPercent = 0
+    Background.Gradient2.Point1YPercent = 0
+    Background.Gradient2.Point2XPercent = 0
+    Background.Gradient2.Point2YPercent = 100
+    Background.Gradient1EndPercent = 35
+    Background.Style = bbsColor
+    BevelInner = bvNone
+    BevelOuter = bvRaised
+    BevelWidth = 1
+    Border.Style = bboNone
+    FontEx.Color = clDefault
+    FontEx.FontQuality = fqSystemClearType
+    FontEx.Shadow = False
+    FontEx.ShadowRadius = 5
+    FontEx.ShadowOffsetX = 5
+    FontEx.ShadowOffsetY = 5
+    FontEx.Style = []
+    ParentBackground = False
+    Rounding.RoundX = 1
+    Rounding.RoundY = 1
+    TabOrder = 1
+    object bcImage: TBCRoundedImage
+      Left = 2
+      Height = 370
+      Top = 48
+      Width = 370
+      Style = isSquare
+      BorderStyle = []
+      Rounding = 10
+      Quality = rfBestQuality
+      Proportional = True
+    end
+    object btLoad: TBCButton
+      Left = 4
+      Height = 41
+      Top = 1
+      Width = 154
+      StateClicked.Background.Gradient1.StartColor = 8404992
+      StateClicked.Background.Gradient1.EndColor = 4194304
+      StateClicked.Background.Gradient1.GradientType = gtRadial
+      StateClicked.Background.Gradient1.Point1XPercent = 50
+      StateClicked.Background.Gradient1.Point1YPercent = 100
+      StateClicked.Background.Gradient1.Point2XPercent = 0
+      StateClicked.Background.Gradient1.Point2YPercent = 0
+      StateClicked.Background.Gradient2.StartColor = clWhite
+      StateClicked.Background.Gradient2.EndColor = clBlack
+      StateClicked.Background.Gradient2.GradientType = gtLinear
+      StateClicked.Background.Gradient2.Point1XPercent = 0
+      StateClicked.Background.Gradient2.Point1YPercent = 0
+      StateClicked.Background.Gradient2.Point2XPercent = 0
+      StateClicked.Background.Gradient2.Point2YPercent = 100
+      StateClicked.Background.Gradient1EndPercent = 100
+      StateClicked.Background.Style = bbsGradient
+      StateClicked.Border.Style = bboNone
+      StateClicked.FontEx.Color = 16770790
+      StateClicked.FontEx.FontQuality = fqSystemClearType
+      StateClicked.FontEx.Shadow = True
+      StateClicked.FontEx.ShadowRadius = 2
+      StateClicked.FontEx.ShadowOffsetX = 1
+      StateClicked.FontEx.ShadowOffsetY = 1
+      StateClicked.FontEx.Style = [fsBold]
+      StateHover.Background.Gradient1.StartColor = 16744448
+      StateHover.Background.Gradient1.EndColor = 8404992
+      StateHover.Background.Gradient1.GradientType = gtRadial
+      StateHover.Background.Gradient1.Point1XPercent = 50
+      StateHover.Background.Gradient1.Point1YPercent = 100
+      StateHover.Background.Gradient1.Point2XPercent = 0
+      StateHover.Background.Gradient1.Point2YPercent = 0
+      StateHover.Background.Gradient2.StartColor = clWhite
+      StateHover.Background.Gradient2.EndColor = clBlack
+      StateHover.Background.Gradient2.GradientType = gtLinear
+      StateHover.Background.Gradient2.Point1XPercent = 0
+      StateHover.Background.Gradient2.Point1YPercent = 0
+      StateHover.Background.Gradient2.Point2XPercent = 0
+      StateHover.Background.Gradient2.Point2YPercent = 100
+      StateHover.Background.Gradient1EndPercent = 100
+      StateHover.Background.Style = bbsGradient
+      StateHover.Border.Style = bboNone
+      StateHover.FontEx.Color = clWhite
+      StateHover.FontEx.FontQuality = fqSystemClearType
+      StateHover.FontEx.Shadow = True
+      StateHover.FontEx.ShadowRadius = 2
+      StateHover.FontEx.ShadowOffsetX = 1
+      StateHover.FontEx.ShadowOffsetY = 1
+      StateHover.FontEx.Style = [fsBold]
+      StateNormal.Background.Gradient1.StartColor = 4194304
+      StateNormal.Background.Gradient1.EndColor = 8405056
+      StateNormal.Background.Gradient1.GradientType = gtLinear
+      StateNormal.Background.Gradient1.Point1XPercent = 0
+      StateNormal.Background.Gradient1.Point1YPercent = 0
+      StateNormal.Background.Gradient1.Point2XPercent = 0
+      StateNormal.Background.Gradient1.Point2YPercent = 100
+      StateNormal.Background.Gradient2.StartColor = 8405056
+      StateNormal.Background.Gradient2.EndColor = 4194304
+      StateNormal.Background.Gradient2.GradientType = gtRadial
+      StateNormal.Background.Gradient2.Point1XPercent = 50
+      StateNormal.Background.Gradient2.Point1YPercent = 100
+      StateNormal.Background.Gradient2.Point2XPercent = 0
+      StateNormal.Background.Gradient2.Point2YPercent = 0
+      StateNormal.Background.Gradient1EndPercent = 60
+      StateNormal.Background.Style = bbsGradient
+      StateNormal.Border.Style = bboNone
+      StateNormal.FontEx.Color = 16770790
+      StateNormal.FontEx.FontQuality = fqSystemClearType
+      StateNormal.FontEx.Shadow = True
+      StateNormal.FontEx.ShadowRadius = 2
+      StateNormal.FontEx.ShadowOffsetX = 1
+      StateNormal.FontEx.ShadowOffsetY = 1
+      StateNormal.FontEx.Style = [fsBold]
+      Caption = 'Load Image'
+      Color = clNone
+      DropDownWidth = 16
+      DropDownArrowSize = 8
+      GlobalOpacity = 255
+      OnClick = btLoadClick
+      ParentColor = False
+      Rounding.RoundX = 12
+      Rounding.RoundY = 12
+      RoundingDropDown.RoundX = 1
+      RoundingDropDown.RoundY = 1
+      TextApplyGlobalOpacity = False
+      MemoryUsage = bmuHigh
+    end
+    object lbDetails: TLabel
+      Left = 2
+      Height = 20
+      Top = 426
+      Width = 45
+      Caption = 'image:'
+    end
+  end
+  object openPictBGRA: TBGRAOpenPictureDialog
+    Left = 124
+    Top = 104
+  end
+end

+ 170 - 0
test/test_formatui/test_formatui_main.pas

@@ -0,0 +1,170 @@
+// SPDX-License-Identifier: LGPL-3.0-linking-exception
+{
+  Created by BGRA Controls Team
+
+         (c) 2025 Massimo Magnano
+
+  Test Save File Format Settings Form
+}
+
+unit test_formatui_main;
+
+{$mode ObjFPC}{$H+}
+
+interface
+
+uses
+  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls, StdCtrls, EditBtn, Buttons,
+  FpImage,
+  BGRABitmapTypes,
+  BCPanel, BCRoundedImage, BCButton, BGRADialogs;
+
+type
+  { TSaveFile_Settings }
+
+  TSaveFile_Settings = class(TForm)
+    BCLabel10: TLabel;
+    BCLabel9: TLabel;
+    btLoad: TBCButton;
+    btSave: TBCButton;
+    edFileName: TEdit;
+    lbExt: TLabel;
+    lbDetails: TLabel;
+    openPictBGRA: TBGRAOpenPictureDialog;
+    panelRead: TBCPanel;
+    bcImage: TBCRoundedImage;
+    panelWrite: TBCPanel;
+    cbSaveFormat: TComboBox;
+    dirDestination: TDirectoryEdit;
+    Label6: TLabel;
+    procedure btLoadClick(Sender: TObject);
+    procedure btSaveClick(Sender: TObject);
+    procedure cbSaveFormatChange(Sender: TObject);
+    procedure dirDestinationChange(Sender: TObject);
+    procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
+    procedure FormShow(Sender: TObject);
+
+  private
+    SaveFormat: TBGRAImageFormat;
+    SaveWriter: TFPCustomImageWriter;
+    SavePath: String;
+    panelFormatUI: TBCPanel;
+
+    procedure AdjustFormatPanel;
+
+  public
+
+  end;
+
+var
+  SaveFile_Settings: TSaveFile_Settings;
+
+implementation
+
+{$R *.lfm}
+
+uses Math, BGRAFormatUI, BGRAReadLzp, BGRAWriteLzp, BGRAPaintNet;
+
+{ TSaveFile_Settings }
+
+procedure TSaveFile_Settings.AdjustFormatPanel;
+begin
+  lbExt.Caption:= '.'+SuggestImageExtension(SaveFormat);
+
+  if (panelFormatUI <> nil)
+  then begin
+         panelFormatUI.Top:= 150; panelFormatUI.Left:= 10;
+         Width:= Max(780, panelFormatUI.Width+130);
+         Height:= Max(450, panelFormatUI.Height+150);
+
+         panelFormatUI.Parent:= panelWrite;
+         panelFormatUI.Visible:= True;
+       end
+  else begin
+         Width:= 780;
+         Height:= 450;
+       end;
+end;
+
+procedure TSaveFile_Settings.cbSaveFormatChange(Sender: TObject);
+begin
+  SaveFormat:= TBGRAImageFormat(PTRUInt(cbSaveFormat.Items.Objects[cbSaveFormat.ItemIndex]));
+  SaveWriter.Free;
+  SaveWriter:= CreateBGRAImageWriter(SaveFormat, True);
+
+  if (panelFormatUI <> nil) then panelFormatUI.Visible:= False;
+
+  TBGRAFormatUIContainer.GetUI(SaveFormat, SaveWriter, panelFormatUI);
+  AdjustFormatPanel;
+end;
+
+procedure TSaveFile_Settings.btLoadClick(Sender: TObject);
+begin
+  try
+     if openPictBGRA.Execute then
+     begin
+       bcImage.Bitmap.LoadFromFile(openPictBGRA.FileName); //'c:\tmp\Acquisitions Book 1.03.01, Byzantine.jpg'
+       bcImage.Invalidate;
+       lbDetails.Caption:= 'image: '+IntToStr(bcImage.Bitmap.Width)+' x '+IntToStr(bcImage.Bitmap.Height);
+     end;
+
+  finally
+  end;
+end;
+
+procedure TSaveFile_Settings.btSaveClick(Sender: TObject);
+var
+   ext: String;
+
+begin
+  if not(bcImage.Bitmap.Empty) then
+  begin
+    if (BGRAFormatUIContainer <> nil) and
+       (panelFormatUI <> nil)
+    then BGRAFormatUIContainer.SetWriterProperties(SaveWriter);
+
+    ext:= '.'+SuggestImageExtension(SaveFormat);
+    bcImage.Bitmap.SaveToFile(SavePath+DirectorySeparator+edFileName.Text+ext,
+                              SaveWriter);
+
+    MessageDlg('Saved as '#13#10+edFileName.Text+ext+#13#10'Class='+SaveWriter.ClassName, mtInformation, [mbOk], 0);
+  end;
+end;
+
+procedure TSaveFile_Settings.dirDestinationChange(Sender: TObject);
+begin
+  SavePath :=dirDestination.Directory;
+end;
+
+procedure TSaveFile_Settings.FormClose(Sender: TObject; var CloseAction: TCloseAction);
+begin
+  if (SaveWriter<>nil) then SaveWriter.Free;
+  if (BGRAFormatUIContainer <> nil) then BGRAFormatUIContainer.Free;
+end;
+
+procedure TSaveFile_Settings.FormShow(Sender: TObject);
+begin
+  try
+     panelFormatUI:= nil;
+
+     dirDestination.Directory:= ExtractFileDir(ParamStr(0));
+
+     //Select JPeg as Current Format
+     if (TBGRAFormatUIContainer.BuildSaveFormats(cbSaveFormat, ifJpeg) > 0) then
+     begin
+       SaveFormat:= ifJpeg;
+       SaveWriter:= CreateBGRAImageWriter(SaveFormat, True);
+       TBGRAFormatUIContainer.GetUI(SaveFormat, SaveWriter, panelFormatUI);
+     end
+     else raise Exception.Create('No Writers Registered...');
+
+  finally
+    AdjustFormatPanel;
+  end;
+end;
+
+initialization
+  RegisterPaintNetFormat;
+
+end.
+

+ 1 - 1
test/test_progressbar/test_progressbar.lpi

@@ -26,7 +26,7 @@
           <Version Value="11"/>
           <Version Value="11"/>
           <PathDelim Value="\"/>
           <PathDelim Value="\"/>
           <Target>
           <Target>
-            <Filename Value="test_progressbar"/>
+            <Filename Value="bin\$(TargetCPU)-$(TargetOS)\test_progressbar"/>
           </Target>
           </Target>
           <SearchPaths>
           <SearchPaths>
             <IncludeFiles Value="$(ProjOutDir)"/>
             <IncludeFiles Value="$(ProjOutDir)"/>

+ 139 - 145
test/test_progressbar/umain.lfm

@@ -1,10 +1,10 @@
 object Form1: TForm1
 object Form1: TForm1
   Left = 357
   Left = 357
-  Height = 565
+  Height = 548
   Top = 179
   Top = 179
   Width = 654
   Width = 654
   Caption = 'Form1'
   Caption = 'Form1'
-  ClientHeight = 565
+  ClientHeight = 548
   ClientWidth = 654
   ClientWidth = 654
   DesignTimePPI = 120
   DesignTimePPI = 120
   OnClose = FormClose
   OnClose = FormClose
@@ -30,14 +30,13 @@ object Form1: TForm1
     BackgroundRandomizeMinIntensity = 4000
     BackgroundRandomizeMinIntensity = 4000
     BackgroundRandomizeMaxIntensity = 5000
     BackgroundRandomizeMaxIntensity = 5000
     BackgroundRandomize = True
     BackgroundRandomize = True
-    MarqueeBounce = 0
     OnTimerEnd = BGRAMaxMProgressTimerEnd
     OnTimerEnd = BGRAMaxMProgressTimerEnd
   end
   end
   object edCaption: TEdit
   object edCaption: TEdit
     Left = 75
     Left = 75
     Height = 28
     Height = 28
     Top = 150
     Top = 150
-    Width = 100
+    Width = 150
     TabOrder = 0
     TabOrder = 0
     OnChange = edCaptionChange
     OnChange = edCaptionChange
   end
   end
@@ -49,7 +48,7 @@ object Form1: TForm1
     Caption = 'Caption :'
     Caption = 'Caption :'
   end
   end
   object cbCaptionPercent: TCheckBox
   object cbCaptionPercent: TCheckBox
-    Left = 291
+    Left = 339
     Height = 24
     Height = 24
     Top = 150
     Top = 150
     Width = 111
     Width = 111
@@ -58,14 +57,14 @@ object Form1: TForm1
     OnChange = cbCaptionPercentChange
     OnChange = cbCaptionPercentChange
   end
   end
   object Label6: TLabel
   object Label6: TLabel
-    Left = 320
+    Left = 360
     Height = 20
     Height = 20
     Top = 180
     Top = 180
     Width = 46
     Width = 46
     Caption = 'Digits :'
     Caption = 'Digits :'
   end
   end
   object edCaptionDigits: TSpinEdit
   object edCaptionDigits: TSpinEdit
-    Left = 370
+    Left = 410
     Height = 28
     Height = 28
     Top = 175
     Top = 175
     Width = 62
     Width = 62
@@ -74,7 +73,7 @@ object Form1: TForm1
     OnChange = edCaptionDigitsChange
     OnChange = edCaptionDigitsChange
   end
   end
   object rgCaptionAlign: TRadioGroup
   object rgCaptionAlign: TRadioGroup
-    Left = 184
+    Left = 232
     Height = 81
     Height = 81
     Top = 150
     Top = 150
     Width = 99
     Width = 99
@@ -144,74 +143,36 @@ object Form1: TForm1
     OnChange = cbBackgroundRandomChange
     OnChange = cbBackgroundRandomChange
   end
   end
   object PageControl1: TPageControl
   object PageControl1: TPageControl
-    Left = 10
-    Height = 312
-    Top = 240
-    Width = 630
+    Left = 0
+    Height = 300
+    Top = 248
+    Width = 654
     ActivePage = TabNormal
     ActivePage = TabNormal
+    Align = alBottom
     TabIndex = 0
     TabIndex = 0
     TabOrder = 6
     TabOrder = 6
     OnChange = PageControl1Change
     OnChange = PageControl1Change
     object TabNormal: TTabSheet
     object TabNormal: TTabSheet
       Caption = 'Normal'
       Caption = 'Normal'
-      ClientHeight = 279
-      ClientWidth = 622
-      object Label5: TLabel
-        Left = 20
-        Height = 20
-        Top = 25
-        Width = 32
-        Caption = 'Min :'
-      end
-      object edMin: TFloatSpinEdit
-        Left = 59
-        Height = 25
-        Top = 20
-        Width = 100
-        Font.Color = clWindowText
-        Font.Name = 'Arial'
-        MaxValue = 100
-        ParentFont = False
-        TabOrder = 0
-        OnChange = edMinChange
-      end
-      object Label7: TLabel
-        Left = 18
-        Height = 20
-        Top = 65
-        Width = 35
-        Caption = 'Max :'
-      end
-      object edMax: TFloatSpinEdit
-        Left = 59
-        Height = 25
-        Top = 60
-        Width = 100
-        Font.Color = clWindowText
-        Font.Name = 'Arial'
-        MaxValue = 100
-        ParentFont = False
-        TabOrder = 1
-        Value = 100
-        OnChange = edMaxChange
-      end
+      ClientHeight = 267
+      ClientWidth = 646
       object Label2: TLabel
       object Label2: TLabel
-        Left = 14
+        Left = 16
         Height = 20
         Height = 20
-        Top = 105
+        Top = 20
         Width = 43
         Width = 43
         Caption = 'Value :'
         Caption = 'Value :'
       end
       end
       object edValue: TFloatSpinEdit
       object edValue: TFloatSpinEdit
-        Left = 59
+        Left = 65
         Height = 25
         Height = 25
-        Top = 100
-        Width = 100
+        Top = 20
+        Width = 85
         Font.Color = clWindowText
         Font.Color = clWindowText
         Font.Name = 'Arial'
         Font.Name = 'Arial'
         MaxValue = 100
         MaxValue = 100
         ParentFont = False
         ParentFont = False
-        TabOrder = 2
+        TabOrder = 0
         Value = 30
         Value = 30
         OnChange = edValueChange
         OnChange = edValueChange
       end
       end
@@ -219,27 +180,27 @@ object Form1: TForm1
     object TabMultiProgress: TTabSheet
     object TabMultiProgress: TTabSheet
       Tag = 1
       Tag = 1
       Caption = 'MultiProgress'
       Caption = 'MultiProgress'
-      ClientHeight = 279
-      ClientWidth = 622
+      ClientHeight = 267
+      ClientWidth = 646
       object Label3: TLabel
       object Label3: TLabel
         Left = 190
         Left = 190
         Height = 20
         Height = 20
-        Top = 26
+        Top = 20
         Width = 72
         Width = 72
         Caption = 'Value Sub :'
         Caption = 'Value Sub :'
       end
       end
-      object edMultiPValueSub: TFloatSpinEdit
+      object edValueSub: TFloatSpinEdit
         Left = 270
         Left = 270
         Height = 25
         Height = 25
         Top = 20
         Top = 20
-        Width = 81
+        Width = 85
         Font.Color = clWindowText
         Font.Color = clWindowText
         Font.Name = 'Arial'
         Font.Name = 'Arial'
         MaxValue = 100
         MaxValue = 100
         ParentFont = False
         ParentFont = False
         TabOrder = 0
         TabOrder = 0
         Value = 10
         Value = 10
-        OnChange = edMultiPValueSubChange
+        OnChange = edValueSubChange
       end
       end
       object cbCaptionPercentM: TCheckBox
       object cbCaptionPercentM: TCheckBox
         Left = 190
         Left = 190
@@ -301,62 +262,23 @@ object Form1: TForm1
         StateDisabled.BorderColor = 12566463
         StateDisabled.BorderColor = 12566463
         StateDisabled.BorderWidth = 1
         StateDisabled.BorderWidth = 1
       end
       end
-      object Label11: TLabel
-        Left = 26
-        Height = 20
-        Top = 25
-        Width = 32
-        Caption = 'Min :'
-      end
-      object edMin1: TFloatSpinEdit
-        Left = 65
-        Height = 25
-        Top = 20
-        Width = 100
-        Font.Color = clWindowText
-        Font.Name = 'Arial'
-        MaxValue = 100
-        ParentFont = False
-        TabOrder = 3
-        OnChange = edMinChange
-      end
-      object Label12: TLabel
-        Left = 24
-        Height = 20
-        Top = 65
-        Width = 35
-        Caption = 'Max :'
-      end
-      object edMax1: TFloatSpinEdit
-        Left = 65
-        Height = 25
-        Top = 60
-        Width = 100
-        Font.Color = clWindowText
-        Font.Name = 'Arial'
-        MaxValue = 100
-        ParentFont = False
-        TabOrder = 4
-        Value = 100
-        OnChange = edMaxChange
-      end
       object Label13: TLabel
       object Label13: TLabel
-        Left = 20
+        Left = 16
         Height = 20
         Height = 20
-        Top = 105
+        Top = 20
         Width = 43
         Width = 43
         Caption = 'Value :'
         Caption = 'Value :'
       end
       end
       object edValue1: TFloatSpinEdit
       object edValue1: TFloatSpinEdit
         Left = 65
         Left = 65
         Height = 25
         Height = 25
-        Top = 100
-        Width = 100
+        Top = 20
+        Width = 85
         Font.Color = clWindowText
         Font.Color = clWindowText
         Font.Name = 'Arial'
         Font.Name = 'Arial'
         MaxValue = 100
         MaxValue = 100
         ParentFont = False
         ParentFont = False
-        TabOrder = 5
+        TabOrder = 3
         Value = 30
         Value = 30
         OnChange = edValueChange
         OnChange = edValueChange
       end
       end
@@ -364,8 +286,8 @@ object Form1: TForm1
     object TabMarquee: TTabSheet
     object TabMarquee: TTabSheet
       Tag = 2
       Tag = 2
       Caption = 'Marquee'
       Caption = 'Marquee'
-      ClientHeight = 279
-      ClientWidth = 622
+      ClientHeight = 267
+      ClientWidth = 646
       object rgMarqueeSpeed: TRadioGroup
       object rgMarqueeSpeed: TRadioGroup
         Left = 20
         Left = 20
         Height = 81
         Height = 81
@@ -392,16 +314,16 @@ object Form1: TForm1
         OnClick = rgMarqueeSpeedClick
         OnClick = rgMarqueeSpeedClick
       end
       end
       object edMarqueeWidth: TBCTrackbarUpdown
       object edMarqueeWidth: TBCTrackbarUpdown
-        Left = 255
-        Height = 32
-        Top = 41
-        Width = 80
+        Left = 384
+        Height = 30
+        Top = 74
+        Width = 85
         AllowNegativeValues = False
         AllowNegativeValues = False
         BarExponent = 1
         BarExponent = 1
         Increment = 1
         Increment = 1
         LongTimeInterval = 400
         LongTimeInterval = 400
-        MinValue = 20
-        MaxValue = 100
+        MinValue = 10
+        MaxValue = 475
         OnChange = edMarqueeWidthChange
         OnChange = edMarqueeWidthChange
         Value = 95
         Value = 95
         ShortTimeInterval = 100
         ShortTimeInterval = 100
@@ -463,29 +385,10 @@ object Form1: TForm1
         Font.Name = 'Arial'
         Font.Name = 'Arial'
         HasTrackBar = True
         HasTrackBar = True
         ArrowColor = clBtnText
         ArrowColor = clBtnText
-        Enabled = False
         TabOrder = 1
         TabOrder = 1
         TabStop = True
         TabStop = True
         UseDockManager = False
         UseDockManager = False
       end
       end
-      object Label1: TLabel
-        Left = 170
-        Height = 20
-        Top = 21
-        Width = 47
-        Caption = 'Width :'
-      end
-      object cbMarqueeWidth: TCheckBox
-        Left = 189
-        Height = 24
-        Top = 41
-        Width = 53
-        Caption = 'Auto'
-        Checked = True
-        State = cbChecked
-        TabOrder = 2
-        OnChange = cbMarqueeWidthChange
-      end
       object rgMarqueeDirection: TRadioGroup
       object rgMarqueeDirection: TRadioGroup
         Left = 130
         Left = 130
         Height = 51
         Height = 51
@@ -508,7 +411,7 @@ object Form1: TForm1
           'Right'
           'Right'
           'Left'
           'Left'
         )
         )
-        TabOrder = 3
+        TabOrder = 2
         OnClick = rgMarqueeDirectionClick
         OnClick = rgMarqueeDirectionClick
       end
       end
       object btTimerPlayPause1: TBGRASpeedButton
       object btTimerPlayPause1: TBGRASpeedButton
@@ -535,7 +438,7 @@ object Form1: TForm1
         Caption = 'Auto Start'
         Caption = 'Auto Start'
         Checked = True
         Checked = True
         State = cbChecked
         State = cbChecked
-        TabOrder = 4
+        TabOrder = 3
         OnChange = cbTimerAutoStartChange
         OnChange = cbTimerAutoStartChange
       end
       end
       object edMarqueeBounce: TSpinEdit
       object edMarqueeBounce: TSpinEdit
@@ -546,7 +449,7 @@ object Form1: TForm1
         Width = 62
         Width = 62
         MaxValue = 10
         MaxValue = 10
         ParentShowHint = False
         ParentShowHint = False
-        TabOrder = 5
+        TabOrder = 4
         OnChange = edMarqueeBounceChange
         OnChange = edMarqueeBounceChange
       end
       end
       object Label8: TLabel
       object Label8: TLabel
@@ -556,12 +459,64 @@ object Form1: TForm1
         Width = 52
         Width = 52
         Caption = 'Bounce:'
         Caption = 'Bounce:'
       end
       end
+      object rgMarqueeWidthType: TRadioGroup
+        Left = 280
+        Height = 148
+        Top = 20
+        Width = 96
+        AutoFill = True
+        Caption = 'Width Type'
+        ChildSizing.LeftRightSpacing = 6
+        ChildSizing.EnlargeHorizontal = crsHomogenousChildResize
+        ChildSizing.EnlargeVertical = crsHomogenousChildResize
+        ChildSizing.ShrinkHorizontal = crsScaleChilds
+        ChildSizing.ShrinkVertical = crsScaleChilds
+        ChildSizing.Layout = cclLeftToRightThenTopToBottom
+        ChildSizing.ControlsPerLine = 1
+        ClientHeight = 123
+        ClientWidth = 92
+        ItemIndex = 0
+        Items.Strings = (
+          'Auto'
+          'Fixed'
+          'Value'
+          'ValueSub'
+        )
+        TabOrder = 5
+        OnClick = rgMarqueeWidthTypeClick
+      end
+      object edValueSub1: TFloatSpinEdit
+        Left = 384
+        Height = 25
+        Top = 143
+        Width = 85
+        Font.Color = clWindowText
+        Font.Name = 'Arial'
+        MaxValue = 100
+        ParentFont = False
+        TabOrder = 6
+        Value = 10
+        OnChange = edValueSubChange
+      end
+      object edValue2: TFloatSpinEdit
+        Left = 384
+        Height = 25
+        Top = 112
+        Width = 85
+        Font.Color = clWindowText
+        Font.Name = 'Arial'
+        MaxValue = 100
+        ParentFont = False
+        TabOrder = 7
+        Value = 30
+        OnChange = edValueChange
+      end
     end
     end
     object TabTimer: TTabSheet
     object TabTimer: TTabSheet
       Tag = 3
       Tag = 3
       Caption = 'Timer'
       Caption = 'Timer'
-      ClientHeight = 279
-      ClientWidth = 622
+      ClientHeight = 267
+      ClientWidth = 646
       object btTimerStart: TBGRASpeedButton
       object btTimerStart: TBGRASpeedButton
         Left = 40
         Left = 40
         Height = 28
         Height = 28
@@ -632,8 +587,8 @@ object Form1: TForm1
     object TabGraph: TTabSheet
     object TabGraph: TTabSheet
       Tag = 4
       Tag = 4
       Caption = 'Graph'
       Caption = 'Graph'
-      ClientHeight = 279
-      ClientWidth = 622
+      ClientHeight = 267
+      ClientWidth = 646
       object Label9: TLabel
       object Label9: TLabel
         Left = 202
         Left = 202
         Height = 20
         Height = 20
@@ -829,7 +784,7 @@ object Form1: TForm1
         Left = 415
         Left = 415
         Height = 28
         Height = 28
         Top = 81
         Top = 81
-        Width = 100
+        Width = 150
         TabOrder = 7
         TabOrder = 7
         OnChange = edYLineCaptionChange
         OnChange = edYLineCaptionChange
       end
       end
@@ -844,7 +799,7 @@ object Form1: TForm1
         Left = 415
         Left = 415
         Height = 28
         Height = 28
         Top = 116
         Top = 116
-        Width = 100
+        Width = 150
         TabOrder = 8
         TabOrder = 8
         OnChange = edYLineAfterChange
         OnChange = edYLineAfterChange
       end
       end
@@ -939,6 +894,45 @@ object Form1: TForm1
     StateDisabled.BorderColor = 12566463
     StateDisabled.BorderColor = 12566463
     StateDisabled.BorderWidth = 1
     StateDisabled.BorderWidth = 1
   end
   end
+  object Label5: TLabel
+    Left = 34
+    Height = 20
+    Top = 189
+    Width = 32
+    Caption = 'Min :'
+  end
+  object edMin: TFloatSpinEdit
+    Left = 75
+    Height = 25
+    Top = 184
+    Width = 100
+    Font.Color = clWindowText
+    Font.Name = 'Arial'
+    MaxValue = 100
+    ParentFont = False
+    TabOrder = 8
+    OnChange = edMinChange
+  end
+  object Label7: TLabel
+    Left = 34
+    Height = 20
+    Top = 221
+    Width = 35
+    Caption = 'Max :'
+  end
+  object edMax: TFloatSpinEdit
+    Left = 75
+    Height = 25
+    Top = 216
+    Width = 100
+    Font.Color = clWindowText
+    Font.Name = 'Arial'
+    MaxValue = 100
+    ParentFont = False
+    TabOrder = 9
+    Value = 100
+    OnChange = edMaxChange
+  end
   object ColorDialog1: TColorDialog
   object ColorDialog1: TColorDialog
     Color = clBlack
     Color = clBlack
     CustomColors.Strings = (
     CustomColors.Strings = (

+ 32 - 21
test/test_progressbar/umain.pas

@@ -26,7 +26,6 @@ type
     cbBackgroundRandom: TCheckBox;
     cbBackgroundRandom: TCheckBox;
     cbCaptionPercent1: TCheckBox;
     cbCaptionPercent1: TCheckBox;
     cbCaptionPercentM: TCheckBox;
     cbCaptionPercentM: TCheckBox;
-    cbMarqueeWidth: TCheckBox;
     cbShowDividers: TCheckBox;
     cbShowDividers: TCheckBox;
     cbShowDividersY: TCheckBox;
     cbShowDividersY: TCheckBox;
     cbShowYLine: TCheckBox;
     cbShowYLine: TCheckBox;
@@ -37,6 +36,10 @@ type
     ColorDialog1: TColorDialog;
     ColorDialog1: TColorDialog;
     btBarColor: TColorSpeedButton;
     btBarColor: TColorSpeedButton;
     edCaption: TEdit;
     edCaption: TEdit;
+    edMax: TFloatSpinEdit;
+    edMin: TFloatSpinEdit;
+    edValueSub1: TFloatSpinEdit;
+    edValue2: TFloatSpinEdit;
     edYLineCaption: TEdit;
     edYLineCaption: TEdit;
     edYLineAfter: TEdit;
     edYLineAfter: TEdit;
     edYLineDigits: TSpinEdit;
     edYLineDigits: TSpinEdit;
@@ -46,23 +49,16 @@ type
     edGraphValueY: TFloatSpinEdit;
     edGraphValueY: TFloatSpinEdit;
     edMarqueeBounce: TSpinEdit;
     edMarqueeBounce: TSpinEdit;
     edMarqueeWidth: TBCTrackbarUpdown;
     edMarqueeWidth: TBCTrackbarUpdown;
-    edMax: TFloatSpinEdit;
-    edMax1: TFloatSpinEdit;
     edMax2: TFloatSpinEdit;
     edMax2: TFloatSpinEdit;
     edMaxY: TFloatSpinEdit;
     edMaxY: TFloatSpinEdit;
-    edMin: TFloatSpinEdit;
-    edMin1: TFloatSpinEdit;
     edMin2: TFloatSpinEdit;
     edMin2: TFloatSpinEdit;
     edMinY: TFloatSpinEdit;
     edMinY: TFloatSpinEdit;
-    edMultiPValueSub: TFloatSpinEdit;
+    edValueSub: TFloatSpinEdit;
     edValue: TFloatSpinEdit;
     edValue: TFloatSpinEdit;
     edValue1: TFloatSpinEdit;
     edValue1: TFloatSpinEdit;
     GroupBox1: TGroupBox;
     GroupBox1: TGroupBox;
     GroupBox2: TGroupBox;
     GroupBox2: TGroupBox;
-    Label1: TLabel;
     Label10: TLabel;
     Label10: TLabel;
-    Label11: TLabel;
-    Label12: TLabel;
     Label13: TLabel;
     Label13: TLabel;
     Label14: TLabel;
     Label14: TLabel;
     Label15: TLabel;
     Label15: TLabel;
@@ -89,6 +85,7 @@ type
     rgCaptionAlignM: TRadioGroup;
     rgCaptionAlignM: TRadioGroup;
     rgMarqueeDirection: TRadioGroup;
     rgMarqueeDirection: TRadioGroup;
     rgMarqueeSpeed: TRadioGroup;
     rgMarqueeSpeed: TRadioGroup;
+    rgMarqueeWidthType: TRadioGroup;
     TabNormal: TTabSheet;
     TabNormal: TTabSheet;
     TabMarquee: TTabSheet;
     TabMarquee: TTabSheet;
     TabMultiProgress: TTabSheet;
     TabMultiProgress: TTabSheet;
@@ -119,7 +116,7 @@ type
     procedure edMaxYChange(Sender: TObject);
     procedure edMaxYChange(Sender: TObject);
     procedure edMinChange(Sender: TObject);
     procedure edMinChange(Sender: TObject);
     procedure edMinYChange(Sender: TObject);
     procedure edMinYChange(Sender: TObject);
-    procedure edMultiPValueSubChange(Sender: TObject; AByUser: boolean);
+    procedure edValueSubChange(Sender: TObject; AByUser: boolean);
     procedure edCaptionTimerFormatChange(Sender: TObject);
     procedure edCaptionTimerFormatChange(Sender: TObject);
     procedure edValueChange(Sender: TObject; AByUser: boolean);
     procedure edValueChange(Sender: TObject; AByUser: boolean);
     procedure edMarqueeWidthChange(Sender: TObject; AByUser: boolean);
     procedure edMarqueeWidthChange(Sender: TObject; AByUser: boolean);
@@ -133,6 +130,7 @@ type
     procedure rgCaptionAlignClick(Sender: TObject);
     procedure rgCaptionAlignClick(Sender: TObject);
     procedure rgCaptionAlignMClick(Sender: TObject);
     procedure rgCaptionAlignMClick(Sender: TObject);
     procedure rgMarqueeDirectionClick(Sender: TObject);
     procedure rgMarqueeDirectionClick(Sender: TObject);
+    procedure rgMarqueeWidthTypeClick(Sender: TObject);
     procedure rgMarqueeSpeedClick(Sender: TObject);
     procedure rgMarqueeSpeedClick(Sender: TObject);
   private
   private
     { private declarations }
     { private declarations }
@@ -180,11 +178,7 @@ end;
 
 
 procedure TForm1.cbMarqueeWidthChange(Sender: TObject);
 procedure TForm1.cbMarqueeWidthChange(Sender: TObject);
 begin
 begin
-  if cbMarqueeWidth.checked
-  then BGRAMaxMProgress.MarqueeWidth:= 0
-  else BGRAMaxMProgress.MarqueeWidth:= edMarqueeWidth.Value;
-
-  edMarqueeWidth.Enabled:= not(cbMarqueeWidth.checked);
+  BGRAMaxMProgress.MarqueeWidth:= edMarqueeWidth.Value;
 end;
 end;
 
 
 procedure TForm1.cbCaptionPercentChange(Sender: TObject);
 procedure TForm1.cbCaptionPercentChange(Sender: TObject);
@@ -250,7 +244,9 @@ end;
 
 
 procedure TForm1.edMaxChange(Sender: TObject);
 procedure TForm1.edMaxChange(Sender: TObject);
 begin
 begin
-  BGRAMaxMProgress.MaxValue:= edMax.Value;
+  BGRAMaxMProgress.MaxValue:= TFloatSpinEdit(Sender).Value;
+  edMax.Value:= BGRAMaxMProgress.MaxValue;
+  edMax2.Value:= BGRAMaxMProgress.MaxValue;
   edValue.MaxValue:= BGRAMaxMProgress.MaxValue;
   edValue.MaxValue:= BGRAMaxMProgress.MaxValue;
   edValue1.MaxValue:= BGRAMaxMProgress.MaxValue;
   edValue1.MaxValue:= BGRAMaxMProgress.MaxValue;
   edGraphValue.MaxValue:= BGRAMaxMProgress.MaxValue;
   edGraphValue.MaxValue:= BGRAMaxMProgress.MaxValue;
@@ -264,7 +260,9 @@ end;
 
 
 procedure TForm1.edMinChange(Sender: TObject);
 procedure TForm1.edMinChange(Sender: TObject);
 begin
 begin
-  BGRAMaxMProgress.MinValue:= edMin.Value;
+  BGRAMaxMProgress.MinValue:= TFloatSpinEdit(Sender).Value;
+  edMin.Value:= BGRAMaxMProgress.MinValue;
+  edMin2.Value:= BGRAMaxMProgress.MinValue;
   edValue.MinValue:= BGRAMaxMProgress.MinValue;
   edValue.MinValue:= BGRAMaxMProgress.MinValue;
   edValue1.MinValue:= BGRAMaxMProgress.MinValue;
   edValue1.MinValue:= BGRAMaxMProgress.MinValue;
   edGraphValue.MinValue:= BGRAMaxMProgress.MinValue;
   edGraphValue.MinValue:= BGRAMaxMProgress.MinValue;
@@ -276,10 +274,11 @@ begin
   edGraphValueY.MinValue:= BGRAMaxMProgress.MinYValue;
   edGraphValueY.MinValue:= BGRAMaxMProgress.MinYValue;
 end;
 end;
 
 
-procedure TForm1.edMultiPValueSubChange(Sender: TObject; AByUser: boolean);
+procedure TForm1.edValueSubChange(Sender: TObject; AByUser: boolean);
 begin
 begin
-  BGRAMaxMProgress.ValueSub:= edMultiPValueSub.Value;
-  edMultiPValueSub.Value:= BGRAMaxMProgress.ValueSub;
+  BGRAMaxMProgress.ValueSub:= TFloatSpinEdit(Sender).Value;
+  edValueSub.Value:= BGRAMaxMProgress.ValueSub;
+  edValueSub1.Value:= BGRAMaxMProgress.ValueSub;
 end;
 end;
 
 
 procedure TForm1.edCaptionTimerFormatChange(Sender: TObject);
 procedure TForm1.edCaptionTimerFormatChange(Sender: TObject);
@@ -289,7 +288,10 @@ end;
 
 
 procedure TForm1.edValueChange(Sender: TObject; AByUser: boolean);
 procedure TForm1.edValueChange(Sender: TObject; AByUser: boolean);
 begin
 begin
-  BGRAMaxMProgress.Value:= edValue.Value;
+  BGRAMaxMProgress.Value:= TFloatSpinEdit(Sender).Value;
+  edValue.Value:= BGRAMaxMProgress.Value;
+  edValue1.Value:= BGRAMaxMProgress.Value;
+  edValue2.Value:= BGRAMaxMProgress.Value;
 end;
 end;
 
 
 procedure TForm1.edMarqueeWidthChange(Sender: TObject; AByUser: boolean);
 procedure TForm1.edMarqueeWidthChange(Sender: TObject; AByUser: boolean);
@@ -329,6 +331,9 @@ end;
 
 
 procedure TForm1.PageControl1Change(Sender: TObject);
 procedure TForm1.PageControl1Change(Sender: TObject);
 begin
 begin
+  //Update Controls
+
+
   if (PageControl1.ActivePage.Tag = 4)
   if (PageControl1.ActivePage.Tag = 4)
   then BGRAMaxMProgress.Height:= 100 //Graph
   then BGRAMaxMProgress.Height:= 100 //Graph
   else BGRAMaxMProgress.Height:= 34;
   else BGRAMaxMProgress.Height:= 34;
@@ -361,6 +366,12 @@ begin
   BGRAMaxMProgress.MarqueeDirection:= TBGRAPBarMarqueeDirection(rgMarqueeDirection.ItemIndex);
   BGRAMaxMProgress.MarqueeDirection:= TBGRAPBarMarqueeDirection(rgMarqueeDirection.ItemIndex);
 end;
 end;
 
 
+procedure TForm1.rgMarqueeWidthTypeClick(Sender: TObject);
+begin
+  BGRAMaxMProgress.MarqueeWidthType:= TBGRAPBarMarqueeWidthType(rgMarqueeWidthType.ItemIndex);
+//  edMarqueeWidth.Enabled:= (BGRAMaxMProgress.MarqueeWidthType = pbmwFixed);
+end;
+
 procedure TForm1.rgMarqueeSpeedClick(Sender: TObject);
 procedure TForm1.rgMarqueeSpeedClick(Sender: TObject);
 begin
 begin
   BGRAMaxMProgress.MarqueeSpeed:= TBGRAPBarMarqueeSpeed(rgMarqueeSpeed.ItemIndex);
   BGRAMaxMProgress.MarqueeSpeed:= TBGRAPBarMarqueeSpeed(rgMarqueeSpeed.ItemIndex);

+ 2 - 2
test/test_supergauge/sgtest.lfm

@@ -1,7 +1,7 @@
 object SGTestFrm: TSGTestFrm
 object SGTestFrm: TSGTestFrm
-  Left = 575
+  Left = 1428
   Height = 851
   Height = 851
-  Top = 238
+  Top = 268
   Width = 1323
   Width = 1323
   Caption = 'Super Gauge Test Application Test v0.0'
   Caption = 'Super Gauge Test Application Test v0.0'
   ClientHeight = 851
   ClientHeight = 851

+ 1 - 1
test/test_supergauge/sgtest.pas

@@ -25,7 +25,7 @@ uses
   BGRAShape, BGRAImageList, SuperGaugeCommon, SuperGauge,about;
   BGRAShape, BGRAImageList, SuperGaugeCommon, SuperGauge,about;
 
 
 const
 const
-  VERSIONSTR = '1.02';            // SG TEST version, Should ALWAYS show as a delta when merging!
+  VERSIONSTR = '1.03';            // SG TEST version, Should ALWAYS show as a delta when merging!
 
 
 type
 type
   { TSGTestFrm }
   { TSGTestFrm }

+ 276 - 0
test/test_superspinner/about.lfm

@@ -0,0 +1,276 @@
+object AboutFrm: TAboutFrm
+  Left = 1803
+  Height = 653
+  Top = 149
+  Width = 615
+  BorderStyle = bsSingle
+  Caption = 'About Super Spinner'
+  ClientHeight = 653
+  ClientWidth = 615
+  Color = clSkyBlue
+  DesignTimePPI = 144
+  FormStyle = fsStayOnTop
+  Icon.Data = {
+    9E09000000000100010018180000010020008809000016000000280000001800
+    0000300000000100200000000000000900006400000064000000000000000000
+    0000FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
+    FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
+    FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
+    FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
+    FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
+    FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
+    FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00F2F2
+    F2FF7B7B7BFF393939FF101010FF090909FF090909FF101010FF323232FF7373
+    73FFDEDEDEFFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
+    FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFFFF737373FF0909
+    09FF282828FF535353FF6C6C6CFF7B7B7BFF7B7B7BFF6C6C6CFF5A5A5AFF3232
+    32FF090909FF535353FFE7E7E7FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
+    FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00F2F2F2FF323232FF212121FF6C6C
+    6CFF848484FF848484FF848484FF848484FF848484FF848484FF848484FF8484
+    84FF7B7B7BFF424242FF101010FFADADADFFFFFFFF00FFFFFF00FFFFFF00FFFF
+    FF00FFFFFF00FFFFFF00FFFFFF00FFFFFFFF323232FF323232FF7B7B7BFFA4A4
+    A4FFCECECEFFD6D6D6FFD6D6D6FF959595FF8B8B8BFFD6D6D6FFDEDEDEFFCECE
+    CEFFC4C4C4FF848484FF646464FF101010FFADADADFFFFFFFF00FFFFFF00FFFF
+    FF00FFFFFF00FFFFFF00FFFFFF00737373FF212121FF7B7B7BFF848484FFB4B4
+    B4FFB4B4B4FF848484FFCECECEFFCECECEFFD6D6D6FFCECECEFF848484FFB4B4
+    B4FFDEDEDEFF848484FF848484FF6C6C6CFF101010FFCECECEFFFFFFFF00FFFF
+    FF00FFFFFF00FFFFFF00F2F2F2FF090909FF6C6C6CFF848484FF848484FF8B8B
+    8BFFDEDEDEFFFFFFFFFFF7F7F7FFA4A4A4FFF2F2F2FFADADADFF848484FFCECE
+    CEFFCECECEFF9E9E9EFF848484FF848484FF5A5A5AFF323232FFFFFFFFFFFFFF
+    FF00FFFFFF00FFFFFF00848484FF282828FF848484FF848484FF848484FFADAD
+    ADFFE7E7E7FF959595FF9E9E9EFFADADADFFD6D6D6FFCECECEFF848484FF9595
+    95FFCECECEFF848484FF848484FF848484FF848484FF282828FFBBBBBBFFFFFF
+    FF00FFFFFF00FFFFFF00323232FF535353FF848484FF848484FF848484FF8B8B
+    8BFFDEDEDEFFCECECEFFDEDEDEFFB4B4B4FF8B8B8BFFDEDEDEFFDEDEDEFFD6D6
+    D6FFCECECEFF848484FF848484FF848484FF848484FF646464FF646464FFFFFF
+    FF00FFFFFF00FFFFFF00101010FF6C6C6CFF848484FF848484FF848484FF8484
+    84FF848484FF848484FF848484FF737373FF6C6C6CFF848484FF848484FF8484
+    84FF848484FF848484FF6B6B95FF6978A7FF809393FF7B7B7BFF282828FFFFFF
+    FF00FFFFFF00FFFFFF00090909FF7C9E9EFF809B9BFF848484FF848484FF8484
+    84FF848484FF848484FF737373FF737373FF6C6C6CFF737373FF848484FF8484
+    84FF848484FF848484FF4C4CB4FF6287C3FF80A5A5FF7B7B7BFF090909FFFFFF
+    FF00FFFFFF00FFFFFF00090909FF7C9E9EFF80B5B5FF848484FF848484FF8484
+    84FF848484FF848484FF8B8B8BFF5B81B6FF4F75A9FF7B7B7BFF848484FF8484
+    84FF848484FF848484FF3131CEFF77B5C6FF809393FF7B7B7BFF090909FFFFFF
+    FF00FFFFFF00FFFFFF00101010FF728282FF7FC6C7FF848484FF848484FF8484
+    84FF848484FF848484FF848484FF5B81B6FF4D76AFFF848484FF848484FF8484
+    84FF848484FF7A7A86FF2929D6FF7FD6D7FF848484FF7B7B7BFF282828FFFFFF
+    FF00FFFFFF00FFFFFF00323232FF5A5A5AFF7FDCDCFF848484FF848484FF8484
+    84FF848484FF848484FF848484FF4777B9FF4777B9FF848484FF848484FF8484
+    84FF848484FF3939C6FF637AB4FF7FBFBFFF848484FF6C6C6CFF5A5A5AFFFFFF
+    FF00FFFFFF00FFFFFF00737373FF323232FF80AFAFFF80B5B5FF848484FF8484
+    84FF848484FF848484FF848484FF4777B9FF4777B9FF848484FF848484FF8484
+    84FF4F4FB0FF3E41C4FF7FDCDCFF848484FF848484FF323232FFB4B4B4FFFFFF
+    FF00FFFFFF00FFFFFF00DEDEDEFF090909FF7B7B7BFF7FD6D7FF809B9BFF8484
+    84FF848484FF848484FF848484FF4777B9FF4777B9FF848484FF70708FFF3131
+    CEFF3E41C4FF7FD6D7FF809B9BFF848484FF737373FF212121FFF7F7F7FFFFFF
+    FF00FFFFFF00FFFFFF00FFFFFF00535353FF424242FF848484FF7ECDD0FF80BD
+    BDFF848484FF848484FF848484FF4777B9FF2A5AD5FF2929D6FF3131CEFF6C8F
+    B8FF7FDCDCFF809393FF848484FF7B7B7BFF282828FFA4A4A4FFFFFFFF00FFFF
+    FF00FFFFFF00FFFFFF00FFFFFF00E7E7E7FF101010FF646464FF848484FF80A5
+    A5FF7FD6D7FF7FD1D2FF80BDBDFF4792D4FF3F8CDDFF7ECDD0FF7FD6D7FF80B5
+    B5FF848484FF848484FF848484FF424242FF5A5A5AFFFFFFFF00FFFFFF00FFFF
+    FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00B4B4B4FF101010FF6C6C6CFF8484
+    84FF848484FF848484FF809B9BFF4789CCFF4786C9FF808989FF848484FF8484
+    84FF848484FF7B7B7BFF424242FF393939FFF7F7F7FFFFFFFF00FFFFFF00FFFF
+    FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00ADADADFF101010FF5A5A
+    5AFF848484FF848484FF848484FF848484FF848484FF848484FF848484FF8484
+    84FF6C6C6CFF282828FF535353FFF7F7F7FFFFFFFF00FFFFFF00FFFFFF00FFFF
+    FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00CECECEFF3232
+    32FF282828FF646464FF7B7B7BFF7B7B7BFF7B7B7BFF7B7B7BFF6C6C6CFF3939
+    39FF212121FFA4A4A4FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
+    FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
+    FFFFB4B4B4FF646464FF282828FF101010FF101010FF282828FF5A5A5AFFADAD
+    ADFFF7F7F7FFFFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
+    FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
+    FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
+    FF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFFFF00FFFF
+    FF00000000000000000000000000000000000000000000000000000000000000
+    0000000000000000000000000000000000000000000000000000000000000000
+    0000000000000000000000000000000000000000000000000000000000000000
+    0000
+  }
+  OnCreate = FormCreate
+  OnShow = FormShow
+  Position = poMainFormCenter
+  LCLVersion = '3.6.0.0'
+  object Panel1: TPanel
+    Left = 0
+    Height = 53
+    Top = 600
+    Width = 615
+    Align = alBottom
+    ClientHeight = 53
+    ClientWidth = 615
+    ParentBackground = False
+    TabOrder = 1
+    object CloseBtn: TBitBtn
+      Left = 480
+      Height = 45
+      Top = 3
+      Width = 113
+      Caption = 'Close'
+      ImageIndex = 6
+      OnClick = CloseBtnClick
+      TabOrder = 0
+    end
+  end
+  object SGVersionLbl: TLabel
+    Left = 16
+    Height = 45
+    Top = 208
+    Width = 185
+    Caption = 'SGVersionLbl'
+    Font.CharSet = ANSI_CHARSET
+    Font.Height = -32
+    Font.Pitch = fpVariable
+    Font.Quality = fqDraft
+    ParentFont = False
+  end
+  object NameLbl: TLabel
+    Left = 16
+    Height = 54
+    Top = 64
+    Width = 550
+    Caption = 'Super Spinner Test Program'
+    Font.CharSet = ANSI_CHARSET
+    Font.Height = -40
+    Font.Name = 'Segoe UI Black'
+    Font.Pitch = fpVariable
+    Font.Quality = fqDraft
+    Font.Style = [fsBold]
+    ParentFont = False
+  end
+  object NameLbl1: TLabel
+    Left = 16
+    Height = 54
+    Top = 160
+    Width = 433
+    Caption = 'Super Spinner Version'
+    Font.CharSet = ANSI_CHARSET
+    Font.Height = -40
+    Font.Name = 'Segoe UI Black'
+    Font.Pitch = fpVariable
+    Font.Quality = fqDraft
+    Font.Style = [fsBold]
+    ParentFont = False
+  end
+  object SGTestVersionLbl: TLabel
+    Left = 16
+    Height = 45
+    Top = 112
+    Width = 241
+    Caption = 'SGTestVersionLbl'
+    Font.CharSet = ANSI_CHARSET
+    Font.Height = -32
+    Font.Pitch = fpVariable
+    Font.Quality = fqDraft
+    ParentFont = False
+  end
+  object Memo1: TMemo
+    Left = 0
+    Height = 280
+    Top = 320
+    Width = 615
+    Align = alBottom
+    Lines.Strings = (
+      'Super SpinnerAbout '
+      ''
+      ''
+      '-- TO DO''s (Feel Free to do them and submit) --'
+      '* Code cleaning, always in need from an expert'
+      '* Linux Testing (ARM and x86)'
+    )
+    ReadOnly = True
+    ScrollBars = ssBoth
+    TabOrder = 0
+    TabStop = False
+  end
+  object Image1: TImage
+    Left = 16
+    Height = 50
+    Top = 22
+    Width = 54
+    Picture.Data = {
+      1754506F727461626C654E6574776F726B477261706869632808000089504E47
+      0D0A1A0A0000000D494844520000003000000030080300000060DC09B5000003
+      00504C5445FFFFFF000000020202030303040404050505060606070707080808
+      0909090A0A0A0B0B0B0C0C0C0D0D0D0E0E0E0F0F0F1010101111111212121313
+      131414141515151616161717171818181919191A1A1A1B1B1B1C1C1C1D1D1D1E
+      1E1E1F1F1F202020212121222222232323242424252525262626272727282828
+      2929292A2A2A2B2B2B2C2C2C2D2D2D2E2E2E2F2F2F3030303131313232323333
+      333434343535353636363737373838383939393A3A3A3B3B3B3C3C3C3D3D3D3E
+      3E3E3F3F3F404040414141424242434343444444454545464646474747484848
+      4949494A4A4A4B4B4B4C4C4C4D4D4D4E4E4E4F4F4F5050505151515252525353
+      535454545555555656565757575858585959595A5A5A5B5B5B5C5C5C5D5D5D5E
+      5E5E5F5F5F606060616161626262636363646464656565666666676767686868
+      6969696A6A6A6B6B6B6C6C6C6D6D6D6E6E6E6F6F6F7070707171717272727373
+      737474747575757676767777777878787979797A7A7A7B7B7B7C7C7C7D7D7D7E
+      7E7E7F7F7F808080818181828282838383848484858585868686878787888888
+      8989898A8A8A8B8B8B8C8C8C8D8D8D8E8E8E8F8F8F9090909191919292929393
+      939494949595959696969797979898989999999A9A9A9B9B9B9C9C9C9D9D9D9E
+      9E9E9F9F9FA0A0A0A1A1A1A2A2A2A3A3A3A4A4A4A5A5A5A6A6A6A7A7A7A8A8A8
+      A9A9A9AAAAAAABABABACACACADADADAEAEAEAFAFAFB0B0B0B1B1B1B2B2B2B3B3
+      B3B4B4B4B5B5B5B6B6B6B7B7B7B8B8B8B9B9B9BABABABBBBBBBCBCBCBDBDBDBE
+      BEBEBFBFBFC0C0C0C1C1C1C2C2C2C3C3C3C4C4C4C5C5C5C6C6C6C7C7C7C8C8C8
+      C9C9C9CACACACBCBCBCCCCCCCDCDCDCECECECFCFCFD0D0D0D1D1D1D2D2D2D3D3
+      D3D4D4D4D5D5D5D6D6D6D7D7D7D8D8D8D9D9D9DADADADBDBDBDCDCDCDDDDDDDE
+      DEDEDFDFDFE0E0E0E1E1E1E2E2E2E3E3E3E4E4E4E5E5E5E6E6E6E7E7E7E8E8E8
+      E9E9E9EAEAEAEBEBEBECECECEDEDEDEEEEEEEFEFEFF0F0F0F1F1F1F2F2F2F3F3
+      F3F4F4F4F5F5F5F6F6F6F7F7F7F8F8F8F9F9F9FAFAFAFBFBFBFCFCFCFDFDFDFE
+      FEFEFFFFFF9571E25E0000000174524E530040E6D86600000001624B4744FFA5
+      07F2C500000009704859730002CFBC0002CFBC018797D5F7000004B449444154
+      48C7C5565B6C145518EE4F6F782901312AC65E424A8988696DBC34F24480A845
+      892962C048628DE1454385D06E4313342AC1AA2424220F8DA62A3E808D6D77B7
+      DB36BD41B7DBEEEEEC9E33B333BB337B87EE05DBDA8B965AA4EA1ECFCCEECEEE
+      B6151EF99ECE9CF37DF39FFF3FFFF966B2B2EE014AF22081BC92BBB3B7C8C4FC
+      E28A1D3B2A8AF3E571E91DE94594515E77D128780301AF3072B1AE9C4E14FD2F
+      FD058035073B7D21BFCB21359F961C2E7FC8A73D940DF0FCEAFC4701AAFB235E
+      16218CC586E32E8C1162BD9181BD008FACC6CF8707BF0E7B643685D8A4712903
+      843CE1F3EB206F253F1B4A87C2384E4F1350090E5FDD0AB09C9F07DB1D417B92
+      9F214042942F879C4CFEC3B0990B381DAB081072777FEF17CA60433AFF597860
+      28DA5A8F5C2BB684B057F7FA0921605C07156902807311DE50F3BE4D4C26AD54
+      49E6FB2EEFD360CE1EBE909EC626D815C1481A3878C4925088CD9FC9025AD6D6
+      BD1F3B053A19AD4E2B2E64F7D37A22F7F0E15A9384E4F76296552A265C78E54B
+      8997A7A4AB796A8812A88922253D53EDDBFD7499E564058791D8F6F2790FA7C4
+      44D1B7E00935830E2F520A229ADF3B2361D63CD8DBDDD367441C67D2BAD87856
+      C86350B380277D6AC9919543033A9DBEAB4BAF358CB19CA89E250E3C03BB147E
+      297C10424905E740BDDA2E8302BDD6C4A9748C4227128D9B0F3FF853D3EC80D6
+      90845E6765D515ECBB94386EC8353AD559D69C7CBF0C5D3F9B0A2198EF8B2701
+      853C970A30980A4043E899540E9C589A105478784954E042A8479F26E8D29958
+      2C2680FD5509C18BA1BEC6934D14F5E7786448DB91C1A0353AF0170DF25AC359
+      EEDA4E55D07BBC5EA3D1347CF8158FBB332268471CF8F431BAA639D69C12D02D
+      89AE3830EED3656CC9CC6257724DDD52A1904A9A1BD6762537450FAF1BA527BD
+      055696155B3A7F69EFE86CA7256D6F6F6FBB62B7A595F57E48DCCE1FD583B333
+      C1A99949DBB0A3ABA3DB353D1B7206BCAAC27B19B215C16638AAB606334D96A6
+      6748ECAFF69E0532F9EB3F844C59D4D66880C244F33D958C60F592F931F31596
+      DCBC14242EFD9875924C33C9248295B033D9DE5A6FBCE72D136451B031C373B7
+      077E27539C6DCCBAE4B1C5AD01B97BD4F62E8203D1F87DE00442C8ADD971276B
+      9B2524B6301532BCF989D3A92C460FC3E3EA15CD1974CBF5F3B5BDC110197F5F
+      B7F3F3CAE8D6CFAFD6637AD19164CC8734577D29221BCA4FAF9DC2E2F8EC6D4A
+      B430A2149DFF978EB4078E8C8AD404F6C1C6749BF9268CF896EA8F66096BB2DA
+      FD31F2B98398CC0C9A204BDEA1770EF5F2A1960CB7AC8002E3F8D0FEB3C20D32
+      8F2CA38E1839C59288D532729D2C30A2A5EEBBC8D87AD89E6E7DEB61ABD33D28
+      305112237FCC1132ABF1D14CE66E92988741BCE013B7C1BAE5E65DE91C473677
+      90BDB1B038E3E34FB65E73FEB6F8E78493DE20BB5F7C0ED62CB76F806D23216C
+      63EC5686B132AEA6A3BC4D1ED9683142A34FAFB4FBACAC1CD8D01291A829D21A
+      26CC58799022DF6E4C34D1323C0450331CF128FD9C306384DD11E37EC8B4FA14
+      2A01D6D6F604433E8193CE7C2A72822F14EC79772D64187D2636D1AF6C55639B
+      450A048301C9D2D65845271EBBE397BA58FE98179455EDDE5D5556208F8BEFFA
+      2FB0A72437F9EB905BB2E71EFCBAFC07F609FDFF63C8E2BD0000000049454E44
+      AE426082
+    }
+  end
+  object AuthorLbl: TLabel
+    Left = 72
+    Height = 32
+    Top = 264
+    Width = 429
+    Caption = 'Author : Sandy Ganz, [email protected]'
+    Font.CharSet = ANSI_CHARSET
+    Font.Height = -24
+    Font.Pitch = fpVariable
+    Font.Quality = fqDraft
+    ParentFont = False
+  end
+end

+ 60 - 0
test/test_superspinner/about.pas

@@ -0,0 +1,60 @@
+unit about;
+
+{$mode ObjFPC}{$H+}
+
+interface
+
+uses
+  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls,
+  Buttons, SuperSpinner;
+
+type
+
+  { TAboutFrm }
+
+  TAboutFrm = class(TForm)
+    CloseBtn: TBitBtn;
+    Image1: TImage;
+    AuthorLbl: TLabel;
+    Memo1: TMemo;
+    NameLbl: TLabel;
+    NameLbl1: TLabel;
+    Panel1: TPanel;
+    SGVersionLbl: TLabel;
+    SGTestVersionLbl: TLabel;
+    procedure CloseBtnClick(Sender: TObject);
+    procedure FormCreate(Sender: TObject);
+    procedure FormShow(Sender: TObject);
+  private
+
+  public
+    VersionStr: string;
+  end;
+
+var
+  AboutFrm: TAboutFrm;
+
+implementation
+
+{$R *.lfm}
+
+{ TAboutFrm }
+
+procedure TAboutFrm.FormCreate(Sender: TObject);
+begin
+  SGTestVersionLbl.Caption := '??';
+end;
+
+procedure TAboutFrm.FormShow(Sender: TObject);
+begin
+  SGTestVersionLbl.Caption := VersionStr;
+    SGVersionLbl.Caption := SuperSpinner.VERSIONSTR;
+end;
+
+procedure TAboutFrm.CloseBtnClick(Sender: TObject);
+begin
+  Close;
+end;
+
+end.
+

BIN
test/test_superspinner/project1.ico


+ 99 - 0
test/test_superspinner/project1.lpi

@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<CONFIG>
+  <ProjectOptions>
+    <Version Value="12"/>
+    <PathDelim Value="\"/>
+    <General>
+      <SessionStorage Value="InProjectDir"/>
+      <Title Value="project1"/>
+      <Scaled Value="True"/>
+      <ResourceType Value="res"/>
+      <UseXPManifest Value="True"/>
+      <XPManifest>
+        <DpiAware Value="True"/>
+      </XPManifest>
+      <Icon Value="0"/>
+    </General>
+    <BuildModes>
+      <Item Name="Default" Default="True"/>
+    </BuildModes>
+    <PublishOptions>
+      <Version Value="2"/>
+      <UseFileFilters Value="True"/>
+    </PublishOptions>
+    <RunParams>
+      <FormatVersion Value="2"/>
+    </RunParams>
+    <RequiredPackages>
+      <Item>
+        <PackageName Value="LazControls"/>
+      </Item>
+      <Item>
+        <PackageName Value="LazControlDsgn"/>
+      </Item>
+      <Item>
+        <PackageName Value="uEControls"/>
+      </Item>
+      <Item>
+        <PackageName Value="bgracontrols"/>
+      </Item>
+      <Item>
+        <PackageName Value="LCL"/>
+      </Item>
+    </RequiredPackages>
+    <Units>
+      <Unit>
+        <Filename Value="project1.lpr"/>
+        <IsPartOfProject Value="True"/>
+      </Unit>
+      <Unit>
+        <Filename Value="sstest.pas"/>
+        <IsPartOfProject Value="True"/>
+        <ComponentName Value="SSTestFrm"/>
+        <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
+      </Unit>
+      <Unit>
+        <Filename Value="about.pas"/>
+        <IsPartOfProject Value="True"/>
+        <ComponentName Value="AboutFrm"/>
+        <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
+      </Unit>
+    </Units>
+  </ProjectOptions>
+  <CompilerOptions>
+    <Version Value="11"/>
+    <PathDelim Value="\"/>
+    <Target>
+      <Filename Value="project1"/>
+    </Target>
+    <SearchPaths>
+      <IncludeFiles Value="$(ProjOutDir)"/>
+      <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
+    </SearchPaths>
+    <Linking>
+      <Debugging>
+        <DebugInfoType Value="dsDwarf3"/>
+      </Debugging>
+      <Options>
+        <Win32>
+          <GraphicApplication Value="True"/>
+        </Win32>
+      </Options>
+    </Linking>
+  </CompilerOptions>
+  <Debugging>
+    <Exceptions>
+      <Item>
+        <Name Value="EAbort"/>
+      </Item>
+      <Item>
+        <Name Value="ECodetoolError"/>
+      </Item>
+      <Item>
+        <Name Value="EFOpenError"/>
+      </Item>
+    </Exceptions>
+  </Debugging>
+</CONFIG>

+ 26 - 0
test/test_superspinner/project1.lpr

@@ -0,0 +1,26 @@
+program project1;
+
+{$mode objfpc}{$H+}
+
+uses
+  {$IFDEF UNIX}
+  cthreads,
+  {$ENDIF}
+  {$IFDEF HASAMIGA}
+  athreads,
+  {$ENDIF}
+  Interfaces, // this includes the LCL widgetset
+  Forms, lazcontrols, uecontrols, sstest, about
+  { you can add units after this };
+
+{$R *.res}
+
+begin
+  RequireDerivedFormResource:=True;
+  Application.Scaled:=True;
+  Application.Initialize;
+  Application.CreateForm(TSSTestFrm, SSTestFrm);
+  Application.CreateForm(TAboutFrm, AboutFrm);
+  Application.Run;
+end.
+

+ 1923 - 0
test/test_superspinner/sstest.lfm

@@ -0,0 +1,1923 @@
+object SSTestFrm: TSSTestFrm
+  Left = 525
+  Height = 877
+  Top = 153
+  Width = 1247
+  Caption = 'SSTestFrm'
+  ClientHeight = 877
+  ClientWidth = 1247
+  DesignTimePPI = 144
+  Menu = MainMenu1
+  OnCreate = FormCreate
+  OnDestroy = FormDestroy
+  LCLVersion = '3.6.0.0'
+  Scaled = False
+  object GaugeTs: TPageControl
+    Left = 536
+    Height = 877
+    Top = 0
+    Width = 711
+    ActivePage = BasicTab
+    Align = alRight
+    HotTrack = True
+    TabIndex = 0
+    TabOrder = 0
+    object BasicTab: TTabSheet
+      Caption = 'Basic'
+      ClientHeight = 839
+      ClientWidth = 703
+      ParentShowHint = False
+      object GroupBox1: TGroupBox
+        Left = 8
+        Height = 280
+        Top = 16
+        Width = 248
+        Caption = 'Size'
+        ClientHeight = 250
+        ClientWidth = 244
+        ParentBackground = False
+        TabOrder = 0
+        object WidthAddBtn: TBitBtn
+          Left = 8
+          Height = 45
+          Hint = 'Increase Width'
+          Top = 40
+          Width = 113
+          Caption = 'Width'
+          Images = ImageList1
+          ImageIndex = 0
+          OnClick = WidthAddBtnClick
+          ParentShowHint = False
+          ShowHint = True
+          TabOrder = 0
+        end
+        object WidthSubBtn: TBitBtn
+          Left = 128
+          Height = 45
+          Hint = 'Decrease Width'
+          Top = 40
+          Width = 113
+          Caption = 'Width'
+          Images = ImageList1
+          ImageIndex = 1
+          OnClick = WidthSubBtnClick
+          ParentShowHint = False
+          ShowHint = True
+          TabOrder = 1
+        end
+        object HeightAddBtn: TBitBtn
+          Left = 8
+          Height = 45
+          Hint = 'Increase Height'
+          Top = 88
+          Width = 113
+          Caption = 'Height'
+          Images = ImageList1
+          ImageIndex = 0
+          OnClick = HeightAddBtnClick
+          ParentShowHint = False
+          ShowHint = True
+          TabOrder = 2
+        end
+        object HeightSubBtn: TBitBtn
+          Left = 128
+          Height = 45
+          Hint = 'Decrease Height'
+          Top = 88
+          Width = 113
+          Caption = 'Height'
+          Images = ImageList1
+          ImageIndex = 1
+          OnClick = HeightSubBtnClick
+          ParentShowHint = False
+          ShowHint = True
+          TabOrder = 3
+        end
+        object ResetSizeBtn: TBitBtn
+          Left = 9
+          Height = 45
+          Hint = 'Reset to Default'
+          Top = 192
+          Width = 232
+          Caption = 'Reset Size'
+          Images = ImageList1
+          ImageIndex = 7
+          OnClick = ResetSizeBtnClick
+          ParentShowHint = False
+          ShowHint = True
+          TabOrder = 4
+        end
+        object WidthLbl: TLabel
+          Left = 8
+          Height = 25
+          Top = 0
+          Width = 62
+          Caption = 'Width : '
+        end
+        object WidthValLbl: TLabel
+          Left = 72
+          Height = 25
+          Top = 0
+          Width = 35
+          Caption = 'N/A'
+          Font.Style = [fsBold]
+          ParentFont = False
+        end
+        object HeightLbl: TLabel
+          Left = 128
+          Height = 25
+          Top = 0
+          Width = 67
+          Caption = 'Height : '
+        end
+        object HeightValLbl: TLabel
+          Left = 192
+          Height = 25
+          Top = 0
+          Width = 35
+          Caption = 'N/A'
+          Font.Style = [fsBold]
+          ParentFont = False
+        end
+        object WidthHeightAddBtn: TBitBtn
+          Left = 9
+          Height = 45
+          Hint = 'Increase Size Width and Height, This is a SQUARE compoent '#13#10'so will alway fit the minimum sized Width or Height. This does'#13#10'them together.'
+          Top = 136
+          Width = 113
+          Caption = 'W&&H Size'
+          Images = ImageList1
+          ImageIndex = 0
+          OnClick = WidthHeightAddBtnClick
+          ParentShowHint = False
+          ShowHint = True
+          TabOrder = 5
+        end
+        object WidthHeightSubBtn: TBitBtn
+          Left = 128
+          Height = 45
+          Hint = 'Decrease Size Width and Height, This is a SQUARE compoent '#13#10'so will alway fit the minimum sized Width or Height. This does'#13#10'them together.'
+          Top = 136
+          Width = 113
+          Caption = 'W&&H Size'
+          Images = ImageList1
+          ImageIndex = 1
+          OnClick = WidthHeightSubBtnClick
+          ParentShowHint = False
+          ShowHint = True
+          TabOrder = 6
+        end
+      end
+      object GroupBox2: TGroupBox
+        Left = 264
+        Height = 280
+        Top = 16
+        Width = 248
+        Caption = 'Position'
+        ClientHeight = 250
+        ClientWidth = 244
+        ParentBackground = False
+        TabOrder = 1
+        object LeftAddBtn: TBitBtn
+          Left = 8
+          Height = 45
+          Hint = 'Increase Left'
+          Top = 40
+          Width = 113
+          Caption = 'Left'
+          Images = ImageList1
+          ImageIndex = 0
+          OnClick = LeftAddBtnClick
+          ParentShowHint = False
+          ShowHint = True
+          TabOrder = 0
+        end
+        object TopSubBtn: TBitBtn
+          Left = 120
+          Height = 45
+          Hint = 'Decrease Top'
+          Top = 88
+          Width = 113
+          Caption = 'Top'
+          Images = ImageList1
+          ImageIndex = 1
+          OnClick = TopSubBtnClick
+          ParentShowHint = False
+          ShowHint = True
+          TabOrder = 1
+        end
+        object TopAddBtn: TBitBtn
+          Left = 8
+          Height = 45
+          Hint = 'Increase Top'
+          Top = 88
+          Width = 113
+          Caption = 'Top'
+          Images = ImageList1
+          ImageIndex = 0
+          OnClick = TopAddBtnClick
+          ParentShowHint = False
+          ShowHint = True
+          TabOrder = 2
+        end
+        object LeftSubBtn: TBitBtn
+          Left = 120
+          Height = 45
+          Hint = 'Decrease Left'
+          Top = 40
+          Width = 113
+          Caption = 'Left'
+          Images = ImageList1
+          ImageIndex = 1
+          OnClick = LeftSubBtnClick
+          ParentShowHint = False
+          ShowHint = True
+          TabOrder = 3
+        end
+        object ResetPositionBtn: TBitBtn
+          Left = 8
+          Height = 45
+          Hint = 'Reset To Default'
+          Top = 192
+          Width = 232
+          Caption = 'Reset Position'
+          Images = ImageList1
+          ImageIndex = 7
+          OnClick = ResetPositionBtnClick
+          ParentShowHint = False
+          ShowHint = True
+          TabOrder = 4
+        end
+        object LeftLbl: TLabel
+          Left = 8
+          Height = 25
+          Top = 0
+          Width = 43
+          Caption = 'Left : '
+        end
+        object LeftValLbl: TLabel
+          Left = 72
+          Height = 25
+          Top = 0
+          Width = 35
+          Caption = 'N/A'
+          Font.Style = [fsBold]
+          ParentFont = False
+        end
+        object TopLbl: TLabel
+          Left = 128
+          Height = 25
+          Top = 0
+          Width = 43
+          Caption = 'Top : '
+        end
+        object TopValLbl: TLabel
+          Left = 192
+          Height = 25
+          Top = 0
+          Width = 35
+          Caption = 'N/A'
+          Font.Style = [fsBold]
+          ParentFont = False
+        end
+      end
+      object Memo2: TMemo
+        Left = 264
+        Height = 520
+        Top = 304
+        Width = 424
+        Lines.Strings = (
+          'Basic Settings'
+          ''
+          'Setting the color here is for the background'
+          'of the client area of the control.'
+          ''
+          'Spin Resolution is the number of detents '
+          'that it takes to move the spinner. These '
+          'are somewhat abstracted.'
+          ''
+          'Mouse Wheel Speed determines how many '
+          'degrees the mouse will move the wheel, but '
+          'one movement of any degrees is one event.'
+          ''
+          'Position Snap If ENABLED will snap the spinner'
+          'to the mouse position if clicked'
+          ''
+          'Spinner Locked will stop events for specific'
+          'Spinner functions, movement, in/out, etc.'
+          ''
+          'Auto Scale - Hover Over the Check Box!!'
+        )
+        TabOrder = 2
+      end
+      object GroupBox5: TGroupBox
+        Left = 8
+        Height = 320
+        Top = 312
+        Width = 248
+        Caption = 'Basic'
+        ClientHeight = 290
+        ClientWidth = 244
+        TabOrder = 3
+        object BackgroundColorLbl: TLabel
+          Left = 8
+          Height = 25
+          Top = 0
+          Width = 43
+          Caption = 'Color'
+        end
+        object BackgroundColorCb: TColorBox
+          Left = 64
+          Height = 26
+          Hint = 'Client Color'
+          Top = 0
+          Width = 171
+          Style = [cbStandardColors, cbExtendedColors, cbIncludeNone, cbCustomColor]
+          ItemHeight = 20
+          OnChange = BackgroundColorCbChange
+          ParentShowHint = False
+          ShowHint = True
+          TabOrder = 0
+        end
+        object SpinResolutionCb: TComboBox
+          Left = 8
+          Height = 33
+          Hint = 'Resolution of the spinner movements.'#13#10'srHighest - Maximum from mouse movements'#13#10'srHigh, '#13#10'srHighMedium, '#13#10'srMedium, '#13#10'srMediumLow'#13#10'srLow, '#13#10'srLowest - Slowest Like Old iPod, notchy'#13#10#13#10'These are really hiding the number of clicks per revolution since '#13#10'that might not always be the same and you should NOT rely'#13#10'on them for accuracy'
+          Top = 64
+          Width = 168
+          ItemHeight = 25
+          Items.Strings = (
+            'srHighest'
+            'srHigh'
+            'srHighMedium'
+            'srMedium'
+            'srMediumLow'
+            'srLow'
+            'srLowest'
+          )
+          ParentShowHint = False
+          ShowHint = True
+          TabOrder = 1
+          OnChange = SpinResolutionCbChange
+        end
+        object FaceFillStyleLbl1: TLabel
+          Left = 10
+          Height = 25
+          Top = 32
+          Width = 123
+          Caption = 'Spin Resolution'
+        end
+        object SpinnerLockedCb: TCheckBox
+          Left = 10
+          Height = 29
+          Hint = 'Locks movement and clicks on Cap and Knob. General component '#13#10'OnClick handlers will still fire.'#13#10#13#10'Typically this is to prevent movent and a few other actions including'#13#10'some Mouse Events specific to the Spinner but NOT the overall compoent'
+          Top = 224
+          Width = 146
+          Caption = 'Spinner Locked'
+          ParentShowHint = False
+          ShowHint = True
+          TabOrder = 2
+          OnChange = SpinnerLockedCbChange
+        end
+        object MouseWheelSpeedTB: TBCTrackbarUpdown
+          Left = 9
+          Height = 34
+          Hint = '0 - Mouse Wheel Disabled'#13#10'1 - Slowest/Highest Resolution'#13#10'255 - Fastest/Lowest Resolution'
+          Top = 143
+          Width = 85
+          AllowNegativeValues = False
+          BarExponent = 1
+          Increment = 1
+          LongTimeInterval = 400
+          MinValue = 0
+          MaxValue = 255
+          OnChange = MouseWheelSpeedTBChange
+          Value = 50
+          ShortTimeInterval = 100
+          Background.Color = clWindow
+          Background.Gradient1.StartColor = clWhite
+          Background.Gradient1.EndColor = clBlack
+          Background.Gradient1.GradientType = gtLinear
+          Background.Gradient1.Point1XPercent = 0
+          Background.Gradient1.Point1YPercent = 0
+          Background.Gradient1.Point2XPercent = 0
+          Background.Gradient1.Point2YPercent = 100
+          Background.Gradient2.StartColor = clWhite
+          Background.Gradient2.EndColor = clBlack
+          Background.Gradient2.GradientType = gtLinear
+          Background.Gradient2.Point1XPercent = 0
+          Background.Gradient2.Point1YPercent = 0
+          Background.Gradient2.Point2XPercent = 0
+          Background.Gradient2.Point2YPercent = 100
+          Background.Gradient1EndPercent = 35
+          Background.Style = bbsColor
+          ButtonBackground.Gradient1.StartColor = clBtnShadow
+          ButtonBackground.Gradient1.EndColor = clBtnFace
+          ButtonBackground.Gradient1.GradientType = gtLinear
+          ButtonBackground.Gradient1.Point1XPercent = 0
+          ButtonBackground.Gradient1.Point1YPercent = -50
+          ButtonBackground.Gradient1.Point2XPercent = 0
+          ButtonBackground.Gradient1.Point2YPercent = 50
+          ButtonBackground.Gradient2.StartColor = clBtnFace
+          ButtonBackground.Gradient2.EndColor = clBtnShadow
+          ButtonBackground.Gradient2.GradientType = gtLinear
+          ButtonBackground.Gradient2.Point1XPercent = 0
+          ButtonBackground.Gradient2.Point1YPercent = 50
+          ButtonBackground.Gradient2.Point2XPercent = 0
+          ButtonBackground.Gradient2.Point2YPercent = 150
+          ButtonBackground.Gradient1EndPercent = 50
+          ButtonBackground.Style = bbsGradient
+          ButtonDownBackground.Color = clBtnShadow
+          ButtonDownBackground.Gradient1.StartColor = clWhite
+          ButtonDownBackground.Gradient1.EndColor = clBlack
+          ButtonDownBackground.Gradient1.GradientType = gtLinear
+          ButtonDownBackground.Gradient1.Point1XPercent = 0
+          ButtonDownBackground.Gradient1.Point1YPercent = 0
+          ButtonDownBackground.Gradient1.Point2XPercent = 0
+          ButtonDownBackground.Gradient1.Point2YPercent = 100
+          ButtonDownBackground.Gradient2.StartColor = clWhite
+          ButtonDownBackground.Gradient2.EndColor = clBlack
+          ButtonDownBackground.Gradient2.GradientType = gtLinear
+          ButtonDownBackground.Gradient2.Point1XPercent = 0
+          ButtonDownBackground.Gradient2.Point1YPercent = 0
+          ButtonDownBackground.Gradient2.Point2XPercent = 0
+          ButtonDownBackground.Gradient2.Point2YPercent = 100
+          ButtonDownBackground.Gradient1EndPercent = 35
+          ButtonDownBackground.Style = bbsColor
+          Border.Color = clWindowText
+          Border.Style = bboSolid
+          Rounding.RoundX = 1
+          Rounding.RoundY = 1
+          Font.Color = clWindowText
+          Font.Name = 'Arial'
+          HasTrackBar = True
+          ArrowColor = clBtnText
+          ParentShowHint = False
+          ShowHint = True
+          TabOrder = 3
+          TabStop = True
+          UseDockManager = False
+        end
+        object Label14: TLabel
+          Left = 10
+          Height = 25
+          Top = 112
+          Width = 193
+          Caption = 'Set Mouse Wheel Speed'
+        end
+        object PositionSnapCb: TCheckBox
+          Left = 9
+          Height = 29
+          Hint = 'Position Snap if ENABLED will cause the Spinners position to Advance'#13#10'to the point where the Mouse Clicked to Move. This Will NOT call the '#13#10'position movement callback, just simply sets the indicator.'#13#10#13#10'IF Position Snap is DISABLED then the mouse pointer can grab the'#13#10'spinner at any point and spin without having the position jumping'#13#10'to the point where the mouse was clicked. You are Welcome.'
+          Top = 192
+          Width = 134
+          Caption = 'Position Snap'
+          ParentShowHint = False
+          ShowHint = True
+          TabOrder = 4
+          OnChange = PositionSnapCbChange
+        end
+        object MouseWheelDisabledLbl: TLabel
+          Left = 105
+          Height = 25
+          Hint = 'Set Mouse Wheel Speed > 0 to Enable'
+          Top = 152
+          Width = 132
+          Caption = 'Wheel Disabled'
+          Font.Color = clRed
+          Font.Style = [fsBold]
+          ParentFont = False
+          ParentShowHint = False
+          ShowHint = True
+          Visible = False
+        end
+        object AutoScaleCb: TCheckBox
+          Left = 10
+          Height = 29
+          Hint = 'Auto Scale causes the control to automatically scale many elements as the size '#13#10'changes.'#13#10#13#10'This can be helpful if the display system is in highDPI mode or scaling. '#13#10#13#10'Also if the component does change size at runtime due to the Form''s ''Scaled'' '#13#10'property being set, it''s better to have the Spinner enable ''Auto Scale''.'#13#10#13#10'To stop all scaling on the form, set the Forms property Scaled = False.'#13#10#13#10'If you don''t care or won''t any Spinner scaling, you can set the Auto Scale property of the '#13#10'Spinner to False.'#13#10#13#10'All scaling of the Spinner is based on a Nominal Width and Height of 150 '#13#10'Pixels.'
+          Top = 256
+          Width = 109
+          Caption = 'Auto Scale'
+          ParentShowHint = False
+          ShowHint = True
+          TabOrder = 5
+          OnChange = AutoScaleCbChange
+        end
+      end
+    end
+    object FrameTab: TTabSheet
+      Caption = 'Frame'
+      ClientHeight = 839
+      ClientWidth = 703
+      object FrameBorderColorLbl: TLabel
+        Left = 8
+        Height = 25
+        Top = 24
+        Width = 101
+        Caption = 'Border Color'
+      end
+      object FrameBorderColorCb: TColorBox
+        Left = 136
+        Height = 26
+        Hint = 'Sets the Frames Border Color which is around the spinner.'
+        Top = 23
+        Width = 171
+        Style = [cbStandardColors, cbExtendedColors, cbIncludeNone, cbCustomColor]
+        ItemHeight = 20
+        OnChange = FrameBorderColorCbChange
+        ParentShowHint = False
+        ShowHint = True
+        TabOrder = 0
+      end
+      object FrameRadiusSpe: TSpinEditEx
+        Left = 136
+        Height = 33
+        Hint = 'Sets the Border Width'
+        Top = 56
+        Width = 97
+        MaxLength = 0
+        ParentShowHint = False
+        ShowHint = True
+        TabOrder = 1
+        OnChange = FrameRadiusSpeChange
+        MaxValue = 1000
+        MinRepeatValue = 10
+        NullValue = 0
+        Value = 0
+      end
+      object FrameBorderWidthLbl: TLabel
+        Left = 8
+        Height = 25
+        Top = 64
+        Width = 106
+        Caption = 'Border Width'
+      end
+      object Memo3: TMemo
+        Left = 8
+        Height = 400
+        Top = 152
+        Width = 680
+        Lines.Strings = (
+          'The Frame is the circle around the spinner. Currently you can set this with the'
+          'Border Color Selector.'
+          ''
+          'You can also set the width of the border. This will correctly resize the Knob'
+          'portion of the control but not currently other aspects such as the '
+          'Position or Cap. '
+          ''
+          'The Frame can be useful by chaning the color to indicate things such as'
+          'errors, active control, clicked, etc.'
+        )
+        TabOrder = 2
+      end
+    end
+    object FaceTab: TTabSheet
+      Caption = 'Knob'
+      ClientHeight = 839
+      ClientWidth = 703
+      object KnobFillColorCb: TColorBox
+        Left = 120
+        Height = 26
+        Hint = 'Knobs Fill Color'
+        Top = 56
+        Width = 171
+        Style = [cbStandardColors, cbExtendedColors, cbCustomColor]
+        ItemHeight = 20
+        OnChange = KnobFillColorCbChange
+        ParentShowHint = False
+        ShowHint = True
+        TabOrder = 0
+      end
+      object KnobEdgeColorCb: TColorBox
+        Left = 120
+        Height = 26
+        Hint = 'Knobs Edge Color'
+        Top = 88
+        Width = 171
+        Style = [cbStandardColors, cbExtendedColors, cbCustomColor]
+        ItemHeight = 20
+        OnChange = KnobEdgeColorCbChange
+        ParentShowHint = False
+        ShowHint = True
+        TabOrder = 1
+      end
+      object KnobEdgeColorLbl: TLabel
+        Left = 8
+        Height = 25
+        Top = 89
+        Width = 88
+        Caption = 'Edge Color'
+      end
+      object KnobFillColorLbl: TLabel
+        Left = 8
+        Height = 25
+        Top = 57
+        Width = 69
+        Caption = 'Fill Color'
+      end
+      object KnobStyleLbl: TLabel
+        Left = 8
+        Height = 25
+        Top = 24
+        Width = 37
+        Caption = 'Style'
+      end
+      object KnobStyleCb: TComboBox
+        Left = 120
+        Height = 33
+        Hint = 'Shading Style of the Knob as   TSSStyle '#13#10#13#10'ssFlat - Solid Fill Color'#13#10'ssShaded - Shaded using Edge Color to Fill Color'#13#10'ssPhong - Phong Shading, more 3D'
+        Top = 16
+        Width = 157
+        ItemHeight = 25
+        Items.Strings = (
+          'ssFlat'
+          'ssShaded'
+          'ssPhong'
+        )
+        ParentShowHint = False
+        ShowHint = True
+        TabOrder = 2
+        OnChange = KnobStyleCbChange
+      end
+      object KnobCurveExponentSpe: TFloatSpinEditEx
+        Left = 191
+        Height = 33
+        Hint = 'Shader Curve, Typically for Phong shader'
+        Top = 208
+        Width = 97
+        MaxLength = 0
+        ParentShowHint = False
+        ShowHint = True
+        TabOrder = 3
+        OnChange = KnobCurveExponentSpeChange
+        Increment = 0.01
+        MaxValue = 10
+        MinValue = -10
+        MinRepeatValue = 10
+      end
+      object KnobCurveExponentLbl: TLabel
+        Left = 8
+        Height = 25
+        Top = 216
+        Width = 124
+        Caption = 'Curve Exponent'
+      end
+      object KnobLightIntensitySpe: TSpinEditEx
+        Left = 191
+        Height = 33
+        Hint = 'Light Intensity of the Shader'
+        Top = 168
+        Width = 97
+        MaxLength = 0
+        ParentShowHint = False
+        ShowHint = True
+        TabOrder = 4
+        OnChange = KnobLightIntensitySpeChange
+        MaxValue = 1000
+        MinValue = -1000
+        MinRepeatValue = 10
+        NullValue = 0
+        Value = 0
+      end
+      object KnobLightIntensityLbl: TLabel
+        Left = 9
+        Height = 25
+        Top = 176
+        Width = 111
+        Caption = 'Light Intensity'
+      end
+      object FaceMemo: TMemo
+        Left = 9
+        Height = 440
+        Top = 360
+        Width = 681
+        Lines.Strings = (
+          'Set up various face parameters. '
+          ''
+          'For shading, several options, Phong gives pleasing results.'
+          ''
+          'The Knob portion of the control (Excluding Frame, Client Area, and Cap) '
+          'can trigger specific Knob related events.'
+          ''
+          'The Curve Exponent is for the Phong Shader, and can'
+          'give some nice results. This only applies to the Phong '
+          'shader, values less that 1.0 give good results, Light Intensity'
+          'is basically brightness of the shade'
+          ''
+          'For Phong shading, it''s nice to follow the same settings'
+          'as the pointer cap. Also try using inner and outer colors'
+          'as the same with Phong shading as a start, clBlack/clBlack'
+          'to see the effect.'
+        )
+        TabOrder = 5
+      end
+      object KnobEdgeThicknessLbl: TLabel
+        Left = 8
+        Height = 25
+        Top = 136
+        Width = 120
+        Caption = 'Edge Thickness'
+      end
+      object KnobEdgeThicknessSpe: TSpinEditEx
+        Left = 191
+        Height = 33
+        Hint = 'Edge Thickness'
+        Top = 128
+        Width = 97
+        MaxLength = 0
+        ParentShowHint = False
+        ShowHint = True
+        TabOrder = 6
+        OnChange = KnobEdgeThicknessSpeChange
+        MaxValue = 1000
+        MinRepeatValue = 10
+        NullValue = 0
+        Value = 0
+      end
+    end
+    object PointerTab: TTabSheet
+      Caption = 'Position'
+      ClientHeight = 839
+      ClientWidth = 703
+      object PointerFillColorLbl: TLabel
+        Left = 8
+        Height = 25
+        Top = 57
+        Width = 69
+        Caption = 'Fill Color'
+      end
+      object PositionFillColorCb: TColorBox
+        Left = 120
+        Height = 26
+        Hint = 'Position Fill Color'
+        Top = 56
+        Width = 177
+        Style = [cbStandardColors, cbExtendedColors, cbCustomColor]
+        ItemHeight = 20
+        OnChange = PositionFillColorCbChange
+        ParentShowHint = False
+        ShowHint = True
+        TabOrder = 0
+      end
+      object PositionRadiusSpe: TSpinEditEx
+        Left = 200
+        Height = 33
+        Hint = 'Radiusof the Position Indicator, may not apply to Line type'
+        Top = 168
+        Width = 97
+        MaxLength = 0
+        ParentShowHint = False
+        ShowHint = True
+        TabOrder = 1
+        OnChange = PositionRadiusSpeChange
+        MaxValue = 1000
+        MinRepeatValue = 10
+        NullValue = 0
+        Value = 0
+      end
+      object PositionRadiusLbl: TLabel
+        Left = 8
+        Height = 25
+        Top = 176
+        Width = 72
+        Caption = 'Diameter'
+      end
+      object PositionMarginLbl: TLabel
+        Left = 8
+        Height = 25
+        Top = 256
+        Width = 56
+        Caption = 'Margin'
+      end
+      object PositionMarginSpe: TSpinEditEx
+        Left = 199
+        Height = 33
+        Hint = 'The Margin from the EDGE of the Knob for the '#13#10'chosen Position indicator including Line type'
+        Top = 248
+        Width = 97
+        MaxLength = 0
+        ParentShowHint = False
+        ShowHint = True
+        TabOrder = 2
+        OnChange = PositionMarginSpeChange
+        MaxValue = 1000
+        MinRepeatValue = 10
+        NullValue = 0
+        Value = 0
+      end
+      object PositonMemo: TMemo
+        Left = 24
+        Height = 336
+        Top = 480
+        Width = 647
+        Lines.Strings = (
+          'Position is where the wheel indicator is. This can be a couple of types or omitted.'
+          ''
+          'The Line style is similar to an old iPod style, color, count, margins are all '
+          'adjustable for a nice visual experience. '
+          ''
+          'Several Circular ''Finger'' hole styles are also available. Some Flat, Shaded, etc.'
+          ''
+          'Not some setttings do not affect the display depending on the Style.'
+          'For example Line Count and Line Width don''t affect any of the circular'
+          'style Position indicators.'
+        )
+        TabOrder = 3
+      end
+      object PositionStyleCb: TComboBox
+        Left = 120
+        Height = 33
+        Hint = 'Set the Style for the Positoin Indicator as TSSPositionStyle'#13#10#13#10'psFilledCircle - Solid Flat Circle'#13#10'psHollowCircle - Outline of a Circle, no Fill'#13#10'psShaded - Simple Shading'#13#10'psIndentCircle - Indented looking Finger Hole'#13#10'psLines - Radial Lines (like old iPod, etc)'#13#10'psNone - No indicator'
+        Top = 16
+        Width = 176
+        ItemHeight = 25
+        Items.Strings = (
+          'psNone'
+          'psFilledCircle'
+          'psHollowCircle'
+          'psShaded'
+          'psIndentCircle '
+          'psLines'
+          ''
+        )
+        ParentShowHint = False
+        ShowHint = True
+        TabOrder = 4
+        OnChange = PositionStyleCbChange
+      end
+      object PositionStyleLbl: TLabel
+        Left = 8
+        Height = 25
+        Top = 24
+        Width = 37
+        Caption = 'Style'
+      end
+      object KnobEdgeColorLbl1: TLabel
+        Left = 8
+        Height = 25
+        Top = 89
+        Width = 88
+        Caption = 'Edge Color'
+      end
+      object PositionEdgeColorCb: TColorBox
+        Left = 120
+        Height = 26
+        Hint = 'Position Edge Color'
+        Top = 88
+        Width = 176
+        Style = [cbStandardColors, cbExtendedColors, cbCustomColor]
+        ItemHeight = 20
+        OnChange = PositionEdgeColorCbChange
+        ParentShowHint = False
+        ShowHint = True
+        TabOrder = 5
+      end
+      object PositionEdgeThicknessLbl: TLabel
+        Left = 8
+        Height = 25
+        Top = 216
+        Width = 120
+        Caption = 'Edge Thickness'
+      end
+      object PositionEdgeThicknessSpe: TSpinEditEx
+        Left = 199
+        Height = 33
+        Hint = 'Edge Thickness around the Position indicator, may not apply to Line type of indicator'
+        Top = 208
+        Width = 97
+        MaxLength = 0
+        ParentShowHint = False
+        ShowHint = True
+        TabOrder = 6
+        OnChange = PositionEdgeThicknessSpeChange
+        MaxValue = 1000
+        MinRepeatValue = 10
+        NullValue = 0
+        Value = 0
+      end
+      object PositionCenterMarginLbl: TLabel
+        Left = 9
+        Height = 25
+        Top = 296
+        Width = 112
+        Caption = 'Center Margin'
+      end
+      object PositionCenterMarginSpe: TSpinEditEx
+        Left = 200
+        Height = 33
+        Hint = 'The Margin from the CENTER of the Knob, Typically for Line Type'
+        Top = 288
+        Width = 97
+        MaxLength = 0
+        ParentShowHint = False
+        ShowHint = True
+        TabOrder = 7
+        OnChange = PositionCenterMarginSpeChange
+        MaxValue = 1000
+        MinRepeatValue = 10
+        NullValue = 0
+        Value = 0
+      end
+      object PositionLineCountLbl: TLabel
+        Left = 9
+        Height = 25
+        Top = 336
+        Width = 84
+        Caption = 'Line Count'
+      end
+      object PositionLineCountSpe: TSpinEditEx
+        Left = 199
+        Height = 33
+        Hint = 'Length of the Radial Lines for Position Line type'
+        Top = 328
+        Width = 97
+        MaxLength = 0
+        ParentShowHint = False
+        ShowHint = True
+        TabOrder = 8
+        OnChange = PositionLineCountSpeChange
+        MaxValue = 1000
+        MinRepeatValue = 10
+        NullValue = 0
+        Value = 0
+      end
+      object PositonLineWidthLbl: TLabel
+        Left = 8
+        Height = 25
+        Top = 376
+        Width = 84
+        Caption = 'Line Width'
+      end
+      object PositionLineWidthSpe: TSpinEditEx
+        Left = 199
+        Height = 33
+        Hint = 'Width of the Radial Lines for  Position Line type'
+        Top = 368
+        Width = 97
+        MaxLength = 0
+        ParentShowHint = False
+        ShowHint = True
+        TabOrder = 9
+        OnChange = PositionLineWidthSpeChange
+        MaxValue = 1000
+        MinRepeatValue = 10
+        NullValue = 0
+        Value = 0
+      end
+      object PositonFillOpacityLbl: TLabel
+        Left = 8
+        Height = 25
+        Top = 136
+        Width = 135
+        Caption = 'Fill Color Opacity'
+      end
+      object PositionFillOpacitySpe: TSpinEditEx
+        Left = 200
+        Height = 33
+        Hint = 'Opacity of the Positions Fill Color'
+        Top = 128
+        Width = 97
+        MaxLength = 0
+        ParentShowHint = False
+        ShowHint = True
+        TabOrder = 10
+        OnChange = PositionFillOpacitySpeChange
+        MaxValue = 255
+        MinRepeatValue = 10
+        NullValue = 0
+        Value = 0
+      end
+      object Label2: TLabel
+        Left = 8
+        Height = 25
+        Top = 416
+        Width = 488
+        Caption = 'Center Margin, Line Count and Line Width are for psLines only'
+      end
+    end
+    object CapTab: TTabSheet
+      Caption = 'Cap'
+      ClientHeight = 839
+      ClientWidth = 703
+      object CapStyleLbl: TLabel
+        Left = 8
+        Height = 25
+        Top = 24
+        Width = 37
+        Caption = 'Style'
+      end
+      object CapStyleCb: TComboBox
+        Left = 120
+        Height = 33
+        Hint = 'Cap Style'
+        Top = 16
+        Width = 174
+        ItemHeight = 25
+        Items.Strings = (
+          'csNone'
+          'csFlat'
+          'csShaded'
+          'csPhong'
+          'csOutline'
+        )
+        ParentShowHint = False
+        ShowHint = True
+        TabOrder = 0
+        OnChange = CapStyleCbChange
+      end
+      object CapCurveExponentSpe: TFloatSpinEditEx
+        Left = 184
+        Height = 33
+        Hint = 'Shader Curve, Typically for Phong shader'
+        Top = 248
+        Width = 97
+        MaxLength = 0
+        ParentShowHint = False
+        ShowHint = True
+        TabOrder = 1
+        OnChange = CapCurveExponentSpeChange
+        Increment = 0.01
+        MaxValue = 10
+        MinValue = -10
+        MinRepeatValue = 10
+      end
+      object CapCurveExponentLbl: TLabel
+        Left = 8
+        Height = 25
+        Top = 256
+        Width = 124
+        Caption = 'Curve Exponent'
+      end
+      object CapEdgeColorLbl: TLabel
+        Left = 8
+        Height = 25
+        Top = 89
+        Width = 88
+        Caption = 'Edge Color'
+      end
+      object CapEdgeColorCb: TColorBox
+        Left = 120
+        Height = 26
+        Hint = 'Edge Color'
+        Top = 88
+        Width = 176
+        Style = [cbStandardColors, cbExtendedColors, cbCustomColor]
+        ItemHeight = 20
+        OnChange = CapEdgeColorCbChange
+        ParentShowHint = False
+        ShowHint = True
+        TabOrder = 2
+      end
+      object CapFillColorLbl: TLabel
+        Left = 8
+        Height = 25
+        Top = 57
+        Width = 69
+        Caption = 'Fill Color'
+      end
+      object CapFillColorCb: TColorBox
+        Left = 120
+        Height = 26
+        Hint = 'Fill Color'
+        Top = 56
+        Width = 176
+        Style = [cbStandardColors, cbExtendedColors, cbCustomColor]
+        ItemHeight = 20
+        OnChange = CapFillColorCbChange
+        ParentShowHint = False
+        ShowHint = True
+        TabOrder = 3
+      end
+      object CapLightIntensitySpe: TSpinEditEx
+        Left = 184
+        Height = 33
+        Hint = 'Light Intensity of the Shader'
+        Top = 208
+        Width = 97
+        MaxLength = 0
+        ParentShowHint = False
+        ShowHint = True
+        TabOrder = 4
+        OnChange = CapLightIntensitySpeChange
+        MaxValue = 1000
+        MinRepeatValue = 10
+        NullValue = 0
+        Value = 0
+      end
+      object CapLightIntensityLbl: TLabel
+        Left = 8
+        Height = 25
+        Top = 216
+        Width = 111
+        Caption = 'Light Intensity'
+      end
+      object CapRadiusLbl: TLabel
+        Left = 8
+        Height = 25
+        Top = 176
+        Width = 53
+        Caption = 'Radius'
+      end
+      object CapRadiusSpe: TSpinEditEx
+        Left = 184
+        Height = 33
+        Hint = 'Radius of the Cap'
+        Top = 168
+        Width = 97
+        MaxLength = 0
+        ParentShowHint = False
+        ShowHint = True
+        TabOrder = 5
+        OnChange = CapRadiusSpeChange
+        MaxValue = 1000
+        MinRepeatValue = 10
+        NullValue = 0
+        Value = 0
+      end
+      object CapMemo: TMemo
+        Left = 8
+        Height = 488
+        Top = 328
+        Width = 657
+        Lines.Strings = (
+          'Various settings for the Cap'
+          ''
+          'If the Cap is enabled it can also act as a Button and events related can be'
+          'captured.'
+          ''
+          'Use the Dropdown Style selector to see various types of center Caps.'
+          ''
+          'The Curve Exponent is for the Phong Shader, and can'
+          'give some nice results. This only applies to the Phong '
+          'shader, values less that 1.0 give good results, Light Intensity'
+          'is basically brightness of the shade'
+          ''
+          'Again, play and experiment!'
+        )
+        TabOrder = 6
+      end
+      object CapEdgeThicknessSpe: TSpinEditEx
+        Left = 184
+        Height = 33
+        Hint = 'Thickness of the outer edge, 0 for none'
+        Top = 128
+        Width = 97
+        MaxLength = 0
+        ParentShowHint = False
+        ShowHint = True
+        TabOrder = 7
+        OnChange = CapEdgeThicknessSpeChange
+        MaxValue = 1000
+        MinRepeatValue = 10
+        NullValue = 0
+        Value = 0
+      end
+      object CapEdgeThicknessLbl: TLabel
+        Left = 8
+        Height = 25
+        Top = 136
+        Width = 120
+        Caption = 'Edge Thickness'
+      end
+    end
+  end
+  object SpinnerMovementGb: TGroupBox
+    Left = 8
+    Height = 260
+    Top = 328
+    Width = 493
+    Caption = 'Spinner Movement'
+    ClientHeight = 230
+    ClientWidth = 489
+    ParentBackground = False
+    TabOrder = 1
+    object PerfTestBtn: TBitBtn
+      Left = 0
+      Height = 38
+      Hint = 'Runs a performance test, always calls ProcessMesages()'
+      Top = 152
+      Width = 152
+      Caption = 'Perf Test'
+      Images = ImageList1
+      ImageIndex = 15
+      OnClick = PerfTestBtnClick
+      ParentShowHint = False
+      ShowHint = True
+      TabOrder = 0
+    end
+    object ValuePlus1Btn: TBitBtn
+      Left = 88
+      Height = 38
+      Hint = 'Bump 1 CW'
+      Top = 192
+      Width = 56
+      Caption = '1'
+      Images = ImageList1
+      ImageIndex = 0
+      OnClick = ValuePlus1BtnClick
+      ParentShowHint = False
+      ShowHint = True
+      TabOrder = 1
+    end
+    object ValueMinus1Btn: TBitBtn
+      Left = 144
+      Height = 38
+      Hint = 'Bump 1 CCW'
+      Top = 192
+      Width = 56
+      Caption = '1'
+      Images = ImageList1
+      ImageIndex = 1
+      OnClick = ValueMinus1BtnClick
+      ParentShowHint = False
+      ShowHint = True
+      TabOrder = 2
+    end
+    object ValueZeroBtn: TBitBtn
+      Left = 0
+      Height = 38
+      Hint = 'Runs a performance test.'
+      Top = 192
+      Width = 80
+      Caption = 'Zero'
+      ImageIndex = 6
+      OnClick = ValueZeroBtnClick
+      ParentShowHint = False
+      ShowHint = True
+      TabOrder = 3
+    end
+    object ValuePlus10Btn: TBitBtn
+      Left = 208
+      Height = 38
+      Hint = 'Spin 10 CW'
+      Top = 192
+      Width = 56
+      Caption = '10'
+      Images = ImageList1
+      ImageIndex = 0
+      OnClick = ValuePlus10BtnClick
+      ParentShowHint = False
+      ShowHint = True
+      TabOrder = 4
+    end
+    object ValueMinus10Btn: TBitBtn
+      Left = 264
+      Height = 38
+      Hint = 'Spin 10 CCW'
+      Top = 192
+      Width = 56
+      Caption = '10'
+      Images = ImageList1
+      ImageIndex = 1
+      OnClick = ValueMinus10BtnClick
+      ParentShowHint = False
+      ShowHint = True
+      TabOrder = 5
+    end
+    object UseProcessMsgCb: TCheckBox
+      Left = 192
+      Height = 29
+      Hint = 'Use ProcessMessages() for Spin Calls.'#13#10'Provides smooth movement'
+      Top = 160
+      Width = 250
+      Caption = 'Spin Uses Process Messages'
+      ParentShowHint = False
+      ShowHint = True
+      TabOrder = 6
+    end
+    object ValueMinus50Btn: TBitBtn
+      Left = 384
+      Height = 38
+      Hint = 'Spin 60 CCW'
+      Top = 192
+      Width = 56
+      Caption = '60'
+      Images = ImageList1
+      ImageIndex = 1
+      OnClick = ValueMinus50BtnClick
+      ParentShowHint = False
+      ShowHint = True
+      TabOrder = 7
+    end
+    object ValuePlus50Btn: TBitBtn
+      Left = 328
+      Height = 38
+      Hint = 'Spin 60 CW'
+      Top = 192
+      Width = 56
+      Caption = '60'
+      Images = ImageList1
+      ImageIndex = 0
+      OnClick = ValuePlus50BtnClick
+      ParentShowHint = False
+      ShowHint = True
+      TabOrder = 8
+    end
+    object Label1: TLabel
+      Left = 0
+      Height = 45
+      Top = -8
+      Width = 118
+      Caption = 'Totalizer'
+      Font.Height = -32
+      ParentFont = False
+    end
+    object TotalizerValueLbl: TLabel
+      Left = 168
+      Height = 45
+      Top = -8
+      Width = 17
+      Caption = '0'
+      Font.Height = -32
+      ParentFont = False
+    end
+    object AngleLbl: TLabel
+      Left = 0
+      Height = 45
+      Top = 32
+      Width = 83
+      Caption = 'Angle'
+      Font.Height = -32
+      ParentFont = False
+    end
+    object AngleValueLbl: TLabel
+      Left = 168
+      Height = 45
+      Top = 32
+      Width = 17
+      Caption = '0'
+      Font.Height = -32
+      ParentFont = False
+    end
+    object DirectionLbl: TLabel
+      Left = 0
+      Height = 45
+      Top = 72
+      Width = 129
+      Caption = 'Direction'
+      Font.Height = -32
+      ParentFont = False
+    end
+    object DirectionValueLbl: TLabel
+      Left = 168
+      Height = 45
+      Top = 72
+      Width = 57
+      Caption = 'N/A'
+      Font.Height = -32
+      ParentFont = False
+    end
+    object WrappedValueLbl: TLabel
+      Left = 0
+      Height = 25
+      Top = 120
+      Width = 120
+      Caption = 'Wrapped : N/A'
+      ParentFont = False
+    end
+    object AngleTb: TBCTrackbarUpdown
+      Left = 368
+      Height = 45
+      Hint = 'Set the Angle of the Spinner to 0-360 degrees. If 360 will wrap '#13#10'the spinner to 0.'#13#10#13#10'Note that this calls the Angle method on the spinner which'#13#10'does NOT fire any events, it simple changes the Angle of '#13#10'the spinner, that is all.'
+      Top = 40
+      Width = 113
+      AllowNegativeValues = False
+      BarExponent = 1
+      Increment = 1
+      LongTimeInterval = 400
+      MinValue = 0
+      MaxValue = 360
+      OnChange = AngleTbChange
+      Value = 0
+      ShortTimeInterval = 100
+      Background.Color = clWindow
+      Background.Gradient1.StartColor = clWhite
+      Background.Gradient1.EndColor = clBlack
+      Background.Gradient1.GradientType = gtLinear
+      Background.Gradient1.Point1XPercent = 0
+      Background.Gradient1.Point1YPercent = 0
+      Background.Gradient1.Point2XPercent = 0
+      Background.Gradient1.Point2YPercent = 100
+      Background.Gradient2.StartColor = clWhite
+      Background.Gradient2.EndColor = clBlack
+      Background.Gradient2.GradientType = gtLinear
+      Background.Gradient2.Point1XPercent = 0
+      Background.Gradient2.Point1YPercent = 0
+      Background.Gradient2.Point2XPercent = 0
+      Background.Gradient2.Point2YPercent = 100
+      Background.Gradient1EndPercent = 35
+      Background.Style = bbsColor
+      ButtonBackground.Gradient1.StartColor = clBtnShadow
+      ButtonBackground.Gradient1.EndColor = clBtnFace
+      ButtonBackground.Gradient1.GradientType = gtLinear
+      ButtonBackground.Gradient1.Point1XPercent = 0
+      ButtonBackground.Gradient1.Point1YPercent = -50
+      ButtonBackground.Gradient1.Point2XPercent = 0
+      ButtonBackground.Gradient1.Point2YPercent = 50
+      ButtonBackground.Gradient2.StartColor = clBtnFace
+      ButtonBackground.Gradient2.EndColor = clBtnShadow
+      ButtonBackground.Gradient2.GradientType = gtLinear
+      ButtonBackground.Gradient2.Point1XPercent = 0
+      ButtonBackground.Gradient2.Point1YPercent = 50
+      ButtonBackground.Gradient2.Point2XPercent = 0
+      ButtonBackground.Gradient2.Point2YPercent = 150
+      ButtonBackground.Gradient1EndPercent = 50
+      ButtonBackground.Style = bbsGradient
+      ButtonDownBackground.Color = clBtnShadow
+      ButtonDownBackground.Gradient1.StartColor = clWhite
+      ButtonDownBackground.Gradient1.EndColor = clBlack
+      ButtonDownBackground.Gradient1.GradientType = gtLinear
+      ButtonDownBackground.Gradient1.Point1XPercent = 0
+      ButtonDownBackground.Gradient1.Point1YPercent = 0
+      ButtonDownBackground.Gradient1.Point2XPercent = 0
+      ButtonDownBackground.Gradient1.Point2YPercent = 100
+      ButtonDownBackground.Gradient2.StartColor = clWhite
+      ButtonDownBackground.Gradient2.EndColor = clBlack
+      ButtonDownBackground.Gradient2.GradientType = gtLinear
+      ButtonDownBackground.Gradient2.Point1XPercent = 0
+      ButtonDownBackground.Gradient2.Point1YPercent = 0
+      ButtonDownBackground.Gradient2.Point2XPercent = 0
+      ButtonDownBackground.Gradient2.Point2YPercent = 100
+      ButtonDownBackground.Gradient1EndPercent = 35
+      ButtonDownBackground.Style = bbsColor
+      Border.Color = clWindowText
+      Border.Style = bboSolid
+      Rounding.RoundX = 1
+      Rounding.RoundY = 1
+      Font.Color = clWindowText
+      Font.Name = 'Arial'
+      HasTrackBar = True
+      ArrowColor = clBtnText
+      ParentShowHint = False
+      ShowHint = True
+      TabOrder = 9
+      TabStop = True
+      UseDockManager = False
+    end
+  end
+  object SuperSpinner: TSuperSpinner
+    Left = 50
+    Height = 150
+    Top = 50
+    Width = 150
+    PositionSettings.CenterMargin = 40
+    KnobSettings.CurveExponent = 0.200000002980232
+    Angle = 0
+    OnPosChanged = SuperSpinnerPosChanged
+    OnCapClick = SuperSpinnerCapClick
+    OnKnobClick = SuperSpinnerKnobClick
+    OnWrapped = SuperSpinnerWrapped
+    OnMouseCapEnter = SuperSpinnerMouseCapEnter
+    OnMouseCapLeave = SuperSpinnerMouseCapLeave
+    OnMouseKnobEnter = SuperSpinnerMouseKnobEnter
+    OnMouseKnobLeave = SuperSpinnerMouseKnobLeave
+    OnMouseWheel = SuperSpinnerMouseWheel
+    OnClick = SuperSpinnerClick
+    OnDblClick = SuperSpinnerDblClick
+    OnMouseDown = SuperSpinnerMouseDown
+    OnMouseUp = SuperSpinnerMouseUp
+    OnMouseMove = SuperSpinnerMouseMove
+    OnMouseEnter = SuperSpinnerMouseEnter
+    OnMouseLeave = SuperSpinnerMouseLeave
+  end
+  object GroupBox3: TGroupBox
+    Left = 8
+    Height = 264
+    Hint = 'Events will highlight in GREEN when they change. They will be reset by the '#13#10'time (Green LED will toggle).'#13#10#13#10'Some events are TComponent and are NOT affected if the Spinner is Locked.'#13#10#13#10'Holding down SHIFT while using the Mouse Wheel will 10x the counts.'
+    Top = 600
+    Width = 496
+    Caption = 'Spinner Click and Mouse Events'
+    ClientHeight = 234
+    ClientWidth = 492
+    ParentShowHint = False
+    ShowHint = True
+    TabOrder = 2
+    object OnClickValueLbl: TLabel
+      Left = 8
+      Height = 25
+      Top = 0
+      Width = 60
+      Caption = 'OnClick'
+      ParentFont = False
+    end
+    object OnCapClickValueLbl: TLabel
+      Left = 8
+      Height = 25
+      Top = 48
+      Width = 91
+      Caption = 'OnCapClick'
+    end
+    object OnKnobClickValueLbl: TLabel
+      Left = 8
+      Height = 25
+      Top = 72
+      Width = 102
+      Caption = 'OnKnobClick'
+    end
+    object OnDblClickValueLbl: TLabel
+      Left = 8
+      Height = 25
+      Top = 24
+      Width = 88
+      Caption = 'OnDblClick'
+    end
+    object OnMouseCapEnterValueLbl: TLabel
+      Left = 136
+      Height = 25
+      Top = 48
+      Width = 149
+      Caption = 'OnMouseCapEnter'
+      ParentFont = False
+    end
+    object OnMouseCapLeaveValueLbl: TLabel
+      Left = 136
+      Height = 25
+      Top = 72
+      Width = 153
+      Caption = 'OnMouseCapLeave'
+      ParentFont = False
+    end
+    object OnMouseKnobEnterValueLbl: TLabel
+      Left = 136
+      Height = 25
+      Top = 96
+      Width = 160
+      Caption = 'OnMouseKnobEnter'
+      ParentFont = False
+    end
+    object OnMouseKnobLeaveValueLbl: TLabel
+      Left = 136
+      Height = 25
+      Top = 120
+      Width = 164
+      Caption = 'OnMouseKnobLeave'
+      ParentFont = False
+    end
+    object OnMouseEnterValueLbl: TLabel
+      Left = 136
+      Height = 25
+      Top = 0
+      Width = 118
+      Caption = 'OnMouseEnter'
+      ParentFont = False
+    end
+    object OnMouseLeaveValueLbl: TLabel
+      Left = 136
+      Height = 25
+      Top = 24
+      Width = 122
+      Caption = 'OnMouseLeave'
+      ParentFont = False
+    end
+    object OnMouseDownValueLbl: TLabel
+      Left = 310
+      Height = 25
+      Top = 0
+      Width = 125
+      Caption = 'OnMouseDown'
+      ParentFont = False
+    end
+    object OnMouseUpValueLbl: TLabel
+      Left = 312
+      Height = 25
+      Top = 24
+      Width = 101
+      Caption = 'OnMouseUp'
+      ParentFont = False
+    end
+    object OnMouseMoveValueLbl: TLabel
+      Left = 312
+      Height = 25
+      Top = 48
+      Width = 123
+      Caption = 'OnMouseMove'
+      ParentFont = False
+    end
+    object MouseBtnValueLbl: TLabel
+      Left = 136
+      Height = 25
+      Top = 144
+      Width = 32
+      Caption = 'N/A'
+      ParentFont = False
+    end
+    object ShiftValueLbl: TLabel
+      Left = 56
+      Height = 25
+      Top = 168
+      Width = 32
+      Caption = 'N/A'
+      ParentFont = False
+    end
+    object ShiftLbl: TLabel
+      Left = 8
+      Height = 25
+      Top = 168
+      Width = 50
+      Caption = 'Shift : '
+    end
+    object MouseBtnLbl: TLabel
+      Left = 8
+      Height = 25
+      Top = 144
+      Width = 126
+      Caption = 'Mouse Button : '
+    end
+    object TimerLED: TuELED
+      Left = 448
+      Height = 36
+      Hint = 'Shows Clearing Event State Indicators and a few other things.'#13#10'Click to force reset'
+      Top = -11
+      Width = 36
+      Debug = False
+      Active = False
+      LedType = ledRound
+      Bright = True
+      Reflection = True
+      Color = clLime
+      ParentColor = False
+      ParentShowHint = False
+      ShowHint = True
+      OnClick = TimerLEDClick
+    end
+    object MouseWheelLbl: TLabel
+      Left = 8
+      Height = 25
+      Top = 192
+      Width = 63
+      Caption = 'Wheel : '
+    end
+    object MouseWheelValueLbl: TLabel
+      Left = 72
+      Height = 25
+      Top = 192
+      Width = 32
+      Caption = 'N/A'
+      ParentFont = False
+    end
+    object OnPosChangeValueLbl: TLabel
+      Left = 8
+      Height = 25
+      Top = 96
+      Width = 112
+      Caption = 'OnPosChange'
+    end
+  end
+  object ResetSpinnerLAFBtn: TBitBtn
+    Left = 352
+    Height = 45
+    Hint = 'Reset to Default'
+    Top = 272
+    Width = 157
+    Caption = 'Defaults'
+    Images = ImageList1
+    ImageIndex = 7
+    OnClick = ResetSpinnerLAFBtnClick
+    ParentShowHint = False
+    ShowHint = True
+    TabOrder = 3
+  end
+  object PresetsCb: TComboBox
+    Left = 352
+    Height = 33
+    Hint = 'Presets to try'
+    Top = 232
+    Width = 157
+    ItemHeight = 25
+    Items.Strings = (
+      'Default'
+      'Lines 1'
+      'Lines 2'
+      'Lines 3'
+      'Finger 1'
+      'Finger 2'
+    )
+    ParentShowHint = False
+    ShowHint = True
+    TabOrder = 4
+    Text = 'Try a Preset'
+    OnChange = PresetsCbChange
+  end
+  object AutoScaleEnabledLbl: TLabel
+    Left = 8
+    Height = 25
+    Top = 0
+    Width = 234
+    Caption = 'Spinner Auto Scale Enabled'
+    Font.Color = clRed
+    Font.Style = [fsBold]
+    ParentFont = False
+    Visible = False
+  end
+  object Timer1: TTimer
+    Interval = 2000
+    OnTimer = Timer1Timer
+    Left = 456
+    Top = 80
+  end
+  object ImageList1: TBGRAImageList
+    UseBGRADraw = False
+    Left = 456
+    Top = 152
+    Bitmap = {
+      4C7A100000001000000010000000611600000000000078DAED9B0B5C4ED9FAC7
+      776F737119628ECC8C199718CC08E18C19CC4C62189719D78663308C5B382E43
+      A4DC2A9752547289081DB98F4BA84C88123A2E6FE1AD7457E99E222AEA8D7EFF
+      E759ED376FAF423EFFCFE77FE67F667F3EBFCFDA7BADE7BBD6B3D77A9EB5F7EE
+      2249FF994790C3B832958F2D54BB972168F5CF65B5E555BB9703091781B810A8
+      BC17A3D6BCF712202608883E0DD5CE45B5E78941E429E0961F545E96AFE48356
+      8D2953EDB21163A9762C44EE1F9BCB71C317083F825C3FD772D5D6D9506D9905
+      95C74C04D90E2B7BD9781A0ED70F0157F703577C80306FE0D20E20741B549BA6
+      BDE08FCA6BC12B39847800E73742B561F20B7CD00AF332D5B6DFA0F29C0BF635
+      E7984325977BD8AE5CB571AAE054EE9310B4F48757AEA76AB3C54BC77B25BF71
+      0A10B41E38B30EAAF5136BCFBBFF0A043A01A71CA0721D5F6B3E68C9A032E654
+      AEE310B4646099F4D7F1D7518BFCE77C546D9F0FED9CFA33E5BFC657D5E6E934
+      9E05B4F3FFECD2C1E57FADF87FC601E0459516374261AE391EE52E90652EEAAA
+      B3ADCA1922236E1B526FA84B2FEFC1637F27149D74C493D05D284F8D50539B27
+      D934A9813546467472E9E9F578E8D4070FECBB237F5957E45A7740E6FC36489B
+      DF1605876D4136A964DBE1C571A3539FEC9F8FA235BD51ECDC07454EA678B8B2
+      87E823C7EA33A4CF6981640B4364BB8FA23EA252AAF8413E9704AE27A6374ADC
+      0640BDE1072ABF4791EB40A8A3CEE0D1E1A5C898678494191F20E1D706B87F60
+      31901EEB59395774BFEC338FCB6CF9D69150BB0FC6939DF40C918F8223B6C47F
+      88C45F1B226EF2FB284F0957136BC0735B7AD947DC6FD11A5394B87E2F582E8B
+      1CBF1573A839EE1F5C22C6BF3D461F8567B680D66824AFCF633F27BACF2E283E
+      4039FC548D971DD9DBA723FA1F0AE4EDB582667D8B4E388A797EB467EE2BF9AC
+      6D53113D4A42DEBE45CC5BA2F09E39AF6FE6FCD6C8B66A4FFD18E39E4D475166
+      2F6C8F8774DF40B960737D2C85EF51C4179E95FDA7F9E3D848B76C87F4B92DC4
+      3C67CE6B2DCA6CC77E325B4EEC7CC48C7D478C1D33B1A13C7F4506620D682D1E
+      1EB517EBCB6B944AF32CCA39AD5078F528B2BD6654B0A3253176FE7E1B5EBFAD
+      5AF1D3846382632381D687D788E7397E427DC48EAB237CE671994D5F3B946330
+      E58538E698CC884AE2358A9FD2A48219ADA8E4622634A471ADAB8FDFCA3E8A9A
+      B05FE529116A5EDFBC7D56423C575C277CAE297FAAFA6240EB3252AC4FC51A8D
+      1475AFE2FECAFFCAFC2F3BED8AB2DBE750E0608A5C1BE3DAE57FE876395BCAF1
+      C8E31F221F383F5E27FFCB823655E65AC969373CB0FB42E452C66FCFC7AF29FF
+      1F9F58F5D2DC45592932DCCC6BCCFFA2A3B62FC5CB894F7719FED2FC2FF45D59
+      69FFC87795C8E5BBB33E46F2B426944FEFBD56FE8B7596F33E73A59960F9BE6B
+      93FF79BB7F43D1B56348FE677324D0B87FE5FF7F5FFE733E704CBF51FE9F7B9E
+      8785271CDE30FF51EBE77F59A06B4DD92762FA65F9CFFB1DEF5962EF08747B9E
+      87C7560ABEE8FA3111D335E53FF751E0D85BEC59BCEF680ECEE1CC95BD913CAB
+      39E5C3BBAFFDFCD71CB5CD7FCDF35F73BC49FE73C9FB15EB4DF33FC3ED27A4B9
+      8CF82BFFFFA4F95F7C60A178A77DD3F77FF12E4CAAF5F35F7EFFD71CB579FE6B
+      BFFF6B8ED77DFE73FE17D337CB931DBF8ABE2ADFF9E93D3CDBF13BA4D0FBF0AB
+      F29FBF77F8781CB0B6927F78D44EEC1F85F43C7D55FEF3B752757B178BDFC36B
+      F7FC7FCED7F6FD3F7FFFA24A3A47C3D632FFB3BDA623CB73EA7F5DFE1717A351
+      6E2ECC490B649973DD6B70867171D876E346B97ACF9E223839E5C1D13117BB76
+      3D4044C43335B579924D931A58E3E86824AF5FFF087DFAA4A37BF71474ED9A88
+      0E1DA2D1A64D38DAB6BD025BDBBB209B54B2EDA03B2ED7CF9F7F1FBD7BE7109F
+      0753D31CF4E891467D24E0B3CF22D1A2C555181A0663D4A85B888A428AB61FEC
+      338FCBECA8518FE0E5552A4A53D36C7CF14532BEF9260E0E0E69D44F181A3408
+      C4E2C589888D85A766AEF87ED9671E97593E52529E61F8F03C0C1C988EC4C48A
+      3A3BBB24346C1888F7DFF74778F85335B1063CB73E3E45E27ED9671E3739F999
+      B0BF73A70C494915DFE3F1F18FF1F9E7FF16E3EBEB1FC3962D99207624AF0FCF
+      73972E09E27EB90F1E3729A9AC32FE13129EA05BB7703469128CFAF54F41A138
+      022BAB3868D6D7D13147CC33F7D1BD7B32060C48AF1C57C377EE7C158D1B9FC5
+      BBEF9E84241DC6A24582B7BC770FE6BCBEAD5B87A37D7B9598AB848452D9E727
+      888B7B22CE63638BD0B26590F05D927EAFF49FE78F63A35DBBAB628D56AF4E13
+      F671718F69EDC2D1A9D335C4C41489BA850B2305DBB0E171317F45451031CD6B
+      616F9F46EB1B22D688E7B9438730B1DEEC338F6B651589BFFDED84E06D6CE279
+      FDB66AC54F138E098E0D5E1F16CF33CF559D3A27659F0F0B76E8D0AB1C8329BA
+      71CC31497D242D599244F31C20189E670DC73E5B5BC7571BBF1AD1FD3461BF22
+      229EAA797E788D587CCE75DC5653FEE8F86240EBC2B16129AF11CFF3EB3FFF65
+      65C546E8D796890CD8D5D57741FF5DD7772ECE8B3CE0F8ECE61EFBD2734E13C3
+      CEAD9F353B2736BC0EDBE4C4851B1C98F1D5656D2E2525440ADE346F69C2898D
+      65E5A937E8A5318292E83A25C11520F1321E5D3D5A7EC975CADD731BFE392270
+      F9F0ABE11E73A0CD5F18D56D75FA69AF677187D7A995DB173C507ACECF8DF259
+      569A1FECFD0C9101C0AD9340842FEE1E5EFD4C7D7937549ECFF9F3EBA70FF55F
+      3AF4D1F98D73EC6EF97B7DA2A98F3AE653DF6FCBF89FCFD80EBF92E7E7F20C97
+      ABFE0C5D63B77FE6577BAF9D716B59D39C441DF36C7C7AC9A08C8ADF47B90081
+      CE50AD9F80D799CF9C38A541E0A2FE57D5C7E999E84FEFF17EF6422AD7B1AFC7
+      C787F754F96DB7D355A49FD7328D0D5D2F25D9EA8AD81EAF3386F045F68B7D64
+      5F03ADFB5F25DF1BBD16EFF68B9813313734473C5711FE5B1AD7389FC7F7B4A4
+      39DF53C96FB2A8F8FD04AF0DAD513EAD15AF99BFC7F89F238FF9BC57199B7E5E
+      2D4236CCB60F583AB430C865FA8F953CC582FAD26E111BE277201C2BAA00DCA7
+      D889F2595EAADC669917EE6555107FC4459D767AFBD30B9BE7AFD0F687639163
+      926393623485639563164914BB294AD0FB2138A6E37D37A88337CDB541484895
+      FBE15CE09CE0F3ECB8F03A41EEB36605AD9970E9868F7D896ABF03AE7BD9E41E
+      B3ECEF1515B0B3536DF3F14D647FD3BE91FD757B73D20259E65CF72A2E2423C4
+      50F290B6499E925ADA23413A248BCFB9CE53F2249B6AF72FB3CB66C6D2162959
+      D89F2385E8E8BCDCD71629D56C9F598717C6A57AE944359CAE4E883E52B4FD10
+      3E1F7A3957EF62BDE7D787C4FD6CD5CC95B8B77335B3866186882C8AC4EAD4D5
+      15756CBB5552136BC0732BE6A786B1342C1F5CD6BF58BFA28D186247F2FA68FB
+      6E9F628FE8E2687C10F6411596EB9A8635AD720F9AF5D5F0DCB7F658DAE7DC57
+      95FBAAE02D75FDD71EB34696E523FB5FCDFC69FA78C1678DCE55C493FD657B91
+      33D266BA3AF4E29C573B2EEB20699BB4552B7E9A704CBC69FCC8F1DB81EA936A
+      8CDF73F2B81CBF97CD3AD4903F4D38473836AAE48F4F45BC704B4DF953257F2F
+      8BB81A29AF8FA5380FB3AFF5F3DFDEBEDC88F5A6FB88425F7D86F5BAF62121E5
+      CDF5F4D5417A8AD24D321FC2E273AEA3B6B36C53134BB6774905667DCB86EBF2
+      5CC76D6C43B69F5465A97F7D75B060CDCA3B6AF95FC98B3ECCCA3BD1F543B23D
+      AFCDAF58F1CC8CEAE9FD5F6DA173FF5578569F3E6533D876C58A72534D9D9EE2
+      A9BB42A12E0A097956E7553CF95E876C8B8971ABB453949D22BB6BBA73D2B76F
+      D94C5635EBA224264087BF5E8B75255EEDAFE5FFFA0AFFCBEBBEC61AD7655B66
+      B4E6CFB462FECAA6BF8AAF6EFE840FFAEA73BC36B4469D6B62B98DD7986D79CD
+      75FCFA58133F34C64CEDB5E073AED38A9F6635DCDB27B21F2015F39CCAE27354
+      8C5B35F65EECA3623E787D695DFC597CCE75BA3EFF7F919E9E5E47D2112DFD83
+      24BD4A5ABC832C3E5F4D3A5E4B5EA3FE2467D2A95AF21F92F691F6925C4941B5
+      E47F272D96EB2791426BC95F23D9F1DF7350E9295F3793EDDE268D2679C9D2CC
+      75254FC75CD21D594348274807647E28693FA993ACFD9AF9D5F0D6FDFE2EE5AC
+      9A3478E6976DA58EEFD795C6B76D22756FDA40E327FBB354CBEFA59AF9D5F0F7
+      9D2C98F7BD32B56F77DF41EDA5C041ED113FAEA759AF8F1AB3FD1FA4A95AFC54
+      CDFCEAF0ED890F3F3AB0DD53E253888765D7566CEF23C784A4151FA1BAFE5BF5
+      ED2A4DED6624ADFAF21331BECF771DCC7A7ED488ED7F269D277D21EBBC3CBFDA
+      F327A69EA44F7A9B5487549F6440FA90B49474857495345C9E6783D7E4DF2719
+      923E207D446A466AAACDFF992599796C2329DF50DB24D38DCA3B77EEE04DC4AC
+      F4B59B3225250570E905B87E03B8F70136F507D6F706DCE811B3953E13BDE9FB
+      76377DAF07D0F76DC461407900CC302BF558A74C4B4B030ED263F6C86F6443DF
+      E9211B81934B803F56559C876C004ED127E3CDA3C0BD44204305669895BA3B2A
+      B3B2B270C96F1532948780187A5DBA7D9A4A52FA4D3A0F248EBE4923FD80840B
+      405634703F15CC302B755DA9BC77EF1E26ADE88D59CE83107486C6CAB8556177
+      2F01C88DAFB87E403EE6DD010AD281E2FB608659A9F372E583070FF0F52F1FE1
+      87D99F63FCB25ED87D74258243BD91181588FCBB1128CE8E212EAD827D7C1F28
+      7B026698953AD8280B0B0B317AE8FB183BA229268F6D8185B3BB60F9C29E70B2
+      ED87CDCEC3F12F8FF138BA7B26CE1EB341F4450F14C49E0233CC4AED2C954F9E
+      3CC1F6E95FE05F737AE1C0C23E3861FB03CEAD1B836BDBA723F6B00D32829C51
+      70DD132571FBF12CFD04DD57209861566A3D4759565656853FBE7C30CEACF909
+      97364CC08D1D33117F6021D24FD822FFCC1A1487AEC7D3AB5BC00CB392E12FCA
+      F2F272BC899895DEE9B9537A77B0F28DC4AC24BD23E773B35A8A9977FEAFF3B7
+      9DC25FD61FED49E34896A4C55AB2224D230D6AA738F511DBB6559C241D933E56
+      AC24EE24F3DF0DE91CB6719F7B5E4CF0D1E2C7174F3E066B9B5312AEFCF14CED
+      BFFB61D65ACBD40BFDDB863A93ED60E2EB32FFA9E228F573FCF31F3B5DF43873
+      F051D1E983F7F1C7FE3C5C3B5D8EC003F942DC075F9F39741F6453BCE0E7685F
+      627E21BE2EF39F2A8E4CF15E97151FB0370FFF3E5506FF3DF770787B7AE539EB
+      FCD1C2CAF3358B944FE78DBE7592B8619F2A0E4B6D14075604EC7DF0C4DB2D01
+      C7BD73C8EF12ECDD74074ED661D8EA1889DFB7A5C37757B668635FB8F4DB9DFF
+      D8CCE8D466629BB756F8AC3CB23DB7F08857163C564761A74B3C6CA69FC66EF7
+      3BE0BA9D2E09F05A1B87439EE9E29A75744736965B445E2376B49162E7D839A3
+      C22E72FD56C7DB983DCE17DB9D63E1609D8A0E9FDEC6FC699138B5EF01F9904B
+      F795855DAE89E45F2ABC5D52EF11BBCC48B1BDA191BEA7F5CF6681410B279DCF
+      FAE7185FF556E7088C1EB20BF6B4DD19B70FC61687DBB0B63803878561D4773C
+      7C36DCC5BECDE965C46D265E6AA5EF51AFA5FEA6012DF5372E6FFAC5AE133673
+      778AFC1C356423A68FBD287CDAB4329AC64E610E3BD6DD117D90BD77E33E3E52
+      63F3835223F303929ECF99DE6FB91C0D5B9CF904464D87DC6A5E67FEA996FAAE
+      FB36D8470AD675D94D9A8B3B3427C9586F7B0BDC26D91D92F4F69C15ACDED6E3
+      01CCEA2DF7086EF0ADD5F016FACE5D49235AE83BFDCE73CFB29D132A4AAEA3B6
+      6F04AFCDDA7A9C972C9DFB35F8668144ED2427A9B9FE6AD3E6FAAB7C357DF039
+      D7711BF19FEAB2528F7E55FEC6B6A9FE24E9137D5B7F66B9FC44DFAEB781A26F
+      45E302CFC57145209F37074B237E1D4C35F5480D488DB49FFD1FEB2F09649E4B
+      B9AE81FCAE00BD61338EE87DFD633FB9EE6FF27B424BD2A7A4CF499D1A2A4C27
+      34D3B73E4BE544B98E6DEA6AFB696464244D9B364D5AB76E1DE7959CA77E949F
+      015F51DE2D6DABF05F46D73DDA2A7C396F28F60F0AAE79F3E6D2F8F1E3257B7B
+      7BC9D1D15197277BBF6557039F893C6CAB38614B7C4F0D3F72E44869C18205D2
+      E2C58BABE37BB27D58C0538AE13CCAE78ADCA6BC5D41FCD7CCCF9E3DBB269ED8
+      E37697FCCAE0E7938B80BDF774FA38E448FCB7D5F16BD7AE35717272B2B870BC
+      54E4EBC9DD3955FAF05C9348797D1F6D14FBD6EAF20E0E0E26AB56ADB2282929
+      417171318EEDCCAED287C7EA38CAD304CAE77CB456EC76D5E6EDECEC4C962F5F
+      6EF1F8F1633C7AF4A8529A3E36AEB88DCDAB62B1DF2397586F77521F0D6F6363
+      63B268D1228BA2A222F16CD695DB7215DCEDA2B067631628EF371A2976F43552
+      EC12F367696969326FDE3C0B7E26E7E5E5BDA0888808DCBC7913DE6E69C47A79
+      50CE7F47BCC4FCAC59B34C66CE9C69F1F0E143E4E4E4BCA06BD7AE41A9542231
+      31115F7EF9A5452B7DCFFE468A6D92869F32658A454141013233335F50585818
+      AE5CB982D8D858B46FDFDEA24D9B3626ADF4B74ACC7FA4B0921AEB0D93C68C19
+      23787E9FD2566868282E5DBA84A8A828346BD6CCA269D3A626AD5AB5923ED49F
+      2FD5D16B5719F303070E34F9F6DB6F451FA9A9A942C1C1C108A18FED5BB76EC1
+      C0C0C0E2BDF7DE33A152AA53A7CE0B7F3FDFA85123A9458B16DCB7457272B298
+      2B8DDE7EFB6D0B854261427AD99FE03726B5AB5BB7EEC07AF5EA59C88C057D5F
+      5850BD19A9859CAB9CD70DE59CE5BCD7745A576E6B26DB7D28BF1B3491F70F03
+      795FE07DE55D99D597BF3774BF7F7B92B6E87C8B6BB4456EAFF2EDA9C36F9D3E
+      7D7A16E5102807E1EEEE0E373737383B3B8362248BDB6BE07D35A21C02C5243A
+      77EE0C5A3351F235D76BDBE9F07FA75B091F3E7C38287FD0AF5F3FD0BAE2B7DF
+      7E13255F733DB7B31DDBEBFA4F077FDBF92D59B204A6A6A69833670EACACAC44
+      C9D75CCFEDB25D4DF77FCADADA1A23468CC0E8D1A341B9214ABEE67AED9F2BE8
+      F0811AF1986C6F626282962D5B8A92AFB95EDB4E87FF8AFC8AEFDFBF3F2897D1
+      A74F1F0C183000B4978B92AFB99EDBD98EEDABB9FF1EFC6D3B77EE5CF4EDDB17
+      93274F06ADA528F99AEBB95DB6ABE9FE2FF03DB3BFB4AF63E2C489A2E46BAEE7
+      F61AEEFF9246142782E9D1A3078C8D8D45C9D75CAF6DA7C37F4D7EE5F6ECD913
+      161616F8FEFBEFD1725E4B18EE3614255F733DB7B31DDB5773FF5FF3773DED47
+      225E8C038D617CC958947CCDF5DC2EDB55FD86D5FA37AA499326E1C71F7F446F
+      FA7E1C143D48947CCDF5DC5EDDFFDDC8B9C9797B89C67A326AD4280C1D3A54F8
+      CD255F733DB7CB395E5F87E75CEF429A415292B2AB91526EEFCAFBC3FFE63700
+      F928516E49E6E6E6D24F3FFDD480FCFD8A644E9A2C8BCFBFE236B6615B66AAE1
+      3F9B3CF9D799DEDE5B7E8F895126E7E56594B0F89CEBB88D6D6AE03F9B31639A
+      4D4444705A5656021E3EBC870B17CE8B92C575DC468F2A1BB6D5E6870C19D2F0
+      975FC6CD0E0F0F4E4B49B98982821CC1B8B8B854F25CC76D376E84A64D98307E
+      36331A7EF0E0C1BDB66F773F9294A4A4E7567C25B37AF5EACA7316B725274760
+      E7CE2D4798D1F0B4CE63AF5F3F97CABCB6E89D0ABA752C95EADFA9CC3CFF397F
+      DF590909CA124DBB66BC61C3865519FFC1832CD17EF7EEED1266343CED6F7392
+      92224AB4D9EEDDBB8B3D8BF74F8E3FDE07780F3874E85FC8C8882B6146C3F7EA
+      D56B424444E85D6D3E393951E42E97CF9580B8382562626EDC6546C3D358BD37
+      6F76F3E3F9651FB5FBD0F5FFEEDD4878796DF36346C377EBD6AD51BF7EDF5947
+      455DCBCCCF4FAFC2682B3F3F03F1F1D199B40F5A33A3E1BB74E92275ECD8D1E4
+      871F063B737B61E1FD1758AEBB7327219372D9996D99D1CE7F7A764B868686DD
+      68BF5EE1EAEA72362E2E86FA79505A5858501A1F1F9B49CFC1B3C4AC601BB6D5
+      DE03E8784BFEF99EF15B6FBDF50DBD0B4CA0770E1BD21A59365CC76DFC2E2FEF
+      156F69F1FC2EF0DE6BFE1CE103D956C1ECFF0018A27FCF
+    }
+  end
+  object MainMenu1: TMainMenu
+    Left = 456
+    Top = 8
+    object FileMenu: TMenuItem
+      Caption = 'File'
+      object AboutSubMenu: TMenuItem
+        Caption = 'About'
+        OnClick = AboutSubMenuClick
+      end
+      object Separator1: TMenuItem
+        Caption = '-'
+      end
+      object ExitSubMenu: TMenuItem
+        Caption = 'Exit'
+        OnClick = ExitSubMenuClick
+      end
+    end
+  end
+end

+ 1469 - 0
test/test_superspinner/sstest.pas

@@ -0,0 +1,1469 @@
+unit sstest;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ComCtrls, StdCtrls,
+  ExtCtrls, Menus, Buttons, SuperSpinner, ColorBox,
+  SpinEx, ueLED, BGRAImageList, BCTrackbarUpdown, SuperSpinnerCommon, about, Types;
+
+const
+  VERSIONSTR = '1.00';            // SG TEST version, Should ALWAYS show as a delta when merging!
+
+type
+  { TSSTestFrm }
+
+  TSSTestFrm = class(TForm)
+    AboutSubMenu: TMenuItem;
+    AngleTb: TBCTrackbarUpdown;
+    AutoScaleCb: TCheckBox;
+    DirectionLbl: TLabel;
+    GroupBox3: TGroupBox;
+    MouseWheelDisabledLbl: TLabel;
+    Memo3: TMemo;
+    AutoScaleEnabledLbl: TLabel;
+    PresetsCb: TComboBox;
+    Label2: TLabel;
+    OnPosChangeValueLbl: TLabel;
+    PositonFillOpacityLbl: TLabel;
+    PositionFillOpacitySpe: TSpinEditEx;
+    PositionLineCountLbl: TLabel;
+    PositonLineWidthLbl: TLabel;
+    PositionLineCountSpe: TSpinEditEx;
+    PositionLineWidthSpe: TSpinEditEx;
+    PositionEdgeThicknessLbl: TLabel;
+    PositionEdgeThicknessSpe: TSpinEditEx;
+    PositionEdgeColorCb: TColorBox;
+    KnobEdgeColorLbl1: TLabel;
+    KnobEdgeThicknessLbl: TLabel;
+    KnobEdgeThicknessSpe: TSpinEditEx;
+    PositionCenterMarginLbl: TLabel;
+    PositionCenterMarginSpe: TSpinEditEx;
+    PositionStyleCb: TComboBox;
+    PositionStyleLbl: TLabel;
+    MouseWheelLbl: TLabel;
+    MouseBtnLbl: TLabel;
+    ResetSpinnerLAFBtn: TBitBtn;
+    ShiftLbl: TLabel;
+    ShiftValueLbl: TLabel;
+    OnMouseCapEnterValueLbl: TLabel;
+    OnDblClickValueLbl: TLabel;
+    OnKnobClickValueLbl: TLabel;
+    OnClickValueLbl: TLabel;
+    OnCapClickValueLbl: TLabel;
+    MouseBtnValueLbl: TLabel;
+    OnMouseUpValueLbl: TLabel;
+    OnMouseEnterValueLbl: TLabel;
+    OnMouseDownValueLbl: TLabel;
+    OnMouseLeaveValueLbl: TLabel;
+    OnMouseKnobEnterValueLbl: TLabel;
+    OnMouseKnobLeaveValueLbl: TLabel;
+    OnMouseCapLeaveValueLbl: TLabel;
+    OnMouseMoveValueLbl: TLabel;
+    MouseWheelValueLbl: TLabel;
+    PositionSnapCb: TCheckBox;
+    TimerLED: TuELED;
+    WrappedValueLbl: TLabel;
+    DirectionValueLbl: TLabel;
+    AngleValueLbl: TLabel;
+    BackgroundColorCb: TColorBox;
+    BackgroundColorLbl: TLabel;
+    BasicTab: TTabSheet;
+    CapCurveExponentLbl: TLabel;
+    CapCurveExponentSpe: TFloatSpinEditEx;
+    CapEdgeColorCb: TColorBox;
+    CapEdgeColorLbl: TLabel;
+    CapEdgeThicknessLbl: TLabel;
+    CapEdgeThicknessSpe: TSpinEditEx;
+    CapFillColorCb: TColorBox;
+    CapFillColorLbl: TLabel;
+    CapLightIntensityLbl: TLabel;
+    CapLightIntensitySpe: TSpinEditEx;
+    CapMemo: TMemo;
+    CapRadiusLbl: TLabel;
+    CapRadiusSpe: TSpinEditEx;
+    CapStyleCb: TComboBox;
+    CapStyleLbl: TLabel;
+    CapTab: TTabSheet;
+    UseProcessMsgCb: TCheckBox;
+    Label1: TLabel;
+    Label14: TLabel;
+    AngleLbl: TLabel;
+    TotalizerValueLbl: TLabel;
+    MouseWheelSpeedTB: TBCTrackbarUpdown;
+    SpinnerLockedCb: TCheckBox;
+    ExitSubMenu: TMenuItem;
+    KnobCurveExponentLbl: TLabel;
+    KnobCurveExponentSpe: TFloatSpinEditEx;
+    KnobStyleCb: TComboBox;
+    SpinResolutionCb: TComboBox;
+    KnobStyleLbl: TLabel;
+    FaceFillStyleLbl1: TLabel;
+    KnobFillColorCb: TColorBox;
+    KnobFillColorLbl: TLabel;
+    KnobLightIntensityLbl: TLabel;
+    KnobLightIntensitySpe: TSpinEditEx;
+    FaceMemo: TMemo;
+    KnobEdgeColorCb: TColorBox;
+    KnobEdgeColorLbl: TLabel;
+    FaceTab: TTabSheet;
+    FileMenu: TMenuItem;
+    FrameBorderColorCb: TColorBox;
+    FrameBorderColorLbl: TLabel;
+    FrameBorderWidthLbl: TLabel;
+    FrameRadiusSpe: TSpinEditEx;
+    FrameTab: TTabSheet;
+    GaugeTs: TPageControl;
+    GroupBox1: TGroupBox;
+    GroupBox2: TGroupBox;
+    SpinnerMovementGb: TGroupBox;
+    GroupBox5: TGroupBox;
+    HeightAddBtn: TBitBtn;
+    WidthHeightAddBtn: TBitBtn;
+    HeightLbl: TLabel;
+    HeightSubBtn: TBitBtn;
+    WidthHeightSubBtn: TBitBtn;
+    HeightValLbl: TLabel;
+    ImageList1: TBGRAImageList;
+    LeftAddBtn: TBitBtn;
+    LeftLbl: TLabel;
+    LeftSubBtn: TBitBtn;
+    LeftValLbl: TLabel;
+    MainMenu1: TMainMenu;
+    Memo2: TMemo;
+    PerfTestBtn: TBitBtn;
+    PositionFillColorCb: TColorBox;
+    PointerFillColorLbl: TLabel;
+    PositionMarginLbl: TLabel;
+    PositionMarginSpe: TSpinEditEx;
+    PositionRadiusLbl: TLabel;
+    PositionRadiusSpe: TSpinEditEx;
+    PositonMemo: TMemo;
+    PointerTab: TTabSheet;
+    ResetPositionBtn: TBitBtn;
+    ResetSizeBtn: TBitBtn;
+    Separator1: TMenuItem;
+    SuperSpinner: TSuperSpinner;
+    Timer1: TTimer;
+    TopAddBtn: TBitBtn;
+    TopLbl: TLabel;
+    TopSubBtn: TBitBtn;
+    TopValLbl: TLabel;
+    ValueMinus10Btn: TBitBtn;
+    ValueMinus50Btn: TBitBtn;
+    ValueMinus1Btn: TBitBtn;
+    ValuePlus10Btn: TBitBtn;
+    ValuePlus50Btn: TBitBtn;
+    ValuePlus1Btn: TBitBtn;
+    ValueZeroBtn: TBitBtn;
+    WidthAddBtn: TBitBtn;
+    WidthLbl: TLabel;
+    WidthSubBtn: TBitBtn;
+    WidthValLbl: TLabel;
+    procedure AboutSubMenuClick(Sender: TObject);
+    procedure AutoScaleCbChange(Sender: TObject);
+    procedure BackgroundColorCbChange(Sender: TObject);
+    procedure AngleTbChange(Sender: TObject; {%H-}AByUser: boolean);
+    procedure CapCurveExponentSpeChange(Sender: TObject);
+    procedure CapEdgeColorCbChange(Sender: TObject);
+    procedure CapEdgeThicknessSpeChange(Sender: TObject);
+    procedure CapFillColorCbChange(Sender: TObject);
+    procedure CapLightIntensitySpeChange(Sender: TObject);
+    procedure CapRadiusSpeChange(Sender: TObject);
+    procedure CapStyleCbChange(Sender: TObject);
+    procedure ExitSubMenuClick(Sender: TObject);
+    procedure FormDestroy(Sender: TObject);
+    procedure FrameBorderColorCbChange(Sender: TObject);
+    procedure FrameRadiusSpeChange(Sender: TObject);
+    procedure KnobCurveExponentSpeChange(Sender: TObject);
+    procedure KnobEdgeColorCbChange(Sender: TObject);
+    procedure KnobEdgeThicknessSpeChange(Sender: TObject);
+    procedure KnobFillColorCbChange(Sender: TObject);
+    procedure KnobLightIntensitySpeChange(Sender: TObject);
+    procedure KnobStyleCbChange(Sender: TObject);
+    procedure MouseWheelSpeedTBChange(Sender: TObject; {%H-}AByUser: boolean);
+    procedure PerfTestBtnClick(Sender: TObject);
+    procedure PositionCenterMarginSpeChange(Sender: TObject);
+    procedure PositionRadiusSpeChange(Sender: TObject);
+    procedure PositionEdgeColorCbChange(Sender: TObject);
+    procedure PositionEdgeThicknessSpeChange(Sender: TObject);
+    procedure PositionFillColorCbChange(Sender: TObject);
+    procedure PositionLineCountSpeChange(Sender: TObject);
+    procedure PositionLineWidthSpeChange(Sender: TObject);
+    procedure PositionMarginSpeChange(Sender: TObject);
+    procedure PositionStyleCbChange(Sender: TObject);
+    procedure PositionFillOpacitySpeChange(Sender: TObject);
+    procedure PositionSnapCbChange(Sender: TObject);
+    procedure PresetsCbChange(Sender: TObject);
+    procedure ResetSpinnerLAFBtnClick(Sender: TObject);
+    procedure SpinnerLockedCbChange(Sender: TObject);
+    procedure SpinResolutionCbChange(Sender: TObject);
+    procedure FormCreate(Sender: TObject);
+    procedure SuperSpinnerCapClick(Sender: TObject; Button: TMouseButton;
+      Shift: TShiftState);
+    procedure SuperSpinnerClick(Sender: TObject);
+    procedure SuperSpinnerDblClick(Sender: TObject);
+    procedure SuperSpinnerKnobClick(Sender: TObject; {%H-}Button: TMouseButton;
+      Shift: TShiftState);
+    procedure SuperSpinnerMouseCapEnter(Sender: TObject; Shift: TShiftState; {%H-}X,
+      {%H-}Y: Integer);
+    procedure SuperSpinnerMouseCapLeave(Sender: TObject; Shift: TShiftState; {%H-}X,
+      {%H-}Y: Integer);
+    procedure SuperSpinnerMouseDown(Sender: TObject; Button: TMouseButton;
+      Shift: TShiftState; {%H-}X, {%H-}Y: Integer);
+    procedure SuperSpinnerMouseEnter(Sender: TObject);
+    procedure SuperSpinnerMouseKnobEnter(Sender: TObject; Shift: TShiftState;
+      {%H-}X, {%H-}Y: Integer);
+    procedure SuperSpinnerMouseKnobLeave(Sender: TObject; Shift: TShiftState;
+      {%H-}X, {%H-}Y: Integer);
+    procedure SuperSpinnerMouseLeave(Sender: TObject);
+    procedure SuperSpinnerMouseMove(Sender: TObject; Shift: TShiftState; {%H-}X,
+      {%H-}Y: Integer);
+    procedure SuperSpinnerMouseUp(Sender: TObject; Button: TMouseButton;
+      Shift: TShiftState; {%H-}X, {%H-}Y: Integer);
+    procedure SuperSpinnerMouseWheel(Sender: TObject; Shift: TShiftState;
+      WheelDelta: Integer; MousePos: TPoint; var {%H-}Handled: Boolean);
+    procedure SuperSpinnerPosChanged(Sender: TObject; Shift: TShiftState;
+      Value: single; MoveDir: TSSDirection);
+    procedure SuperSpinnerWrapped(Sender: TObject; {%H-}Shift: TShiftState;
+      OldAngle, NewAngle: single; {%H-}MoveDir: TSSDirection);
+    procedure Timer1Timer(Sender: TObject);
+    procedure TimerLEDClick(Sender: TObject);
+    procedure ValueMinus50BtnClick(Sender: TObject);
+    procedure ValueMinus10BtnClick(Sender: TObject);
+    procedure ValueMinus1BtnClick(Sender: TObject);
+    procedure ValuePlus50BtnClick(Sender: TObject);
+    procedure ValuePlus10BtnClick(Sender: TObject);
+    procedure ValuePlus1BtnClick(Sender: TObject);
+    procedure ValueZeroBtnClick(Sender: TObject);
+    procedure WidthHeightAddBtnClick(Sender: TObject);
+    procedure HeightAddBtnClick(Sender: TObject);
+    procedure HeightSubBtnClick(Sender: TObject);
+    procedure LeftAddBtnClick(Sender: TObject);
+    procedure LeftSubBtnClick(Sender: TObject);
+    procedure ResetPositionBtnClick(Sender: TObject);
+    procedure ResetSizeBtnClick(Sender: TObject);
+    procedure TopAddBtnClick(Sender: TObject);
+    procedure TopSubBtnClick(Sender: TObject);
+    procedure WidthAddBtnClick(Sender: TObject);
+    procedure WidthHeightSubBtnClick(Sender: TObject);
+    procedure WidthSubBtnClick(Sender: TObject);
+  private
+    FTotalizer: integer;
+    FSavedSpinner: TSuperSpinner;
+
+  public
+    procedure PresetDefault;
+    procedure PresetLines1;
+    procedure PresetLines2;
+    procedure PresetLines3;
+    procedure PresetFinger1;
+    procedure PresetFinger2;
+
+    procedure UpdateBasicStats;
+    procedure UpdateWHStats;
+    procedure UpdateLTStats;
+    procedure UpdateFrameStats;
+    procedure UpdateKnobStats;
+    procedure UpdatePositionStats;
+    procedure UpdateCapStats;
+    procedure UpdateSpinnerMovementStats; // only some
+
+    function MouseButtonToStr(Button: TMouseButton): string;
+    function MouseShiftToStr(Shift: TShiftState): string;
+    procedure SetShiftState(Highlight: boolean; Shift: TShiftState);
+    procedure SetMouseButtonState(Highlight: boolean; Button: TMouseButton);
+  end;
+
+var
+  SSTestFrm: TSSTestFrm;
+
+implementation
+
+{$R *.lfm}
+
+{ SSTestFrm }
+
+procedure TSSTestFrm.FormCreate(Sender: TObject);
+begin
+  Caption := 'Super Spinner Test Application ' + VERSIONSTR;
+
+  UpdateBasicStats;
+  UpdateWHStats;
+  UpdateLTStats;
+  UpdateFrameStats;
+  UpdateKnobStats;
+  UpdatePositionStats;
+  UpdateCapStats;
+  UpdateSpinnerMovementStats;
+
+  // Create a Spinner to have defaults, clean up in form destroy
+
+  FSavedSpinner := TSuperSpinner.Create(nil);
+
+  FTotalizer := 0;
+end;
+
+procedure TSSTestFrm.FormDestroy(Sender: TObject);
+begin
+  // Free anything created like the temp Spinner
+
+  FSavedSpinner.Free;
+end;
+
+function TSSTestFrm.MouseButtonToStr(Button: TMouseButton): string;
+begin
+  // Decode any mouse buttons that might be pushed. Only one at a time
+  // can be in the Button, not like Shift
+
+  case Button of
+    mbLeft:
+      Exit('mbLeft');
+    mbRight:
+      Exit('mbRight');
+    mbMiddle:
+      Exit('mbMiddle');
+    mbExtra1:
+      Exit('mbExtra1');
+    mbExtra2:
+      Exit('mbExtra2');
+  end;
+  Result := 'None';
+end;
+
+function TSSTestFrm.MouseShiftToStr(Shift: TShiftState): string;
+var
+  shiftStr: string;
+
+begin
+  // Decode the keyboard shift states, multiple can exist
+  // Also interesting is that Mouse Buttons will show up as
+  // ssLeft/ssRight/etc. Not sure why but seems like it's
+  // supposed to
+
+  shiftStr := '';
+
+  if ssShift in Shift then
+    shiftStr += 'ssShift ';
+
+  if ssAlt in Shift then
+    shiftStr += 'ssAlt ';
+
+  if ssCtrl in Shift then
+    shiftStr += 'ssCtrl ';
+
+  if ssLeft in Shift then
+    shiftStr += 'ssLeft ';
+
+  if ssRight in Shift then
+    shiftStr += 'ssRight ';
+
+  if ssMiddle in Shift then
+    shiftStr += 'ssMiddle ';
+
+  if ssDouble in Shift then
+    shiftStr += 'ssDouble ';
+
+  if ssMeta in Shift then
+    shiftStr += 'ssMeta ';
+
+  if ssSuper in Shift then
+    shiftStr += 'ssSuper ';
+
+  if ssHyper in Shift then
+    shiftStr += 'ssHyper ';
+
+  if ssAltGr in Shift then
+    ShiftStr += 'ssAltGr ';
+
+  if ssCaps in Shift then
+    ShiftStr += 'ssCaps ';
+
+  if ssNum in Shift then
+    ShiftStr += 'ssNum ';
+
+  if ssScroll in Shift then
+    ShiftStr += 'ssScroll ';
+
+  if ssTriple in Shift then
+    ShiftStr += 'ssTriple ';
+
+  if ssQuad in Shift then
+    ShiftStr += 'ssQuad ';
+
+  if ssExtra1 in Shift then
+    ShiftStr += 'ssExtra1 ';
+
+  if ssExtra2 in Shift then
+    ShiftStr += 'ssExtra2 ';
+
+  if Length(ShiftStr) = 0 then
+    ShiftStr := 'None';
+
+  Result := trim(ShiftStr);
+end;
+
+procedure TSSTestFrm.SetShiftState(Highlight: boolean; Shift: TShiftState);
+begin
+  // Helper to Set and Highlight Shift State Display
+
+  ShiftValueLbl.Caption := MouseShiftToStr(Shift);
+
+  if Highlight then
+  begin
+    ShiftValueLbl.Font.Color := clGreen;
+    ShiftValueLbl.Font.Style := [fsBold];
+  end
+    else
+      begin
+        ShiftValueLbl.Font.Color := clDefault;
+        ShiftValueLbl.Font.Style := [];
+      end;
+end;
+
+procedure TSSTestFrm.SetMouseButtonState(Highlight: boolean; Button: TMouseButton);
+begin
+  // Helper to Set and Hightlight Mouse Button State Display
+
+  MouseBtnValueLbl.Caption := MouseButtonToStr(Button);
+
+  if Highlight then
+  begin
+    MouseBtnValueLbl.Caption := MouseButtonToStr(Button);
+    MouseBtnValueLbl.Font.Color := clGreen;
+    MouseBtnValueLbl.Font.Style := [fsBold];
+  end
+    else
+      begin
+        MouseBtnValueLbl.Caption := 'None';
+        MouseBtnValueLbl.Font.Color := clDefault;
+        MouseBtnValueLbl.Font.Style := [];
+      end;
+end;
+
+procedure TSSTestFrm.SuperSpinnerCapClick(Sender: TObject;
+  Button: TMouseButton; Shift: TShiftState);
+begin
+  // If the Spinner Cap is visable it can have it's own Click Handler
+  // This is disabled if the Spinner is LOCKED
+
+  OnCapClickValueLbl.Font.Color := clGreen;
+  OnCapClickValueLbl.Font.Style := [fsBold];
+  SetMouseButtonState(True, Button);
+  SetShiftState(True, Shift);
+end;
+
+procedure TSSTestFrm.SuperSpinnerClick(Sender: TObject);
+begin
+  // Normal TControl type of click, it's NOT subjected to being controled
+  // by the spinners lock state since its a generic click
+
+  OnClickValueLbl.Font.Color := clGreen;
+  OnClickValueLbl.Font.Style := [fsBold];
+end;
+
+procedure TSSTestFrm.SuperSpinnerDblClick(Sender: TObject);
+begin
+  OnDblClickValueLbl.Font.Color := clGreen;
+  OnDblClickValueLbl.Font.Style := [fsBold];
+end;
+
+procedure TSSTestFrm.SuperSpinnerKnobClick(Sender: TObject;
+  Button: TMouseButton; Shift: TShiftState);
+begin
+  // This is another Spinner specific event, and only triggered
+  // when the KNOB portion is clicked. It will NOT trigger on the
+  // Cap if that is enabled, and will not trigger on the Frame of the
+  // Spinner or other parts of the client area
+
+  OnKnobClickValueLbl.Font.Color := clGreen;
+  OnKnobClickValueLbl.Font.Style := [fsBold];
+  SetShiftState(True, Shift);
+  UpdateSpinnerMovementStats;
+end;
+
+procedure TSSTestFrm.SuperSpinnerMouseCapEnter(Sender: TObject;
+  Shift: TShiftState; X, Y: Integer);
+begin
+  // Will track Mouse Entering the Cap if the Cap is enabled
+
+  OnMouseCapEnterValueLbl.Font.Color := clGreen;
+  OnMouseCapEnterValueLbl.Font.Style := [fsBold];
+  SetShiftState(True, Shift);
+end;
+
+procedure TSSTestFrm.SuperSpinnerMouseCapLeave(Sender: TObject;
+  Shift: TShiftState; X, Y: Integer);
+begin
+  // Will track Mouse Leaving the Cap if the Cap is Enabled
+
+  OnMouseCapLeaveValueLbl.Font.Color := clGreen;
+  OnMouseCapLeaveValueLbl.Font.Style := [fsBold];
+  SetShiftState(True, Shift);
+end;
+
+procedure TSSTestFrm.SuperSpinnerMouseDown(Sender: TObject;
+  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
+begin
+  // General Mouse Down event, will trigger anywhere in the control
+
+  OnMouseDownValueLbl.Font.Color := clGreen;
+  OnMouseDownValueLbl.Font.Style := [fsBold];
+  SetMouseButtonState(True, Button);
+  SetShiftState(True, Shift);
+end;
+
+procedure TSSTestFrm.SuperSpinnerMouseEnter(Sender: TObject);
+begin
+  // General Mouse Enter, will trigger anywhere in the control
+
+  OnMouseEnterValueLbl.Font.Color := clGreen;
+  OnMouseEnterValueLbl.Font.Style := [fsBold];
+end;
+
+procedure TSSTestFrm.SuperSpinnerMouseKnobEnter(Sender: TObject;
+  Shift: TShiftState; X, Y: Integer);
+begin
+  // Will trigger when the mouse enters the Knob, Spinner specific
+  // If the cap is enabled, and the mouse is in the Cap, and moves
+  // to the Knob, it will also trigger.
+
+  OnMouseKnobEnterValueLbl.Font.Color := clGreen;
+  OnMouseKnobEnterValueLbl.Font.Style := [fsBold];
+  SetShiftState(True, Shift);
+end;
+
+procedure TSSTestFrm.SuperSpinnerMouseKnobLeave(Sender: TObject;
+  Shift: TShiftState; X, Y: Integer);
+begin
+  // Triggers anytime the mouse leaves the knob portion of the spinner. This
+  // includes leaving the knob area into the Cap if that is enabled.
+
+  OnMouseKnobLeaveValueLbl.Font.Color := clGreen;
+  OnMouseKnobLeaveValueLbl.Font.Style := [fsBold];
+  SetShiftState(True, Shift);
+end;
+
+procedure TSSTestFrm.SuperSpinnerMouseLeave(Sender: TObject);
+begin
+  OnMouseLeaveValueLbl.Font.Color := clGreen;
+  OnMouseLeaveValueLbl.Font.Style := [fsBold];
+end;
+
+procedure TSSTestFrm.SuperSpinnerMouseMove(Sender: TObject; Shift: TShiftState;
+  X, Y: Integer);
+begin
+  // General mouse movement capture, will capture anywhere in the component
+
+  OnMouseMoveValueLbl.Font.Color := clGreen;
+  OnMouseMoveValueLbl.Font.Style := [fsBold];
+  SetShiftState(True, Shift);
+end;
+
+procedure TSSTestFrm.SuperSpinnerMouseUp(Sender: TObject; Button: TMouseButton;
+  Shift: TShiftState; X, Y: Integer);
+begin
+  // General mouse movement capture, will capture anywhere in the component
+
+  OnMouseUpValueLbl.Font.Color := clGreen;
+  OnMouseUpValueLbl.Font.Style := [fsBold];
+  SetMouseButtonState(True, Button);
+  SetShiftState(True, Shift);
+end;
+
+procedure TSSTestFrm.SuperSpinnerMouseWheel(Sender: TObject;
+  Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint;
+  var Handled: Boolean);
+begin
+  // General mouse movement capture, will capture anywhere in the component
+
+  MouseWheelValueLbl.Caption := 'Mouse: ' + MouseShiftToStr(Shift) + ', Delta:' + IntToStr(WheelDelta) + ', Mouse: '
+    + IntToStr(MousePos.X) + ',' + IntToStr(MousePos.Y);
+  MouseWheelValueLbl.Font.Color := clGreen;
+  MouseWheelValueLbl.Font.Style := [fsBold];
+end;
+
+// Rest stuff ever timer tick, this just flashes
+// stuff and not synced to anythings so flashes
+// may be short or longer depending when you click
+
+procedure TSSTestFrm.Timer1Timer(Sender: TObject);
+begin
+  // called every timer interval to reset the display
+
+  OnCapClickValueLbl.Font.Color := clDefault;
+  OnCapClickValueLbl.Font.Style := [];
+  OnClickValueLbl.Font.Color := clDefault;
+  OnClickValueLbl.Font.Style := [];
+  OnDblClickValueLbl.Font.Color := clDefault;
+  OnDblClickValueLbl.Font.Style := [];
+  OnKnobClickValueLbl.Font.Color := clDefault;
+  OnKnobClickValueLbl.Font.Style := [];
+  OnPosChangeValueLbl.Font.Color := clDefault;
+  OnPosChangeValueLbl.Font.Style := [];
+  OnMouseEnterValueLbl.Font.Color := clDefault;
+  OnMouseEnterValueLbl.Font.Style := [];
+  OnMouseLeaveValueLbl.Font.Color := clDefault;
+  OnMouseLeaveValueLbl.Font.Style := [];
+  OnMouseCapEnterValueLbl.Font.Color := clDefault;
+  OnMouseCapEnterValueLbl.Font.Style := [];
+  OnMouseCapLeaveValueLbl.Font.Color := clDefault;
+  OnMouseCapLeaveValueLbl.Font.Style := [];
+  OnMouseKnobEnterValueLbl.Font.Color := clDefault;
+  OnMouseKnobEnterValueLbl.Font.Style := [];
+  OnMouseKnobLeaveValueLbl.Font.Color := clDefault;
+  OnMouseKnobLeaveValueLbl.Font.Style := [];
+  OnMouseUpValueLbl.Font.Color := clDefault;
+  OnMouseUpValueLbl.Font.Style := [];
+  OnMouseDownValueLbl.Font.Color := clDefault;
+  OnMouseDownValueLbl.Font.Style := [];
+  OnMouseMoveValueLbl.Font.Color := clDefault;
+  OnMouseMoveValueLbl.Font.Style := [];
+  SetShiftState(False, []);
+  SetMouseButtonState(False, TMouseButton(0)); // fake out a mouse button, not used if False
+  MouseWheelValueLbl.Caption := '';
+  MouseWheelValueLbl.Font.Color := clDefault;
+  MouseWheelValueLbl.Font.Style := [];
+
+  WrappedValueLbl.Font.Style := [];
+
+  TimerLED.Active := not TimerLED.Active;
+end;
+
+procedure TSSTestFrm.TimerLEDClick(Sender: TObject);
+begin
+  // Click on the LED for a forced reset of the Event display
+
+  Timer1Timer(self);
+end;
+
+procedure TSSTestFrm.HeightAddBtnClick(Sender: TObject);
+begin
+  SuperSpinner.Height := SuperSpinner.Height + 10;
+  UpdateWHStats;
+end;
+
+procedure TSSTestFrm.BackgroundColorCbChange(Sender: TObject);
+begin
+  SuperSpinner.Color := BackgroundColorCb.Selected;
+end;
+
+procedure TSSTestFrm.AboutSubMenuClick(Sender: TObject);
+begin
+  AboutFrm.VersionStr := VERSIONSTR;
+  AboutFrm.show;
+end;
+
+procedure TSSTestFrm.AutoScaleCbChange(Sender: TObject);
+begin
+  SuperSpinner.AutoScale := AutoScaleCb.Checked;
+  AutoScaleEnabledLbl.Visible := SuperSpinner.AutoScale;
+end;
+
+procedure TSSTestFrm.AngleTbChange(Sender: TObject; AByUser: boolean);
+begin
+  SuperSpinner.Angle := AngleTb.Value;  // This will NOT call OnPosChanged internally to the spinner
+  AngleValueLbl.Caption := FloatToStrF(SuperSpinner.Angle, ffFixed, 3,3); // Update display
+  // again, setting the Angle will not do anything except reposition the spinner
+  // No events will be called
+end;
+
+procedure TSSTestFrm.CapCurveExponentSpeChange(Sender: TObject);
+begin
+  SuperSpinner.CapSettings.CurveExponent := CapCurveExponentSpe.Value;
+end;
+
+procedure TSSTestFrm.CapEdgeColorCbChange(Sender: TObject);
+begin
+  SuperSpinner.CapSettings.EdgeColor := CapEdgeColorCb.Selected;
+end;
+
+procedure TSSTestFrm.CapEdgeThicknessSpeChange(Sender: TObject);
+begin
+  SuperSpinner.CapSettings.EdgeThickness := CapEdgeThicknessSpe.Value;
+end;
+
+procedure TSSTestFrm.CapFillColorCbChange(Sender: TObject);
+begin
+  SuperSpinner.CapSettings.FillColor := CapFillColorCb.Selected;
+end;
+
+procedure TSSTestFrm.CapLightIntensitySpeChange(Sender: TObject);
+begin
+  SuperSpinner.CapSettings.LightIntensity := CapLightIntensitySpe.Value;
+end;
+
+procedure TSSTestFrm.CapRadiusSpeChange(Sender: TObject);
+begin
+  SuperSpinner.CapSettings.Radius := CapRadiusSpe.Value;
+end;
+
+procedure TSSTestFrm.CapStyleCbChange(Sender: TObject);
+begin
+  // csNone, csFlat, csShaded, csPhong, csOutline
+
+  case CapStyleCb.ItemIndex of
+    0 : {csNone}
+      SuperSpinner.CapSettings.Style := csNone;
+    1 : {csFlat}
+      SuperSpinner.CapSettings.Style := csFlat;
+    2: {csShaded}
+      SuperSpinner.CapSettings.Style := csShaded;
+    3: {csPhong}
+      SuperSpinner.CapSettings.Style := csPhong;
+    4: {csOutline}
+      SuperSpinner.CapSettings.Style := csOutline;
+  else
+    // Unknown type, warn somewhere...
+  end;
+
+  UpdateCapStats;
+end;
+
+procedure TSSTestFrm.ExitSubMenuClick(Sender: TObject);
+begin
+  Close;
+end;
+
+procedure TSSTestFrm.FrameBorderColorCbChange(Sender: TObject);
+begin
+  SuperSpinner.FrameSettings.BorderColor := FrameBorderColorCb.Selected;
+end;
+
+procedure TSSTestFrm.FrameRadiusSpeChange(Sender: TObject);
+begin
+  SuperSpinner.FrameSettings.BorderWidth := FrameRadiusSpe.Value;
+end;
+
+procedure TSSTestFrm.KnobCurveExponentSpeChange(Sender: TObject);
+begin
+  SuperSpinner.KnobSettings.CurveExponent := KnobCurveExponentSpe.Value;
+end;
+
+procedure TSSTestFrm.KnobEdgeColorCbChange(Sender: TObject);
+begin
+  SuperSpinner.KnobSettings.EdgeColor := KnobEdgeColorCb.Selected;
+end;
+
+procedure TSSTestFrm.KnobEdgeThicknessSpeChange(Sender: TObject);
+begin
+  SuperSpinner.KnobSettings.EdgeThickness := KnobEdgeThicknessSpe.Value;
+end;
+
+procedure TSSTestFrm.KnobFillColorCbChange(Sender: TObject);
+begin
+  SuperSpinner.KnobSettings.FillColor := KnobFillColorCb.Selected;
+end;
+
+procedure TSSTestFrm.KnobLightIntensitySpeChange(Sender: TObject);
+begin
+  SuperSpinner.KnobSettings.LightIntensity := KnobLightIntensitySpe.Value;
+end;
+
+procedure TSSTestFrm.KnobStyleCbChange(Sender: TObject);
+begin
+  // ssFlat, ssShaded, ssPhong
+
+  case KnobStyleCb.ItemIndex of
+    0 : {ssFlat}
+      SuperSpinner.KnobSettings.Style := ssFlat;
+    1 : {ssShaded}
+      SuperSpinner.KnobSettings.Style  := ssShaded;
+    2: {ssPhong}
+      SuperSpinner.KnobSettings.Style := ssPhong;
+  else
+    // Unknown type, warn somewhere...
+  end;
+
+  UpdateKnobStats;
+end;
+
+procedure TSSTestFrm.MouseWheelSpeedTBChange(Sender: TObject; AByUser: boolean);
+begin
+  SuperSpinner.WheelSpeed:= MouseWheelSpeedTB.Value;
+  MouseWheelDisabledLbl.Visible := (SuperSpinner.WheelSpeed = 0);
+end;
+
+procedure TSSTestFrm.PerfTestBtnClick(Sender: TObject);
+var
+  i, j : integer;
+begin
+  // Just move the knove, no event's, mainly for paint tests
+
+  for j := 0 to 10 do
+  begin
+      for i := 0 to 100 do
+      begin
+        SuperSpinner.Angle := i;
+        Application.ProcessMessages;
+      end;
+  end;
+  beep;
+end;
+
+procedure TSSTestFrm.PositionCenterMarginSpeChange(Sender: TObject);
+begin
+  SuperSpinner.PositionSettings.CenterMargin := PositionCenterMarginSpe.Value;
+end;
+
+procedure TSSTestFrm.PositionRadiusSpeChange(Sender: TObject);
+begin
+  SuperSpinner.PositionSettings.Radius := PositionRadiusSpe.Value;
+end;
+
+procedure TSSTestFrm.PositionEdgeColorCbChange(Sender: TObject);
+begin
+  SuperSpinner.PositionSettings.EdgeColor := PositionEdgeColorCb.Selected;
+end;
+
+procedure TSSTestFrm.PositionEdgeThicknessSpeChange(Sender: TObject);
+begin
+  SuperSpinner.PositionSettings.EdgeThickness := PositionEdgeThicknessSpe.Value;
+end;
+
+procedure TSSTestFrm.PositionFillColorCbChange(Sender: TObject);
+begin
+  SuperSpinner.PositionSettings.FillColor := PositionFillColorCb.Selected;
+end;
+
+procedure TSSTestFrm.PositionLineCountSpeChange(Sender: TObject);
+begin
+  SuperSpinner.PositionSettings.LineCount := PositionLineCountSpe.Value;
+end;
+
+procedure TSSTestFrm.PositionLineWidthSpeChange(Sender: TObject);
+begin
+  SuperSpinner.PositionSettings.LineWidth := PositionLineWidthSpe.Value;
+end;
+
+procedure TSSTestFrm.PositionMarginSpeChange(Sender: TObject);
+begin
+  SuperSpinner.PositionSettings.Margin := PositionMarginSpe.Value;
+end;
+
+procedure TSSTestFrm.PositionStyleCbChange(Sender: TObject);
+begin
+  // Update Position Style
+  // psNone, psFilledCircle, psHollowCircle, psIndentCircle, psLines
+
+  case PositionStyleCb.ItemIndex of
+    0 : {psNone}
+      SuperSpinner.PositionSettings.Style := psNone;
+    1 : {psFilledCircle}
+      SuperSpinner.PositionSettings.Style := psFilledCircle;
+    2 : {psHollowCircle}
+      SuperSpinner.PositionSettings.Style := psHollowCircle;
+    3 : {psShaded}
+      SuperSpinner.PositionSettings.Style := psShaded;
+    4 : {psIndentCircle}
+      SuperSpinner.PositionSettings.Style := psIndentCircle;
+    5 : {psLines}
+      SuperSpinner.PositionSettings.Style := psLines;
+
+  else
+    // Unknown type, Warn
+  end;
+end;
+
+procedure TSSTestFrm.PositionFillOpacitySpeChange(Sender: TObject);
+begin
+  SuperSpinner.PositionSettings.Opacity := PositionFillOpacitySpe.Value;
+end;
+
+procedure TSSTestFrm.PositionSnapCbChange(Sender: TObject);
+begin
+  SuperSpinner.PositionSnap := PositionSnapCb.Checked;
+  UpdateSpinnerMovementStats;
+end;
+
+procedure TSSTestFrm.PresetsCbChange(Sender: TObject);
+begin
+  case PresetsCb.ItemIndex of
+    0 : {default}
+      PresetDefault;
+    1 : {Lines 1}
+      PresetLines1;
+    2 : {Lines 2}
+      PresetLines2;
+    3 : {Lines 3}
+      PresetLines3;
+    4 : {Finger 1}
+      PresetFinger1;
+    5 : {Finger 2}
+      PresetFinger2;
+  end;
+end;
+
+procedure TSSTestFrm.PresetDefault;
+begin
+  // Resets mostly everything back to default Look and Feel
+  // If adding or removing props don't forget to fix here!
+
+  // Basic Settings
+
+  with FSavedSpinner do
+  begin
+    SuperSpinner.Angle := Angle;
+    SuperSpinner.Color := Color;
+    SuperSpinner.Locked := Locked;
+    SuperSpinner.PositionSnap := PositionSnap;
+    SuperSpinner.SpinResolution := SpinResolution;
+    SuperSpinner.WheelSpeed := WheelSpeed;
+  end;
+  UpdateBasicStats;
+
+  // Cap Settings
+
+  with FSavedSpinner.CapSettings do
+  begin
+    SuperSpinner.CapSettings.CurveExponent := CurveExponent;
+    SuperSpinner.CapSettings.EdgeColor := EdgeColor;
+    SuperSpinner.CapSettings.EdgeThickness := EdgeThickness;
+    SuperSpinner.CapSettings.FillColor := FillColor;
+    SuperSpinner.CapSettings.LightIntensity := LightIntensity;
+    SuperSpinner.CapSettings.Radius := Radius;
+    SuperSpinner.CapSettings.Style := Style;
+  end;
+  UpdateCapStats;
+
+  // Frame Settings
+
+  with FSavedSpinner.FrameSettings do
+  begin
+    SuperSpinner.FrameSettings.BorderColor := BorderColor;
+    SuperSpinner.FrameSettings.BorderWidth := BorderWidth;
+  end;
+  UpdateFrameStats;
+
+  // Knob Settings
+
+  with FSavedSpinner.KnobSettings do
+  begin
+    SuperSpinner.KnobSettings.CurveExponent := CurveExponent;
+    SuperSpinner.KnobSettings.EdgeColor := EdgeColor;
+    SuperSpinner.KnobSettings.EdgeThickness := EdgeThickness;
+    SuperSpinner.KnobSettings.FillColor := FillColor;
+    SuperSpinner.KnobSettings.LightIntensity := LightIntensity;
+    SuperSpinner.KnobSettings.Style := Style;
+  end;
+  UpdateKnobStats;
+
+  // Position Settings
+
+  with FSavedSpinner.PositionSettings do
+  begin
+    SuperSpinner.PositionSettings.CenterMargin := CenterMargin;
+    SuperSpinner.PositionSettings.Radius := Radius;  // This may need to be renamed to RADIUS for consistancy
+    SuperSpinner.PositionSettings.EdgeColor := EdgeColor;
+    SuperSpinner.PositionSettings.EdgeThickness := EdgeThickness;
+    SuperSpinner.PositionSettings.FillColor := FillColor;
+    SuperSpinner.PositionSettings.LineCount := LineCount;
+    SuperSpinner.PositionSettings.LineWidth := LineWidth;
+    SuperSpinner.PositionSettings.Margin := Margin;
+    SuperSpinner.PositionSettings.Opacity := Opacity;
+    SuperSpinner.PositionSettings.Style := Style;
+  end;
+  UpdatePositionStats;
+
+end;
+procedure TSSTestFrm.PresetLines1;
+begin
+  // Lines 1 type, just different in a few aspects to show settings
+
+  with SuperSpinner.PositionSettings do
+  begin
+    Style := psLines;
+    FillColor := clGray;
+    LineCount := 6;
+    LineWidth := 3;
+    Margin := 15;
+    CenterMargin := 40;
+  end;
+
+  with SuperSpinner.CapSettings do
+  begin
+    Style := csShaded;
+    FillColor := clGray;
+    EdgeColor := clSilver;
+    EdgeThickness := 2;
+  end;
+
+  with SuperSpinner.KnobSettings do
+  begin
+    Style := ssFlat;
+    FillColor := clSilver;
+    EdgeColor := clMedGray;
+    EdgeThickness := 4;
+  end;
+
+  UpdatePositionStats;
+  UpdateCapStats;
+  UpdateKnobStats;
+end;
+
+procedure TSSTestFrm.PresetLines2;
+begin
+  // Lines 2 type, just different in a few aspects to show settings
+
+  with SuperSpinner.PositionSettings do
+  begin
+    Style := psLines;
+    FillColor := clRed;
+    LineCount := 5;
+    LineWidth := 6;
+    Margin := 15;
+    CenterMargin := 40;
+  end;
+
+  with SuperSpinner.CapSettings do
+  begin
+    Style := csFlat;
+    FillColor := clRed;
+    EdgeColor := clBlue;
+    EdgeThickness := 4;
+  end;
+
+  with SuperSpinner.KnobSettings do
+  begin
+    Style := ssFlat;
+    FillColor := clMedGray;
+    EdgeColor := clGray;
+    EdgeThickness := 4;
+  end;
+
+  UpdatePositionStats;
+  UpdateCapStats;
+  UpdateKnobStats;
+end;
+
+procedure TSSTestFrm.PresetLines3;
+begin
+  // Lines 3 type, just different in a few aspects to show settings
+
+  with SuperSpinner.PositionSettings do
+  begin
+    Style := psLines;
+    FillColor := clRed;
+    LineCount := 15;
+    LineWidth := 4;
+    CenterMargin := 0;
+  end;
+
+  with SuperSpinner.CapSettings do
+  begin
+    Style := csNone;
+    FillColor := clRed;
+    EdgeColor := clBlue;
+    EdgeThickness := 4;
+  end;
+
+  with SuperSpinner.KnobSettings do
+  begin
+    Style := ssShaded;
+    FillColor := clWhite;
+    EdgeColor := clSilver;
+    EdgeThickness := 4;
+  end;
+
+  UpdatePositionStats;
+  UpdateCapStats;
+  UpdateKnobStats;
+end;
+
+procedure TSSTestFrm.PresetFinger1;
+begin
+  // Finger Hole type of spinner
+
+  with SuperSpinner.PositionSettings do
+  begin
+    Style := psIndentCircle;
+    FillColor := clBlack;
+    EdgeColor := clGray;
+    Radius := 20;
+    EdgeThickness := 2;
+  end;
+
+  with SuperSpinner.CapSettings do
+  begin
+    Style := csNone;
+  end;
+
+  with SuperSpinner.KnobSettings do
+  begin
+    Style := ssPhong;
+    FillColor := clWhite;
+    EdgeColor := clMedGray;
+    EdgeThickness := 2;
+  end;
+
+  UpdatePositionStats;
+  UpdateCapStats;
+  UpdateKnobStats;
+end;
+
+procedure TSSTestFrm.PresetFinger2;
+begin
+  // Another Finger Hole type
+
+  with SuperSpinner.PositionSettings do
+  begin
+    Style := psHollowCircle;
+    FillColor := clBlack;
+    EdgeColor := clGray;
+    Radius := 20;
+    EdgeThickness := 2;
+    LineWidth := 2;
+  end;
+
+  with SuperSpinner.CapSettings do
+  begin
+    Style := csNone;
+  end;
+
+  with SuperSpinner.KnobSettings do
+  begin
+    Style := ssPhong;
+    FillColor := clWhite;
+    EdgeColor := clMedGray;
+    EdgeThickness := 2;
+  end;
+
+  UpdatePositionStats;
+  UpdateCapStats;
+  UpdateKnobStats;
+end;
+
+procedure TSSTestFrm.ResetSpinnerLAFBtnClick(Sender: TObject);
+begin
+  PresetDefault;
+  PresetsCb.ItemIndex := 0;
+end;
+
+procedure TSSTestFrm.SpinnerLockedCbChange(Sender: TObject);
+begin
+  // If we lock the spinner, set a few colors to Red, if unlocked
+  // restore to what the form has them set to. This is dumb as it
+  // just doesn't update the contols on the test page so can just be restored.
+  // So DON'T UpdateStats on these or you will have to save off the state
+  // and that's more work!
+
+  SuperSpinner.Locked := SpinnerLockedCb.Checked;
+
+  if SuperSpinner.Locked then
+  begin
+    SuperSpinner.KnobSettings.FillColor := clRed;
+    SuperSpinner.KnobSettings.EdgeColor := clRed;
+    SuperSpinner.CapSettings.FillColor := clRed;
+    SuperSpinner.CapSettings.EdgeColor := clBlack;
+    SuperSpinner.FrameSettings.BorderColor := clBlack;
+  end
+    else
+      begin
+        SuperSpinner.KnobSettings.FillColor := KnobFillColorCb.Selected;
+        SuperSpinner.KnobSettings.EdgeColor := KnobEdgeColorCb.Selected;
+        SuperSpinner.CapSettings.FillColor := CapFillColorCb.Selected;
+        SuperSpinner.CapSettings.EdgeColor := CapEdgeColorCb.Selected;
+        SuperSpinner.FrameSettings.BorderColor := FrameBorderColorCb.Selected;
+      end;
+end;
+
+procedure TSSTestFrm.SpinResolutionCbChange(Sender: TObject);
+begin
+  // SpinResolution
+  // TSSResolution = (srHighest = 0, srHigh = 1, srHighMedium =2, srMedium = 3,
+  //                  srMediumLow = 4, srLow = 5, srLowest = 6)
+  //
+  // These are abstracted to not get into any trouble with actual events per
+  // revolution of the spinner. srHighest is full resolution of the mouse movement
+  // and it gets more of a detent feel as you go to lower settings.
+  //
+  // Also an interesting note if using a Position type of lines it may not look
+  // like the spinner is moving, switch to a different style to see the movement.
+
+  case SpinResolutionCb.ItemIndex of
+    0 : {srHighest}
+      SuperSpinner.SpinResolution := srHighest;
+    1 : {srHigh}
+      SuperSpinner.SpinResolution := srHigh;
+    2: {srHighMedium}
+      SuperSpinner.SpinResolution := srHighMedium;
+    3: {srMedium}
+      SuperSpinner.SpinResolution := srMedium;
+    4: {srMediumLow}
+      SuperSpinner.SpinResolution := srMediumLow;
+    5: {srLow}
+      SuperSpinner.SpinResolution := srLow;
+    6: {srLowest}
+      SuperSpinner.SpinResolution := srLowest;
+  else
+    // Unknown type, warn somewhere...
+  end;
+
+  UpdateBasicStats;
+end;
+
+procedure TSSTestFrm.SuperSpinnerPosChanged(Sender: TObject;
+  Shift: TShiftState; Value: single; MoveDir: TSSDirection);
+var
+  Direction: integer;
+  DirectionStr: string;
+
+begin
+  // This gets called anytime the Spinner is moved by the user EXCEPT if the
+  // Angle() method is called as it do NOT call this event handler
+  //
+  // This handler is where you would, for example, increase/decrease a value
+  // as the user spins. Radio Frequency, Song Selection (like iPod), etc.
+  // Typically the Angle should NOT be used other than to set the Position
+  // Visually.
+
+  // See which way we are moving, get the direction string decoded as well
+
+  if MoveDir = sdCW then
+  begin
+      Direction := 1;
+      DirectionStr := 'sdCW';
+  end
+    else
+      begin
+        Direction := -1;
+        DirectionStr := 'sdCCW';
+      end;
+
+  // If moving the Spinner and the SHIFT is held down, it will just 10x the
+  // bump in the Totalizer. This is arbitrary and just to show some examples
+  // of a FAST way to move the spinner if needed.
+
+  if ssShift in Shift then
+  begin
+    Direction := Direction * 10; // if shifting just 10x the count
+  end;
+
+  DirectionValueLbl.Caption := DirectionStr;
+  AngleValueLbl.Caption := FloatToStrF(Value, ffFixed, 3,3);
+  AngleTb.Value := Round(Value);
+  FTotalizer := FTotalizer + Direction;
+  TotalizerValueLbl.Caption := IntToStr(FTotalizer);
+  ShiftValueLbl.Caption := MouseShiftToStr(Shift);
+  ShiftValueLbl.Font.Color := clGreen;
+  ShiftValueLbl.Font.Style := [fsBold];
+  OnPosChangeValueLbl.Font.Color := clGreen;
+  OnPosChangeValueLbl.Font.Style := [fsBold];
+end;
+
+procedure TSSTestFrm.SuperSpinnerWrapped(Sender: TObject; Shift: TShiftState;
+  OldAngle, NewAngle: single; MoveDir: TSSDirection);
+begin
+  // If the spinners angle passes over 0 degrees by any direction or step it
+  // trigger this. This could come in handy for something, but not sure what...
+
+  WrappedValueLbl.Caption := 'Last Wrap@ Old: ' + FloatToStrF(OldAngle, ffFixed, 3,3) + ' New: ' + FloatToStrF(NewAngle, ffFixed, 3,3);
+  WrappedValueLbl.Font.Style := [fsBold];
+end;
+
+procedure TSSTestFrm.ValueMinus50BtnClick(Sender: TObject);
+begin
+  SuperSpinner.Spin(sdCCW, 1, 60, UseProcessMsgCb.Checked);
+end;
+
+procedure TSSTestFrm.ValueMinus10BtnClick(Sender: TObject);
+begin
+  SuperSpinner.Spin(sdCCW, 1, 10, UseProcessMsgCb.Checked);
+end;
+
+procedure TSSTestFrm.ValueMinus1BtnClick(Sender: TObject);
+begin
+    SuperSpinner.Bump(sdCCW, 1);
+end;
+
+procedure TSSTestFrm.ValuePlus50BtnClick(Sender: TObject);
+begin
+  SuperSpinner.Spin(sdCW, 1, 60, UseProcessMsgCb.Checked);
+end;
+
+procedure TSSTestFrm.ValuePlus10BtnClick(Sender: TObject);
+begin
+  // Spin the wheel Clockwise 1 degree, 10 time. If UseProcessMsgCb is
+  // True, then between each step ProcessMessages will be called to let
+  // the screen update at each move. Otherwise it will just jump to the
+  // end position depening on how the O/S does screen updates.
+  //
+  // Will cause the OnPosChange Event to fire 10 times in this case as
+  // internally the Spin method will call bump as many times as needed.
+
+  // To change speed or smoothness you can change the Angle and count, for
+  // example you can have the Angle = 0.1 and the Step be 100. Will be slow
+  // and smooth. You will however get 100 calls to OnPosChange() but you
+  // can compensate by scaling your totalizer or other schemes. Play...
+
+    SuperSpinner.Spin(sdCW, 1, 10, UseProcessMsgCb.Checked);
+end;
+
+procedure TSSTestFrm.ValuePlus1BtnClick(Sender: TObject);
+begin
+  // Bump the wheel Clockwise 1 degree, Will cause the
+  // OnPosChange Event to fire
+
+  SuperSpinner.Bump(sdCW, 1);
+end;
+
+procedure TSSTestFrm.ValueZeroBtnClick(Sender: TObject);
+begin
+  SuperSpinner.Angle :=0;
+  FTotalizer := 0;
+  TotalizerValueLbl.Caption := '0';
+  AngleValueLbl.Caption := '0';
+  DirectionValueLbl.Caption := 'N/A';
+  WrappedValueLbl.Caption := 'Wrapped : N/A';
+  AngleTb.Value := 0;
+end;
+
+procedure TSSTestFrm.WidthHeightAddBtnClick(Sender: TObject);
+begin
+  SuperSpinner.Width := SuperSpinner.Width + 10;
+  SuperSpinner.Height := SuperSpinner.Height + 10;
+  UpdateWHStats;
+end;
+
+procedure TSSTestFrm.HeightSubBtnClick(Sender: TObject);
+begin
+  SuperSpinner.Height := SuperSpinner.Height - 10;
+  UpdateWHStats;
+end;
+
+procedure TSSTestFrm.LeftAddBtnClick(Sender: TObject);
+begin
+  SuperSpinner.Left := SuperSpinner.Left + 10;
+  UpdateLTStats;
+end;
+
+procedure TSSTestFrm.LeftSubBtnClick(Sender: TObject);
+begin
+  SuperSpinner.Left := SuperSpinner.Left - 10;
+  UpdateLTStats;
+end;
+
+procedure TSSTestFrm.ResetPositionBtnClick(Sender: TObject);
+begin
+  SuperSpinner.Top := 50;
+  SuperSpinner.Left := 50;
+  UpdateLTStats;
+end;
+
+procedure TSSTestFrm.ResetSizeBtnClick(Sender: TObject);
+begin
+    SuperSpinner.Width := 150;
+    SuperSpinner.Height := 150;
+    UpdateWHStats;
+end;
+
+procedure TSSTestFrm.TopAddBtnClick(Sender: TObject);
+begin
+    SuperSpinner.Top := SuperSpinner.Top + 10;
+    UpdateLTStats;
+end;
+
+procedure TSSTestFrm.TopSubBtnClick(Sender: TObject);
+begin
+  SuperSpinner.Top := SuperSpinner.Top - 10;
+  UpdateLTStats;
+end;
+
+procedure TSSTestFrm.WidthAddBtnClick(Sender: TObject);
+begin
+  SuperSpinner.Width := SuperSpinner.Width + 10;
+  UpdateWHStats;
+end;
+
+procedure TSSTestFrm.WidthHeightSubBtnClick(Sender: TObject);
+begin
+  SuperSpinner.Width := SuperSpinner.Width - 10;
+  SuperSpinner.Height := SuperSpinner.Height - 10;
+  UpdateWHStats;
+end;
+
+procedure TSSTestFrm.WidthSubBtnClick(Sender: TObject);
+begin
+  SuperSpinner.Width := SuperSpinner.Width - 10;
+  UpdateWHStats;
+end;
+
+procedure TSSTestFrm.UpdateSpinnerMovementStats;
+begin
+   // Only updates a few things since OnPosChange has more data
+
+   AngleValueLbl.Caption := FloatToStrF(SuperSpinner.Angle, ffFixed, 3,3);
+   AngleTb.Value := Round(SuperSpinner.Angle);
+   TotalizerValueLbl.Caption := IntToStr(FTotalizer);
+end;
+
+procedure TSSTestFrm.UpdateBasicStats;
+begin
+  BackgroundColorCb.Selected := SuperSpinner.Color;
+  SpinResolutionCb.ItemIndex := ord(SuperSpinner.SpinResolution);
+  MouseWheelSpeedTB.Value := SuperSpinner.WheelSpeed;
+  SpinnerLockedCb.Checked := SuperSpinner.Locked;
+  PositionSnapCb.Checked := SuperSpinner.PositionSnap;
+  AutoScaleCb.Checked := SuperSpinner.AutoScale;
+  AutoScaleEnabledLbl.Visible := SuperSpinner.AutoScale;
+  AngleTb.Value := Round(SuperSpinner.Angle);
+
+end;
+
+procedure TSSTestFrm.UpdateWHStats;
+begin
+    WidthValLbl.Caption := IntToStr(SuperSpinner.Width);
+    HeightValLbl.Caption := IntToStr(SuperSpinner.Height);
+end;
+
+procedure TSSTestFrm.UpdateLTStats;
+begin
+    LeftValLbl.Caption := IntToStr(SuperSpinner.Left);
+    TopValLbl.Caption := IntToStr(SuperSpinner.Top);
+end;
+
+procedure TSSTestFrm.UpdateFrameStats;
+begin
+  FrameBorderColorCb.Selected := SuperSpinner.FrameSettings.BorderColor;
+  FrameRadiusSpe.Value := SuperSpinner.FrameSettings.BorderWidth;
+end;
+
+procedure TSSTestFrm.UpdateKnobStats;
+begin
+  KnobStyleCb.ItemIndex := ord(SuperSpinner.KnobSettings.Style);
+  KnobFillColorCb.Selected := SuperSpinner.KnobSettings.FillColor;
+  KnobEdgeColorCb.Selected := SuperSpinner.KnobSettings.EdgeColor;
+  KnobEdgeThicknessSpe.Value := SuperSpinner.KnobSettings.EdgeThickness;
+  KnobLightIntensitySpe.Value := SuperSpinner.KnobSettings.LightIntensity;
+  KnobCurveExponentSpe.Value := SuperSpinner.KnobSettings.CurveExponent;
+end;
+
+procedure TSSTestFrm.UpdatePositionStats;
+begin
+  PositionStyleCb.ItemIndex := ord(SuperSpinner.PositionSettings.Style);
+  PositionFillColorCb.Selected := SuperSpinner.PositionSettings.FillColor;
+  PositionEdgeColorCb.Selected := SuperSpinner.PositionSettings.EdgeColor;
+  PositionFillOpacitySpe.Value := SuperSpinner.PositionSettings.Opacity;
+  PositionRadiusSpe.Value := SuperSpinner.PositionSettings.Radius;
+  PositionEdgeThicknessSpe.Value := SuperSpinner.PositionSettings.EdgeThickness;
+  PositionMarginSpe.Value := SuperSpinner.PositionSettings.Margin;
+  PositionCenterMarginSpe.Value := SuperSpinner.PositionSettings.CenterMargin;
+  PositionLineCountSpe.Value := SuperSpinner.PositionSettings.LineCount;
+  PositionLineWidthSpe.Value := SuperSpinner.PositionSettings.LineWidth;
+end;
+
+procedure TSSTestFrm.UpdateCapStats;
+begin
+  CapStyleCb.ItemIndex := ord(SuperSpinner.CapSettings.Style);
+  CapFillColorCb.Selected := SuperSpinner.CapSettings.FillColor;
+  CapEdgeColorCb.Selected := SuperSpinner.CapSettings.EdgeColor;
+  CapEdgeThicknessSpe.Value := SuperSpinner.CapSettings.EdgeThickness;
+  CapRadiusSpe.Value := SuperSpinner.CapSettings.Radius;
+  CapLightIntensitySpe.Value := SuperSpinner.CapSettings.LightIntensity;
+  CapCurveExponentSpe.Value := SuperSpinner.CapSettings.CurveExponent;
+end;
+
+end.
+

+ 3 - 3
update_bgracontrols_force.json

@@ -1,6 +1,6 @@
 {
 {
   "UpdatePackageData" : {
   "UpdatePackageData" : {
-    "DownloadZipURL" : "https://github.com/bgrabitmap/bgracontrols/archive/v9.0.1.7.zip",
+    "DownloadZipURL" : "https://github.com/bgrabitmap/bgracontrols/archive/v9.0.2.zip",
     "Name" : "bgracontrols-master"
     "Name" : "bgracontrols-master"
   },
   },
   "UpdatePackageFiles" : [
   "UpdatePackageFiles" : [
@@ -8,13 +8,13 @@
       "ForceNotify" : true,
       "ForceNotify" : true,
       "InternalVersion" : 25,
       "InternalVersion" : 25,
       "Name" : "bgracontrols.lpk",
       "Name" : "bgracontrols.lpk",
-      "Version" : "9.0.1.7"
+      "Version" : "9.0.2.0"
     },
     },
     {
     {
       "ForceNotify" : false,
       "ForceNotify" : false,
       "InternalVersion" : 1,
       "InternalVersion" : 1,
       "Name" : "bgrapascalscriptcomponent.lpk",
       "Name" : "bgrapascalscriptcomponent.lpk",
-      "Version" : "9.0.1.7"
+      "Version" : "9.0.2.0"
     }
     }
   ]
   ]
 }
 }