unit uBafControls;

// this code is under the BAF fair use license (BFUL) - https://bafbal.de/index.php?title=Bful
// some controls

interface

uses FMX.StdCtrls, System.SysUtils, System.Classes, FMX.Types, FMX.Controls,
    uBafTypes, System.StrUtils, System.UITypes, FMX.TreeView, FMX.Edit,
    System.Contnrs,System.Types, uStringIniFile, FMX.Layouts, FMX.ListBox,
    FMX.Graphics, FMX.Forms;

type

  TBafButtonDock = class(TPanel)
  protected
    FOldWidth, FOldHeight: Single;
    FOnHeightChange: TNotifyEvent;
    procedure Resize; override;
  public
    class function Place(AOwner, AParent: TPanel): TBafButtonDock;
    procedure PlaceControls;
    property OnHeightChange: TNotifyEvent
        read FOnHeightChange write FOnHeightChange;
  end;

  TBafButton = class(TButton)
  protected
    FCommand: string;
    FRight: TBafRight;
    FStatusEnabled: string;
    FLineP: string;
  public
    constructor Create(AOwner: TComponent); override;
    procedure OpEnabled(AGridStatus: TBafGridStatus);
  published
    property Command: string read FCommand write FCommand;
    property Right: TBafRight read FRight write FRight;
    property StatusEnabled: string read FStatusEnabled write FStatusEnabled;
    property LineP: string read FLineP write FLineP;
  end;

  TBafCheckBox = class(TCheckBox)
  private
    function GetValue: string;
    procedure SetValue(const Value: string);
  public
    constructor Create(AOwner: TComponent); override;
  published
    property Value: string read GetValue write SetValue;
  end;

  TBafEdit = class(TEdit)
  private
    FHasTab2Return: boolean;
  protected
    FTab2Return: boolean;
    FIsLabel: boolean;
    FOnFilter: TNotifyEvent;
    FDataType: TBafPageCellType;
    procedure KeyUp(var AKey: Word; var AKeyChar: Char; AShift: TShiftState); override;
    procedure KeyDown(var Key: Word; var KeyChar: Char; Shift: TShiftState); override;
    procedure DialogKey(var Key: Word; Shift: TShiftState); override;
  public
  published
    property DataType: TBafPageCellType read FDataType write FDataType;
    property OnFilter: TNotifyEvent read FOnFilter write FOnFilter;
    property IsLabel: boolean read FIsLabel write FIsLabel;
    property Tab2Return: boolean read FTab2Return write FTab2Return;
    property HasTab2Return: boolean read FHasTab2Return write FHasTab2Return;
  end;

  TBafSplitter = class(TPanel)
  private
    FOnButtonClick: TNotifyEvent;
    FPanel1Size: integer;
    FPanel1, FPanel2: TPanel;
    FPanel01, FPanel02: TPanel;
    FDock1, FDock2: TBafButtonDock;
    procedure SetPanel1Size(const Value: integer);
  protected
    FVertical: boolean;
    FSplit: TSplitter;
  public
    constructor Create(AOwner: TComponent; AVertical: boolean = false);
    procedure RefreshDocks;
    procedure ResizePage;
    function GetPanel2X: single;
    property Panel1Size: integer read FPanel1Size write SetPanel1Size;
    property Dock1: TBafButtonDock read FDock1;
    property Dock2: TBafButtonDock read FDock2;
    property Panel1: TPanel read FPanel1;
    property Panel2: TPanel read FPanel2;
    property OnButtonClick: TNotifyEvent read FOnButtonClick write FOnButtonClick;
  end;

  TBafSingleSplitter = class(TPanel)
  private
    FOnButtonClick: TNotifyEvent;
    FPanel1: TPanel;
    FPanel01: TPanel;
    FDock1: TBafButtonDock;
  public
    constructor Create(AOwner: TComponent); override;
    procedure RefreshDocks;
    property Dock1: TBafButtonDock read FDock1;
    property Panel1: TPanel read FPanel1;
    property OnButtonClick: TNotifyEvent read FOnButtonClick write FOnButtonClick;
  end;

  TBafTreeView = class;

  TBafTreeViewItemLookupEvent = procedure(ASpecial: boolean; AName, AKey: string; var AResult: string) of object;
  TBafTreeViewFunctionEvent = procedure(var AText: string) of object;

  TBafTreeViewItem = class(TTreeViewItem)
  private
    FOnChangeExpanded,
    FOnChangeCollapsed: TNotifyEvent;
    FIni: TStringIniFile;
    FNodeParent: TBafTreeViewItem;
    FOwnerTree: TBafTreeView;
    FObjectToRemove: boolean;
    FOnLookup: TBafTreeViewItemLookupEvent;
    FOnFunction: TBafTreeViewFunctionEvent;
    FBafData: TFmxObject;
    function GetItemByIndex(const Index: Integer): TBafTreeViewItem;
  protected
    procedure SetIsExpanded(const Value: Boolean); override;
  public
    constructor Create(AOwner: TComponent); override;
    class function CreateInsertItem(AOwner: TBafTreeView;
        AParent: TBafTreeViewItem; ACaption: string): TBafTreeViewItem;
    destructor Destroy; override;
    function IsDummy: boolean;
    function GetCaption: string;
    function GetSelectionCommand: string;
    function GetOpenCommand: string;
    function GetTableName: string;
    function GetId: string;
//    function GetItem: string;
    function GetNodeOrParentValue(ASection, AName, ADefault: string): string;
    function GetNodeValue(ASection, AName, ADefault: string): string;
    procedure DbCancel;
    function IsValue(AFieldName, AValue: string): boolean;
    property Items[const Index: Integer]: TBafTreeViewItem read GetItemByIndex; default;
    property Ini: TStringIniFile read FIni;
    property NodeParent: TBafTreeViewItem read FNodeParent;
    property ObjectToRemove: boolean read FObjectToRemove write FObjectToRemove;
    property BafData: TFmxObject read FBafData write FBafData;
  published
    property OnChangeExpanded: TNotifyEvent read FOnChangeExpanded write FOnChangeExpanded;
    property OnChangeCollapsed: TNotifyEvent read FOnChangeCollapsed write FOnChangeCollapsed;
    property OnLookup: TBafTreeViewItemLookupEvent read FOnLookup write FOnLookup;
    property OnFunction: TBafTreeViewFunctionEvent read FOnFunction write FOnFunction;
  end;

  TItemExpandCollapseEvent = procedure(Sender: TBafTreeView;
      AItem: TBafTreeViewItem) of object;

  TBafTreeView = class(TTreeView)
  private
    FNamedCacheList: TStringList;
    FOnItemExpand: TItemExpandCollapseEvent;
    FOnItemCollapse: TItemExpandCollapseEvent;
    function GetBafItem(Index: Integer): TBafTreeViewItem;
    function GetBafSelected: TBafTreeViewItem;
    procedure SetBafSelected(const Value: TBafTreeViewItem);
  protected
    procedure DoResized; override;
    function DoCalcContentBounds: TRectF; override;
    procedure DoItemCollapseExpand(AItem: TBafTreeViewItem; AExpand: boolean);
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    class function Place(AOwner: TComponent; AParent: TControl): TBafTreeView;
    property NamedCacheList: TStringList read FNamedCacheList;
    property BafItems[Index: Integer]: TBafTreeViewItem read GetBafItem;
    property BafSelected: TBafTreeViewItem read GetBafSelected write SetBafSelected;
  published
    property OnItemExpand: TItemExpandCollapseEvent read FOnItemExpand write FOnItemExpand;
    property OnItemCollapse: TItemExpandCollapseEvent read FOnItemCollapse write FOnItemCollapse;
  end;

  TBafHorzScrollBox = class(THorzScrollBox)
  protected
    function DoCalcContentBounds: TRectF; override;
  end;

  TBafVertScrollBox = class(TVertScrollBox)
  protected
    function DoCalcContentBounds: TRectF; override;
  end;

  TBafListBox = class(TListBox)
  protected
    function DoCalcContentBounds: TRectF; override;
  end;

  TBafComboBox = class(TComboBox)
  private
    FWithTabClosed: boolean;
  protected
    FTabCloses: boolean;
    procedure ComboBoxPaint(Sender: TObject; Canvas: TCanvas; const ARect: TRectF);
    procedure ListCalcContentBounds(Sender: TObject; var ContentBounds: TRectF);
    procedure DialogKey(var Key: Word; Shift: TShiftState); override;
  public
    constructor Create(AOwner: TComponent); override;
  published
    property TabCloses: boolean read FTabCloses write FTabCloses;
    property WithTabClosed: boolean read FWithTabClosed write FWithTabClosed;
  end;

  TBafSplitterBar = class(TSplitter)
  public
    class function Place(AParent: TControl; AHorz: boolean): TBafSplitterBar;
  end;

  TBafPanel = class(TPanel)
  public
    class function Place(AParent: TControl; AAlign: TAlignLayout): TBafPanel;
  end;

procedure Register;

implementation

//uses uBafDataCache;

procedure Register;
begin
  RegisterComponents('BAF', [TBafHorzScrollBox, TBafVertScrollBox, TBafListBox]);
end;


{ TBafButtonDock }

class function TBafButtonDock.Place(AOwner, AParent: TPanel): TBafButtonDock;
begin
  result := TBafButtonDock.Create(AOwner);
  result.Parent := AParent;
  result.StyleLookup := 'pushpanel';
  result.Align := TAlignLayout.Top;
end;

procedure TBafButtonDock.PlaceControls;
const
  LTOPPLUS = 35;
var
  i: integer;
  LLeft, LLeft2, LTop: Single;
  LControl: TControl;
begin
  LLeft := 8;
  LTop := 8;
  for i := 0 to ChildrenCount - 1 do begin
    LControl := Children.Items[i] as TControl;
    if (LControl is TBafButton) or (LControl is TBafEdit)
         or (LControl is TBafCheckBox) then begin
      if LControl.Tag = 1337 then begin   // fix new line
        if LLeft > 8 then
          LTop := LTop + LTOPPLUS;
        LLeft := 8;
      end;
      LLeft2 := LLeft + LControl.Width + 4;
      if (LLeft > 8) and (LLeft2 > (Width - 8)) then begin
        LTop := LTop + LTOPPLUS;
        LLeft := 8;
      end;
      LControl.Position.X := LLeft;
      LControl.Position.Y := LTop;
      LLeft := LLeft + LControl.Width + 4;
    end;
  end;
  if LLeft > 8 then
    LTop := LTop + LTOPPLUS;
  if FOldHeight <> LTop then begin
    FOldHeight := LTop;
    Height := LTop;
    if Assigned(FOnHeightChange) then
      FOnHeightChange(self);
  end;
end;

procedure TBafButtonDock.Resize;
begin
  inherited;
  if Width <> FOldWidth then begin
    FOldWidth := Width;
    PlaceControls;
  end;
end;

{ TBafButton }

constructor TBafButton.Create(AOwner: TComponent);
begin
  inherited;
  Height := 30;
end;

procedure TBafButton.OpEnabled(AGridStatus: TBafGridStatus);
var
  LEnabled: boolean;
begin
  case AGridStatus of
    gsBrowse: LEnabled := Pos('b', StatusEnabled) > 0;
    gsChanged: LEnabled := Pos('c', StatusEnabled) > 0;
    gsEditing: LEnabled := Pos('e', StatusEnabled) > 0;
    gsCheckFailed: LEnabled := (Pos('p', StatusEnabled) > 0)
        or (Pos('f', StatusEnabled) > 0);
  else
    LEnabled := false;
  end;
  Enabled := LEnabled and (FRight = brWrite);
end;

{ TBafCheckBox }

constructor TBafCheckBox.Create(AOwner: TComponent);
begin
  inherited;
  StyleLookup := 'checkboxstyle';
  Height := 23;
end;

function TBafCheckBox.GetValue: string;
begin
  result := IfThen(IsChecked, BAFYESCHAR, BAFNOCHAR);
end;

procedure TBafCheckBox.SetValue(const Value: string);
begin
  IsChecked := BafIsYesChar(Value);
end;

{ TBafEdit }


{ TBafEdit }

procedure TBafEdit.DialogKey(var Key: Word; Shift: TShiftState);
begin
  inherited;
//  if (Key = vkTab) and FTab2Return then begin
//    Key := vkReturn;
//    FHasTab2Return := true;
//  end;
end;

procedure TBafEdit.KeyDown(var Key: Word; var KeyChar: Char; Shift: TShiftState);

  procedure lokDate(ADiff: integer);
  begin
    Text := FormatDateTime('dd.mm.yyyy', trunc(now) + ADiff);
    KeyChar := #0;
  end;

begin
  case FDataType of
    ctInt: if not (KeyChar in ['0'..'9', '-', #8]) then
      KeyChar := #0;
    ctCurr, ctCurr4, ctCurrInt: if not (KeyChar in ['0'..'9', ',', '-', #8]) then
      KeyChar := #0;
    ctDate: begin
      case KeyChar of
        'h', 't', 'H', 'T': lokDate(0);
        'm', 'M': lokDate(1);
        'y', 'g', 'Y', 'G': lokDate(-1);
        'ü', 'Ü': lokDate(2);
        'v', 'V': lokDate(-2);
        '0'..'9', '.': ;
        else
          KeyChar := #0;
      end;
    end;
    ctDateMin, ctDateSek: begin
      case KeyChar of
        'h', 't', 'H', 'T': lokDate(0);
        'm', 'M': lokDate(1);
        'y', 'g', 'Y', 'G': lokDate(-1);
        'ü', 'Ü': lokDate(2);
        'v', 'V': lokDate(-2);
        '0'..'9', '.', ' ', ':': ;
      end;
    end;
  end;
  inherited;
// procedure TBafEdit.KeyDown
end;

procedure TBafEdit.KeyUp(var AKey: Word; var AKeyChar: Char;
  AShift: TShiftState);
var
  p: integer;
begin
  inherited;
  if (FDataType in [ctDate, ctDateMin, ctDateSek]) and (Length(Text) in [6, 8])
      and (AKey in [vkReturn, vkF12]) then begin
    p := Pos('.', Text);
    if p = 0 then
      Text := copy(Text, 1, 2) + '.' + copy(Text, 3, 2) + '.'
          + copy(Text, 5, MaxInt);
  end;
  if (AKey in [vkReturn, vkF12]) and Assigned(FOnFilter) then
    FOnFilter(Self);
end;

{ TBafSplitter }

constructor TBafSplitter.Create(AOwner: TComponent; AVertical: boolean);

  function lokCreatePanel(AAlign: TAlignLayout; AParent: TControl): TPanel;
  begin
    result := TPanel.Create(Self);
    result.Parent := AParent;
    result.Align := AAlign;
    result.StyleLookup := 'pushpanel';
  end;

begin
  inherited Create(AOwner);
  FVertical := AVertical;
  if AVertical then begin
    FPanel01 := lokCreatePanel(TAlignLayout.Top, Self);
    FSplit := TSplitter.Create(Self);
    FSplit.Parent := Self;
    FSplit.Align := TAlignLayout.Top;
    FSplit.Height := 5;
    FPanel02 := lokCreatePanel(TAlignLayout.Client, Self);
  end
  else begin
    FPanel01 := lokCreatePanel(TAlignLayout.Left, Self);
    FSplit := TSplitter.Create(Self);
    FSplit.Parent := Self;
    FSplit.Width := 5;
    FSplit.Margins.Left := 1;
    FSplit.Margins.Top := 1;
    FSplit.Margins.Right := 1;
    FSplit.Margins.Bottom := 1;
    FPanel02 := lokCreatePanel(TAlignLayout.Client, Self);
  end;
  FDock1 := TBafButtonDock.Place(FPanel01, FPanel01);
  FDock2 := TBafButtonDock.Place(FPanel02, FPanel02);
  FPanel1 := lokCreatePanel(TAlignLayout.Client, FPanel01);
  FPanel2 := lokCreatePanel(TAlignLayout.Client, FPanel02);
end;

function TBafSplitter.GetPanel2X: single;
begin
  result := FPanel02.Position.X;
end;

procedure TBafSplitter.RefreshDocks;
begin
  FDock1.PlaceControls;
  FDock2.PlaceControls;
end;

procedure TBafSplitter.ResizePage;
begin
{ TODO -cStriche : Code entfernt }
  Application.ProcessMessages;
  Panel1Size := Panel1Size + 1;
  Application.ProcessMessages;
  Panel1Size := Panel1Size - 1;
//  Application.ProcessMessages;
//  Panel1Size := Panel1Size + 1;
//  Application.ProcessMessages;
//  Panel1Size := Panel1Size - 1;
end;

procedure TBafSplitter.SetPanel1Size(const Value: integer);
begin
  FPanel1Size := Value;
  if FVertical then
    FPanel01.Height := Value
  else
    FPanel01.Width := Value;
end;

{ TBafTreeView }

constructor TBafTreeView.Create(AOwner: TComponent);
begin
  inherited;
  FNamedCacheList := TStringList.Create;
  FNamedCacheList.OwnsObjects := true;
  FNamedCacheList.Sorted := true;
  FNamedCacheList.Duplicates := dupError;
end;

destructor TBafTreeView.Destroy;
begin
  FreeAndNil(FNamedCacheList);
  inherited;
end;

function TBafTreeView.DoCalcContentBounds: TRectF;
begin
  // to avoid Scrollbars caused by rounding inaccuracies
  result := inherited DoCalcContentBounds;
  result.Inflate(-0.01, -0.01);
end;

procedure TBafTreeView.DoItemCollapseExpand(AItem: TBafTreeViewItem; AExpand: boolean);
begin
  if AExpand and Assigned(FOnItemExpand) then
    FOnItemExpand(Self, AItem)
  else if not AExpand and Assigned(FOnItemCollapse) then
    FOnItemCollapse(Self, AItem);
end;

procedure TBafTreeView.DoResized;
begin
  inherited;
  ShowScrollBars := false;
  ShowScrollBars := true;
end;

function TBafTreeView.GetBafItem(Index: Integer): TBafTreeViewItem;
begin
  result := Items[Index] as TBafTreeViewItem;
end;

function TBafTreeView.GetBafSelected: TBafTreeViewItem;
begin
  result := Selected as TBafTreeViewItem;
end;

class function TBafTreeView.Place(AOwner: TComponent;
    AParent: TControl): TBafTreeView;
begin
  result := TBafTreeView.Create(AOwner);
  result.Parent := AParent;
  result.Align := TAlignLayout.Client;
  result.Margins.Left := 8;
  result.Margins.Top := 8;
  result.Margins.Right := 8;
  result.Margins.Bottom := 8;
end;

procedure TBafTreeView.SetBafSelected(const Value: TBafTreeViewItem);
begin
  Selected := Value;
end;

{ TBafTreeViewItem }


constructor TBafTreeViewItem.Create(AOwner: TComponent);
begin
  inherited;
  FIni := TStringIniFile.Create('');
end;


class function TBafTreeViewItem.CreateInsertItem(AOwner: TBafTreeView;
  AParent: TBafTreeViewItem; ACaption: string): TBafTreeViewItem;
begin
  result := TBafTreeViewItem.Create(AOwner);
  result.FOwnerTree := AOwner;
  result.FNodeParent := AParent;
  if AParent = nil then
    result.Parent := AOwner
  else
    result.Parent := AParent;
  result.Text := ACaption;
end;

procedure TBafTreeViewItem.DbCancel;
var
  i: integer;
  sl: TStringList;
  LInserted: boolean;
begin
  LInserted := FIni.ReadBool(SEC_ADD, 'ins', false);
  if LInserted then begin                             // Insert
    ObjectToRemove := true;
    FOwnerTree.RemoveObject(Self);
  end
  else begin                                        // Update
    sl := TStringList.Create;
    try
      FIni.ReadSection(SEC_DB, sl);
      for i := 0 to sl.Count - 1 do
        FIni.WriteString(SEC_DATA, sl[i], FIni.ReadString(SEC_DB, sl[i], ''));
      FIni.WriteBool(SEC_ADD, 'ins', false);
    finally
      sl.Free;
    end;
  end;
  if Assigned(Self) then
    Text := GetCaption;
end;

destructor TBafTreeViewItem.Destroy;
begin
  FreeAndNil(FIni);
  inherited;
end;

function TBafTreeViewItem.GetCaption: string;
var
  s1, s2, c1, c2, ld: string;
begin
  c1 := FIni.ReadString(SEC_ADD, 'c1', 'krzl');
  c2 := FIni.ReadString(SEC_ADD, 'c2', 'bez');
  s1 := FIni.ReadString(SEC_DATA, c1, '');
  s2 := FIni.ReadString(SEC_DATA, c2, '');

  ld := FIni.ReadString(SEC_ADD, 'ld1', '');
  if (ld <> '') and Assigned(FOnLookup) then
    FOnLookup(false, ld, s1, s1);
  ld := FIni.ReadString(SEC_ADD, 'ls1', '');
  if (ld <> '') and Assigned(FOnLookup) then
    FOnLookup(true, ld, s1, s1);
  ld := FIni.ReadString(SEC_ADD, 'ld2', '');
  if (ld <> '') and Assigned(FOnLookup) then
    FOnLookup(false, ld, s2, s2);
  ld := FIni.ReadString(SEC_ADD, 'ls2', '');
  if (ld <> '') and Assigned(FOnLookup) then
    FOnLookup(true, ld, s2, s2);

  result := s1 + IfThen((s1 <> '') and (s2 <> ''), ' - ') + s2;
  if result = '' then
    result := Text;
  if (Pos('$', result) > 0) and Assigned(FOnFunction) then
    FOnFunction(result);
end;

function TBafTreeViewItem.GetId: string;
begin
  result := FIni.ReadString(SEC_DATA, 'id', '');
end;

function TBafTreeViewItem.GetItemByIndex(const Index: Integer): TBafTreeViewItem;
begin
  result := ItemByIndex(Index) as TBafTreeViewItem;
end;

function TBafTreeViewItem.GetNodeOrParentValue(ASection, AName,
    ADefault: string): string;
const
  NOFOUND = '{95D96D3B-B82E-4A86-803D-ACBC86BE6018}';
var
  LIni: TStringIniFile;
  LNode: TBafTreeViewItem;
begin
  LNode := Self;
  LIni := LNode.Ini;
  result := NOFOUND;
  if Assigned(LIni) then
    result := LIni.ReadString(ASection, AName, NOFOUND);
  while (result = NOFOUND) and Assigned(LNode) do begin
    LNode := LNode.NodeParent;
    if Assigned(LNode) then
      LIni := LNode.Ini;
    if Assigned(LIni) then
      result := LIni.ReadString(ASection, AName, NOFOUND);
  end;
  if result = NOFOUND then
    result := ADefault;
end;

function TBafTreeViewItem.GetNodeValue(ASection, AName, ADefault: string): string;
begin
  result := FIni.ReadString(ASection, AName, ADefault);
end;

function TBafTreeViewItem.GetOpenCommand: string;
begin
  result := FIni.ReadString(SEC_ADD, 'open', '');
end;

function TBafTreeViewItem.GetSelectionCommand: string;
begin
  result := FIni.ReadString(SEC_ADD, 'sel', '');
end;

function TBafTreeViewItem.GetTableName: string;
begin
  result := FIni.ReadString(SEC_ADD, 'table', '');
end;

function TBafTreeViewItem.IsDummy: boolean;
begin
  result := false;
  if Assigned(Self) then begin
    try
//      result := (Count = 1) and (Items[0].Text = BAF_DUMMYNODE_CAPTION);
    except
      // sometimes it failes, we can continue with result = false
    end;
  end;
end;

function TBafTreeViewItem.IsValue(AFieldName, AValue: string): boolean;
begin
  result := false;
  if AFieldName <> '' then
    result := AnsiCompareText(
        Ini.ReadString(SEC_DATA, AFieldName, ''), AValue) = 0;
end;

procedure TBafTreeViewItem.SetIsExpanded(const Value: Boolean);
var
  LWasExpanded: boolean;
begin
  LWasExpanded := IsExpanded;
  inherited;
  if (IsExpanded) and (LWasExpanded = false) then begin
    if Assigned(OnChangeExpanded) then
      OnChangeExpanded(Self);
    if Owner is TBafTreeView then
      (Owner as TBafTreeView).DoItemCollapseExpand(Self, true);
  end
  else begin
    if Assigned(OnChangeCollapsed) then
      OnChangeCollapsed(Self);
    if Owner is TBafTreeView then
      (Owner as TBafTreeView).DoItemCollapseExpand(Self, false);
  end;
end;

{ TBafVertScrollBox }

function TBafVertScrollBox.DoCalcContentBounds: TRectF;
begin
  // to avoid Scrollbars caused by rounding inaccuracies
  result := inherited DoCalcContentBounds;
  result.Inflate(-0.01, -0.01);
end;

{ TBafHorzScrollBox }

function TBafHorzScrollBox.DoCalcContentBounds: TRectF;
begin
  // to avoid Scrollbars caused by rounding inaccuracies
  result := inherited DoCalcContentBounds;
  result.Inflate(-0.01, -0.01);
end;

{ TBafSplitterBar }

class function TBafSplitterBar.Place(AParent: TControl;
    AHorz: boolean): TBafSplitterBar;
begin
  result := TBafSplitterBar.Create(AParent);
  result.Parent := AParent;
  if AHorz then begin
    result.Position.Y := 20000;
    result.Align := TAlignLayout.Top;
    result.Height := 5;
  end
  else begin
    result.Position.X := 20000;
    result.Align := TAlignLayout.Left;
    result.Width := 5;
  end;
end;

{ TBafPanel }

class function TBafPanel.Place(AParent: TControl;
    AAlign: TAlignLayout): TBafPanel;
begin
  result := TBafPanel.Create(AParent);
  result.Parent := AParent;
  result.StyleLookup := 'pushpanel';
  case AAlign of
    TAlignLayout.Left: result.Position.X := MaxInt;
    TAlignLayout.Top: result.Position.Y := MaxInt;
  end;
  result.Align := AAlign;
end;

{ TBafComboBox }


{ TBafComboBox }

procedure TBafComboBox.ComboBoxPaint(Sender: TObject; Canvas: TCanvas;
  const ARect: TRectF);
var
  LText: string;
  LRect: TRectF;
begin
  if ItemIndex >= 0 then
    LText := Items[ItemIndex];
  Canvas.Fill.Color := TAlphaColorRec.Black;
  LRect := ARect;
  LRect.Left := LRect.Left + 4;
  Canvas.FillText(LRect, LText, false, 1, [], TTextAlign.Leading);
end;

constructor TBafComboBox.Create(AOwner: TComponent);
begin
  inherited;
  ListBox.OnCalcContentBounds := ListCalcContentBounds;
  OnPaint := ComboBoxPaint;
end;

procedure TBafComboBox.DialogKey(var Key: Word; Shift: TShiftState);
begin
  inherited;
  if IsFocused and (Key > 0) then begin
    if TabCloses and (Key = 9) and DroppedDown then begin
      FWithTabClosed := true;
      Key := 13;
    end;
  end;
end;

procedure TBafComboBox.ListCalcContentBounds(Sender: TObject;
    var ContentBounds: TRectF);
begin
  ContentBounds.Inflate(-1, -1);
end;


{ TBafListBox }

function TBafListBox.DoCalcContentBounds: TRectF;
begin
  // to avoid Scrollbars caused by rounding inaccuracies
  result := inherited DoCalcContentBounds;
  result.Inflate(-0.01, -0.01);
end;

{ TBafSingleSplitter }

constructor TBafSingleSplitter.Create(AOwner: TComponent);

  function lokCreatePanel(AAlign: TAlignLayout; AParent: TControl): TPanel;
  begin
    result := TPanel.Create(Self);
    result.Parent := AParent;
    result.Align := AAlign;
    result.StyleLookup := 'pushpanel';
  end;

begin
  inherited Create(AOwner);
  FPanel01 := lokCreatePanel(TAlignLayout.Client, Self);
  FDock1 := TBafButtonDock.Place(FPanel01, FPanel01);
  FPanel1 := lokCreatePanel(TAlignLayout.Client, FPanel01);
end;

procedure TBafSingleSplitter.RefreshDocks;
begin
  FDock1.PlaceControls;
end;

end.
