GradientClass and example added to ClarionClasses repo

I have had this class around for ages and in fact stopped using it a while back, opting instead for more minimalist approach to styling, but I noticed the topic popped up again on the newsgroups and in skype chat so figured it was worth a share!

  • It requires that you are using ABC. It shouldn’t be hard to decouple from the WindowComponent, it’s just a convenience for simple implementation!
  • You would probably also want to add the correct link/dll flags if you are going to use it in a multi-dll scenario.
  • It does not do everything but was sufficient for my needs at the time, hopefully it is useful to someone! </disclaimer>
  • Pull Requests welcomed!!! (repo is linked below)

Implementation is dead simple:

Gradient.Init(SELF, GRADIENT_FILL_RECT_H, 000b5f8h, 0bbeafch)

That’s it!

Result is:

Mmmm… :hotdog:

Classes are found in the Fushnisoft ClarionClasses github repo.

Ok how we dont know ABC very well but does anyone know where we find examples of the classes for what AI is called , gradient animation with RGB interpolation. This is what its calling the code in the module . C:\Clarion n \LibSrc\win\ABDOCK.CLW.. this look very interesting for rending some nice styles in standard clarion plain looking windows? This from AI…

Relevant Discovery: ABDOCK Gradient & Color Animation Framework

This file provides the foundation for Windows 11-style gradient border effects:

Key Components:

1. Gradient Properties Already Implemented:

clarion

SELF.BkRegion{PROP:GradientType} = SELF.GetGradientStyle(false)
SELF.BkRegion{PROP:GradientFromColor} = SELF.DockBackgroundColor1
SELF.BkRegion{PROP:GradientToColor} = SELF.DockBackgroundColor2

2. Color Mixing Algorithm:

clarion

DockToolbar.ToolMixColors PROCEDURE(LONG pColor1, LONG pColor2, BYTE pPercentageMix)
    RetRGBT.R = (RGBT1.R + ((RGBT2.R - RGBT1.R) * pPercentageMix/100))
    RetRGBT.G = (RGBT1.G + ((RGBT2.G - RGBT1.G) * pPercentageMix/100))
    RetRGBT.B = (RGBT1.B + ((RGBT2.B - RGBT1.B) * pPercentageMix/100))
    RETURN RetCWColor

3. Automatic Theme-Aware Colors:

clarion

DockToolbar.SetAutomaticColors PROCEDURE()
    IF SYSTEM{PROP:ThemeActive} = TRUE
        SELF.DockBackgroundColor1 = SELF.ToolMixColors(COLOR:WHITE,COLOR:ACTIVECAPTION,10)
        SELF.DockBackgroundColor2 = SELF.ToolMixColors(COLOR:WHITE,COLOR:ACTIVECAPTION,50)
        SELF.GripColor = SELF.DockShadowColor1
        SELF.GripShadowColor = SELF.ToolMixColors(COLOR:WHITE,SELF.GripColor,20)

4. Available Gradient Types:

  • GradientTypes:VerticalCylinder (Luna themes)
  • GradientTypes:Horizontal
  • GradientTypes:Vertical
  • GradientTypes:HorizontalCylinder
  • GradientTypes:DiagonalTopLeft/Right/BottomLeft/Right

Implementation Approach for Animated Border:

Combine ABDOCK’s gradient system with timer-based color cycling from UBS3.clw:

  1. Use ToolMixColors() to generate color progression array
  2. Apply PROP:GradientFromColor and PROP:GradientToColor on EVENT:Timer
  3. Cycle through color mix percentages (0-100%) for smooth transitions
  4. Use GetGradientStyle() to switch gradient directions dynamically

The technical term is gradient animation with RGB interpolation.

Ok Nice now just mix the colors with runtime color pickers may be using a virtual list box and get some nice styling windows..without using a single API call..

AI generating and playing with clarion gradients.

   OF EVENT:Timer
            
            ! Random ripple trigger - cycle through colors
            IF RANDOM(1, 500) = 1 THEN
                RippleActive = 1
                RipplePosition = 0
                RippleColorIndex += 1
                IF RippleColorIndex > 3 THEN
                    RippleColorIndex = 1
                END
            END
                        
            ! Vibrant panel animation
            AnimationStep += 1
            IF AnimationStep > 200
                AnimationStep = 0
                ColorOffset += 1
                IF ColorOffset > 8 THEN 
                    ColorOffset = 0 
                END
            END
            
            MixPercent += Direction
            IF MixPercent >= 100
                MixPercent = 100
                Direction = -1
            ELSIF MixPercent <= 0
                MixPercent = 0
                Direction = 1
            END

            ! Subtle taskbar animation
            TaskbarAnimStep += 1
            IF TaskbarAnimStep > 400
                TaskbarAnimStep = 0
                TaskbarColorOffset += 1
                IF TaskbarColorOffset > 7 THEN
                    TaskbarColorOffset = 0
                END
                ! Toggle flow direction each cycle
                TaskbarFlowDirection = -TaskbarFlowDirection
            END
            
            IF TaskbarAnimStep % 4 = 0
                TaskbarMixPercent += TaskbarDirection
                IF TaskbarMixPercent >= 100
                    TaskbarMixPercent = 100
                    TaskbarDirection = -1
                ELSIF TaskbarMixPercent <= 0
                    TaskbarMixPercent = 0
                    TaskbarDirection = 1
                END
            END

            ! Calculate BASE taskbar colors first
            TaskbarBaseColor1 = MixColors(TaskbarColorArray[TaskbarColorOffset + 1], TaskbarColorArray[((TaskbarColorOffset + 1) % 8) + 1], TaskbarMixPercent)
            TaskbarBaseColor2 = MixColors(TaskbarColorArray[((TaskbarColorOffset + 2) % 8) + 1], TaskbarColorArray[((TaskbarColorOffset + 3) % 8) + 1], TaskbarMixPercent)

            ! Apply ripple to base colors
            IF RippleActive
                RipplePosition += RippleSpeed
                IF RipplePosition > 200
                    RippleActive = 0
                    RipplePosition = 0
                ELSE
                    IF RipplePosition <= 100
                        ! Flash current ripple color
                        IF TaskbarFlowDirection = 1
                            ?TaskbarGradient{PROP:GradientFromColor} = MixColors(TaskbarBaseColor1, RippleColorArray[RippleColorIndex], RipplePosition)
                            ?TaskbarGradient{PROP:GradientToColor} = MixColors(TaskbarBaseColor2, RippleColorArray[RippleColorIndex], RipplePosition)
                        ELSE
                            ?TaskbarGradient{PROP:GradientFromColor} = MixColors(TaskbarBaseColor2, RippleColorArray[RippleColorIndex], RipplePosition)
                            ?TaskbarGradient{PROP:GradientToColor} = MixColors(TaskbarBaseColor1, RippleColorArray[RippleColorIndex], RipplePosition)
                        END
                    ELSE
                        ! Fade back
                        IF TaskbarFlowDirection = 1
                            ?TaskbarGradient{PROP:GradientFromColor} = MixColors(TaskbarBaseColor1, RippleColorArray[RippleColorIndex], 200 - RipplePosition)
                            ?TaskbarGradient{PROP:GradientToColor} = MixColors(TaskbarBaseColor2, RippleColorArray[RippleColorIndex], 200 - RipplePosition)
                        ELSE
                            ?TaskbarGradient{PROP:GradientFromColor} = MixColors(TaskbarBaseColor2, RippleColorArray[RippleColorIndex], 200 - RipplePosition)
                            ?TaskbarGradient{PROP:GradientToColor} = MixColors(TaskbarBaseColor1, RippleColorArray[RippleColorIndex], 200 - RipplePosition)
                        END
                    END
                END
            ELSE
                ! No ripple - use base colors with current flow direction
                IF TaskbarFlowDirection = 1
                    ?TaskbarGradient{PROP:GradientFromColor} = TaskbarBaseColor1
                    ?TaskbarGradient{PROP:GradientToColor} = TaskbarBaseColor2
                ELSE
                    ?TaskbarGradient{PROP:GradientFromColor} = TaskbarBaseColor2
                    ?TaskbarGradient{PROP:GradientToColor} = TaskbarBaseColor1
                END
            END
            
            ! Update vibrant panel gradients
            ?PANEL1{PROP:GradientFromColor} = ColorArray[((ColorOffset + 0) % 9) + 1]
            ?PANEL1{PROP:GradientToColor} = ColorArray[((ColorOffset + 1) % 9) + 1]

            ?PANEL2{PROP:GradientFromColor} = ColorArray[((ColorOffset + 2) % 9) + 1]
            ?PANEL2{PROP:GradientToColor} = ColorArray[((ColorOffset + 3) % 9) + 1]

            ?PANEL3{PROP:GradientFromColor} = ColorArray[((ColorOffset + 4) % 9) + 1]
            ?PANEL3{PROP:GradientToColor} = ColorArray[((ColorOffset + 5) % 9) + 1]

            ?PANEL4{PROP:GradientFromColor} = ColorArray[((ColorOffset + 6) % 9) + 1]
            ?PANEL4{PROP:GradientToColor} = ColorArray[((ColorOffset + 7) % 9) + 1]

            ?PANEL5{PROP:GradientFromColor} = MixColors(ColorArray[ColorOffset + 1], ColorArray[((ColorOffset + 1) % 9) + 1], MixPercent)
            ?PANEL5{PROP:GradientToColor} = ColorArray[((ColorOffset + 2) % 9) + 1]

            ?PANEL6{PROP:GradientFromColor} = MixColors(ColorArray[((ColorOffset + 3) % 9) + 1], ColorArray[((ColorOffset + 4) % 9) + 1], MixPercent)
            ?PANEL6{PROP:GradientToColor} = ColorArray[((ColorOffset + 5) % 9) + 1]

            ?PANEL7{PROP:GradientFromColor} = ColorArray[((ColorOffset + 6) % 9) + 1]
            ?PANEL7{PROP:GradientToColor} = MixColors(ColorArray[((ColorOffset + 7) % 9) + 1], ColorArray[((ColorOffset + 8) % 9) + 1], MixPercent)

            DISPLAY()
        END
    END

Attach the code files in a Zip so we can easily try it, i.e. Clw, App, CwProj, etc

PROGRAM
 

    MAP
      INCLUDE('CWUTIL.INC'),ONCE
       LMixColors(LONG Color1, LONG Color2, BYTE Percentage), LONG
       INCLUDE('CWUTIL.INC'),ONCE
    
    END

Window              WINDOW('Gradients AI Example '),AT(,,395,347),GRAY, |
                        SYSTEM,FONT('Calibri',9,COLOR:Navy,FONT:regular),TIMER(5), |
                       Centered,HVSCROLL
                        BUTTON('&OK'),AT(299,325,41,14),USE(?OkButton),DEFAULT
                        BUTTON('&Cancel'),AT(342,325,42,14),USE(?CancelButton),STD(STD:Close)
                        BOX,AT(0,0,395,74),USE(?TaskbarGradient) 
                        BOX,AT(7,83,375,35),USE(?PANEL1) 
                        BOX,AT(7,129,145,35),USE(?PANEL2) 
                        BOX,AT(176,129,145,35),USE(?PANEL3)
                        BOX,AT(7,173,375,35),USE(?PANEL4)
                        BOX,AT(7,222,104,25),USE(?PANEL5)
                        BOX,AT(124,222,104,25),USE(?PANEL6)
                        BOX,AT(255,222,104,25),USE(?PANEL7)
                        GROUP,AT(12,2,369,64),USE(?GROUPTaskBar),BOXED
                            BUTTON('Button1'),AT(23,12),USE(?BUTTON1),FLAT,TRN
                            LIST,AT(110,14,249,44),USE(?LIST1),TRN,FLAT,HVSCROLL,VCR,FROM('TEST|TEST'), |
                                FORMAT('63L(2)|M~Col 1~20L(2)|M~Col 2~')
                        END
                    END

! Vibrant colors
ColorArray          LONG,DIM(9)
ColorOffset         BYTE(0)
AnimationStep       BYTE(0)
MixPercent          BYTE(0)
Direction           BYTE(1)

! Taskbar colors
TaskbarColorArray   LONG,DIM(8)
TaskbarColorOffset  BYTE(0)
TaskbarAnimStep     LONG(0)
TaskbarMixPercent   BYTE(0)
TaskbarDirection    BYTE(1)
TaskbarBaseColor1   LONG(0)
TaskbarBaseColor2   LONG(0)

! Ripple with color cycling
RippleActive        BYTE(0)
RipplePosition      LONG(0)
RippleSpeed         BYTE(2)
RippleColorArray    LONG,DIM(3)
RippleColorIndex    BYTE(0)
TaskbarFlowDirection BYTE(1)

! Screen capture variables
CaptureCounter      LONG(0)
CaptureDir          STRING('captures')

  CODE
        
     createdirectory(longpath()&'\'&CaptureDir) 
    
    ! Vibrant colors for panels
    ColorArray[1] = 0FF0000h
    ColorArray[2] = 0FF7F00h
    ColorArray[3] = 0FFFF00h
    ColorArray[4] = 000FF00h
    ColorArray[5] = 000FFFFh
    ColorArray[6] = 00000FFh
    ColorArray[7] = 08B00FFh
    ColorArray[8] = 0FF00FFh
    ColorArray[9] = 0FF1493h

    ! Subtle taskbar colors
    TaskbarColorArray[1] = 0FFE4C4h  ! Bisque
    TaskbarColorArray[2] = 0E0E8FFh  ! Light coral
    TaskbarColorArray[3] = 0FFFFE0h  ! Light yellow
    TaskbarColorArray[4] = 0E6E6FAh  ! Lavender
    TaskbarColorArray[5] = 0F0E68Ch  ! Khaki
    TaskbarColorArray[6] = 0EED5B7h  ! Pale turquoise
    TaskbarColorArray[7] = 0FFB6C1h  ! Light pink
    TaskbarColorArray[8] = 0E0FFFFh  ! Light cyan

    ! Ripple colors (BGR format)
    RippleColorArray[1] = 0FFCC99h  ! Brighter pale blue
    RippleColorArray[2] = 0FFFFB0h  ! Powder blue
    RippleColorArray[3] = 0FFE0C0h  ! Sky blue

    OPEN(Window)

    ! Initialize taskbar gradient
    ?TaskbarGradient{PROP:GradientFromColor} = TaskbarColorArray[1]
    ?TaskbarGradient{PROP:GradientToColor} = TaskbarColorArray[2]
    ?TaskbarGradient{PROP:GradientType} = GradientTypes:Horizontal

    ?PANEL1{PROP:GradientFromColor} = ColorArray[1]
    ?PANEL1{PROP:GradientToColor} = ColorArray[2]
    ?PANEL1{PROP:GradientType} = GradientTypes:Horizontal

    ?PANEL2{PROP:GradientFromColor} = ColorArray[3]
    ?PANEL2{PROP:GradientToColor} = ColorArray[4]
    ?PANEL2{PROP:GradientType} = GradientTypes:Vertical

    ?PANEL3{PROP:GradientFromColor} = ColorArray[5]
    ?PANEL3{PROP:GradientToColor} = ColorArray[6]
    ?PANEL3{PROP:GradientType} = GradientTypes:HorizontalCylinder

    ?PANEL4{PROP:GradientFromColor} = ColorArray[7]
    ?PANEL4{PROP:GradientToColor} = ColorArray[8]
    ?PANEL4{PROP:GradientType} = GradientTypes:VerticalCylinder

    ?PANEL5{PROP:GradientFromColor} = ColorArray[1]
    ?PANEL5{PROP:GradientToColor} = ColorArray[2]
    ?PANEL5{PROP:GradientType} = GradientTypes:DiagonalTopLeft

    ?PANEL6{PROP:GradientFromColor} = ColorArray[3]
    ?PANEL6{PROP:GradientToColor} = ColorArray[4]
    ?PANEL6{PROP:GradientType} = GradientTypes:DiagonalTopRight

    ?PANEL7{PROP:GradientFromColor} = ColorArray[5]
    ?PANEL7{PROP:GradientToColor} = ColorArray[6]
    ?PANEL7{PROP:GradientType} = GradientTypes:Horizontal

    ACCEPT
        CASE EVENT()
        OF EVENT:Timer
            
            ! Random ripple trigger - cycle through colors
            IF RANDOM(1, 500) = 1 THEN
                RippleActive = 1
                RipplePosition = 0
                RippleColorIndex += 1
                IF RippleColorIndex > 3 THEN
                    RippleColorIndex = 1
                END
            END
                        
            ! Vibrant panel animation
            AnimationStep += 1
            IF AnimationStep > 200
                AnimationStep = 0
                ColorOffset += 1
                IF ColorOffset > 8 THEN 
                    ColorOffset = 0 
                END
            END
            
            MixPercent += Direction
            IF MixPercent >= 100
                MixPercent = 100
                Direction = -1
            ELSIF MixPercent <= 0
                MixPercent = 0
                Direction = 1
            END

            ! Subtle taskbar animation
            TaskbarAnimStep += 1
            IF TaskbarAnimStep > 400
                TaskbarAnimStep = 0
                TaskbarColorOffset += 1
                IF TaskbarColorOffset > 7 THEN
                    TaskbarColorOffset = 0
                END
                ! Toggle flow direction each cycle
                TaskbarFlowDirection = -TaskbarFlowDirection
            END
            
            IF TaskbarAnimStep % 4 = 0
                TaskbarMixPercent += TaskbarDirection
                IF TaskbarMixPercent >= 100
                    TaskbarMixPercent = 100
                    TaskbarDirection = -1
                ELSIF TaskbarMixPercent <= 0
                    TaskbarMixPercent = 0
                    TaskbarDirection = 1
                END
            END

            ! Calculate BASE taskbar colors first
            TaskbarBaseColor1 = lMixColors(TaskbarColorArray[TaskbarColorOffset + 1], |
                TaskbarColorArray[((TaskbarColorOffset + 1) % 8) + 1], TaskbarMixPercent)
            TaskbarBaseColor2 = lMixColors(TaskbarColorArray[((TaskbarColorOffset + 2) % 8) + 1], |
                TaskbarColorArray[((TaskbarColorOffset + 3) % 8) + 1], TaskbarMixPercent)

            ! Apply ripple to base colors
            IF RippleActive
                RipplePosition += RippleSpeed
                IF RipplePosition > 200
                    RippleActive = 0
                    RipplePosition = 0
                ELSE
                    IF RipplePosition <= 100
                        ! Flash current ripple color
                        IF TaskbarFlowDirection = 1
                            ?TaskbarGradient{PROP:GradientFromColor} = lMixColors(TaskbarBaseColor1, |
                                RippleColorArray[RippleColorIndex], RipplePosition)
                            ?TaskbarGradient{PROP:GradientToColor} = lMixColors(TaskbarBaseColor2, |
                                RippleColorArray[RippleColorIndex], RipplePosition)
                        ELSE
                            ?TaskbarGradient{PROP:GradientFromColor} = lMixColors(TaskbarBaseColor2, |
                                RippleColorArray[RippleColorIndex], RipplePosition)
                            ?TaskbarGradient{PROP:GradientToColor} = lMixColors(TaskbarBaseColor1, |
                                RippleColorArray[RippleColorIndex], RipplePosition)
                        END
                    ELSE
                        ! Fade back
                        IF TaskbarFlowDirection = 1
                            ?TaskbarGradient{PROP:GradientFromColor} = lMixColors(TaskbarBaseColor1, |
                                RippleColorArray[RippleColorIndex], 200 - RipplePosition)
                            ?TaskbarGradient{PROP:GradientToColor} = lMixColors(TaskbarBaseColor2, |
                                RippleColorArray[RippleColorIndex], 200 - RipplePosition)
                        ELSE
                            ?TaskbarGradient{PROP:GradientFromColor} = lMixColors(TaskbarBaseColor2, |
                                RippleColorArray[RippleColorIndex], 200 - RipplePosition)
                            ?TaskbarGradient{PROP:GradientToColor} = lMixColors(TaskbarBaseColor1, |
                                RippleColorArray[RippleColorIndex], 200 - RipplePosition)
                        END
                    END
                END
            ELSE
                ! No ripple - use base colors with current flow direction
                IF TaskbarFlowDirection = 1
                    ?TaskbarGradient{PROP:GradientFromColor} = TaskbarBaseColor1
                    ?TaskbarGradient{PROP:GradientToColor} = TaskbarBaseColor2
                ELSE
                    ?TaskbarGradient{PROP:GradientFromColor} = TaskbarBaseColor2
                    ?TaskbarGradient{PROP:GradientToColor} = TaskbarBaseColor1
                END
            END
            
            ! Update vibrant panel gradients
            ?PANEL1{PROP:GradientFromColor} = ColorArray[((ColorOffset + 0) % 9) + 1]
            ?PANEL1{PROP:GradientToColor} = ColorArray[((ColorOffset + 1) % 9) + 1]

            ?PANEL2{PROP:GradientFromColor} = ColorArray[((ColorOffset + 2) % 9) + 1]
            ?PANEL2{PROP:GradientToColor} = ColorArray[((ColorOffset + 3) % 9) + 1]

            ?PANEL3{PROP:GradientFromColor} = ColorArray[((ColorOffset + 4) % 9) + 1]
            ?PANEL3{PROP:GradientToColor} = ColorArray[((ColorOffset + 5) % 9) + 1]

            ?PANEL4{PROP:GradientFromColor} = ColorArray[((ColorOffset + 6) % 9) + 1]
            ?PANEL4{PROP:GradientToColor} = ColorArray[((ColorOffset + 7) % 9) + 1]

            ?PANEL5{PROP:GradientFromColor} = lMixColors(ColorArray[ColorOffset + 1], |
                ColorArray[((ColorOffset + 1) % 9) + 1], MixPercent)
            ?PANEL5{PROP:GradientToColor} = ColorArray[((ColorOffset + 2) % 9) + 1]

            ?PANEL6{PROP:GradientFromColor} = lMixColors(ColorArray[((ColorOffset + 3) % 9) + 1], |
                ColorArray[((ColorOffset + 4) % 9) + 1], MixPercent)
            ?PANEL6{PROP:GradientToColor} = ColorArray[((ColorOffset + 5) % 9) + 1]

            ?PANEL7{PROP:GradientFromColor} = ColorArray[((ColorOffset + 6) % 9) + 1]
            ?PANEL7{PROP:GradientToColor} = lMixColors(ColorArray[((ColorOffset + 7) % 9) + 1], |
                ColorArray[((ColorOffset + 8) % 9) + 1], MixPercent)

            DISPLAY()

         
            
        OF EVENT:Accepted
            CASE FIELD()
            OF ?OkButton
                BREAK
            END
            
        OF EVENT:CloseWindow
            BREAK
            
       
        
        END
    END

    CLOSE(Window)

lMixColors           PROCEDURE(LONG Color1, LONG Color2, BYTE Percentage)
CWColor1              LONG
RGBT1                 GROUP,OVER(CWColor1)
R                       BYTE
G                       BYTE
B                       BYTE
NotUsed                 BYTE
                      END
CWColor2              LONG
RGBT2                 GROUP,OVER(CWColor2)
R                       BYTE
G                       BYTE
B                       BYTE
NotUsed                 BYTE
                      END
RetCWColor            LONG
RetRGBT               GROUP,OVER(RetCWColor)
R                       BYTE
G                       BYTE
B                       BYTE
NotUsed                 BYTE
                      END

  CODE
    IF Percentage > 100 THEN 
        Percentage = 100
    END
    IF Percentage < 0 THEN 
        Percentage = 0
    END
    
    CWColor1 = Color1
    CWColor2 = Color2

    RetRGBT.R = (RGBT1.R + ((RGBT2.R - RGBT1.R) * Percentage / 100))
    RetRGBT.G = (RGBT1.G + ((RGBT2.G - RGBT1.G) * Percentage / 100))
    RetRGBT.B = (RGBT1.B + ((RGBT2.B - RGBT1.B) * Percentage / 100))

    RETURN(RetCWColor)