unit PngButtonFunctions;

interface

uses
  Windows, Buttons, Graphics, pngimage;

{$IF RTLVersion < 20.0 }
type
  TPngImage = TPNGObject;
{$IFEND}

procedure CalcButtonLayout(Canvas: TCanvas; PngImage: TPngImage; const Client:
  TRect; Pressed, Down: Boolean; const Caption: string; Layout: TButtonLayout;
  Margin, Spacing: Integer; var GlyphPos, TextPos: TPoint; BiDiFlags: LongInt);

implementation

uses
  Classes;

procedure CalcButtonLayout(Canvas: TCanvas; PngImage: TPngImage; const Client:
  TRect; Pressed, Down: Boolean; const Caption: string; Layout: TButtonLayout;
  Margin, Spacing: Integer; var GlyphPos, TextPos: TPoint; BiDiFlags: LongInt);
var
  ClientSize, GlyphSize, TextSize, TotalSize: TPoint;
  TextBounds: TRect;
begin
  if (BiDiFlags and DT_RIGHT) = DT_RIGHT then begin
    if Layout = blGlyphLeft then
      Layout := blGlyphRight
    else if Layout = blGlyphRight then
      Layout := blGlyphLeft;
  end;

  //Calculate the item sizes
  ClientSize := Point(Client.Right - Client.Left, Client.Bottom - Client.Top);

  if PngImage <> nil then
    GlyphSize := Point(PngImage.Width, PngImage.Height)
  else
    GlyphSize := Point(0, 0);

  if Length(Caption) > 0 then begin
    TextBounds := Rect(0, 0, Client.Right - Client.Left, 0);
    DrawText(Canvas.Handle, PChar(Caption), Length(Caption), TextBounds,
      DT_CALCRECT or BiDiFlags);
    TextSize := Point(TextBounds.Right - TextBounds.Left, TextBounds.Bottom -
      TextBounds.Top);
  end
  else begin
    TextBounds := Rect(0, 0, 0, 0);
    TextSize := Point(0, 0);
  end;

  //If the layout has the glyph on the right or the left, then both the
  //text and the glyph are centered vertically.  If the glyph is on the top
  //or the bottom, then both the text and the glyph are centered horizontally.
  if Layout in [blGlyphLeft, blGlyphRight] then
    GlyphPos.Y := (ClientSize.Y - GlyphSize.Y + 1) div 2
  else
    GlyphPos.X := (ClientSize.X - GlyphSize.X + 1) div 2;

  //If there is no text or no bitmap, then Spacing is irrelevant
  if (TextSize.X = 0) or (GlyphSize.X = 0) then
    Spacing := 0;

  //Adjust Margin and Spacing
  if Margin = -1 then begin
    if Spacing = -1 then begin
      TotalSize := Point(GlyphSize.X + TextSize.X, GlyphSize.Y + TextSize.Y);
      if Layout in [blGlyphLeft, blGlyphRight] then
        Margin := (ClientSize.X - TotalSize.X) div 3
      else
        Margin := (ClientSize.Y - TotalSize.Y) div 3;
    end
    else begin
      TotalSize := Point(GlyphSize.X + Spacing + TextSize.X, GlyphSize.Y +
        Spacing + TextSize.Y);
      if Layout in [blGlyphLeft, blGlyphRight] then
        Margin := (ClientSize.X - TotalSize.X) div 2
      else
        Margin := (ClientSize.Y - TotalSize.Y) div 2;
    end
  end
  else if Spacing = -1 then begin
    TotalSize := Point(ClientSize.X - (Margin + GlyphSize.X), ClientSize.Y -
      (Margin + GlyphSize.Y));
  end;

  case Layout of
    blGlyphLeft: GlyphPos.X := Margin;
    blGlyphRight: GlyphPos.X := ClientSize.X - Margin - GlyphSize.X;
    blGlyphTop: GlyphPos.Y := Margin;
    blGlyphBottom: GlyphPos.Y := ClientSize.Y - Margin - GlyphSize.Y;
  end;

  if Layout in [blGlyphLeft, blGlyphRight] then
    TextPos.Y := (ClientSize.Y - TextSize.Y) div 2
  else
    TextPos.X := (ClientSize.X - TextSize.X) div 2;
  case Layout of
    blGlyphLeft: TextPos.X := GlyphPos.X + GlyphSize.X + Spacing;
    blGlyphRight: TextPos.X := GlyphPos.X - Spacing - TextSize.X;
    blGlyphTop: TextPos.Y := GlyphPos.Y + GlyphSize.Y + Spacing;
    blGlyphBottom: TextPos.Y := GlyphPos.Y - Spacing - TextSize.Y;
  end;

  //Fixup the result variables
  with GlyphPos do begin
    Inc(X, Client.Left + Integer(Pressed or Down));
    Inc(Y, Client.Top + Integer(Pressed or Down));
  end;
  with TextPos do begin
    Inc(X, Client.Left + Integer(Pressed or Down));
    Inc(Y, Client.Top + Integer(Pressed or Down));
  end;
end;

end.
