| Using Color Gradients |
|
|
|
| Contributed by Rick Kelly | |
| 03 July 2004 | |
|
Rick shows us how to get that color gradient effect in Paradox forms! Using Color Gradients© 2004 Rick Kelly www.crooit.com Preface The example OPAL (Paradox® 9) presented in this article is available as a download here. After downloading into the folder of your choice, make that folder :WORK: and run the included script for a demonstration. Introduction Ever wonder how to achieve the effect of color gradients with your Paradox® forms? Having an UIObject blend two colors together is an interesting effect and adds a bit of catchy eye appeal to your applications. Before I go on, there are some limitations to be aware of. Normally, Paradox® takes care of all those tedious details when it comes to rendering or painting parent and child windows and cooperates with Windows when messages are received that indicate regenerating or repainting a window is required. To fully integrate any custom painting requires that a window be subclassed. Subclassing is just a fancy way to describe a small piece of code that is inserted into the windows messaging flow and examines each message, altering or inserting messages as required. Subclassing a Paradox® form is beyond the scope of this article (perhaps a later followup?) and the methods presented are best used in modal windows and dialogs. One of best uses I've found is the initial application logon and an example of that usage is provided. Color gradients were first introduced with Windows 98 and a common use was in the title bar background of windows when one color would slowly morph to another left to right. The basic steps for applying a color gradient are:
Check for Windows gradient support Checking if the current version of Windows supports gradients is necessary if we want to automatically use the colors from the active color schema and can be omitted if custom colors are to be used. Gradient support can be directly interrogated using a win32 api call to SystemParametersInfo. Const
cnTrue = 1
;
; System Parameters Info
;
cnSPIGetGradientCaptions = 4104
endConst
Uses "user32"
SystemParametersInfo(uAction cLong,
uParam cLong,
lpvParam cPtr,
fuWinIni cLong) cLong [stdcall "SystemParametersInfoA"]
endUses
method GradientColorsAvailable() Logical
var
liReturn LongInt
liParam LongInt
endVar
liParam = 0
liReturn = SystemParametersInfo(
cnSPIGetGradientCaptions,
0,
liParam,
0)
return (liReturn = cnTrue and liParam = cnTrue)
endMethodDetermine what gradient colors to use The two gradient colors we'll use are the color for the active window caption bar and the gradient color as defined in the current Windows color schema. Each color can be retrieved using the win32 api method GetSysColor. Const
cnColorActiveCaption = 2
cnColorGradientActiveCaption = 27
endConst
Uses "user32"
GetSysColor(lpIndex cLong) cLong [stdcall]
endUses
method GetWindowsColor(liWindowsColorIndex LongInt) LongInt
;
; Retrieves the current color of the specified display element.
; Display elements are the parts of a window and the display
; that appear on the system display screen.
;
; If an invalid index is used, the return value is zero
;
return GetSysColor(liWindowsColorIndex)
endMethod
var
liToColor LongInt
liFromColor LongInt
endVar
liToColor = GetWindowsColor(cnColorActiveCaption)
switch
case GradientColorsAvailable() = True :
liFromColor = GetWindowsColor(cnColorGradientActiveCaption)
otherwise :
;
; Use solid color (No gradient)
;
liFromColor = liToColor
endSwitchPainting the gradient Many different types of gradient shapes may be created. It's all about screen coordinates and the appropriate mathematical calculations and we will present the two most common which are top-to-bottom (vertical) and left-to-right (horizontal). Basically, we will calculate a starting/ending pixel coordinate pair, adjust the color to be used and draw a one pixel width line all with native win32 api methods. Uses "user32"
FillRect(hdc cLong,
lpRect cLong,
hBrush cLong) cLong [stdcall]
SetRect(lpRect cLong,
X1 cLong,
Y1 cLong,
X2 cLong,
Y2 cLong) cLong [stdcall]
GetDC(hWnd cLong) cLong [stdcall]
endUses
Uses "kernel32"
GlobalAlloc(wFlags cLong,
dwBytes cLong) cLong [stdcall]
GlobalFree(hMem cLong) cLong [stdcall]
endUses
Uses "gdi32"
CreateSolidBrush(crColor cLong) cLong [stdcall]
SelectObject(hdc cLong,
hObject cLong) cLong [stdcall]
DeleteObject(hObject cLong) cLong [stdcall]
endUses
Proc cmGlobalAlloc(liSize LongInt) LongInt
return GlobalAlloc(fromHex("0x40"),liSize)
endProc
Proc cmGlobalFree(var liPointer LongInt) LongInt
return GlobalFree(liPointer)
endProc
Proc cmPaintGradientHorizontal(var siRed1 SmallInt,
var siGreen1 SmallInt,
var siBlue1 SmallInt,
var siRed2 SmallInt,
var siGreen2 SmallInt,
var siBlue2 SmallInt,
var liLeft LongInt,
var liRight LongInt,
var liTop LongInt,
var liBottom LongInt,
var liColorRect LongInt,
var liDeviceContext LongInt)
var
liBrush LongInt
liPreviousBrush LongInt
liY LongInt
siCurrentRed SmallInt
siCurrentGreen SmallInt
siCurrentBlue SmallInt
nuAny Number
endVar
for liY from liLeft to liRight
nuAny = cmDivide(liRight - liLeft,
liY - liLeft)
siCurrentRed = smallInt(siRed1 +
cmDivide(siRed2 - siRed1,
nuAny))
siCurrentGreen = smallInt(siGreen1 +
cmDivide(siGreen2 - siGreen1,
nuAny))
siCurrentBlue = smallInt(siBlue1 +
cmDivide(siBlue2 - siBlue1,
nuAny))
SetRect(liColorRect,
liY,
liTop,
liRight,
liBottom + 1)
liBrush = CreateSolidBrush(rgb(siCurrentRed,
siCurrentGreen,
siCurrentBlue))
liPreviousBrush = SelectObject(liDeviceContext,liBrush)
FillRect(liDeviceContext,
liColorRect,
liBrush)
SelectObject(liDeviceContext,
liPreviousBrush)
DeleteObject(liBrush)
endFor
endProc
Proc cmPaintGradientVertical(var siRed1 SmallInt,
var siGreen1 SmallInt,
var siBlue1 SmallInt,
var siRed2 SmallInt,
var siGreen2 SmallInt,
var siBlue2 SmallInt,
var liLeft LongInt,
var liRight LongInt,
var liTop LongInt,
var liBottom LongInt,
var liColorRect LongInt,
var liDeviceContext LongInt)
var
liBrush LongInt
liPreviousBrush LongInt
liY LongInt
siCurrentRed SmallInt
siCurrentGreen SmallInt
siCurrentBlue SmallInt
nuAny Number
endVar
for liY from liTop to liBottom
nuAny = cmDivide(liBottom - liTop,
liY - liTop)
siCurrentRed = smallInt(siRed1 +
cmDivide(siRed2 - siRed1,
nuAny))
siCurrentGreen = smallInt(siGreen1 +
cmDivide(siGreen2 - siGreen1,
nuAny))
siCurrentBlue = smallInt(siBlue1 +
cmDivide(siBlue2 - siBlue1,
nuAny))
SetRect(liColorRect,
liLeft,
liY,
liRight,
liY + 1)
liBrush = CreateSolidBrush(rgb(siCurrentRed,
siCurrentGreen,
siCurrentBlue))
liPreviousBrush = SelectObject(liDeviceContext,liBrush)
FillRect(liDeviceContext,
liColorRect,
liBrush)
SelectObject(liDeviceContext,
liPreviousBrush)
DeleteObject(liBrush)
endFor
endProc
Proc cmDivide(nuX1 Number,
nuX2 Number) Number
return iif(nuX2 = 0,0,nuX1 / nuX2)
endProcBefore we can use our low-level gradient routines, some setup work is necessary.Identify portion of screen to be painted We will support any UIObject as a target object to be painted, extracting the underlying screen position in twips converted to pixels and identified as a Windows rectangle. Assume uiAny is our UIObject target object. var liColorRect LongInt liLeft LongInt liRight LongInt liTop LongInt liBottom LongInt ptAny Point endVar ; ; Allocate Windows rectangle structure ; liColorRect = cmGlobalAlloc(16) ; ; Identify portion of screen to be painted ; ptAny = uiAny.Position ptAny = twipsToPixels(ptAny) liLeft = ptAny.x() liTop = ptAny.y() ptAny = uiAny.Size ptAny = twipsToPixels(ptAny) liRight = ptAny.x() + liLeft liBottom = ptAny.y() + liTopGet Windows Device Context for GDI paint operations Windows GDI painting operations require a device context handle. The client portion of our Paradox form is used. Assume that liWindowClientHandle is a LongInt Type returned from OPAL method windowClientHandle(). var liDeviceContext LongInt endVar ; ; Retrieve device context for gdi operations ; liDeviceContext = GetDC(liWindowClientHandle)Get RGB values for our gradient colors Assume liFromColor and liToColor are our gradient colors and defined as a LongInt Type. var
siRed1 SmallInt
siGreen1 SmallInt
siBlue1 SmallInt
siRed2 SmallInt
siGreen2 SmallInt
siBlue2 SmallInt
endVar
;
; Get RGB values for our gradient colors
;
liFromColor.getRGB(siRed1,
siGreen1,
siBlue1)
liToColor.getRGB(siRed2,
siGreen2,
siBlue2)Putting everything together in one method and adding an option for requesting a vertical or horizontal gradient looks like: method PaintGradient(liFromColor LongInt,
liToColor LongInt,
liWindowClientHandle LongInt,
var uiAny UIObject,
loPaintTopToBottom Logical)
;
; Paint a color gradient on UIObject
;
var
liColorRect LongInt
liLeft LongInt
liRight LongInt
liTop LongInt
liBottom LongInt
liDeviceContext LongInt
siRed1 SmallInt
siGreen1 SmallInt
siBlue1 SmallInt
siRed2 SmallInt
siGreen2 SmallInt
siBlue2 SmallInt
ptAny Point
endVar
liColorRect = cmGlobalAlloc(16)
;
; Identify portion of screen to be painted
;
ptAny = uiAny.Position
ptAny = twipsToPixels(ptAny)
liLeft = ptAny.x()
liTop = ptAny.y()
ptAny = uiAny.Size
ptAny = twipsToPixels(ptAny)
liRight = ptAny.x() + liLeft
liBottom = ptAny.y() + liTop
;
; Retrieve device context for gdi operations
;
liDeviceContext = GetDC(liWindowClientHandle)
;
; Get RGB values for our gradient colors
;
liFromColor.getRGB(siRed1,
siGreen1,
siBlue1)
liToColor.getRGB(siRed2,
siGreen2,
siBlue2)
;
; Paint gradient
;
delayScreenUpdates(Yes)
switch
case loPaintTopToBottom = True :
cmPaintGradientVertical(siRed1,
siGreen1,
siBlue1,
siRed2,
siGreen2,
siBlue2,
liLeft,
liRight,
liTop,
liBottom,
liColorRect,
liDeviceContext)
otherwise :
cmPaintGradientHorizontal(siRed1,
siGreen1,
siBlue1,
siRed2,
siGreen2,
siBlue2,
liLeft,
liRight,
liTop,
liBottom,
liColorRect,
liDeviceContext)
endSwitch
delayScreenUpdates(No)
cmGlobalFree(liColorRect)
endMethodOn our example logon form (a modal dialog), we have two boxes identified - RightDivider and LeftDivider which as positioned next to each other to form a separator band across the width of the form. We will take our gradient colors and paint a horizontal gradient across LeftDivider, reverse the colors and paint a horizontal gradient across RightDivider. The end result will be a band across the form that begins and ends with one color with the middle portion being another color all progressing smoothly from left to right.![]() Conclusion We now have methods that provide basic painting of gradients. Use the parts that work for you and spice up your forms remembering the restrictions noted in the beginning. If you add or improve to what is shown here, share it with the rest of us. From my Paradox toolbox to yours! Rick Kelly |
|
| Last Updated ( 24 February 2005 ) |
| < Prev | Next > |
|---|






