-
Dec 9th, 2017, 09:35 AM
#1
Thread Starter
Hyperactive Member
Grdient Button
Hello Group,
Using vbRichClient I want to simply fill a picturebox with a themed color and create a modern looking button based on a theme color. SO the fill will have shades of that color as a gradient.
What is the easiest way to do this with vbrichlient cairo engine? Is thera another way?
I am currently just rendering the button using a jpg like the one attached, but need to make the color configurable.
WP
-
Dec 9th, 2017, 09:58 AM
#2
Re: Grdient Button
"Modern looking???"
I think the word is "gauche and non-Windows looking."
-
Dec 9th, 2017, 10:47 AM
#3
Re: Grdient Button
BTW Cairo-Gradients are explained (linear and radial ones) in detail in the Cairo-Tutorial -
(where they reside in a dedicated Project in a separate SubFolder).
But well, a "boiled down" version for first experiments would be (starting with an empty VB-Project and an empty Form1)...
Every Drawing-Op in Cairo needs a "Surface" first, here's what I often do for a fast test, to visualize such a Surface in the Form.Picture:
Code:
Private Sub Form_Load()
Set Me.Picture = CreateButtonSurface(150, 30).Picture
End Sub
The above assuming, that the CreateButtonSurface-Function will create and return a cCairoSurface-Object
(which in turn can hand-out also a normal VB-StdPicture-Object, above placed in the Forms "Me.Picture"-Prop).
Ok, here's the Function, which creates this Surface-Object
(not drawing anything so far, so you will see only a black Rectangle, in the above given Surface-Dimensions of 150x30 Pixels).
Code:
Function CreateButtonSurface(dx, dy) As cCairoSurface
Dim Srf As cCairoSurface
Set Srf = Cairo.CreateSurface(dx, dy)
'...draw something on the Cairo-Surface
Set CreateButtonSurface = Srf 'return the Surface-Object
End Function
With that little "Base-Setup" in your Form, you can now add a Sub-Routine (or two) below the Comment-Line,
as e.g. one, that is added and called this way from within the above Function:
Code:
'...draw something on the Cairo-Surface
DrawButtonFace Srf.CreateContext
Note, that Cairo-Drawing needs to happen on a so called "Cairo-Context"-Object,
which any Surface will happily provide at any time, per oSurface.CreateContext (as the above line shows).
Within the Drawing-Functions of most of the RichClient-Demos, I use CC as the Parameter-Name of the ContextObject -
so the SubRoutine we already named above could look this way:
Code:
Sub DrawButtonFace(CC As cCairoContext)
Dim Pat As cCairoPattern 'to draw Gradients, we need a cCairoPattern-Type
'create and set up a linear Gradient (using X0,Y0 and X1,Y1 to describe its direction)
Set Pat = Cairo.CreateLinearPattern(0, 0, 0, CC.Surface.Height)
Pat.AddColorStop 0, vbBlue
Pat.AddColorStop 1, vbBlack
'render the Pattern
CC.Paint 1, Pat '<- the first Parameter '1' is an Alpha-Value
End Sub
Putting all of the above together, you will end up with this:
Olaf
-
Dec 9th, 2017, 10:51 AM
#4
Thread Starter
Hyperactive Member
Re: Grdient Button
Thanks Olaf,
Looking at the tutorials, I thought this would be the approach, I just wanted to make sure. I just was not sure about the first part 'CreateButtonSurface'
Thanks for the detailed help
WP
-
Dec 9th, 2017, 12:20 PM
#5
Re: Grdient Button
Originally Posted by axisdj
Looking at the tutorials, I thought this would be the approach, I just wanted to make sure. I just was not sure about the first part 'CreateButtonSurface'
Ok - you can alternatively make use of the built-in Cairo-Theme-Engine of course, which already encapsulates
a whole lot of GUI-rendering-tasks (for Button-Faces, ScrollBar-Faces, ProgressBar-Faces, etc.)
Here the basic-construct from above again, this time with a Cairo-themed Button-Rendering-Routine:
Code:
Option Explicit
Private W As cWidgetBase 'we need a cWidgetBase, to make use of the Theme-Engine...
Private Sub Form_Load()
Set W = Cairo.WidgetBase '<- and so we instantiate it here with Form-wide scope
Set Me.Picture = CreateButtonSurface(150, 30).Picture
End Sub
Function CreateButtonSurface(dx, dy) As cCairoSurface
Dim Srf As cCairoSurface
Set Srf = Cairo.CreateSurface(dx, dy)
'...draw something on the Cairo-Surface
DrawThemedButtonFace Srf.CreateContext, dx, dy 'use the default-theme-color (leaving out the optional BackColor-Param)
Set CreateButtonSurface = Srf 'return the Surface-Object
End Function
Sub DrawThemedButtonFace(CC As cCairoContext, dx, dy, _
Optional ByVal BackColor As Long = -1, _
Optional ByVal State As enmThemeDrawingState, _
Optional ByVal Radius As Double)
With Cairo.Theme '<- let's make use of the Cairo-Theme-Engine
W.BackColor = IIf(BackColor = -1, .GetThemeColor(thmBackColor), BackColor) 'override the the default-Theme-BC if needed
.DrawTo CC, W, thmTypeButtonFace, State, 0, 0, dx, dy, Radius 'render the themed Button-Face
CC.SetLineWidth 1 'finally draw a themed Border with LineWidth=1
.DrawTo CC, W, thmTypeBorder, State, 0, 0, dx, dy, Radius
End With
End Sub
The above default-Cairo-theme (with thmTypeButtonFace) produces this output:
The Theme-Eingine is overridable of course (with your own "general ideas") - but it is also flexible enough,
to allow "individual exceptions" (whilst keeping up the general "rendering-idea").
To check-out what I mean, you could call the above DrawThemedButtonFace with these alternatives:
Code:
' individual (the theme-color-ignoring) BackColor-overrides can be done this way
DrawThemedButtonFace Srf.CreateContext, dx, dy, RGB(77, 244, 77) 'light green
' DrawThemedButtonFace Srf.CreateContext, dx, dy, RGB(244, 244, 77) 'light yellow
' DrawThemedButtonFace Srf.CreateContext, dx, dy, RGB(244, 77, 244) 'light magenta
The above Line which I've left uncommented draws the themed ButtonFace in a light-green then:
But once you work at that level alread - the question arises, why you want to render your Button-Ctl
in a VB.PictureBox... - instead of directly implementing a true Cairo-Widget-Ctl (using less code).
Olaf
-
Dec 9th, 2017, 12:29 PM
#6
Thread Starter
Hyperactive Member
Re: Grdient Button
There ya go, now you are talking... THANK you!
-
Dec 9th, 2017, 01:39 PM
#7
Re: Grdient Button
Originally Posted by axisdj
There ya go, now you are talking... THANK you!
That still leaves my question unanswered, why you want to make your own Button.
Whilst nice Button-Faces are easy to draw with Cairo, one tends to
forget a whole lot of other things we'd expect from a proper Button-Ctl as e.g.:
- correct behaviour with Tabbing
- Focus-Visualizing
- Button-Caption definable Access-Keys (as e.g. with "&The Caption")
- Button- Default and Cancel-Properties, which raise the Click-Event, when Return or Esc is pressed
- Click-Event-Raising when Focused and the Space-Key is pressed
- proper Button-Image-Rendering
If you want to implement all that yourself, you will basically end up with the same Code,
which exists already encapsulated within the vbWidgets-Project.
Below some Form-Code, which shows how "visually adaptable" the cwButton-Widget-Implementation is.
To work with RC5-Cairo-Widgets on a normal VB-Form (interacting with "old" VB-Controls on that Form),
one has to add a normal "StdExe-Project-Private-UserControl" first, which I usually name 'ucPanel':
Code:
Option Explicit 'just a generic Helper-Control, to make Cairo-Widgets usable on normal VB-Forms
Public Root As cWidgetRoot
Public Property Get Widgets() As cWidgets
If Root Is Nothing Then Set Root = Cairo.WidgetRoot: Root.RenderContentIn Me
Set Widgets = Root.Widgets
End Property
Private Sub UserControl_Show()
If Not Root Is Nothing Then Root.Widget.Refresh
End Sub
Public Sub Cleanup()
If Not Root Is Nothing Then Root.Widgets.RemoveAll: Root.Disconnect
End Sub
The above will allow us, to add Cairo-Widgets to it, which you can of course implement yourself
(in your own cwClasses), or you make use of the already implemented ones of vbWidgets.dll.
As said, the following Form-Code demonstrates the cwButton-Widget in action:
(your Test-Project will need references to vbRichClient5 and vbWidgets) - and the above 'ucPanel' as a UserControl-Module)
Code:
Option Explicit
Private WithEvents B1 As cwButton, WithEvents B2 As cwButton, WithEvents B3 As cwButton
Private Sub Form_Load()
Cairo.ImageList.AddIconFromResourceFile "icoB3", "shell32.dll", 167, 48, 48
Set B1 = ucPanel1.Widgets.Add(New cwButton, "B1", 20, 15, 145, 27)
B1.Caption = "&Normal with Image"
B1.Widget.ImageKey = "icoB3"
Set B2 = ucPanel1.Widgets.Add(New cwButton, "B2", 20, 55, 90, 27)
B2.Caption = "&Rectangular"
B2.BorderRadius = 0
B2.Widget.BackColor = vbYellow
Set B3 = ucPanel1.Widgets.Add(New cwButton, "B3", 20, 95, 65, 65)
B3.Caption = "&Circular"
B3.BorderRadius = B3.Widget.Height / 2
B3.Widget.BackColor = vbMagenta
End Sub
Private Sub B1_Click()
MsgBox "Hello From Button1"
End Sub
Private Sub B2_Click()
MsgBox "Hello From Button2"
End Sub
Private Sub B3_Click()
MsgBox "Hello From Button3"
End Sub
Private Sub Form_Resize() 'resize the Panel to cover the entire Form
ucPanel1.Move 0, 0, ScaleWidth, ScaleHeight
End Sub
Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
ucPanel1.Cleanup 'cleanup the Panel and its Widgets
End Sub
Private Sub Form_Terminate()
If Forms.Count = 0 Then New_c.CleanupRichClientDll 'cleanup the RC5-lib when the App terminates
End Sub
Olaf
-
Dec 9th, 2017, 01:59 PM
#8
Thread Starter
Hyperactive Member
Re: Grdient Button
Ok got that working, but the gradient does not seem apparent enough on certain colors... is there a way to amplify the effect?
Also I need to print text on button, is there an auto align way of doing that?
I will look at the example in tutorial, but I thought I would ask here.. sorry it must appear im a bit lazy.
-
Dec 9th, 2017, 02:19 PM
#9
Thread Starter
Hyperactive Member
Re: Grdient Button
"That still leaves my question unanswered, why you want to make your own Button."
To answer your question was using pictureBoxes for all my buttons .. so changing them out may not be an issue. I was just thinking of the safest path for now.
So.. yes I may want to change them out, but it is hundreds of buttons, so for now I would just like the cairo to aid in drawing the gradient and maybe text on top.
Next rev and future buttons will use the cwButton.
WP
-
Dec 9th, 2017, 02:52 PM
#10
Re: Grdient Button
Originally Posted by axisdj
Ok got that working, but the gradient does not seem apparent enough on certain colors... is there a way to amplify the effect?
You can implement your own Cairo-Theme-Override (for thmTypeButtonFace) quite easily (using the Power of VBs Implements-Keyword).
For that you will need a new cMyTheme Class in your Project, with the following Content:
Code:
Option Explicit
Private TOrig As cTheme, MyThemeColorArray(0 To 6) As Long, i As Long
Implements cTheme
Private Sub Class_Initialize()
Set TOrig = New vbWidgets.cThemeWin7 'store the original vbWidgets-Theme-Instance in TOrig
'here you can override the default-Widget-Colors - and the Demo shows a "dark-Theme"
'(the originally implemented Theme-Color-Values are commented out)
MyThemeColorArray(thmBackColor) = &H444444 'RGB(235, 235, 235)
MyThemeColorArray(thmForeColor) = vbYellow 'RGB(30, 30, 30)
MyThemeColorArray(thmBorderColor) = vbWhite ' RGB(103, 103, 98)
MyThemeColorArray(thmHoverColor) = vbGreen ' RGB(190, 228, 255)
MyThemeColorArray(thmFocusColor) = vbMagenta ' RGB(0, 140, 195)
MyThemeColorArray(thmSelectionColor) = vbYellow ' RGB(178, 214, 255)
MyThemeColorArray(thmDisabledColor) = &HCCCCCC ' RGB(166, 166, 166)
End Sub
'Overrides for 3 of the Methods of the original Implementation
Private Sub cTheme_DrawTo(CC As cCairoContext, W As cWidgetBase, ByVal What As enmThemeDrawingType, ByVal State As enmThemeDrawingState, ByVal x As Double, ByVal y As Double, ByVal dx As Double, ByVal dy As Double, Optional ByVal Radius As Double, Optional ByVal Direction As enmThemeDrawingDirection)
Select Case What
Case thmTypeButtonFace 'your chance to override the ButtonFace-Rendering
'currently we paint nothing here - meaning the Button remains "unfaced" (the BackGround-Color of the Container showing through)
Case Else 'everything else will be delegated to and drawn using the Original Implementation
TOrig.DrawTo CC, W, What, State, x, y, dx, dy, Radius, Direction
End Select
End Sub
Private Sub cTheme_FillThemeColorArray(DstThemeColorArray() As Long)
For i = 0 To UBound(MyThemeColorArray)
DstThemeColorArray(i) = MyThemeColorArray(i)
Next
End Sub
Private Function cTheme_GetThemeColor(WhichColor As enmThemeColor) As Long
cTheme_GetThemeColor = MyThemeColorArray(WhichColor)
End Function
'the remaining two Methods of the cTheme-Interface just delegate to the Original Implementation
Private Function cTheme_GetIconFontName() As String
cTheme_GetIconFontName = TOrig.GetIconFontName
End Function
Private Function cTheme_GetScrollerSize() As Long
cTheme_GetScrollerSize = TOrig.GetScrollerSize
End Function
What then remains is, to apply this new cMyTheme Override-instance as the new Cairo-Theme for your App.
You can do that in the first line of Form_Load in the previous Example:
Code:
Private Sub Form_Load()
Set Cairo.Theme = New cMyTheme
'... with the rest of the Form_Load-code as it was before
With the (dark-colored) Theme-Override, the previous Demo-Form-Code produces the following:
Olaf
-
Dec 9th, 2017, 02:58 PM
#11
Re: Grdient Button
Originally Posted by axisdj
"That still leaves my question unanswered, why you want to make your own Button."
To answer your question was using pictureBoxes for all my buttons .. so changing them out may not be an issue. I was just thinking of the safest path for now.
If you already have a (VB.PictureBox-based) Button-Implementation throughout your whole App,
then indeed the simple Cairo-Drawing from my first two replies might be entirely sufficient for a first "fast Hack".
Just keep in mind, that the original VB6-Form and UserControl-engine does not support Zooming
(is not really DPI-aware) - whilst the RC5-Widget-Engine does support that.
Edit: for more convenience with regards to checking it out, I've now zipped the Theme-Override-Demo up -
including a few enhancements as:
- implementing something concrete for the thmTypeButtonFace-Case
- adding a user-switchable BackGround-Image (to be able to easier determine, what is transparent and what not)
- added modRC5regfree.bas into the Project, so that the Demo can serve as a Base-Wireframe for portable (regfree) deployment of the RC5-libs
. (for the above you will have to add the 4 needed RC5-Dlls into the \Bin\-Subfolder of the App yourself)
WidgetThemeOverrides.zip
Here is a ScreenShot again:
Olaf
Last edited by Schmidt; Dec 9th, 2017 at 05:23 PM.
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|