![]() |
![]() |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Introduction Arrays are an efficient way to organize large amounts of similar data. A graphic-user-interface uses many buttons with similar characteristics to start processes or select modes of operation. So, why not organize buttons as arrays? This article describes a Delphi ArrayButton Component, which arranges buttons in a rectangle of columns and rows. Characteristics The component can have a maximum of 48 buttons [0..47] which may be organized as a single row, single column or any combination of rows and columns. All buttons have the same width and height. Properties:
Button Behaviour A button can have the status:
In addition, a button can operate in the following modes:
Each button is assigned to a group, 0..15. In a group only one button can have the status stDown. So, pressing a button releases any stDown button with the same group number. Responsibilities The component takes care of: - painting edges and background colors - generating events when the status of a button changes The application programmer should: - paint the foreground image or caption according to button status Colors The property "color" is the color of the border and spacing between the buttons. Five colors in an array[bcInactBG..bcLO] of longInt are used to indicate the status of a button.
How they look (foreground not painted)
Events
On receiving an onBtnChange or onBtnPaint event along with the button number and the button status, the application should request the rectangle by the method function GetBtnRect(btnNr); It is the responsability of the application programmer not to paint over the button edges. Click on download (lightning) icon at the top of the page to download the complete Delphi-7 project. Application An application has been build, with the sole purpose to demonstrate some arraybuttons. It only is a skeleton where the user may select an action from a menubutton, select a penwidth or color. Below is the main form with menubuttons load..roundrect at the top of the page. On the left side are the property buttons which open dialog forms.
Click [ here] to load the program. Click [ here] to load the Delphi-7 demo project. Below is the complete source code of the component. The complete ArrayButton Component
unit davArrayBtn;
{
aug.8 2004: corrected column/row problem when using max number of buttons
columns and rows of buttons, 0..47 maximal, for menu's and controls
common properties:
- button width,height
- button edge 1..2 , borderwidth
- shape: - flat, corners rounded
- raised, 3D, sharp corners
- colortable for alternate colors of edges/background
per button:
- button belongs to group 0..15
only -1- button of a group can be down at the time
- button operating mode
- mom : released on mouse-up
- press : user press down, released by other button
- toggle: additional user 2 ND press Up action
- button status:
- hidden
- flat (inactive)
- down (activated)
component:
- paints edges and backgrounds
- triggers onBtnPaint event if button foreground needs painting
- triggers onBtnChange event when button pressed or released
application:
- paints foreground when triggered by event
- responds to button pressed/released, Enter/Leave
controlbyte per Button:
bit 0..3 : group code 0..15
bit 4,5 : action : 00:mom 01:press 10:toggle
bit 6,7 : status : 00:hidden 01:flat 10:down
}
interface
uses windows,extctrls,controls,classes,graphics,messages;
const davmaxBtn = 48;//buttons 0..47; btn 48 = no button
type TBtnShape = (bsFlat,bs3D);
TBtnStatus = (stHidden,stFlat,stDown,stHI);
TBtnOpmode = (omMom,omPress,omToggle);
TBtnColorIndex = (bcInactBG,bcActiveBG,bcFlat,bcHI,bcLO);
TBtnChangeProc = procedure(sender : TObject; BtnNr : byte;
status : TBtnStatus; button : TmouseButton) of object;
TBtnPaintProc = procedure(sender : TObject; BtnNr : byte;
status : TBtnStatus) of object;
TColorTable = array[bcInactBG..bcLO] of LongInt;
TPColorTable = ^TColorTable;
TDavArrayBtn = class(TGraphicControl)
private
FonBtnchange : TBtnChangeProc;
FonBtnPaint : TBtnPaintProc;
FonEnter : TNotifyEvent;
FonLeave : TNotifyEvent;
FHiBtn : byte; //mouse over this button
Frows : byte; //rows
Fcolumns : byte; //columns
FbtnWidth : byte; //button width
FbtnHeight : byte; //button height
FBtnEdge : byte; //button edge
FBtnSpacing : byte; //space between buttons
FBorder : byte; //border
FBtnShape : TBtnShape; //rounded flat, 3D
FPcolorTable : TPColorTable;
FBtnControl : array[0..davmaxBtn] of byte;
FNextRelease : byte;
procedure setRows(n : byte);
procedure setColumns(n : byte);
procedure setBtnWidth(n : byte);
procedure setBtnHeight(n : byte);
procedure setBtnshape(bs : TBtnShape);
procedure setBorder(b : byte);
procedure setSpacing(b : byte);
procedure setBtnEdge(edge : byte);
procedure repaintBtns;
procedure fixdimensions;
procedure BtnPaint(BtnNr : byte; bst : TBtnStatus);
procedure CMmouseLeave(var message : Tmessage); message CM_MOUSELEAVE;
procedure CMmouseEnter(var message : Tmessage); message CM_MOUSEENTER;
procedure InitBtns;
procedure SetBtnStatus(BtnNr : byte; status : TBtnStatus);
function GetBtnGroup(BtnNr : byte) : byte;
function GetBtnOpMode(BtnNr : byte) : TBtnOpMode;
protected
procedure paint; 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;
procedure AssignColorTable(p : TPcolorTable);
procedure TestReleaseBtn(downBtn : byte);
public
constructor Create(AOwner: TComponent); override;
function GetBtnRect(btnNr : byte) : TRect;
function GetBtnStatus(btnNr : byte) : TBtnStatus;
procedure setBtnOpmode(BtnNr : byte; opMode: TBtnOpmode);
procedure BtnHide(btnNr : byte);
procedure BtnShow(btnNr : byte);
procedure BtnDown(btnNr : byte);
procedure BtnRelease(btnNr : byte);
procedure setBtnGroup(btnNr,group : byte);
property canvas;
property PColorTable : TPColorTable read FPColorTable
write AssignColorTable;
published
property Border : byte read FBorder write setBorder default 10;
property BtnHeight : byte read FBtnHeight write setBtnHeight default 20;
property BtnSpacing : byte read FBtnSpacing write setSpacing default 5;
property BtnShape : TBtnShape read FBtnShape write setBtnShape default bs3D;
property BtnWidth : byte read FBtnWidth write setbtnwidth default 30;
property BtnEdge : byte read FBtnEdge write SetBtnEdge default 1;
property Color;
property Columns : byte read Fcolumns write setcolumns default 2;
property Enabled;
property Font;
property onBtnChange : TBtnChangeProc read FonBtnChange write FonBtnChange;
property onBtnPaint : TBtnPaintProc read FonBtnPaint write FonBtnPaint;
property OnEnter : TNotifyEvent read FOnEnter write FOnEnter;
property OnLeave : TNotifyEvent read FonLeave write FOnLeave;
property Rows : byte read Frows write setRows default 2;
property Visible;
end;
procedure Register;
implementation
const defaultColors : TColorTable =
($c0c0c0,$f0f0f0,$808080,$ffffff,$202020);
procedure Register;
begin
RegisterComponents('system',[TdavArrayBtn]);
end;
procedure TdavArrayBtn.SetBtnOpMode(BtnNr : byte; Opmode : TBtnOpMode);
var cb : byte;
begin
cb := FBtnControl[BtnNr] and $cf;
FBtnControl[BtnNr] := cb or (byte(OpMode) shl 4);
end;
function TdavArrayBtn.GetBtnOpMode(BtnNr : byte) : TbtnOpMode;
begin
result := TBtnOpMode((FBtnControl[BtnNr] shr 4) and $3);
end;
procedure TdavArrayBtn.setBtnStatus(BtnNr : byte; status : TBtnStatus);
var bc : byte;
begin
bc := FBtnControl[BtnNr] and $3f;
FBtnControl[BtnNr] := bc or (byte(status) shl 6);
end;
function TdavArrayBtn.GetBtnGroup(BtnNr : byte) : byte;
begin
result := FBtnControl[BtnNr] and $f;
end;
procedure TdavArrayBtn.setBtnGroup(btnNr,group : byte);
//add button to group
var bc : byte;
begin
bc := FBtnControl[BtnNr] and $f0;
FBtnControl[BtnNr] := bc or group;
end;
procedure TdavArrayBtn.initBtns;
//alle buttons group -0-, press
var i,top : byte;
control : byte;
begin
top := FRows*Fcolumns-1;
control := (byte(stFlat) shl 6) or (byte(omPress) shl 4);
for i := 0 to davmaxBtn do
if i <= top then FBtnControl[i] := control
else FBtnControl[i] := 0;
end;
function TdavarrayBtn.GetBtnStatus(btnNr : byte) : TBtnStatus;
begin
result := TBtnStatus((FBtnControl[btnNr] shr 6) and $3);
end;
procedure TdavArrayBtn.BtnHide(btnNr : byte);
//hide button
begin
if GetBtnStatus(btnNr) <> stHidden then
begin
SetBtnStatus(btnNr,stHidden);
if FHiBtn = btnNr then FHiBtn := davmaxBtn;
BtnPaint(btnNr,stHidden);
end;
end;
procedure TdavarrayBtn.BtnShow(btnNr : byte);
//show a hidden button, set flat
begin
if GetBtnStatus(btnNr) = stHidden then
begin
SetBtnStatus(btnNr,stFlat);
BtnPaint(BtnNr,stFlat);
end;
end;
procedure TdavArrayBtn.BtnRelease(btnNr : byte);
//set button from DOWN to Flat
begin
if GetBtnStatus(btnNr) = stDown then
begin
SetBtnStatus(btnNr,stFlat);
BtnPaint(btnNr,stFlat);
end;
end;
procedure TdavArrayBtn.BtnDown(btnNr : byte);
begin
if GetBtnStatus(btnNr) = stFlat then
begin
SetBtnStatus(btnNr,stDown);
BtnPaint(btnNr,stDown);
TestReleaseBtn(btnNr);//to release other buttons
end;
end;
procedure TdavArrayBtn.TestReleaseBtn(downBtn : byte);
//downBtn was pressed down, test to release buttons of same group
var groupNr,i : byte;
begin
groupNr := GetBtnGroup(downBtn);
for i := 0 to Frows*Fcolumns-1 do
if (i <> downBtn) and (GetBtnGroup(i) = groupNr)
and (GetBtnStatus(i) = stDown) then
begin
SetBtnStatus(i,stFlat);
btnPaint(i,stFlat);
end;
end;
procedure TdavArrayBtn.BtnPaint(btnNr : byte; bst : TBtnStatus);
//if button hidden: erase
var r : Trect;
radius : byte;
k1,k2 : LongInt;
i : byte;
begin
r := GetBtnRect(btnNr);
with canvas do
begin
pen.Width := 1;
brush.style := bssolid;
case bst of
stHidden : begin
brush.color := color;
brush.style := bsSolid;
fillrect(r);
exit;
end;
stFlat : begin
brush.color := PColorTable^[bcInactBG];
k1 := PColorTable^[bcFlat]; k2 := k1;
end;
stHI : begin
brush.color := PColorTable^[bcInactBG];
k1 := PColorTable^[bcHI]; k2 := PColorTable^[bcLO];
end;
stDown : begin
brush.color := PColorTable^[bcActiveBG];
k1 := PColorTable^[bcLO]; k2 := PColorTable^[bcHI];
end;
end;//case
if FBtnShape = bsFlat then //vlak,ronde hoeken
begin
radius := FBtnHeight div 2;
if radius > 40 then radius := 40;
if radius < 10 then radius := 10;
pen.Width := FbtnEdge;
pen.color := k1;
roundrect(r.left+1,r.top+1,r.right,r.bottom,radius,radius);
end
else
begin
fillrect(r);
for i := 0 to FbtnEdge-1 do
begin
pen.color := k1;
moveto(r.right-1-i,r.top+i);
lineto(r.left+i,r.top+i);
lineto(r.left+i,r.bottom-1-i);
pen.color := k2;
lineto(r.right-1-i,r.bottom-1-i);
lineto(r.right-1-i,r.top+i);
end;//for
end;//else
end;//with canvas
if not (csDesigning in componentstate) and assigned(onBtnPaint) then
onBtnPaint(self,btnNr,bst);
end;
procedure TdavArrayBtn.RepaintBtns;
//na initialiseren hele paintbox
var i : byte;
begin
for i := 0 to FRows*Fcolumns-1 do BtnPaint(i,GetBtnStatus(i));
end;
procedure TdavArrayBtn.FixDimensions;
//adjust width,height na verandering van knop of spacing
//generates onPaint event
begin
if FRows = 0 then FRows := 1;
if FColumns = 0 then FColumns := 1;
width := FColumns*(FBtnWidth + FBtnSpacing) - FBtnSpacing + 2*Fborder;
height := FRows*(FBtnHeight + FBtnspacing) - FBtnSpacing + 2*Fborder;
end;
constructor TdavArrayBtn.Create(AOwner: TComponent);
begin
inherited create(Aowner);
canvas.font := font;
FHiBtn := davmaxBtn;//=off
FBtnShape := bs3D;
FbtnEdge := 1;
FPColorTable := @defaultColors;
Frows := 2;
Fcolumns := 2;
InitBtns;
FbtnWidth := 40;
FbtnHeight := 30;
FBtnSpacing := 5;
FBorder := 10;
fixDimensions;//set width , height
end;
procedure TdavArrayBtn.AssignColorTable(p : TPcolorTable);
begin
FPcolorTable := p;
invalidate;
end;
procedure TdavArrayBtn.MouseDown(Button: TMouseButton; Shift: TShiftState;
X, Y: Integer);
var status : TBtnStatus;
begin
FNextRelease := davmaxBtn;
if (FHiBtn = davmaxBtn) then exit; //no button selected
//----
status := GetBtnstatus(FHIbtn);
if status = stFlat then
begin
SetBtnStatus(FHIbtn,stDown);
BtnPaint(FHIbtn,stDown);
TestReleaseBtn(FHIbtn);
if assigned(FonBtnChange) and (not (csDesigning in componentstate)) then
onBtnChange(self,FHiBtn,stDown,button);
end;
case GetBtnOpMode(FHIbtn) of
omMom : FNextRelease := FHIbtn;
omToggle : if status = stDown then FNextrelease := FHIbtn;
end;//case
end;
procedure TdavArrayBtn.MouseUp(Button: TMouseButton; Shift: TShiftState;
X, Y: Integer);
begin
if FNextRelease <> davmaxBtn then
begin
SetBtnStatus(FNextRelease,stFlat);
BtnPaint(FNextRelease,stFlat);
if (not (csDesigning in componentstate)) and assigned(FonBtnChange) then
onBtnChange(self,FNextRelease,stFlat,button);
end;
end;
procedure TdavArrayBtn.MouseMove(Shift: TShiftState; X, Y: Integer);
var dx,maxX,maxY,dy : integer;
button : byte;
px,py : integer;
status : TBtnStatus;
begin
x := x - FBorder; y := y - FBorder;
dx := FBtnSpacing + FBtnWidth;
dy := FBtnSpacing + FBtnHeight;
maxX := FColumns * dx; maxY := FRows * dy;
px := x mod dx; py := y mod dy;
if (x < maxX) and (y < maxY) and
(px > FBtnEdge) and (px < dx-FBtnEdge-FBtnSpacing) and
(py > FBtnEdge) and (py < dy-FBtnEdge-FBtnSpacing) then
begin
button := x div dx + FColumns*(y div dy);
end
else button := davmaxBtn;
status := GetBtnStatus(button);
if (status = stHidden) then button := davmaxBtn;
if button = FHiBtn then exit;//if no change
//---process Btn change
if button <> davmaxBtn then cursor := crhandpoint
else cursor := crArrow;
if (FHIbtn <> davmaxBtn) and (GetBtnStatus(FHIbtn) <> stDown) then
BtnPaint(FHIbtn,stFlat);//remove HI edge
if (button <> davmaxBtn) and (GetBtnStatus(button) <> stDown) then
BtnPaint(button,stHI);//paint HI
FHIbtn := button;
end;
procedure TdavarrayBtn.Paint;
//teken de paintbox
//teken alle buttons
var i : byte;
k1,k2 : LongInt;
begin
FHiBtn := davmaxBtn;
with canvas do
begin
brush.color := color;
pen.Width := 1;
pen.color := PcolorTable^[bcFlat];
fillrect(rect(0,0,width,height));
if FBorder > 0 then
begin
if FBtnShape = bs3D then
begin
k1 := PcolorTable^[bcHI]; k2 := PcolorTable^[bcFlat];
end
else begin
k1 := Pcolortable^[bcFlat]; k2 := k1;
end;
pen.color := k1;
moveto(width-1,0);
lineto(0,0); lineto(0,height-1);
pen.color := k2;
lineto(width-1,height-1); lineto(width-1,0);
end;//if border
end;//with
for i := 0 to FColumns*Frows-1 do
if GetBtnStatus(i) <> stHidden then Btnpaint(i,GetBtnStatus(i));
end;
function TdavArrayBtn.GetBtnRect(btnNr : byte) : TRect;
//geef rectangle waarin button getekend moet worden
var x,y : integer;
begin
x := btnNr mod Fcolumns;
y := btnNr div FColumns;
with result do
begin
left := Fborder + (FBtnWidth + FBtnspacing)*x;
right := left + FBtnWidth;
top := Fborder + (FBtnHeight + FBtnspacing)*y;
bottom := top + FbtnHeight;
end;
end;
procedure TdavArrayBtn.setRows(n : byte);
begin
if n = 0 then n := 1;
if n > davmaxBtn then n := davmaxBtn;
if n * Fcolumns > davMaxBtn then Fcolumns := 1;
Frows := n;
initBtns;
FixDimensions;
end;
procedure TdavArrayBtn.setColumns(n : byte);
begin
if n = 0 then n := 1;
if n > davMaxBtn then n := davMaxbtn;
if n * Frows > davMaxBtn then Frows := 1;
Fcolumns := n;
initBtns;
FixDimensions;
end;
procedure TdavArrayBtn.setBtnWidth(n : byte);
begin
if n < 10 then n := 10;
FBtnWidth := n;
FixDimensions;
end;
procedure TdavarrayBtn.setBtnHeight(n : byte);
begin
if n < 10 then n := 10;
FBtnHeight := n;
FixDimensions;
end;
procedure TdavArrayBtn.setBtnShape(bs : TBtnShape);
begin
FBtnShape := bs;
invalidate;
end;
procedure TdavArrayBtn.setBtnEdge(edge : byte);
begin
if edge = 0 then edge := 1
else if edge > 2 then edge := 2;
FBtnEdge := edge;
repaintBtns;
end;
procedure TdavArrayBtn.setBorder(b : byte);
begin
Fborder := b;
FixDimensions;
end;
procedure TdavarrayBtn.setSpacing(b : byte);
begin
FBtnSpacing := b;
FixDimensions;
end;
procedure TdavArrayBtn.CMmouseLeave(var message : Tmessage);
begin
if (FHiBtn <> davmaxbtn) then
begin
if GetBtnStatus(FHIbtn) <> stDown then BtnPaint(FHIbtn,stFlat);
FHIbtn := davMaxBtn;
end;
if not (csDesigning in componentstate) and assigned(FOnLeave) then
onLeave(self);
end;
procedure TdavarrayBtn.CMmouseEnter(var message : Tmessage);
begin
if not (csDesigning in componentstate) and assigned(FOnLeave) then
onEnter(self);
end;
end.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
![]() |
![]() |