Tuesday, April 16, 2013

Custom widget in Visual Basic GTK#

Custom widget

Have you ever looked at an application and wondered, how a particular gui item was created? Probably every wannabe programmer has. Then you were looking at a list of widgets provided by your favourite gui library. But you couldn't find it. Toolkits usually provide only the most common widgets like buttons, text widgets, sliders etc. No toolkit can provide all possible widgets.
There are actually two kinds of toolkits. Spartan toolkits and heavy weight toolkits. The FLTK toolkit is a kind of a spartan toolkit. It provides only the very basic widgets and assumes, that the programemer will create the more complicated ones himself. wxWidgets is a heavy weight one. It has lots of widgets. Yet it does not provide the more specialized widgets. For example a speed meter widget, a widget that measures the capacity of a CD to be burned (found e.g. in nero). Toolkits also don't have usually charts.
Programmers must create such widgets by themselves. They do it by using the drawing tools provided by the toolkit. There are two possibilities. A programmer can modify or enhance an existing widget. Or he can create a custom widget from scratch.

Burning widget

This is an example of a widget, that we create from scratch. This widget can be found in various media burning applications, like Nero Burning ROM.
custom.vb
Imports Gtk

NameSpace BurningWidget

Public Class Burning
Inherits DrawingArea

Const PANEL_HEIGHT As Integer = 30
Const DIVISIONS As Integer = 10
Const FULL_CAPACITY As Double = 700
Const MAX_CAPACITY As Double = 750

Dim redColor As New Gdk.Color(1, 0.7, 0.7)
Dim yellowColor As New Gdk.Color(1, 1, 0.7)

Dim parent As Widget

Dim num() As String = { _
"75", "150", "225", "300", _
"375", "450", "525", "600", _
"675" _
}


Public Sub New(ByVal parent As Widget)

Me.SetSizeRequest(1, PANEL_HEIGHT)

Me.parent = parent
AddHandler Me.ExposeEvent, AddressOf Me.OnExpose

End Sub


Private Sub OnExpose(ByVal sender As Object, ByVal e As ExposeEventArgs)

Dim cc As Cairo.Context = Gdk.CairoHelper.Create(sender.GdkWindow)

Me.DrawCustomWidget(cc)

Dim disposeTarget As IDisposable = CType(cc.Target, IDisposable)
disposeTarget.Dispose

Dim disposeContext As IDisposable = CType(cc, IDisposable)
disposeContext.Dispose

End Sub

Private Sub DrawCustomWidget(ByVal cc As Cairo.Context)

cc.LineWidth = 0.8

cc.SelectFontFace("Courier 10 Pitch", _
Cairo.FontSlant.Normal, Cairo.FontWeight.Normal)
cc.SetFontSize(11)

Dim burn As Custom.GtkVBApp = CType(parent, Custom.GtkVBApp)

Dim slid_width As Double = burn.GetCurrentWidth
Dim width As Double = Allocation.Width
Dim move As Double = width / DIVISIONS

Dim till As Double = (width / MAX_CAPACITY) * slid_width
Dim full As Double = (width / MAX_CAPACITY) * FULL_CAPACITY


If slid_width >= FULL_CAPACITY

cc.SetSourceRGB(1.0, 1.0, 0.72)
cc.Rectangle(0, 0, full, PANEL_HEIGHT)
cc.Clip
cc.Paint
cc.ResetClip

cc.SetSourceRGB(1.0, 0.68, 0.68)
cc.Rectangle(full, 0, till-full, PANEL_HEIGHT)
cc.Clip
cc.Paint
cc.ResetClip

Else

cc.SetSourceRGB(1.0, 1.0, 0.72)
cc.Rectangle(0, 0, till, PANEL_HEIGHT)
cc.Clip
cc.Paint
cc.ResetClip
End If

cc.SetSourceRGB(0.35, 0.31, 0.24)

For i As Integer = 1 To num.Length

cc.MoveTo(i*move, 0)
cc.LineTo(i*move, 5)
cc.Stroke

Dim extents As Cairo.TextExtents = cc.TextExtents(num(i-1))
cc.MoveTo(i*move-extents.Width/2, 15)
cc.TextPath(num(i-1))
cc.Stroke
Next

End Sub

End Class

End Namespace
We put a DrawingArea on the bottom of the window and draw the entire widget manually. All the important code resides in the DrawCustomWidget which is called from the OnExpose method of the Burning class. This widget shows graphically the total capacity of a medium and the free space available to us. The widget is controlled by a scale widget. The minimum value of our custom widget is 0, the maximum is 750. If we reach value 700, we began drawing in red colour. This normally indicates overburning.
 Dim num() As String = { _
"75", "150", "225", "300", _
"375", "450", "525", "600", _
"675" _
}
These numbers are shown on the burning widget. They show the capacity of the medium.
 Dim burn As Custom.GtkVBApp = CType(parent, Custom.GtkVBApp)

Dim slid_width As Double = burn.GetCurrentWidth
These two lines get the current number from the scale widget. We get the parent widget and from the parent widget, we get the current value.
Dim till As Double = (width / MAX_CAPACITY) * slid_width
Dim full As Double = (width / MAX_CAPACITY) * FULL_CAPACITY
We use the width variable to do the transformations. Between the values of the scale and the custom widget's measures. Note that we use floating point values. We get greater precision in drawing. The till parameter determines the total size to be drawn. This value comes from the slider widget. It is a proportion of the whole area. The full parameter determines the point, where we begin to draw in red color.
 cc.SetSourceRGB(1.0, 1.0, 0.72)
cc.Rectangle(0, 0, till, PANEL_HEIGHT)
cc.Clip
cc.Paint
cc.ResetClip
This code here, draws a yellow rectangle up to point, where the medium is full.
 Dim extents As Cairo.TextExtents = cc.TextExtents(num(i-1))
cc.MoveTo(i*move-extents.Width/2, 15)
cc.TextPath(num(i-1))
cc.Stroke
This code here draws the numbers on the burning widget. We calculate the TextExtents to position the text correctly.
burning.vb
' ZetCode Mono Visual Basic GTK# tutorial
'
' In this program, we create
' a custom widget
'
' author jan bodnar
' last modified May 2009
' website www.zetcode.com

Imports Gtk

NameSpace Custom

Public Class GtkVBApp
Inherits Window

Const MAX_CAPACITY As Integer = 750

Dim cur_value As Integer
Dim burning As BurningWidget.Burning

Public Sub New

MyBase.New("Burning")

Me.InitUI

Me.SetDefaultSize(350, 200)

Me.SetPosition(WindowPosition.Center)
AddHandler Me.DeleteEvent, AddressOf Me.OnDelete

Me.ShowAll

End Sub

Private Sub InitUI

Dim vbox As New VBox(False, 2)

Dim scale As New HScale(0, MAX_CAPACITY, 1)
scale.SetSizeRequest(160, 35)

AddHandler scale.ValueChanged, AddressOf Me.OnChanged

Dim fixed As New Fixed
fixed.Put(scale, 50, 50)

vbox.PackStart(fixed)

burning = New BurningWidget.Burning(Me)
vbox.PackStart(burning, False, False, 0)

Me.Add(vbox)

End Sub

Private Sub OnChanged(ByVal sender As Object, ByVal args As EventArgs)
cur_value = sender.Value
burning.QueueDraw
End Sub


Public Function GetCurrentWidth As Integer
Return cur_value
End Function

Sub OnDelete(ByVal sender As Object, ByVal args As DeleteEventArgs)
Application.Quit
End Sub

Public Shared Sub Main

Application.Init
Dim app As New GtkVBApp
Application.Run

End Sub

End Class

End Namespace
This is the main class.
 Private Sub OnChanged(ByVal sender As Object, ByVal args As EventArgs)
cur_value = sender.Value
burning.QueueDraw
End Sub
We get the value from the scale widget, store it in the cur_value variable for later use. We redraw the burning widget.

Burning widget
Figure: Burning widget

In this chapter, we created a custom widget in GTK# and Visual Basic.

No comments:

Post a Comment