![]() |
![]() |
|
![]() |
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) endMethod Determine 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 endSwitch Painting 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 Discussion of this article |
![]() Feedback | Paradox Day | Who Uses Paradox | I Use Paradox | Downloads ![]() |
|
![]() The information provided on this Web site is not in any way sponsored or endorsed by Corel Corporation. Paradox is a registered trademark of Corel Corporation. ![]() |
|
![]() Modified: 03 Jul 2004 Terms of Use / Legal Disclaimer ![]() |
![]() Copyright © 2001- 2004 Paradox Community. All rights reserved. Company and product names are trademarks or registered trademarks of their respective companies. Authors hold the copyrights to their own works. Please contact the author of any article for details. ![]() |
![]() |
|