in a previous post i showed a class to generate "aqua" buttons using gdi+x from codeplex.

this post is all about some modifications i made to that class to enable me to produce shiny glass buttons. also i did not explain the techniques in obtaining the effect. this post will fix that and i will try to explain as best i can, the inner workings of the class.

the glass button

basically the class uses a subclassed imgcanvas control from the gdiplus.vcx classes that you need to download from the link above. the classes init should not be touched. instead use the setup method to run any code you'd normally run in the init.

.setup

this.originalht = this.height

this.originalwidth = this.width

* resize to fit the shadow

this.width = this.width + this.shadowoffset

this.height = this.height + this.shadowoffset

the original height and width are saved and the canvas is increased to allow the shadow to display. this must be done in the setup method.

.beforedraw

here's where the magic happens.

* declarations

local logfx as xfcgraphics

local lobrush1 as xfcbrush

local lobrush2 as xfcbrush

local lobrush3 as xfcbrush

local lobrush4 as xfcbrush

local lorect1 as xfcrectangle

local lorect2 as xfcrectangle

local lorect3 as xfcrectangle

local path1 as xfcpath

local path2 as xfcpath

local path3 as xfcpath

local lofont as xfcfont

local lostringformat as xfcstringformat

logfx = this.ogfx

local nbuttoncolour1 as integer, nbuttoncolour2 as integer, nbuttoncolour3 as integer, nbuttoncolour4 as integer

with _screen.system.drawing

the canvas is now cleared to the parents background colour which means that these buttons can be placed in a container as well.

* clear

logfx.clear(.color.fromrgb(this.parent.backcolor))

* read in the colours from control's the properties

nbuttoncolour1 = .color.fromrgb(this.pnbuttoncolour1)

nbuttoncolour2 = .color.fromrgb(this.pnbuttoncolour2)

* this is one of the options of the drawing2d.smoothingmode

logfx.smoothingmode = .drawing2d.smoothingmode.antialias

the main button is drawn here. a rectangle to define its position and size is created.

* main button

lorect1 = .rectangle.new(this.btnoffset,this.btnoffset,this.originalwidth, this.originalht)

the getpath method, just plots a path with the button's dimensions, but rounds the corners as per the curvature set in the  pnbtncurvature property

path1 = this.getpath(lorect1, this.pnbtncurvature) && second param less = less curve

this code flips the colours used based on the mouseenter and mouseleave and creates the gardient brush to draw.

if this.hover

lobrush1 = .drawing2d.lineargradientbrush.new(this.rectangle, nbuttoncolour2,nbuttoncolour1,90,.t.)

else

lobrush1 = .drawing2d.lineargradientbrush.new(this.rectangle, nbuttoncolour1,nbuttoncolour2,90,.t.)

endif

lobrush1.gammacorrection = this.hover

the button normal and hover(below) states

* shadow

the same rectangle used above is used here except we offset the left and top by the shadowoffset. this is the reason we resized the object in .setup. so that the shadow would be visible. a pathgradient brush is used.

lorect2 = lorect1

lorect2.offset(this.shadowoffset, this.shadowoffset)

path2 = this.getpath(lorect2, this.pnbtncurvature)

lobrush2 = .drawing2d.pathgradientbrush.new(path2)

lobrush2.centercolor = .color.black

lobrush2.surroundcolors = .color.fromrgb(this.parent.backcolor) && fade into background

here's what the shadow looks like when drawn

* lighter top half

lorect3 = .rectangle.new(this.btnoffset+2,this.btnoffset+2,this.originalwidth -(this.btnoffset+4),;

this.originalht/2 + this.btnoffset)

path3 = this.getpath(lorect3, this.pnbtncurvature,.t.)

nbuttoncolour4 = .color.fromrgb(this.pnbuttoncolour4)

nbuttoncolour3 = .color.fromrgb(this.pnbuttoncolour3)

if this.hover

lobrush3 = .drawing2d.lineargradientbrush.new(lorect3,nbuttoncolour4,;

nbuttoncolour3, .drawing2d.lineargradientmode.vertical)

else

lobrush3 = .drawing2d.lineargradientbrush.new(lorect3,nbuttoncolour3,;

nbuttoncolour4, .drawing2d.lineargradientmode.vertical)

endif

same as above except the rectangle is changed and the curvature for the bottom 2 curves is doubled for effect.

this is the result of the above code:

normal state (above) and hover (below)

the 3 object and then drawn in this sequence.

logfx.fillpath(lobrush2, path2) && shadow

logfx.fillpath(lobrush1, path1) && dark main

logfx.fillpath(lobrush3, path3)

the above 3 lines actually draw the images onto the imgcanvas(ignore the text for now)

finally the text is drawn using the standard gdi+x code

* draw the text

nbuttonfontcolour = .color.fromrgb(this.pnbuttonfontcolour)

lobrush4 = .solidbrush.new(nbuttonfontcolour)

lorect = .rectangle.new(this.btnoffset, this.btnoffset, this.width-this.shadowoffset,this.height-this.shadowoffset)

* get a basic string format object, then set properties

lostringformat = .stringformat.new()

lostringformat.alignment = .stringalignment.center

lostringformat.linealignment =.stringalignment.center

* create a font object

lofont = .font.new(this.pcbuttonfontname,this.pnbuttonfontsize, .fontstyle.bold, .graphicsunit.point)

* draw it

logfx.drawstring(this.pcbuttoncaption , ;

lofont, lobrush4, lorect, lostringformat)

endwith

this code produces the button below

with the same button in "hover" state.

i have also atached code to the mouseenter/mouseleave, mousedown/mouseup to handle these states and give a visual feeedback. the hover effect, just flips the gradient colours. the mousedown/mouseup redefines the rectangle with an offset giving the appearance of the button being pressed and released.

so its just a matter of dropping the class on a form and sizing it to your needs.

i have made most changable properties favourites so those are the only ones that need to be changed.

to assist, i have also designed and attached a live builder. this builder is just a form class that is called from the memberdata script of the custom property aaglassbtnbuilder with this script code. this code is very generic, so if ever you need to attach a custom builder you can modify this snippet for calling your own builder. also check out the code in the builder for how the properties are read and set.

memberdata editor with script code.

the clicking on the builder button displays the builder which has a live preview. make your changes and click "apply" to update the class properties and update the preview. the preview is exactly how your button will finally look at runtime.

here are some samples i created with this class.

also note that just by changing the colours used and the curvature of the corners you can produce a huge variety of spectacular glass buttons. here are a few more i created just by changing the colour sequence and the curvature.

as usual the full source code for the class and the builder are included in the attached file. download and create your own glass buttons courtesy of gdi+x and me. i have not included any sample form in the attachment.

only the class is included but then that is all you need. that and some imagination on your part. the rest is done by my class and of course the gdi+xclasses.

note:

as mentioned in other posts,

i dont include the gdiplusc.vcx since you need to download the latest from the vfpx site.

just open the form/class in vfp and when it asks you for the imgcanvas, navigate it to the gdiplusx.vcx on your computer. the form/class will then resolve the location correctly. save the form/class, set the default path to the directory where your form is (to find any images) and then run the forms. i have subclassed the imgcanvas class from the gdiplusx.vcx class library. unfortunately this would happen since i don't supply the gdiplusx.vcx and i don't know where a user might have stored it.

 

unfortunately because of the amount of junk mail being generated from the weblog i have switched off comments. please post your comments, if any, at www.foxite.com

9 Responses to Shiny Glass Buttons anyone?

  • Jun Tangunan says:

    Bernard, you really are among the masters of GUI!  It is awesome!

    Thanks Jun. Glad you like it.

  • Cesar says:

    Brilliant design Bernard !
    Congratulations!

    Hey Thanks Cesar. Smile <img src=” src=”/emoticons/emotion-1.gif”> That must mean I am getting better at it.

  • Horacio says:

    Bernrad  ¡¡¡ Espectacular !!!

    Gracias por esforzarte en tus explicaciones, y compartir estos trabajos.

    Saludos.

  • Norman says:

    Plz i’m crazy about this technic but i can’t download gdi+x

    so plz provide me from u..

    I would if I had your email address.

  • Norman says:

    Bernard, what about glassy transparent button style like vista interface.
    I have seen in c# example. Is it possible in VFP?

    Sure it is. You just need to learn a bit more about GDI+x and you can do it yourself since you now have all the source code.

    The trick is to Clear with a transparent colour and use ARGB instead of RGB for the others and change RenderMode = 4. However these buttons are not shiny anymore so making them transparent is not a good idea as they lose “shinyness” when transparency takes over. Transparent buttons was to be my next post anyway, but not for these buttons.

  • Norman says:

    Hi Bernard.

    I have tested this & realy amazing work.

    If we will be able to put an icon on the button, isn’t it nice idea?

  • Norman says:

    Hi Bernard
    I have improved the class and builder for different hover color efect like ur aquabutton. It works nice. Thanks.
    I want to make further improvement for my work. When we set picture as form’s background, shadow nn more transparent. How to make shadow transparent? Any tips or tricks?

    1. Issue these commands:

    loGfx.Clear(.Color.Transparent)

     instead of

    loGfx.Clear(.Color.FromRGB(This.Parent.BackColor))

    2. Use FromARGB instead of FromRGB :

    loBrush2

    .SurroundColors = .Color.FromARGB(nTransparency, This.Parent.BackColor)

    Where nTransparency is between 0 for full transparent and 255 which is opaque. 

    You can do this with any colour you use and the colour will have this transparency.

    3. Set the RenderMode property = 4 for the ImgCanvas.

    4. Picture as Form’s background is not possible with the current code without some changes. Basically you have to Draw with a texture. The Sample files have code to show you how. In fact they also have code to draw text using a texture. Check it out.

    Bernard

  • Johan Ballet says:

    Each moment I think ‘this is the limit for VFP, VFP can’t do this anymore’, I find out that it is still possible ! Nice thing, this thing !

    Thanks John.

  • Alek says:

    Hi. Good site.

Leave a Reply

Your email address will not be published. Required fields are marked *