GTK+ started in 2005 with version 2.8 to use Cairo to render the majority of its widgets.Wikipedia wrote:Cairo is a software library used to provide a vector graphics-based, device-independent API for software developers. It is designed to provide primitives for 2-dimensional drawing across a number of different backends. Cairo is designed to use hardware acceleration[1] when available.
I didn't reinvent the wheel but converted C sourcecode snippets from a tutorial of Davyd Madely to PureBasic. The first example simply displays a running clock in a PB window:
Code: Select all
; Converted from C sourcecode in Davyd Madeley's tutorial:
; http://www.gnomejournal.org/article/34/writing-a-widget-using-cairo-and-gtk28
; http://gnomejournal.org/article/36/writing-a-widget-using-cairo-and-gtk28-part-2
EnableExplicit
ImportC ""
gdk_cairo_create(*Drawable.GdkDrawable)
gdk_window_set_composited(*Window.GdkWindowObject, Composited.I)
EndImport
ImportC "-lcairo"
cairo_arc(*CairoContext, xCenter.D, yCenter.D, Radius.D, StartAngle.D, EndAngle.D)
cairo_destroy(*CairoContext)
cairo_fill_preserve(*CairoContext)
cairo_get_line_width(*CairoContext)
cairo_line_to(*CairoContext, x.D, y.D)
cairo_move_to(*CairoContext, x.D, y.D)
cairo_restore(*CairoContext)
cairo_save(*CairoContext)
cairo_set_line_width(*CairoContext, LineWidth.D)
cairo_set_source_rgb(*CairoContext, Red.D, Green.D, Blue.D)
cairo_stroke(*CairoContext)
EndImport
ProcedureC DrawClockCallback(*Widget.GtkWidget, *Event.GdkEventExpose)
Protected CairoContext.I
Protected Hours.I
Protected i.I
Protected Inset.I
Protected Minutes.I
Protected Radius.D
Protected Seconds.I
Protected xCenter.D
Protected yCenter.D
Hours = Hour(Date())
Minutes = Minute(Date())
Seconds = Second(Date())
; ----- Draw clock face
xCenter = *Widget\allocation\width / 2
yCenter = *Widget\allocation\height / 2
If xCenter < yCenter
Radius = *Widget\allocation\width / 2 - 5
Else
Radius = *Widget\allocation\height / 2 - 5
EndIf
CairoContext = gdk_cairo_create(*Widget\window)
cairo_arc(CairoContext, xCenter, yCenter, Radius, 0, 2 * #PI)
cairo_set_source_rgb(CairoContext, 1, 1, 1)
cairo_fill_preserve(CairoContext)
cairo_set_source_rgb(CairoContext, 0, 0, 0)
cairo_stroke(CairoContext)
For i = 0 To 11
If i % 3 = 0
Inset = 0.2 * Radius
Else
Inset = 0.1 * Radius
EndIf
cairo_move_to(CairoContext, xCenter + (Radius - Inset) * Cos(i * #PI / 6), yCenter + (Radius - Inset) * Sin(i * #PI / 6))
cairo_line_to(CairoContext, xCenter + Radius * Cos(i * #PI / 6), yCenter + Radius * Sin(i * #PI / 6))
cairo_stroke(CairoContext)
Next i
cairo_save(CairoContext)
; ----- Draw hour hand
; The hour hand is rotated 30 degrees (Pi/6r) per hour + 1/2 a Degree
; (Pi/360) per minute
; cairo_set_line_width(CairoContext, 2.5 * cairo_get_line_width(CairoContext))
cairo_set_line_width(CairoContext, 5)
cairo_move_to(CairoContext, xCenter, yCenter)
cairo_line_to(CairoContext, xCenter + Radius / 2 * Sin(#PI / 6 * Hours + #PI / 360 * Minutes), yCenter + Radius / 2 * -Cos(#PI / 6 * Hours + #PI / 360 * Minutes))
cairo_stroke(CairoContext)
; ----- Draw minute hand
; The minute hand is rotated 6 degrees (Pi/30r) per minute
cairo_set_line_width(CairoContext, 2)
cairo_move_to (CairoContext, xCenter, yCenter)
cairo_line_to (CairoContext, xCenter + Radius * 0.75 * Sin(#PI / 30 * Minutes), yCenter + Radius * 0.75 * -Cos(#PI / 30 * Minutes))
cairo_stroke (CairoContext)
; ----- Draw seconds hand
; Operates identically to the minute hand
cairo_save(CairoContext)
cairo_set_source_rgb(CairoContext, 1, 0, 0)
cairo_move_to (CairoContext, xCenter, yCenter)
cairo_line_to (CairoContext, xCenter + Radius * 0.7 * Sin(#PI / 30 * Seconds), yCenter + Radius * 0.7 * -Cos(#PI / 30 * Seconds))
cairo_stroke (CairoContext)
cairo_restore(CairoContext)
cairo_destroy(CairoContext)
EndProcedure
Define FixedBox.I
Define Region.I
Define *Window.GtkWidget
*Window = OpenWindow(0, 140, 100, 200, 200, "Clock", #PB_Window_SizeGadget)
FixedBox = g_list_nth_data_(gtk_container_get_children_(gtk_bin_get_child_(WindowID(0))), 0)
g_signal_connect_(FixedBox, "expose-event", @DrawClockCallback(), 0)
AddWindowTimer(0, 0, 1000)
Repeat
Select WaitWindowEvent()
Case #PB_Event_CloseWindow
RemoveWindowTimer(0, 0)
Break
Case #PB_Event_Timer
Region = gdk_drawable_get_clip_region_(*Window\window)
gdk_window_invalidate_region_(*Window\window, Region, #True)
gdk_window_process_updates_(*Window\window, #True)
gdk_region_destroy_(Region)
EndSelect
ForEver
I tested both examples successfully in these Linux distributions:
- andLinux/Kubuntu 9.04 (2nd example will terminate with an error message because of missing support for transparency)
- Linux Mint 13 Cinnamon
- Kubuntu 10.04 (only partial transparency of window in 2nd example)
- Kubuntu 12.04
- OpenSuSE 12.1
- Ubuntu 12.04 (Unity)
- Xubuntu 12.04
Code: Select all
; Converted from C sourcecode in Davyd Madeley's tutorial:
; http://www.gnomejournal.org/article/34/writing-a-widget-using-cairo-and-gtk28
; http://gnomejournal.org/article/36/writing-a-widget-using-cairo-and-gtk28-part-2
EnableExplicit
#CAIRO_OPERATOR_SOURCE = 1
ImportC ""
gdk_cairo_create(*Drawable.GdkDrawable)
gdk_screen_get_rgba_colormap(*Screen.GdkScreen)
EndImport
ImportC "-lcairo"
cairo_arc(*CairoContext, xCenter.D, yCenter.D, Radius.D, StartAngle.D, EndAngle.D)
cairo_destroy(*CairoContext)
cairo_fill_preserve(*CairoContext)
cairo_line_to(*CairoContext, x.D, y.D)
cairo_move_to(*CairoContext, x.D, y.D)
cairo_paint(CairoContext)
cairo_restore(*CairoContext)
cairo_save(*CairoContext)
cairo_set_line_width(*CairoContext, LineWidth.D)
cairo_set_operator(*CairoRegion, CairoOperator.I)
cairo_set_source_rgba(*CairoRegion, Red.D, Green.D, Blue.D, Alpha.D)
cairo_stroke(*CairoContext)
EndImport
ProcedureC DrawClockCallback(*Widget.GtkWidget, *Event.GdkEventExpose)
Protected CairoContext.I
Protected Hours.I
Protected i.I
Protected Inset.I
Protected Minutes.I
Protected Radius.D
Protected Seconds.I
Protected xCenter.D
Protected yCenter.D
; ----- Draw transparent background
CairoContext = gdk_cairo_create(*Widget\window)
cairo_set_source_rgba(CairoContext, 1, 1, 1, 0)
cairo_set_operator(CairoContext, #CAIRO_OPERATOR_SOURCE)
cairo_paint(CairoContext)
; ----- Update current time
Hours = Hour(Date())
Minutes = Minute(Date())
Seconds = Second(Date())
; ----- Draw clock face
xCenter = *Widget\allocation\width / 2
yCenter = *Widget\allocation\height / 2
If xCenter < yCenter
Radius = *Widget\allocation\width / 2 - 5
Else
Radius = *Widget\allocation\height / 2 - 5
EndIf
cairo_arc(CairoContext, xCenter, yCenter, Radius, 0, 2 * #PI)
cairo_set_source_rgba(CairoContext, 1, 1, 1, 1)
cairo_fill_preserve(CairoContext)
cairo_set_source_rgba(CairoContext, 0, 0, 0, 1)
cairo_stroke(CairoContext)
For i = 0 To 11
If i % 3 = 0
Inset = 0.2 * Radius
Else
Inset = 0.1 * Radius
EndIf
cairo_move_to(CairoContext, xCenter + (Radius - Inset) * Cos(i * #PI / 6), yCenter + (Radius - Inset) * Sin(i * #PI / 6))
cairo_line_to(CairoContext, xCenter + Radius * Cos(i * #PI / 6), yCenter + Radius * Sin(i * #PI / 6))
cairo_stroke(CairoContext)
Next i
cairo_save(CairoContext)
; ----- Draw hour hand
; The hour hand is rotated 30 degrees (Pi/6r) per hour + 1/2 a Degree
; (Pi/360) per minute
cairo_set_line_width(CairoContext, 5)
cairo_move_to(CairoContext, xCenter, yCenter)
cairo_line_to(CairoContext, xCenter + Radius / 2 * Sin(#PI / 6 * Hours + #PI / 360 * Minutes), yCenter + Radius / 2 * -Cos(#PI / 6 * Hours + #PI / 360 * Minutes))
cairo_stroke(CairoContext)
; ----- Draw minute hand
; The minute hand is rotated 6 degrees (Pi/30r) per minute
cairo_set_line_width(CairoContext, 2)
cairo_move_to(CairoContext, xCenter, yCenter)
cairo_line_to(CairoContext, xCenter + Radius * 0.75 * Sin(#PI / 30 * Minutes), yCenter + Radius * 0.75 * -Cos(#PI / 30 * Minutes))
cairo_stroke(CairoContext)
; ----- Draw seconds hand
; Operates identically to the minute hand
cairo_save(CairoContext)
cairo_set_source_rgba(CairoContext, 1, 0, 0, 1)
cairo_move_to(CairoContext, xCenter, yCenter)
cairo_line_to(CairoContext, xCenter + Radius * 0.7 * Sin(#PI / 30 * Seconds), yCenter + Radius * 0.7 * -Cos(#PI / 30 * Seconds))
cairo_stroke(CairoContext)
cairo_restore(CairoContext)
cairo_destroy(CairoContext)
EndProcedure
Procedure RedrawWidget(*Widget.GtkWidget)
Protected CairoContext.I
CairoContext = gdk_cairo_create(*Widget\window)
cairo_set_source_rgba(CairoContext, 1.0, 1.0, 1.0, 0.5)
cairo_set_operator(CairoContext, #CAIRO_OPERATOR_SOURCE)
cairo_paint(CairoContext)
cairo_destroy(CairoContext)
EndProcedure
Define Colormap.I
Define FixedBox.I
Define Region.I
Define Screen.I
Define *Window.GtkWidget
*Window = OpenWindow(0, 140, 100, 200, 200, "Clock", #PB_Window_Invisible)
gtk_window_set_decorated_(*Window, #False)
FixedBox = g_list_nth_data_(gtk_container_get_children_(gtk_bin_get_child_(WindowID(0))), 0)
g_signal_connect_(FixedBox, "expose-event", @DrawClockCallback(), 0)
gtk_widget_set_app_paintable_(FixedBox, #True)
; ----- Delete GdkWindow resources to be able to change colormap
gtk_widget_unrealize_(*Window)
; ----- Change colormap to support alpha values and hence allow transparency
Screen = gtk_widget_get_screen_(*Window)
Colormap = gdk_screen_get_rgba_colormap(Screen)
If Colormap
gtk_widget_set_colormap_(*Window, Colormap)
Else
MessageRequester("Error", "Your current system configuration doesn't support transparency!")
End
EndIf
; ----- Realize window and rebuild new GdkWindow
gtk_widget_show_all_(*Window)
AddWindowTimer(0, 0, 1000)
AddKeyboardShortcut(0, #PB_Shortcut_Escape, 0)
Repeat
Select WaitWindowEvent()
Case #PB_Event_Menu
If EventMenu() = 0
RemoveWindowTimer(0, 0)
Break
EndIf
Case #PB_Event_Timer
Region = gdk_drawable_get_clip_region_(*Window\window)
gdk_window_invalidate_region_(*Window\window, Region, #True)
gdk_window_process_updates_(*Window\window, #True)
gdk_region_destroy_(Region)
EndSelect
ForEver