Site hosted by Angelfire.com: Build your free website today!

Home
DelphiZeus
13B. Graphic Controls
Making Graphic Controls,
that do NOT have a Window.

Home



Controls that are just an Image

There are several "Controls" in the Delphi VCL like TLabel and TSpeedButton that are "Graphic" controls and are not a system "Window", but are just an Image painted on the parents DC. These graphic controls have properties like width and height and some have events like OnMouseDown and OnClick. But since there is no system Window for these controls, all of the appearence and functionality comes from the parents system messages and drawing the control in the WM_PAINT message of it's parent.

In this Graphic Controls, GrafCtrls.pas, Unit, there is code for several "Image" only controls, these controls are Not system windows but are just drawn on their parents window in it's WM_PAINT message. These "Graphic" controls include a GLabel, a GButton, a GArrow, and a GBorder. These image controls may not have any usefullness for you, these are mostly for a "How To" example. You may want to create your own code for a Graphic type that you would commonly use.



Types of Graphic Controls

There are four types of graphic controls in this unit, a text display called GLabel, a clickable button called GButton, an Arrow shape called GArrow, and a rectangular border called GBorder. These are here as examples for you to use to make your own re-usable code unit, with image areas (controls) that you have needed or coded in your GUI design. I will not use any "Delphi TObject" creation for these controls, I did not need these graphic controls to have many properties or have many "Options". So to try and keep it simple, I use functions to get and set the properties of a control, like the API functions that use a "Handle" to get and set window properties. All of these controls are Identified by a Number returned from their creation function, which is used much like a system "Handle". I have separate functions for each control type of control, that will create and change that control type.

There are three functions (procedures) that can be used on 3 types of controls, much like the API window functions. They are -   MoveGCtrl( ), , ShowGCtrl( ), , isGCtrlVisible( ). These 3 functions work on the GButton, GArrow and GBorder controls, but NOT on the GLabel controls.

The first graphic control is a GLabel, You have seen this type of thing before in the In Units lesson, there was a ApiFormU.pas unit that had an array of TLabelRec to draw text in the main window's WM_PAINT message, to look like a text STATIC control. I hope that you could see how this "Array of Control Information" method could be used for other types of image areas or controls. The GLabel here is very much like the text draw label in APIformU.pas unit, so you should look at the code an explanation for the Labels in that lesson. The GLabels have an array of image information (position, parent) that is "Public" and can be accessed and changed by code. The other G controls in this do Not allow user access to the array of image information, like the GLabels does. You must use a function or procedure to change any of the properties (array data) for these G controls. I beleive for your own graphic control unit you would want to always have the control's "Array" of image data be public.

All of these graphic controls have a function to create them, I use the word "Make" for my creation functions here, like MakeGButton( ). All of these Make functions will return an Identification number for the control that was created. This ID number is used in the other G control functions to identify which control to change. Each "Make" function will have parameters for the parent window handle and it's position (usually a Left and Top) and other parameters needed for that control. If the control has text there will be a Caption parameter, and if a Color is used a Color paramter. Each control has several more functions to change or get information for that control. You will notice that in this unit there are is NO way to destroy a G control once it has be created, if you need this you can add a function for it. There are many other options and properties you can add, but remember that this also adds bytes to your programs file size.

      The four Make functions -
All of these functions return an ID number to use in other G control functions.

function MakeGLabel(hParent,Left,Top:Integer;const Caption:String;
                  Color1:Cardinal=0;hFont:Integer=-1):Integer;

function MakeGButton(hParent,Left,Top:Integer;Width,Height: Word;
                     const Caption:String;OnClick:TGButProc):Integer;

function MakeGArrow(hParent,PointX,PointY:Integer;Width,Length: Word;
                     ArrowKind:TArrowKind;Color:Integer=0):Integer;

function MakeGBorder(hParent,Left,Top:Integer;Width,Height:Word;
             Kind:TBorderKind;Color:Cardinal;HiLoOff:Byte):Integer;
The hParent parameter in all of these must be the parent window handle. The other parameters should be be recognized by their names. You will need to look at the code for any of these functions that you are interested in and figure out how they work, so you can get some ideas on how to make your own graphic display control.

G Controls need to be Painted in Parents WM_PAINT Message
You must place the procedure to draw a G contol in it's parent's windows WM_PAINT message handler, Each type of control has it own Drawing procedure, they are listed below -

  DrawGLabels( )
  DrawGBut( )
  DrawGArrow( )
  DrawGBorder( )

Without these procedures being called you will not see anything on the GUI. Each of these procedures needs the parameters for the message window's handle (hWnd), the BeginPaint DC (hDC), and the BeginPaint area rectangle (PaintRect). Like this code -

function MessageFunc(hWnd,Msg,wParam,lParam:Integer):Integer; stdcall;
var
PaintS: TPaintStruct;
begin
Result := Zero;
case Msg of
  WM_PAINT:
    begin
    BeginPaint(hWnd, PaintS);
    DrawGLabels(hWnd, PaintS.hDC, PaintS.rcPaint);
    EndPaint(hWnd,PaintS);
    Exit;
    end;
  end;
Result := DefWindowProc(hWnd,Msg,wParam,lParam);
end;

The GButton will get user input from a Left mouse button click, so you must place it's 2 procedures for mouse messages in it's parents left button mouse message handlers. The GButLDown( ) in the WM_LBUTTONDOWN message, , and the GButLUp( ) in the WM_LBUTTONUP message.

Code Examples

If you are interested in this type of graphic control, you will need to look at the code in the unit below to get ideas to help you in your methods for making your own display controls. There are comments in the units code to give a few hints about what a function does, but you should have knowledge of the methods used here already from previous lessons here at Delphi Zeus. You might try and create your own visual control first, and then look at these examples for ideas about methods to create, size and draw it.

On the next page   Using GrafCtrls Program   there is code for a program to show you how to use these G controls.

unit GrafCtrls;
  // unit for Images that act like a control

interface

uses Windows; // the ONLY unit in the uses clause is Windows

{These Graphic Controls are NOT meant to be a complete "Add On" unit for
your projects, this is an example unit, with some code to show some methods
for using a painted area to look like a "control". You will notice that
none of these G controls have any way to Delete or Destroy a G control
once it has been created, although you can hide them}

{I will draw on the DC of the "Parent" window, in the WM_PAINT message for
all of these Graphic controls, by looping through an Array of control
information for each different type of control. All of these controls
have a creation function with the word "Make", like MakeGButton( ). All of
these Make functions will return an Identification number (like a Handle)
to be used in other functions to tell it which control to access. . All
of these controls also have a function with the word "Draw", like
DrawGArrow( ) that MUST be placed in all of the parent windows WM_PAINT
message that have those graphic controls on them. All of these controls
have a function with the word "Change", like ChangeGLabel( ), which will
change some display of a single control}

{the only control here to use any user input is the GButton, which will
get left button mouse clicks, There are two procedures GButLDown and GButLUp
that MUST be placed in all of the GButton parent windows WM_LBUTTONDOWN and
WM_LBUTTONUP messages}


type

  PGLabelRec = ^TGLabelRec;
  TGLabelRec = Record
    hParent1: Integer; // Parent of Label
    GRect: TRect; // Bounds Rectangle of Label
    Hide: Boolean; // Will only draw if false
    TextColor: Cardinal; // Label text color
    FontHnd: Integer; // font used to draw text
    Text: String; // text to draw on Label
    end;
  // this TGLabelRec is a record of Information used to Draw a GLabel
  // GRect is the name for a TRect that will have the Bounds of a G Control
  // Hide is True if a G control is Hidden (negative of Visible)


  PGButProc = ^TGButProc;
  TGButProc = procedure(GButton: Integer);
  // a TGButProc is a procedure called for button Clicks on a GButtons

  PArrowKind = ^TArrowKind;
  TArrowKind = (akRight, akLeft, akUp, akDown, akNon);
  // TArrowKind wiil indicate the direction a GArrow will point, Right, Left
  // Up and Down, the akNon is a NON-Change or "Null"

  PBorderKind = ^TBorderKind;
  TBorderKind = (bkFlat, bkDown, bkOut, bkGrove, bkBump, bkNon);
  // TBorderKind sets the look of the GBorder, the Flat is solid color, no 3D,
  // Down looks depressed, Out looks like a button, the bkNon is for
  // NON-Change, a Null


{All of these controls work by creating a ARRAY of a controls position
 and parent Record. The only array I have made "Public" is for the GLabel.
 The other arrays are in the implementation and can not be accessed in code.}
var
AryGLabel: Array of TGLabelRec;

// = = = = = = =  = = = = = = = = = =


//// GLabel functions

function MakeGLabel(hParent, Left, Top: Integer; const Caption: String;
                  Color1: Cardinal = 0; hFont: Integer = -1): Integer;
   // Creates a GLabel and returns an  ID  number for that GLabel

procedure ChangeGLabel(iGLabel: Integer; const Caption: String;
                          hFont: Integer = -2);
   // Changes the Caption or font of GLabel, the GLabel parameter is the
   // GLabel ID number from the MakeGLabel function

procedure RefreshGLabel(iGLabel: Integer; Visible: Boolean = True);
   // the RefreshGLabel does InvalidateRect on the parent for bounds Rect

procedure DoGLabelRect(iGLabel: Integer);
   // this DoGLabelRect will calculate the Bounds Rect for that Label
   // according to it's text length and Font

   // this DrawGLabels must be placed in the labels's parent WM_PAINT message
procedure DrawGLabels(hWnd, hDC: Integer; const PaintRect: TRect);
   // the hWnd is from the parent message function, the hDC is from the
   // BeginPaint and the PaintRect is from the TPaintStruct rcPaint


// Common C Control Functions
   // these three functions work on the 3 G Controls
   // below, GButton, GArrow and GBorder, does NOT do GLabels

procedure MoveGCtrl(iGControl, Left, Top: Integer; Width, Height: Word);
   // will move a G Control to a new position or size
   // you can set the Width or Height to Zero if you do NOT want them to change

procedure ShowGCtrl(iGControl: Integer; Show: Boolean = True);
   // will Show or Hide a G Control

function isGCtrlVisible(iGControl: Integer): Boolean;
   // returns  True  if the G Control is visible



//// GButton functions

function MakeGButton(hParent, Left, Top: Integer; Width, Height: Word;
                     const Caption: String; OnClick: TGButProc): Integer;
   // Creates a GButton, you need a TGButProc procedure for that Button Click

procedure setGButtonFont(iGButton, hFont: Integer; TextColor: Integer = -1);
   // sets the Font and text Color for a GButton

procedure ChangeGButton(iGButton: Integer; const Caption: String;
                        Enable: Boolean = True);
   // changes the Text or enabled for a GButton

function GetGButBounds(iGButton: Integer): TRect;
   // gets the placement , bounds, in a rectangle

function isGButEnabled(iGButton: Integer): Boolean;
   // returns True if GButton is Enabled

function GButCaption(iGButton: Integer): String;
   // returns the text caption of GButton

   // the next three procedures are placed in the GButton's Parent Message Proc
procedure DrawGBut(hWnd, hDC: Integer; const PaintRect: TRect);
    // WM_PAINT for drawing the GButton

procedure GButLDown(hWnd, lParam: Integer);
    // WM_LBUTTONDOWN to record a mouse down

procedure GButLUp(hWnd: Integer);
    // WM_LBUTTONUP to call the TGButProc click procedure



//// GArrow Functions

function MakeGArrow(hParent, PointX, PointY: Integer; Width, Length: Word;
                     ArrowKind: TArrowKind; Color: Integer = 0): Integer;
   // creates a GArrow

function GetGArrow(iGArrow: Integer; pPosition: PPoint; pBounds: PRect;
                   pKind: PArrowKind): Integer;
   // in this GetGArrow, I use Pointers instead of Types, so I can have a nil
   // if I do not need to Get a value of the GArrow

procedure GArrowColor(iGArrow: Integer; Color: Integer);
   // sets the GArrow Color

procedure ChangeGArrow(iGArrow: Integer; Width, Length: Word;
                       ArrowKind: TArrowKind);
   // changes the width, length and kind of GArrow

   // this DrawGArrow must be placed in the arrow's parent WM_PAINT message
procedure DrawGArrow(hWnd, hDC: Integer; const PaintRect: TRect);



// functions for GBorder

function MakeGBorder(hParent, Left, Top: Integer; Width, Height: Word;
                     Kind: TBorderKind; Color: Cardinal; HiLoOff: Byte): Integer;
   // creates a GBorder, the HiLoOff is the "Offset" or Difference between the
   // hilight and shadow colors

procedure GBorderColors(iGBorder, HiColor, LoColor: Integer);
   // use GBorderColors to assign your own "Special" light and dark colors
   // I have the Colors as Integers, so you can not use the color HiByte

procedure ChangeGBorder(iGBorder, Color: Integer; Kind: TBorderKind; HiLoOff: Byte);
   // changes the Color, kind and color offset of a GBorder

function GetGBorderRect(iGBorder: Integer): TRect;
   // will return the bounds rectangle for a GBorder

   // this DrawGBorder must be placed in the arrow's parent WM_PAINT message
procedure DrawGBorder(hWnd, hDC: Integer; const PaintRect: TRect);


// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =


implementation

const
Zero = 0;
One = 1;
mOne = -1;
Two = 2;
mTwo = -2;
white = $FFFFFF;
GButConst = $020000;
GArrowConst = $030000;
GBorderConst = $040000;

type

  // position record for a GButton
  TGButRec = record
    hParent1: Integer;
    GRect: TRect;
    Hide: Boolean;
    Color: Cardinal;
    hFont1: Integer;
    Enable1: Boolean;
    ClickProc: TGButProc;
    Caption1: String;
    end;

  TArwPoints = Array[Zero..7] of TPoint;

  // position record for a GArrow
  TArrowRec = record
    hParent1: Integer;
    GRect: TRect;
    Hide: Boolean;
    Color1: Integer;
    AKind: TArrowKind;
    ArwPnts: TArwPoints;
    end;

  // position record for a GBorder
  TBorderRec = record
    hParent1: Integer;
    GRect: TRect;
    Hide: Boolean;
    Color1, HiColor, LoColor: Cardinal;
    Off: Byte;
    Dark: Boolean;
    bKind: TBorderKind;
    end;


var
// the 4 Count variables below keep the number of controls created
LabelCount: Integer = Zero;
GButCount: Integer = Zero;
GArrowCount: Integer = Zero;
GBorderCount: Integer = Zero;

// the Arrays below store all of the position records for controls created
AryGBut: Array of TGButRec;
AryGArrow: Array of TArrowRec;
AryGBorder: Array of TBorderRec;
// ButDown will record which GButton got a Left button dowm message
ButDown: Integer = mOne;



// Label functions / / / / / / / / / / / / / / / / / / / / / /

procedure DoGLabelRect(iGLabel: Integer);
var
Size1: TSize;
sDC: Integer;
begin
// this procedure will calculate the bounds rectangle for the text block
// of the font used in a GLabel
if ((iGLabel < Zero) or (iGLabel > High(AryGLabel))) then Exit;
sDC := GetDC(Zero); // text calculation are done on the screen DC
with AryGLabel[iGLabel] do
  begin
  if FontHnd > Zero then
    SelectObject(sDC, FontHnd);

  if Text = '' then
    begin
    GRect.Right := GRect.Left+One;
    GRect.Bottom := GRect.Top+One;
    end else
    if GetTextExtentPoint32(sDC, PChar(Text), Length(Text), Size1) then
    begin
    GRect.Right := GRect.Left+Size1.cx;
    GRect.Bottom := GRect.Top+Size1.cy;
    end else
    begin
    GRect.Right := GRect.Left + (Length(Text)*8);
    GRect.Bottom := GRect.Top+25;
    end;
  end;
ReleaseDC(Zero, sDC);
end;


function MakeGLabel(hParent, Left, Top: Integer; const Caption: String;
                  Color1: Cardinal = Zero; hFont: Integer = mOne): Integer;
begin
// creates a GLabel, and uses DoGLabelRect( ) to get the size of it's display
Result := mOne;
if not IsWindow(hParent) then Exit;
SetLength(AryGLabel, LabelCount+One);
with AryGLabel[LabelCount] do
  begin
  hParent1 := hParent;
  TextColor := Color1;
  GRect.Left := Left;
  GRect.Top := Top;
  if hFont < Zero then
    FontHnd := GetStockObject(ANSI_VAR_FONT)
    else
    if hFont = Zero then
    FontHnd := GetStockObject(SYSTEM_FONT)
    else
    FontHnd := hFont;

  Hide := False;
  Text := Caption;
  end;
DoGLabelRect(LabelCount);
Result := LabelCount;
Inc(LabelCount);
end;


procedure ChangeGLabel(iGLabel: Integer; const Caption: String;
                          hFont: Integer = mTwo);
begin
// used to change the Text or font of a GLabel
with AryGLabel[iGLabel] do
  begin
  if ((iGLabel < Zero) or (iGLabel > High(AryGLabel))) or
   ((Text = Caption) and (hFont = mTwo)) then Exit;
  InvalidateRect(hParent1, @GRect, True);
  if hFont > mTwo then
    if hFont = mOne then
    FontHnd := GetStockObject(ANSI_VAR_FONT)
    else
    if hFont = Zero then
    FontHnd := GetStockObject(SYSTEM_FONT)
    else
    FontHnd := hFont;
  if Caption <> '' then
    Text := Caption;
  DoGLabelRect(iGLabel);
  InvalidateRect(hParent1, @GRect, True);
  end;
end;


procedure RefreshGLabel(iGLabel: Integer; Visible: Boolean = True);
begin
// called to repaint a GLabel
if ((iGLabel < Zero) or (iGLabel > High(AryGLabel))) then Exit;
AryGLabel[iGLabel].Hide := not Visible;
InvalidateRect(AryGLabel[iGLabel].hParent1, @AryGLabel[iGLabel].GRect, True);
end;


procedure DrawGLabels(hWnd, hDC: Integer; const PaintRect: TRect);
var
LN: Integer;
begin
{you will need to call this function in every window's WM_PAINT message in the
Window Proc that has any labels on it, this procedure loops through the
Labeles info array and draws them on the parents windows DC}

SetBkMode(hDC, TRANSPARENT);
// I set the DC to transparent and do NOT reset it
for LN := Zero to High(AryGLabel) do
  if hWnd = AryGLabel[LN].hParent1 then
  with AryGLabel[LN] do
  begin
  if  Hide or
     (PaintRect.Bottom < GRect.Top) or
     (PaintRect.Right < GRect.Left) or
     (PaintRect.Top > GRect.Bottom) or
     (PaintRect.Left > GRect.Right) or
     (Text = '') then Continue;

  SelectObject(hDC, FontHnd);
  SetTextColor(hDC, TextColor);
  TextOut(hDC, GRect.Left, GRect.Top,
          PChar(Text), Length(Text));
  end;
end;



// GButton functions / / / / / / / / / / / / / / / / / / / / /

procedure GButPaint(index, hDC: Integer; Down: Boolean = False);
var
dRect: TRect;
begin
// this procedure will use the DrawFrameControl function to draw a button
with AryGBut[index] do
  begin
  if Down then
    DrawFrameControl(hDC, GRect,DFC_BUTTON, DFCS_BUTTONPUSH or DFCS_PUSHED)
    else
    DrawFrameControl(hDC, GRect,DFC_BUTTON, DFCS_BUTTONPUSH);
  if Caption1 = '' then Exit;
  SetBkMode(hDC, TRANSPARENT);
  SelectObject(hDC, hFont1);
  dRect := GRect;
  InflateRect(dRect, mTwo, mTwo);
  if Enable1 then
    SetTextColor(hDC, Color)
    else
    begin
    OffsetRect(dRect, One,One);
    SetTextColor(hDC, GetSysColor(COLOR_BTNHILIGHT));
    DrawText(hDC, PChar(Caption1), mOne, dRect,
         DT_SINGLELINE or DT_CENTER or DT_VCENTER);
  SetTextColor(hDC, GetSysColor(COLOR_GRAYTEXT));
  OffsetRect(dRect, mOne, mOne);
  end;

  if Down then
    OffsetRect(dRect, One, One);
  DrawText(hDC, PChar(Caption1), mOne, dRect,
         DT_SINGLELINE or DT_CENTER or DT_VCENTER);
  end;
end;


procedure DrawGBut(hWnd, hDC: Integer; const PaintRect: TRect);
var
GN: Integer;
begin
{you will nedd to call this function in every window's WM_PAINT message in the
Window Proc that has any labels on it, this procedure loops through the
Labeles and draws them on the parents windows DC}

for GN := Zero to High(AryGBut) do
  if hWnd = AryGBut[GN].hParent1 then
  with AryGBut[GN] do
  begin
  if (PaintRect.Bottom < GRect.Top) or
     (PaintRect.Right < GRect.Left) or
     (PaintRect.Top > GRect.Bottom) or
     (PaintRect.Left > GRect.Right) or
     Hide then Continue;
  GButPaint(GN, hDC);
  end;
end;

procedure GButLDown(hWnd, lParam: Integer);
var
Pnt1: TPoint;
i, hDC: Integer;
begin
// this procedure is called in the parent's windows WM_LBUTTONDOWN message
Pnt1.x := SmallInt(lParam and $FFFF);
Pnt1.y := SmallInt(lParam shr 16);

for i := Zero to High(AryGBut) do
  with AryGBut[i] do
  begin
  if (hWnd <> hParent1) or Hide then Continue;
  if PtInRect(GRect, Pnt1) then
    begin
    if not Enable1 then Exit;
    ButDown := i;
    SetCapture(hWnd);
    hDC := GetDC(hWnd);
    GButPaint(i, hDC, True);
    ReleaseDC(hWnd, hDC);
    Break;
    end;
  end
end;

procedure GButLUp(hWnd: Integer);
var
hDC: Integer;
begin
// this procedure is called in the parent's windows WM_LBUTTONUP message
if ButDown = mOne then Exit;
ReleaseCapture;
hDC := GetDC(hWnd);
GButPaint(ButDown, hDC);
ReleaseDC(hWnd, hDC);
if @AryGBut[ButDown].ClickProc <> nil then
   AryGBut[ButDown].ClickProc(GButConst+ButDown);
ButDown := mOne;
end;


function MakeGButton(hParent, Left, Top: Integer; Width, Height: Word;
                     const Caption: String; OnClick: TGButProc): Integer;
begin
// creates a GButton and returns the ID number for it
Result := mOne;
if not IsWindow(hParent) then Exit;
SetLength(AryGBut, GButCount+One);
if Width < 10 then Width := 10;
if height < 10 then Height := 10;
with AryGBut[GButCount] do
  begin
  hParent1 := hParent;
  Color := GetSysColor(COLOR_BTNTEXT);
  GRect.Left := Left;
  GRect.Top := Top;
  GRect.Right := Left+Width;
  GRect.Bottom := Top+Height;
  hFont1 := GetStockObject(SYSTEM_FONT);
  Enable1 := True;
  Hide := False;
  ClickProc := OnClick;
  Caption1 := Caption;
  end;
Result := GButConst + GButCount;
Inc(GButCount);
end;

function NoButIndex(var GBut: Integer): Boolean;
var
index: Integer;
begin
// determines if the ID number is in range of array
index := GBut - GButConst;
if (index < Zero) or (index > High(aryGBut)) then
  Result := True
  else
  begin
  GBut := index;
  Result := False;
  end;
end;

procedure setGButtonFont(iGButton, hFont: Integer; TextColor: Integer = mOne);
begin
// sets the font of a GButton
if NoButIndex(iGButton) then Exit;
if hFont > -1 then
  AryGBut[iGButton].hFont1 := hFont;
if TextColor > -1 then
  AryGBut[iGButton].Color := TextColor;
InvalidateRect(AryGBut[iGButton].hParent1, @AryGBut[iGButton].GRect, False);
end;


procedure ChangeGButton(iGButton: Integer; const Caption: String;
                        Enable: Boolean = True);
var
Draw: Boolean;
begin
// changes the Text or enables a GButton
if NoButIndex(iGButton) then Exit;
Draw := False;
with AryGBut[iGButton] do
  begin
  if Caption <> '' then
    begin
    Caption1 := Caption;
    Draw := True;
    end;
  if Enable1 <> Enable then
    Draw := True;
  Enable1 := Enable;
  if Draw then
   InvalidateRect(hParent1, @GRect, False);
  end;
end;


function GetGButBounds(iGButton: Integer): TRect;
begin
// returns the bounds rect for the GButton
if NoButIndex(iGButton) then
  begin
  SetRectEmpty(Result);
  Exit;
  end;
Result := AryGBut[iGButton].GRect;
end;

function isGButEnabled(iGButton: Integer): Boolean;
begin
// returns True if GButton is enabled
if NoButIndex(iGButton) then
  begin
  Result := False;
  Exit;
  end;

Result :=AryGBut[iGButton].Enable1;
end;

function GButCaption(iGButton: Integer): String;
begin
// returns the GButton caption string
if NoButIndex(iGButton) then
  begin
  Result := '';
  Exit;
  end;
Result := AryGBut[iGButton].Caption1;
end;


// GArrow Functions

procedure SetGArrow(index, Width, Length: Word);
const
Six = 6;

var
HalfHWidth, HalfTWidth: Integer;
ALPt, EndPnt: TPoint;
i: Integer;

begin
// this will calculate the 7 points used to drawn a GArrow
if Width < Six then
  Width := Six;

if Length < Width+4 then
  Length := Width+4;

HalfHWidth := Width shr One{div 2};
HalfTWidth := Trunc(HalfHWidth / 2.5);
with AryGArrow[index] do
case AKind of
  akRight..akLeft:
    begin
    ALPt.y := ArwPnts[Zero].y;
    EndPnt.y := ALPt.y;
    if AKind = akRight then
      begin
      ALPt.x := ArwPnts[Zero].x-Width;
      EndPnt.x := ArwPnts[Zero].x-Length;
      GRect.Left := EndPnt.x;
      GRect.Right := ArwPnts[Zero].x+One;
      end else
      begin
      ALPt.x := ArwPnts[Zero].x+Width;
      EndPnt.x := ArwPnts[Zero].x+Length;
      GRect.Left := ArwPnts[Zero].x;
      GRect.Right := EndPnt.x+One;
      end;
    for i := One to Six do
      if (i = 3) or (i = 4) then
      ArwPnts[i].x := EndPnt.x
      else
      ArwPnts[i].x := ALPt.x;

    ArwPnts[One].y := ALPt.y-HalfHWidth;
    ArwPnts[Two].y := ALPt.y-HalfTWidth;
    ArwPnts[3].y := EndPnt.y-HalfTWidth;
    ArwPnts[4].y := EndPnt.y+HalfTWidth;
    ArwPnts[5].y := ALPt.y+HalfTWidth;
    ArwPnts[Six].y := ALPt.y+HalfHWidth;

    GRect.Top := ArwPnts[One].y;
    GRect.Bottom := ArwPnts[Six].y+One;
    end;

  akUp..akDown:
    begin
    ALPt.x := ArwPnts[Zero].x;
    EndPnt.x := ArwPnts[Zero].x;
    if AryGArrow[index].AKind = akUp then
      begin
      ALPt.y := ArwPnts[Zero].y+Width;
      EndPnt.y := ArwPnts[Zero].y+Length;
      GRect.Bottom := EndPnt.y+One;
      GRect.Top := ArwPnts[Zero].y;
      end else
      begin
      ALPt.y := ArwPnts[Zero].y-Width;
      EndPnt.y := ArwPnts[Zero].y-Length;
      GRect.Bottom := ArwPnts[Zero].y+One;
      GRect.Top := EndPnt.y;
      end;
    for i := One to Six do
      if (i = 3) or (i = 4) then
      ArwPnts[i].y := EndPnt.y
      else
      ArwPnts[i].y := ALPt.y;

    ArwPnts[One].x := ALPt.x+HalfHWidth;
    ArwPnts[Two].x := ALPt.x+HalfTWidth;
    ArwPnts[3].x := EndPnt.x+HalfTWidth;
    ArwPnts[4].x := EndPnt.x-HalfTWidth;
    ArwPnts[5].x := ALPt.x-HalfTWidth;
    ArwPnts[Six].x := ALPt.x-HalfHWidth;

    GRect.Left := ArwPnts[Six].x;
    GRect.Right := ArwPnts[One].x+One;
    end;
  end;
end;


function MakeGArrow(hParent, PointX, PointY: Integer; Width, Length: Word;
                     ArrowKind: TArrowKind; Color: Integer = Zero): Integer;
begin
// adds a GArrow info Record to the AryGArrow array
Result := mOne;
if not IsWindow(hParent) then Exit;
SetLength(AryGArrow, GArrowCount+One);
with AryGArrow[GArrowCount] do
  begin
  hParent1 := hParent;
  Color1 := Color;
  Hide := False;
  if ArrowKind = akNon then
    ArrowKind := akLeft;
  AKind := ArrowKind;

  ArwPnts[Zero].x := PointX;
  ArwPnts[Zero].y := PointY;
  ArwPnts[7] := AryGArrow[GArrowCount].ArwPnts[Zero];
  end;

SetGArrow(GArrowCount, Width, Length);


Result := GArrowConst + GArrowCount;
Inc(GArrowCount);
end;

function NoArwIndex(var GArw: Integer): Boolean;
var
index: Integer;
begin
// tests to see if the index is valid
index := GArw - GArrowConst;
if (index < Zero) or (index > High(AryGArrow)) then
  Result := True
  else
  begin
  GArw := index;
  Result := False;
  end;
end;

function GetGArrow(iGArrow: Integer; pPosition: PPoint; pBounds: PRect;
                   pKind: PArrowKind): Integer;
begin
// this fuction can return several of the GArrow properties
// I have used pointers here like PPoint so if you do NOT need that information
// you just place a nil in that parameter
// this returns the arrow color or a minus one if the index is out of range
Result := mOne;
if NoArwIndex(iGArrow) then Exit;
with AryGArrow[iGArrow] do
  begin
  Result := Integer(Color1);
  if pPosition <> nil then
    begin
    pPosition^.x := ArwPnts[Zero].x;
    pPosition^.y := ArwPnts[Zero].y;
    end;
  if pBounds <> nil then
    pBounds^ := GRect;
  if pKind <> nil then
    pKind^ := AKind;
  end;
end;


procedure ChangeGArrow(iGArrow: Integer; Width, Length: Word;
                       ArrowKind: TArrowKind);
begin
// this will change the width, height and Kind for a GArrow
if NoArwIndex(iGArrow) then Exit;
with AryGArrow[iGArrow] do
  begin
  if not Hide then
    InvalidateRect(hParent1, @GRect, True);
  if ArrowKind <> akNon then
    AKind := ArrowKind;
  SetGArrow(iGArrow, Width, Length);
  if not Hide then
    InvalidateRect(hParent1, @GRect, True);
  end;
end;

procedure GArrowColor(iGArrow: Integer; Color: Integer);
begin
// this only changes the Color of a GArrow
if NoArwIndex(iGArrow) then Exit;
if Color <> AryGArrow[iGArrow].Color1 then
  begin
  AryGArrow[iGArrow].Color1 := Color;
  if not AryGArrow[iGArrow].Hide then
    InvalidateRect(AryGArrow[iGArrow].hParent1, @AryGArrow[iGArrow].GRect, True);
  end;
end;


// you Must place this DrawGArrow in the GArrow's parents message function
procedure DrawGArrow(hWnd, hDC: Integer; const PaintRect: TRect);
var
AN, oBrush, Brush, oPen, Pen: Integer;
begin
for AN := Zero to High(AryGArrow) do
  if hWnd = AryGArrow[AN].hParent1 then
  with AryGArrow[AN] do
  begin
  if (PaintRect.Bottom < GRect.Top) or
     (PaintRect.Right < GRect.Left) or
     (PaintRect.Top > GRect.Bottom) or
     (PaintRect.Left > GRect.Right) or
     Hide then Continue;
  Brush := CreateSolidBrush(Color1);
  Pen := CreatePen(PS_SOLID,Zero,Color1);
  oBrush := SelectObject(hDC, Brush);
  oPen := SelectObject(hDC, Pen);
  Polygon(hDC, ArwPnts, 8);
  DeleteObject(SelectObject(hDC, oBrush));
  DeleteObject(SelectObject(hDC, oPen));
  end;
end;



// GBorder functions

procedure SetBorColors(GBorder, Color: Integer; cOff: Byte);
const
red = $FF;
green = $FF00;
blue = $FF0000;

var
r,g,b: Integer;
begin
// this will calculate the HighLight and Shadow colors for the GBorder
r := ((Color and red) + cOff)-Two;
if r > red then
  r := red;
g := (Color and green) + ((cOff+4) shl 8);
if g > green then
  g := green;
b := ((Color and blue) + (cOff shl 16))-$20000;
if b > blue then
  b := blue;
AryGBorder[GBorder].HiColor := r or g or b;

r := ((Color and red) - cOff)+Two;
if r < Zero then
  r := Zero;
g := (Color and green) - ((cOff+4) shl 8);
if g < $100 then
  g := Zero;
b := ((Color and blue) - (cOff shl 16))+$20000;
if b < $10000 then
  b := Zero;
AryGBorder[GBorder].LoColor := r or g or b;

r := Color and red;
g := (Color and green) shr 8;
b := (Color and blue) shr 16;
r := Round((r*0.9)+(g*1.5)+(b*0.6)) div 3;
if r < $8F then
  AryGBorder[GBorder].Dark := True
  else
  AryGBorder[GBorder].Dark := False;
end;

function MakeGBorder(hParent, Left, Top: Integer; Width, Height: Word;
                     Kind: TBorderKind; Color: Cardinal; HiLoOff: Byte): Integer;
begin
// this will add a GBorder to the AryGBorder array
Result := mOne;
if not IsWindow(hParent) then Exit;
SetLength(AryGBorder, GBorderCount+One);
if Width < 8 then Width := 8;
if Height < 8 then Height := 8;
with AryGBorder[GBorderCount] do
  begin
  hParent1 := hParent;
  if HiLoOff = Zero then
    HiLoOff := $1A;
  Off := HiLoOff;
  Color1 := Color and white;
  if Kind = bkNon then Kind := bkFlat;
  if Kind = bkFlat then
    begin
    HiColor := Color1;
    LoColor := Color1;
    end else
    SetBorColors(GBorderCount, Color1, HiLoOff);

  GRect.Left := Left;
  GRect.Top := Top;
  GRect.Right := Left+Width;
  GRect.Bottom := Top+Height;
  bKind := Kind;
  Hide := False;
  end;
  
Result := GBorderConst + GBorderCount;
Inc(GBorderCount);
end;

function NoBorIndex(var GBor: Integer): Boolean;
var
index: Integer;
begin
// tests to see if the GBor is in the Index of the array
index := GBor - GBorderConst;
if (index < Zero) or (index > High(AryGBorder)) then
  Result := True
  else
  begin
  GBor := index;
  Result := False;
  end;
end;


procedure GBorderColors(iGBorder, HiColor, LoColor: Integer);
begin
// this will set the HighLight and Shadow colors to any custom colors
if NoBorIndex(iGBorder) then Exit;
if HiColor > Zero then
  AryGBorder[iGBorder].HiColor := HiColor and white;
if LoColor > Zero then
  AryGBorder[iGBorder].LoColor := LoColor and white;
if not AryGBorder[iGBorder].Hide then
  InvalidateRect(AryGBorder[iGBorder].hParent1, @AryGBorder[iGBorder].GRect, False);
end;

procedure ChangeGBorder(iGBorder, Color: Integer; Kind: TBorderKind; HiLoOff: Byte);
begin
// changes the GBorder Color, Kind and color offset
if NoBorIndex(iGBorder) then Exit;

with AryGBorder[iGBorder] do
  begin
  if HiLoOff > Zero then
    Off := HiLoOff;
  if Kind <> bkNon then
    bKind := Kind;

  if Color > mOne then
    Color1 := Color and white;

  if not ((Color < Zero) and (HiLoOff = Zero)) then
    if bKind = bkFlat then
    begin
    HiColor := Color1;
    LoColor := Color1;
    end else
    SetBorColors(iGBorder, Color1, Off);

  if not Hide then
    InvalidateRect(hParent1, @GRect, True);
  end;
end;

function GetGBorderRect(iGBorder: Integer): TRect;
begin
// gets the bounds Rect of a GBorder
if NoBorIndex(iGBorder) then
  begin
  SetRectEmpty(Result);
  Exit;
  end;
Result := AryGBorder[iGBorder].GRect;
end;


// you Must place this DrawGBorder in the GBorder's parents message function
procedure DrawGBorder(hWnd, hDC: Integer; const PaintRect: TRect);
var
BN, oBrush, oPen, Pen: Integer;
Color: Cardinal;
begin
// draws the rectangles for a GBorder
oPen := Zero;
for BN := Zero to High(AryGBorder) do
  if hWnd = AryGBorder[BN].hParent1 then
  with AryGBorder[BN] do
  begin
  if (PaintRect.Bottom < GRect.Top) or
     (PaintRect.Right < GRect.Left) or
     (PaintRect.Top > GRect.Bottom) or
     (PaintRect.Left > GRect.Right) or
     Hide then Continue;
     
  case bKind of
    bkFlat:
      begin
      Pen := CreatePen(PS_SOLID or PS_INSIDEFRAME,Two,HiColor);
      oPen := SelectObject(hDC, Pen);
      oBrush := SelectObject(hDC, GetStockObject(NULL_BRUSH));
      Rectangle(hDc, GRect.Left, GRect.Top, GRect.Right, GRect.Bottom);
      SelectObject(hDC, oBrush);
      end;
    bkDown..bkOut:
      begin
      if bKind = bkDown then
        Color := HiColor
        else
        Color := LoColor;

      Pen := CreatePen(PS_SOLID,Two, Color);
      oPen := SelectObject(hDC, Pen);
      MovetoEx(hDC, GRect.Right-One, GRect.Top+One, nil);
      LineTo(hDC, GRect.Right-One, GRect.Bottom-One);
      LineTo(hDC, GRect.Left+One, GRect.Bottom-One);
      if bKind = bkDown then
        Color := LoColor
        else
        begin
        if Dark then
          Color := Zero
          else
          Color := $708080;
        Pen := CreatePen(PS_SOLID,Zero, Color);
        oPen := SelectObject(hDC, Pen);
        MovetoEx(hDC, GRect.Right-One, GRect.Top+Two, nil);
        LineTo(hDC, GRect.Right-One, GRect.Bottom-One);
        LineTo(hDC, GRect.Left+Two, GRect.Bottom-One);
        Color := HiColor;
        end;
      Pen := CreatePen(PS_SOLID,Two, Color);
      DeleteObject(SelectObject(hDC, Pen));
      MovetoEx(hDC, GRect.Left+One, GRect.Bottom-One, nil);
      LineTo(hDC, GRect.Left+One, GRect.Top+One);
      LineTo(hDC, GRect.Right-One, GRect.Top+One);
      if bKind = bkOut then
        begin
        SetPixel(hDC, GRect.Left+One, GRect.Bottom-One, LoColor);
        SetPixel(hDC, GRect.Right-One, GRect.Top+One, LoColor);
        end else
        begin
        if Dark then
          Color := Zero
          else
          Color := $708080;
        Pen := CreatePen(PS_SOLID,Zero, Color);
        DeleteObject(SelectObject(hDC, Pen));
        MovetoEx(hDC, GRect.Left+One, GRect.Bottom-3, nil);
        LineTo(hDC, GRect.Left+One, GRect.Top+One);
        LineTo(hDC, GRect.Right-Two, GRect.Top+One);
        end;
      end;

    bkGrove..bkBump:
      begin
      if bKind = bkGrove then
        Color := LoColor
        else
        Color := HiColor;
      Pen := CreatePen(PS_SOLID,Zero, Color);
      oPen := SelectObject(hDC, Pen);

      oBrush := SelectObject(hDC, GetStockObject(NULL_BRUSH));
      Rectangle(hDc, GRect.Left, GRect.Top, GRect.Right-One, GRect.Bottom-One);

      if bKind = bkGrove then
        Color := HiColor
        else
        Color := LoColor;
      Pen := CreatePen(PS_SOLID ,Zero, Color);
      DeleteObject(SelectObject(hDC, Pen));

      Rectangle(hDc, GRect.Left+One, GRect.Top+One, GRect.Right, GRect.Bottom);
      SelectObject(hDC, oBrush);
      end;
    end;
  DeleteObject(SelectObject(hDC, oPen));
  end;
end;


//  G Control functions

{there are 3 Common procedures and function, which can access G controls
of more than One type, they are the MoveGCtrl, ShowGCtrl, and isGCtrlVisible,
which are below, these 3 can do a  GArrow, a GButton and a GBorder, they will
not handle a GLabel}

procedure MoveGCtrl(iGControl, Left, Top: Integer; Width, Height: Word);
var
i, offX, offY: Integer;
begin
// this will change the position of a G Control

{in order to have One function for several control types, you will need to
 test the iGControl in all 3 of the index test functions -
 NoButIndex
 NoArwIndex
 NoBorIndex}
if not NoButIndex(iGControl) then
  with AryGBut[iGControl] do
  begin
  if Width < 10 then
    Width := GRect.Right-GRect.Left;
  if Height < 10 then
    Height := GRect.Bottom-GRect.Top;
  if (GRect.Left = Left) and
     (GRect.Top = Top) and
     (GRect.Right = Left+Width) and
     (GRect.Bottom = Top+Height) then Exit;
  if not Hide then
    InvalidateRect(hParent1, @GRect, True);
  GRect.Left := Left;
  GRect.Top := Top;
  GRect.Right := Left+Width;
  GRect.Bottom := Top+Height;
  if not Hide then
    InvalidateRect(hParent1, @GRect, False);
  Exit;
  end;

if not NoArwIndex(iGControl) then
  with AryGArrow[iGControl] do
  begin
  offX := Left - ArwPnts[Zero].x;
  offY := Top - ArwPnts[Zero].y;
  if (offX = Zero) and (offY = Zero) then Exit;
  if not Hide then
    InvalidateRect(hParent1, @GRect, True);
  for i := Zero to 7 do
    begin
    ArwPnts[i].x := ArwPnts[i].x + offX;
    ArwPnts[i].y := ArwPnts[i].y + offY;
    end;
  OffSetRect(GRect, offX, offY);
  if not Hide then
    InvalidateRect(hParent1, @GRect, True);
  Exit;
  end;

if not NoBorIndex(iGControl) then
  with AryGBorder[iGControl] do
  begin
  if Width < 8 then
    Width := GRect.Right-GRect.Left;
  if Height < 8 then
    Height := GRect.Bottom-GRect.Top;
  if (GRect.Left = Left) and
    (GRect.Top = Top) and
    (GRect.Right = Left+Width) and
    (GRect.Bottom = Top+Height) then Exit;
  if not Hide then
    InvalidateRect(hParent1, @GRect, True);
  GRect.Left := Left;
  GRect.Top := Top;
  GRect.Right := Left+Width;
  GRect.Bottom := Top+Height;
  if not Hide then
    InvalidateRect(hParent1, @GRect, True);
  end;

end;


procedure ShowGCtrl(iGControl: Integer; Show: Boolean = True);
begin
// shows and hides a G Control
if not NoButIndex(iGControl) then
  with AryGBut[iGControl] do
  begin
  if Hide <> Show then Exit;
  Hide := not Show;
  InvalidateRect(hParent1, @GRect, True);
  Exit;
  end;

if not NoArwIndex(iGControl) then
  with AryGArrow[iGControl] do
  begin
  if Hide <> Show then Exit;
  Hide := not Show;
  InvalidateRect(hParent1, @GRect, True);
  Exit;
  end;

if not NoBorIndex(iGControl) then
  with AryGBorder[iGControl] do
  begin
  if Hide <> Show then Exit;
  Hide := not Show;
  InvalidateRect(hParent1, @GRect, True);
  end;

end;


function isGCtrlVisible(iGControl: Integer): Boolean;
begin
// returns True if the G Control is visible
Result := False;
if not NoBorIndex(iGControl) then
  Result := not AryGBorder[iGControl].Hide;
if not NoButIndex(iGControl) then
  Result := not AryGBut[iGControl].Hide;
if not NoArwIndex(iGControl) then
  Result := not AryGArrow[iGControl].Hide;
end;


initialization

finalization
Finalize(AryGLabel);
Finalize(AryGBut);

end.

This should give you some ideas about including code for a graphical control, there can be such a variety of "Picture" and image display for controls, you can have fun making an image button or other visual controls.

You can create your own graphical control units and then try to make some that have your special look and visual style.

The next page will have some example code for a program using this GrafCtrls.pas unit.



                           

Next in Code Container Series
The following page shows you how to create a G control program, using this GrafCtrls unit.
  13C. Using GrafCtrls Program


       

Lesson -     One  Two  Three  Four  Five  Six  Seven  Eight  Nine  Ten  Eleven  Twelve  Thirteen  Fourteen




H O M E