* Improved buttonpanel

- Hide invisible buttons at designtime
- Remove "mandatory" outer spacing (that should be done by the designer)
- Made button spacing configurable
- Show/Hide bevel
- Adjust bevel position based on panel alignment
ToDo: 
= Position buttons based on panel alignment
= Recreate button when IDE deletes it (streaming/property editing fails when not recreated)

git-svn-id: trunk@18152 -
This commit is contained in:
marc 2009-01-06 01:10:40 +00:00
parent 5a20e5938c
commit 6f7cdd43bb

View File

@ -48,22 +48,19 @@ type
TCustomButtonPanel = class(TCustomPanel)
private
FCancelGlyph: TBitmap;
FCloseGlyph: TBitmap;
FHelpGlyph: TBitmap;
FOKGlyph: TBitmap;
FShowBevel: Boolean;
FShowButtons: TPanelButtons;
FShowGlyphs: TPanelButtons;
FBevel: TBevel;
FCancelButton: TPanelBitBtn;
FCloseButton: TPanelBitBtn;
FHelpButton: TPanelBitBtn;
FOKButton: TPanelBitBtn;
FBevel: TBevel;
FGlyphs: array[TPanelButton] of TBitmap;
FButtons: array[TPanelButton] of TPanelBitBtn;
FButtonOrder: TButtonOrder;
FDefaultButton: TPanelButton;
FSpacing: TSpacingSize;
procedure OrderButtonsRightToLeft(TheButtons: array of TControl);
procedure ButtonOrderCloseCancelOK;
procedure ButtonOrderCloseOKCancel;
procedure CreateButton(AButton: TPanelButton);
procedure DoButtonOrder;
procedure DoDefaultButton;
procedure DoRestoreCancel;
@ -71,24 +68,30 @@ type
procedure DoShowGlyphs;
procedure SetButtonOrder(Value: TButtonOrder);
procedure SetDefaultButton(Value: TPanelButton);
procedure SetShowBevel(AValue: Boolean);
procedure SetShowButtons(Value: TPanelButtons);
procedure SetShowGlyphs(Value: TPanelButtons);
procedure SetSpacing(AValue: TSpacingSize);
procedure UpdateBevel;
protected
procedure Loaded; override;
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
procedure SetAlign(Value: TAlign); override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
property OKButton: TPanelBitBtn read FOKButton stored False;
property HelpButton: TPanelBitBtn read FHelpButton stored False;
property CloseButton: TPanelBitBtn read FCloseButton stored False;
property CancelButton: TPanelBitBtn read FCancelButton stored False;
property OKButton: TPanelBitBtn read FButtons[pbOK] stored False;
property HelpButton: TPanelBitBtn read FButtons[pbHelp] stored False;
property CloseButton: TPanelBitBtn read FButtons[pbClose] stored False;
property CancelButton: TPanelBitBtn read FButtons[pbCancel] stored False;
property ButtonOrder: TButtonOrder read FButtonOrder write SetButtonOrder default boDefault;
property DefaultButton: TPanelButton read FDefaultButton write SetDefaultButton default pbOK;
property ShowButtons: TPanelButtons read FShowButtons write SetShowButtons default DefShowButtons;
property ShowGlyphs: TPanelButtons read FShowGlyphs write SetShowGlyphs default DefShowGlyphs;
property ShowBevel: Boolean read FShowBevel write SetShowBevel default True;
property Spacing: TSpacingSize read FSpacing write SetSpacing default 6;
published
end;
@ -106,6 +109,7 @@ type
property ButtonOrder;
property TabOrder;
property DefaultButton;
property Spacing;
property OnClick;
property OnDblClick;
property OnDragDrop;
@ -121,6 +125,7 @@ type
property OnUTF8KeyPress;
property ShowButtons;
property ShowGlyphs;
property ShowBevel;
property Visible;
end;
@ -141,15 +146,34 @@ begin
end;
procedure TCustomButtonPanel.DoShowButtons;
var
btn: TPanelButton;
begin
if FOKButton<>nil then
FOKButton.Visible := (pbOK in FShowButtons);
if FCancelButton<>nil then
FCancelButton.Visible := (pbCancel in FShowButtons);
if FCloseButton<>nil then
FCloseButton.Visible := (pbClose in FShowButtons);
if FHelpButton<>nil then
FHelpButton.Visible := (pbHelp in FShowButtons);
for btn := Low(btn) to High(btn) do
begin
if FButtons[btn] = nil
then CreateButton(btn);
if btn in FShowButtons
then begin
FButtons[btn].Visible := True;
FButtons[btn].Enabled := True;
if btn = pbHelp
then FButtons[btn].Align := alLeft
else FButtons[btn].Align := alRight;
end
else begin
FButtons[btn].Visible := False;
FButtons[btn].Enabled := False;
FButtons[btn].Align := alNone;
// when designing, hide doesn't work, so position button outside panel
if csDesigning in ComponentState
then begin
FButtons[btn].Left := -FButtons[btn].Width - 100;
FButtons[btn].Anchors := [];
end;
end;
end;
DoButtonOrder;
end;
@ -165,49 +189,21 @@ begin
end;
procedure TCustomButtonPanel.DoShowGlyphs;
var
btn: TPanelButton;
begin
if FOKButton<>nil then
for btn := Low(btn) to High(btn) do
begin
if not (pbOK in FShowGlyphs) then
begin
FOKGlyph.Assign(FOKButton.Glyph);
FOKButton.Glyph.Assign(nil);
end
else
FOKButton.Glyph.Assign(FOKGlyph);
end;
if FButtons[btn] = nil then Continue;
if FCancelButton<>nil then
begin
if not (pbCancel in FShowGlyphs) then
begin
FCancelGlyph.Assign(FCancelButton.Glyph);
FCancelButton.Glyph.Assign(nil);
if btn in FShowGlyphs
then begin
FButtons[btn].Glyph.Assign(FGlyphs[btn]);
end
else
FCancelButton.Glyph.Assign(FCancelGlyph);
end;
if FCloseButton<>nil then
begin
if not (pbClose in FShowGlyphs) then
begin
FCloseGlyph.Assign(FCloseButton.Glyph);
FCloseButton.Glyph.Assign(nil);
end
else
FCloseButton.Glyph.Assign(FCloseGlyph);
end;
if FHelpButton<>nil then
begin
if not (pbHelp in FShowGlyphs) then
begin
FHelpGlyph.Assign(FHelpButton.Glyph);
FHelpButton.Glyph.Assign(nil);
end
else
FHelpButton.Glyph.Assign(FHelpGlyph);
else begin
FGlyphs[btn].Assign(FButtons[btn].Glyph);
FButtons[btn].Glyph.Assign(nil);
end;
end;
end;
@ -221,6 +217,61 @@ begin
DoShowGlyphs;
end;
procedure TCustomButtonPanel.SetSpacing(AValue: TSpacingSize);
var
btn: TPanelButton;
begin
if FSpacing = AValue then Exit;
FSpacing := AValue;
for btn := Low(btn) to High(btn) do
begin
if FButtons[btn] = nil then Continue;
FButtons[btn].BorderSpacing.Around := FSpacing;
end;
UpdateBevel;
end;
procedure TCustomButtonPanel.UpdateBevel;
begin
if FBevel = nil then Exit;
case Align of
alTop: begin
FBevel.Shape := bsBottomLine;
FBevel.Align := alBottom;
end;
alLeft: begin
FBevel.Shape := bsRightLine;
FBevel.Align := alRight;
end;
alRight: begin
FBevel.Shape := bsLeftLine;
FBevel.Align := alLeft;
end;
else
// default to bottom
FBevel.Shape := bsTopLine;
FBevel.Align := alTop;
end;
if Align in [alLeft, alRight]
then begin
FBevel.Width := 2;
FBevel.BorderSpacing.Top := FSpacing;
FBevel.BorderSpacing.Bottom := FSpacing;
FBevel.BorderSpacing.Left := 0;
FBevel.BorderSpacing.Right := 0;
end
else begin
FBevel.Height := 2;
FBevel.BorderSpacing.Top := 0;
FBevel.BorderSpacing.Bottom := 0;
FBevel.BorderSpacing.Left := FSpacing;
FBevel.BorderSpacing.Right := FSpacing;
end;
end;
procedure TCustomButtonPanel.DoButtonOrder;
begin
case FButtonOrder of
@ -239,24 +290,28 @@ end;
procedure TCustomButtonPanel.OrderButtonsRightToLeft(TheButtons: array of TControl);
// reorder aligned buttons from left to right.
// The buttons are Align=alRight. The order is determined by the right edge.
// Set the Left+Wifth property to some values in ascending order and the LCL
// Set the Left+Width property to some values in ascending order and the LCL
// will do the rest.
function Previous(AIndex: Integer): Integer;
begin
Result := AIndex;
repeat
Dec(Result)
until (Result < Low(TheButtons))
or ((TheButtons[Result] <> nil) and (TheButtons[Result].Align = alRight));
end;
var
i, x: integer;
begin
i := High(TheButtons);
while (i >= Low(TheButtons)) and (TheButtons[i]=nil) do dec(i);
if i < Low(TheButtons) then
exit; // no buttons
x:=TheButtons[i].Left+TheButtons[i].Width;
Dec(i);
while (i >= Low(TheButtons)) and (TheButtons[i].Left+TheButtons[i].Width < x) do
begin
i := Previous(Length(TheButtons));
if i < Low(TheButtons) then Exit; // no buttons
repeat
x:=TheButtons[i].Left+TheButtons[i].Width;
Dec(i);
end;
if i < Low(TheButtons) then
exit; // all buttons are already in the correct order
i := Previous(i);
if i < Low(TheButtons) then Exit; // all buttons are already in the correct order
until TheButtons[i].Left+TheButtons[i].Width >= x;
DisableAlign;
try
@ -272,34 +327,42 @@ begin
end;
end;
procedure TCustomButtonPanel.ButtonOrderCloseCancelOK;
procedure TCustomButtonPanel.SetAlign(Value: TAlign);
begin
OrderButtonsRightToLeft([FCloseButton, FCancelButton, FOKButton]);
inherited SetAlign(Value);
UpdateBevel;
end;
procedure TCustomButtonPanel.ButtonOrderCloseCancelOK;
const
TABORDERS: array[0..3] of TPanelButton = (pbOK, pbCancel, pbClose, pbHelp);
var
i: Integer;
begin
OrderButtonsRightToLeft([FButtons[pbClose], FButtons[pbCancel], FButtons[pbOK]]);
//set taborder
if FOKButton<>nil then
FOKButton.TabOrder := 0;
if FCancelButton<>nil then
FCancelButton.TabOrder := 1;
if FCloseButton<>nil then
FCloseButton.TabOrder := 2;
if FHelpButton<>nil then
FHelpButton.TabOrder := 3;
for i := Low(TABORDERS) to High(TABORDERS) do
begin
if FButtons[TABORDERS[i]] = nil then Continue;
FButtons[TABORDERS[i]].Taborder := i;
end;
end;
procedure TCustomButtonPanel.ButtonOrderCloseOKCancel;
const
TABORDERS: array[0..3] of TPanelButton = (pbCancel, pbOK, pbClose, pbHelp);
var
i: Integer;
begin
OrderButtonsRightToLeft([FCloseButton, FOKButton, FCancelButton]);
OrderButtonsRightToLeft([FButtons[pbClose], FButtons[pbOK], FButtons[pbCancel]]);
//set taborder
if FCancelButton<>nil then
FCancelButton.TabOrder := 0;
if FOKButton<>nil then
FOKButton.TabOrder := 1;
if FCloseButton<>nil then
FCloseButton.TabOrder := 2;
if FHelpButton<>nil then
FHelpButton.TabOrder := 3;
for i := Low(TABORDERS) to High(TABORDERS) do
begin
if FButtons[TABORDERS[i]] = nil then Continue;
FButtons[TABORDERS[i]].Taborder := i;
end;
end;
procedure TCustomButtonPanel.SetButtonOrder(Value: TButtonOrder);
@ -313,25 +376,23 @@ begin
end;
procedure TCustomButtonPanel.DoDefaultButton;
var
btn: TPanelButton;
begin
if FOKButton<>nil then
FOKButton.Default := FDefaultButton = pbOk;
if FCancelButton<>nil then
FCancelButton.Default := FDefaultButton = pbCancel;
if FCloseButton<>nil then
FCloseButton.Default := FDefaultButton = pbClose;
if FHelpButton<>nil then
FHelpButton.Default := FDefaultButton = pbHelp;
for btn := Low(btn) to High(btn) do
begin
if FButtons[btn] = nil then Continue;
FButtons[btn].Default := FDefaultButton = btn;
end;
end;
procedure TCustomButtonPanel.DoRestoreCancel;
begin
if FCancelButton <> nil then
begin
// to restore cancel button we need to do this hack
FCancelButton.Cancel := False;
FCancelButton.Cancel := True;
end;
if FButtons[pbCancel] = nil then Exit;
// to restore cancel button we need to do this hack
FButtons[pbCancel].Cancel := False;
FButtons[pbCancel].Cancel := True;
end;
procedure TCustomButtonPanel.SetDefaultButton(Value: TPanelButton);
@ -344,35 +405,43 @@ begin
DoDefaultButton;
end;
procedure TCustomButtonPanel.SetShowBevel(AValue: Boolean);
begin
if FShowBevel = AValue then exit;
FShowBevel := AValue;
if not FShowBevel
then begin
FreeAndNil(FBevel);
Exit;
end;
FBevel := TBevel.Create(Self);
FBevel.Parent := Self;
FBevel.Name := 'Bevel';
UpdateBevel;
end;
procedure TCustomButtonPanel.Loaded;
begin
inherited Loaded;
DoRestoreCancel;
DoDefaultButton;
DoShowGlyphs;
DoShowButtons;
end;
procedure TCustomButtonPanel.Notification(AComponent: TComponent;
Operation: TOperation);
var
btn: TPanelButton;
begin
if Operation=opRemove then begin
if AComponent=FOKButton then
if Operation=opRemove
then begin
for btn := Low(btn) to High(btn) do
begin
FOKButton:=nil;
Exclude(FShowButtons,pbOK);
end else if AComponent=FCancelButton then
begin
FCancelButton:=nil;
Exclude(FShowButtons,pbCancel);
end else if AComponent=FCloseButton then
begin
FCloseButton:=nil;
Exclude(FShowButtons,pbClose);
end else if AComponent=FHelpButton then
begin
FHelpButton:=nil;
Exclude(FShowButtons,pbHelp);
if FButtons[btn] <> AComponent then Continue;
FButtons[btn] := nil;
Exclude(FShowButtons, btn);
end;
end;
inherited Notification(AComponent, Operation);
@ -390,94 +459,71 @@ begin
Caption := '';
ControlStyle := ControlStyle - [csSetCaption];
AutoSize := True;
BorderSpacing.Left := 6;
BorderSpacing.Right := 6;
// let the designer decide this
//BorderSpacing.Left := 6;
//BorderSpacing.Right := 6;
FSpacing := 6;
ShowBevel := True;
FBevel := TBevel.Create(Self);
FBevel.Parent := Self;
with FBevel do
begin
Name := 'Bevel';
Shape := bsTopLine;
Align := alTop;
Height := 2;
BorderSpacing.Left := 6;
BorderSpacing.Right := 6;
end;
FCancelButton := TPanelBitBtn.Create(Self);
with FCancelButton do
begin
Name := 'CancelButton';
Parent := Self;
Kind := bkCancel;
BorderSpacing.Around := 6;
AutoSize := True;
Align := alRight;
Caption := rsMbCancel;
end;
FCloseButton := TPanelBitBtn.Create(Self);
with FCloseButton do
begin
Name := 'CloseButton';
Parent := Self;
Kind := bkClose;
BorderSpacing.Around := 6;
AutoSize := True;
Align := alRight;
Caption := rsMbClose;
end;
FHelpButton := TPanelBitBtn.Create(Self);
with FHelpButton do
begin
Name := 'HelpButton';
Parent := Self;
Kind := bkHelp;
BorderSpacing.Around := 6;
AutoSize := True;
Align := alLeft;
Caption := rsMbHelp;
end;
FOKButton := TPanelBitBtn.Create(Self);
with FOKButton do
begin
Name := 'OKButton';
Parent := Self;
Kind := bkOK;
BorderSpacing.Around := 6;
AutoSize := True;
Align := alRight;
Caption := rsMbOK;
end;
FCancelGlyph := TBitmap.Create;
FCloseGlyph := TBitmap.Create;
FHelpGlyph := TBitmap.Create;
FOKGlyph := TBitmap.Create;
FOKGlyph.Assign(FOKButton.Glyph);
FCancelGlyph.Assign(FCancelButton.Glyph);
FCloseGlyph.Assign(FCloseButton.Glyph);
FHelpGlyph.Assign(FHelpButton.Glyph);
FDefaultButton := pbOK;
FButtonOrder := boDefault;
FShowButtons := DefShowButtons;
FShowGlyphs := DefShowGlyphs;
if not (csLoading in ComponentState) then
// create the buttons
DoShowButtons;
end;
procedure TCustomButtonPanel.CreateButton(AButton: TPanelButton);
const
NAMES: array[TPanelButton] of String = (
'OKButton', 'CancelButton', 'CloseButton', 'HelpButton'
);
KINDS: array[TPanelButton] of TBitBtnKind = (
bkOK, bkCancel, bkClose, bkHelp
);
CAPTIONS: array[TPanelButton] of String = (
rsMbOK, rsMbCancel, rsMbClose, rsMbHelp
);
begin
if FButtons[AButton] <> nil then Exit;
FButtons[AButton] := TPanelBitBtn.Create(Self);
with FButtons[AButton] do
begin
DoDefaultButton;
DoShowButtons;
DoShowGlyphs;
Name := NAMES[AButton];
Parent := Self;
Kind := KINDS[AButton];
BorderSpacing.Around := FSpacing;
AutoSize := True;
Caption := CAPTIONS[AButton];
TabOrder := Ord(AButton); //initial order
if AButton = pbHelp
then Align := alLeft
else Align := alRight;
if FGlyphs[AButton] = nil
then begin
// first time
FGlyphs[AButton] := TBitmap.Create;
FGlyphs[AButton].Assign(Glyph);
end;
// (re)set the glyph if needed
if (AButton in FShowGlyphs)
then Glyph.Assign(FGlyphs[AButton])
else Glyph.Assign(nil);
// set default
if AButton = FDefaultButton
then Default := True;
end;
end;
destructor TCustomButtonPanel.Destroy;
var
btn: TPanelButton;
begin
FreeAndNil(FCancelGlyph);
FreeAndNil(FCloseGlyph);
FreeAndNil(FHelpGlyph);
FreeAndNil(FOKGlyph);
for btn := Low(btn) to High(btn) do
FreeAndNil(FGlyphs[btn]);
inherited Destroy;
end;