Tulip.UI.RadioButtons.pas

Source Files

{******************************************************************************}
{                                                                              }
{                        Tulip - User Interface Library                        }
{                                                                              }
{             Copyright(c) 2012 Marcos Gomes. All rights Reserved.             }
{                                                                              }
{  --------------------------------------------------------------------------  }
{                                                                              }
{  This product is based on Asphyre Sphinx (c) 2000 - 2012  Yuriy Kotsarenko.  }
{       All rights reserved. Official web site: http://www.afterwarp.net       }
{                                                                              }
{******************************************************************************}
{                                                                              }
{  Important Notice:                                                           }
{                                                                              }
{  If you modify/use this code or one of its parts either in original or       }
{  modified form, you must comply with Mozilla Public License Version 2.0,     }
{  including section 3, "Responsibilities". Failure to do so will result in    }
{  the license breach, which will be resolved in the court. Remember that      }
{  violating author's rights either accidentally or intentionally is           }
{  considered a serious crime in many countries. Thank you!                    }
{                                                                              }
{  !! Please *read* Mozilla Public License 2.0 document located at:            }
{  http://www.mozilla.org/MPL/                                                 }
{                                                                              }
{  --------------------------------------------------------------------------  }
{                                                                              }
{  The contents of this file are subject to the Mozilla Public License         }
{  Version 2.0 (the "License"); you may not use this file except in            }
{  compliance with the License. You may obtain a copy of the License at        }
{  http://www.mozilla.org/MPL/                                                 }
{                                                                              }
{  Software distributed under the License is distributed on an "AS IS"         }
{  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the     }
{  License for the specific language governing rights and limitations          }
{  under the License.                                                          }
{                                                                              }
{  The Original Code is Tulip.UI.RadioButtons.pas.                             }
{                                                                              }
{  The Initial Developer of the Original Code is Marcos Gomes.                 }
{  Portions created by Marcos Gomes are Copyright (C) 2012, Marcos Gomes.      }
{  All Rights Reserved.                                                        }
{                                                                              }
{******************************************************************************}
{                                                                              }
{  Tulip.UI.RadioButtons.pas                            Modified: 05-Out-2012  }
{  --------------------------------------------------------------------------  }
{                                                                              }
{               Base Implementations for RadioButton Controls                  }
{                                                                              }
{                                Version 1.02                                  }
{                                                                              }
{******************************************************************************}

unit Tulip.UI.RadioButtons;

interface

uses
  System.SysUtils, System.Classes,
  // Asphyre Units
  AbstractCanvas, AsphyreFonts, AsphyreImages, AsphyreTypes, Vectors2,
  // Tulip UI Units
  Tulip.UI.Types, Tulip.UI.Classes, Tulip.UI.Controls, Tulip.UI.Helpers,
  Tulip.UI.Utils;

type
{$REGION 'TCustomARadioButton'}
  TCustomARadioButton = class(TWControl)
  private
    FAntialiased: Boolean;
    FBorder: TBorder;
    FBox: TCkBox;
    FCaption: String;
    FChecked: Boolean;
    FColor: TFillColor;
    FFocusRect: TFocusRect;
    FFont: TFormatedFont;
    FImage: TImage;
    FMargin: Word;
    FReadOnly: Boolean;
    FTransparent: Boolean;

    procedure SetAntialiased(Value: Boolean);
    procedure SetBorder(Value: TBorder);
    procedure SetBox(Value: TCkBox);
    procedure SetCaption(Value: String);
    procedure SetChecked(Value: Boolean);
    procedure SetColor(Value: TFillColor);
    procedure SetFocusRect(Value: TFocusRect);
    procedure SetFont(Value: TFormatedFont);
    procedure SetImage(Value: TImage);
    procedure SetMargin(Value: Word);
    procedure SetReadOnly(Value: Boolean);
    procedure SetTransparent(Value: Boolean);
  protected
    procedure AssignTo(Dest: TPersistent); override;
    procedure Paint; override;

    procedure SetHeight(Value: Integer); override;
    procedure SetWidth(Value: Integer); override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;

    procedure DblClick; override;
    procedure KeyDown(var Key: Word; Shift: TShiftState); override;
    procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
      X, Y: Integer); override;
    procedure MouseUp(Button: TMouseButton; Shift: TShiftState;
      X, Y: Integer); override;

    property Antialiased: Boolean read FAntialiased write SetAntialiased;
    property Border: TBorder read FBorder write SetBorder;
    property Box: TCkBox read FBox write SetBox;
    property Caption: String read FCaption write SetCaption;
    property Checked: Boolean read FChecked write SetChecked;
    property Color: TFillColor read FColor write SetColor;
    property FocusRect: TFocusRect read FFocusRect write SetFocusRect;
    property Font: TFormatedFont read FFont write SetFont;
    property Image: TImage read FImage write SetImage;
    property Margin: Word read FMargin write SetMargin;
    property ReadOnly: Boolean read FReadOnly write SetReadOnly;
    property Transparent: Boolean read FTransparent write SetTransparent;
  end;
{$ENDREGION}

implementation

{$REGION 'TCustomARadioButton'}
{ TCustomARadioButton }

procedure TCustomARadioButton.AssignTo(Dest: TPersistent);
begin
  ControlState := ControlState + [csReadingState];

  inherited AssignTo(Dest);

  if Dest is TCustomARadioButton then
    with TCustomARadioButton(Dest) do
    begin
      Antialiased := Self.Antialiased;
      Border := Self.Border;
      Box := Self.Box;
      Caption := Self.Caption;
      Checked := Self.Checked;
      Color := Self.Color;
      FocusRect := Self.FocusRect;
      Font := Self.Font;
      Image := Self.Image;
      Margin := Self.Margin;
      ReadOnly := Self.ReadOnly;
      Transparent := Self.Transparent;
    end;

  ControlState := ControlState - [csReadingState];
end;

constructor TCustomARadioButton.Create(AOwner: TComponent);
var
  I, Num: Integer;
  Control: TComponent;
begin
  ControlState := ControlState + [csCreating];

  inherited Create(AOwner);

  if (AOwner <> nil) and (AOwner <> Self) and (AOwner is TWControl) then
  begin
    // Auto generate name
    Num := 1;
    begin
      while AOwner.FindComponent('RadioButton' + IntToStr(Num)) <> nil do
        Inc(Num);
      Name := 'RadioButton' + IntToStr(Num);
    end;
  end;

  // Fields
  FAntialiased := True;
  FBorder := TBorder.Create;
  FBorder.Color := $B0FFFFFF;
  FBorder.Size := 0;
  FBox := TCkBox.Create;
  FCaption := Name;

  FChecked := True;
  // check parent for other radiobuttons
  if (AOwner <> nil) and (AOwner <> Self) and (AOwner is TWControl) then
  begin
    for I := 0 to AOwner.ComponentCount - 1 do
    begin
      Control := AOwner.Components[I];

      if (Control <> Self) and (Control is TCustomARadioButton) then
      begin
        if ((Control as TCustomARadioButton).Checked = True) then
        begin
          FChecked := False;
          Break;
        end;
      end;
    end;
  end;

  FColor := TFillColor.Create($FF4090F0, $FF4090F0, $FF4090F0, $FF4090F0);
  FFocusRect := fDark;
  FFont := TFormatedFont.Create;
  FFont.HorizontalAlign := aLeft;
  FImage := TImage.Create;
  FMargin := 2;
  FReadOnly := False;
  FTransparent := True;

  // Properties
  Self.Left := 0;
  Self.Top := 0;
  Self.Width := 120;
  Self.Height := 24;
  Self.TabStop := True;
  Self.Visible := True;

  ControlState := ControlState - [csCreating];
end;

procedure TCustomARadioButton.DblClick;
begin
  if not(FReadOnly) then
  begin
    Self.Checked := True;
  end;

  inherited DblClick;
end;

destructor TCustomARadioButton.Destroy;
var
  I: Integer;
  C: TComponent;
begin
  if (FChecked = True) and (Owner <> nil) and (Owner <> Self) then
  begin
    for I := Owner.ComponentCount - 1 downto 0 do
    begin
      C := Owner.Components[I];

      if (C <> Self) and (C is TCustomARadioButton) then
      begin
        (C as TCustomARadioButton).Checked := True;
        Break;
      end;
    end;
  end;

  FBorder.Free;
  FBox.Free;
  FColor.Free;
  FFont.Free;
  FImage.Free;

  inherited;
end;

procedure TCustomARadioButton.KeyDown(var Key: Word; Shift: TShiftState);
begin
  if not(FReadOnly) then
  begin
    if (Key = $20) then
    begin
      Self.Checked := True;
    end;
  end;

  inherited KeyDown(Key, Shift);
end;

procedure TCustomARadioButton.MouseDown(Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  Self.SetFocus;

  inherited MouseDown(Button, Shift, X, Y);
end;

procedure TCustomARadioButton.MouseUp(Button: TMouseButton; Shift: TShiftState;
  X, Y: Integer);
var
  L, T, H, W: Integer;
begin
  T := Self.ClientTop + FBorder.Size + FMargin;
  H := Self.Height - (FBorder.Size * 2) - (FMargin * 2);
  T := T + (H div 2 - FBox.Size div 2);

  L := Self.ClientLeft + FBorder.Size + FMargin;
  W := Self.ClientLeft + FBorder.Size + FMargin + FBox.Size;
  H := T + FBox.Size;

  if not(FReadOnly) then
  begin
    if (X >= L) and (X < W) and (Y >= T) and (Y < H) then
    begin
      Self.Checked := True;
    end;
  end;

  inherited MouseUp(Button, Shift, X, Y);
end;

procedure TCustomARadioButton.Paint;
var
  X, Y: Integer;
  AFont: TAsphyreFont;
  AImage: TAsphyreImage;
  bTop, bBottom: TConstraintSize;
  L, T, H, W: Integer;
begin
  // Set initial values
  X := ClientLeft;
  Y := ClientTop;

  ControlManager.Canvas.Antialias := FAntialiased;

  // Draw Background
  if not FTransparent then
  begin
    AImage := ControlManager.Images.Image[FImage.Image];
    if AImage <> nil then
    begin
      ControlManager.Canvas.UseImagePx(AImage, pRect4(FImage.Rect));
      ControlManager.Canvas.TexMap(pRect4(Rect(X, Y, X + Width, Y + Height)),
        cAlpha4(FColor), beNormal);
    end
    else
    begin
      ControlManager.Canvas.FillRect(Rect(X, Y, X + Width, Y + Height),
        cColor4(FColor), beNormal);
    end;
  end;

  // Draw Border
  if Border.Size > 0 then
  begin
    bTop := 0;
    bBottom := 0;

    if eTop in Border.Edges then
    begin
      ControlManager.Canvas.FillRect(Rect(X, Y, X + Width, Y + Border.Size),
        Border.Color, beNormal);
      bTop := Border.Size;
    end;

    if eBottom in Border.Edges then
    begin
      ControlManager.Canvas.FillRect(Rect(X, Y + Height - Border.Size,
        X + Width, Y + Height), Border.Color, beNormal);
      bBottom := Border.Size;
    end;

    if eLeft in Border.Edges then
      ControlManager.Canvas.FillRect(Rect(X, Y + bTop, X + Border.Size,
        Y + Height - bBottom), Border.Color, beNormal);

    if eRight in Border.Edges then
      ControlManager.Canvas.FillRect(Rect(X + Width - Border.Size, Y + bTop,
        X + Width, Y + Height - bBottom), Border.Color, beNormal);
  end;

  // Draw DisplayText
  AFont := ControlManager.Fonts.Font[FFont.Name];
  if (AFont <> nil) and (FCaption <> '') then
  begin
    AFont.TextRectEx(Point2(X + Border.Size + Box.Size + (Margin * 3),
      Y + Border.Size + Margin + 1), Point2(Width - Box.Size - (Border.Size * 2)
      - (Margin * 4), Height - (Border.Size * 2) - (Margin * 2)), FCaption,
      cColor2(FFont.Color), 1.0, FFont.HorizontalAlign, FFont.VerticalAlign,
      FFont.ParagraphLine);
  end;

  // Draw box
  // Draw box Background
  T := Y + Border.Size + Margin;
  H := Height - (Border.Size * 2) - (Margin * 2);
  T := T + (H div 2 - FBox.Size div 2);

  L := X + Border.Size + Margin;
  W := X + Border.Size + Margin + FBox.Size;
  H := T + FBox.Size;

  if FChecked then
    AImage := ControlManager.Images.Image[FBox.CheckedImage.Image]
  else
    AImage := ControlManager.Images.Image[FBox.Image.Image];

  if AImage <> nil then
  begin
    if FChecked then
      ControlManager.Canvas.UseImagePx(AImage, pRect4(FBox.CheckedImage.Rect))
    else
      ControlManager.Canvas.UseImagePx(AImage, pRect4(FBox.Image.Rect));

    ControlManager.Canvas.TexMap(pRect4(Rect(L, T, W, H)), cAlpha4(FColor),
      beNormal);
  end
  else
  begin
    ControlManager.Canvas.FillRect(Rect(L, T, W, H), cColor4(FBox.Color),
      beNormal);
    // draw check
    if FChecked then
    begin
      ControlManager.Canvas.FillRect(Rect(L + (FBox.Size div 4),
        T + (FBox.Size div 4), W - (FBox.Size div 4), H - (FBox.Size div 4)),
        cColor4(FBox.CheckedColor), beNormal);
    end;
  end;

  // Draw box Border
  if FBox.Border.Size > 0 then
  begin
    bTop := 0;
    bBottom := 0;

    if eTop in FBox.Border.Edges then
    begin
      ControlManager.Canvas.FillRect(Rect(L, T, W, T + FBox.Border.Size),
        FBox.Border.Color, beNormal);
      bTop := FBox.Border.Size;
    end;

    if eBottom in FBox.Border.Edges then
    begin
      ControlManager.Canvas.FillRect(Rect(L, H - FBox.Border.Size, W, H),
        FBox.Border.Color, beNormal);
      bBottom := FBox.Border.Size;
    end;

    if eLeft in FBox.Border.Edges then
      ControlManager.Canvas.FillRect(Rect(L, T + bTop, L + FBox.Border.Size,
        H - bBottom), FBox.Border.Color, beNormal);

    if eRight in FBox.Border.Edges then
      ControlManager.Canvas.FillRect(Rect(W - FBox.Border.Size, T + bTop, W,
        H - bBottom), FBox.Border.Color, beNormal);
  end;

  // Draw box Focus rect
  if (ControlManager.ActiveControl = Self) and (Self.FocusRect = fLight) then
  begin
    ControlManager.Canvas.FrameRect(Rect(L - 1, T - 1, W + 1, H + 1),
      cColor4($40FFFFFF), beNormal);
  end;
  if (ControlManager.ActiveControl = Self) and (Self.FocusRect = fDark) then
  begin
    ControlManager.Canvas.FrameRect(Rect(L - 1, T - 1, W + 1, H + 1),
      cColor4($30000000), beNormal);
  end;

end;

procedure TCustomARadioButton.SetAntialiased(Value: Boolean);
begin
  FAntialiased := Value;
end;

procedure TCustomARadioButton.SetBorder(Value: TBorder);
begin
  if Value <> nil then
    FBorder.Assign(Value);
end;

procedure TCustomARadioButton.SetBox(Value: TCkBox);
begin
  if Value <> nil then
    FBox.Assign(Value);
end;

procedure TCustomARadioButton.SetCaption(Value: String);
begin
  FCaption := Value;
end;

procedure TCustomARadioButton.SetChecked(Value: Boolean);
var
  I: Integer;
  C: TComponent;
begin
  if (Value = True) and (Owner <> nil) and (Owner <> Self) then
  begin
    for I := Owner.ComponentCount - 1 downto 0 do
    begin
      C := Owner.Components[I];

      if (C <> Self) and (C is TCustomARadioButton) then
      begin
        if (C as TCustomARadioButton).Checked = True then
          (C as TCustomARadioButton).Checked := False;
      end;
    end;
  end;

  FChecked := Value;
end;

procedure TCustomARadioButton.SetColor(Value: TFillColor);
begin
  if Value <> nil then
    FColor.Assign(Value);
end;

procedure TCustomARadioButton.SetFocusRect(Value: TFocusRect);
begin
  FFocusRect := Value;
end;

procedure TCustomARadioButton.SetFont(Value: TFormatedFont);
begin
  if Value <> nil then
    FFont.Assign(Value);
end;

procedure TCustomARadioButton.SetHeight(Value: Integer);
var
  MinH: Integer;
begin
  MinH := FBorder.Size * 2 + FMargin * 2 + FBox.Size;

  if Value < MinH then
    Value := MinH;

  inherited SetHeight(Value);
end;

procedure TCustomARadioButton.SetImage(Value: TImage);
begin
  if Value <> nil then
    FImage.Assign(Value);
end;

procedure TCustomARadioButton.SetMargin(Value: Word);
begin
  FMargin := Value;

  SetHeight(Self.Height);
  SetWidth(Self.Width);
end;

procedure TCustomARadioButton.SetReadOnly(Value: Boolean);
begin
  FReadOnly := Value;
end;

procedure TCustomARadioButton.SetTransparent(Value: Boolean);
begin
  FTransparent := Value;
end;

procedure TCustomARadioButton.SetWidth(Value: Integer);
var
  MinW: Integer;
begin
  MinW := FBorder.Size * 2 + FMargin * 2 + FBox.Size;

  if Value < MinW then
    Value := MinW;

  inherited SetWidth(Value);
end;
{$ENDREGION}

end.