Skinning & Theming Overview
ProGUI features a powerful, CSS-inspired skinning engine that completely separates the visual design of your application from its underlying logic. By leveraging a familiar property-value syntax, developers can create dynamic, scalable, and highly customizable user interfaces with built-in support for themes, state-based styling, and automatic animated transitions.
Key concepts of the skinning system include:
Classes and IDs: Similar to CSS, you style widgets by assigning them a class using WidgetSetClass() or a unique name (ID) using WidgetSetName(). In the skin definitions, classes are typically targeted by name (e.g.,
button), and specific widget IDs are targeted using the#prefix (e.g.,#mySpecialButton).States: Widgets can have dynamic visual states such as
hover,active,disabled, or custom states you define. You change a widget’s current state using WidgetSetSkinState(). ProGUI automatically resolves the correct styling for the active state.Components: Complex widgets (like scrollbars or title bars) are made up of multiple parts. You can target these sub-components in your skin definitions (e.g., styling the
thumbortrackbarcomponent of a scrollbar separately) and render them individually.Themes: ProGUI supports robust theming. You can prefix skin definitions with a theme name (e.g.,
dark:buttonorwin11:window). By calling SkinSetTheme(), you can globally switch the active visual theme across your entire application instantly.CSS-like Properties: The engine supports a wide array of familiar styling properties, including:
background(solid colors, linear-gradients, radial-gradients, images, repeat modes, cover/contain sizing)border(widths, colors, styles like solid/dashed, and border-radius)box-shadow(inset and outset shadows with blur and spread)text(font-family, font-size, weight, color, alignment, wrapping)
Note - See CSS Reference for a complete list including ProGUI specific extensions.
Transitions: You can define
transitionproperties in your skin (e.g.,transition: background-color 0.3s ease-out). When a widget changes state (like hovering over a button), ProGUI automatically generates smooth, hardware-accelerated animations between the property values.Variables: Global CSS-like variables are supported via
var(variable-name). You can dynamically update these at runtime using SkinSetGlobalVariable() to respond to OS-level changes, like updating the UI to match the user’s current Windows Accent Color.Rendering: Instead of manual drawing commands, you simply ask ProGUI to render the resolved skin properties using functions like DrawSkinBackground(), DrawSkinBoxShadow(), and DrawSkinText() within your widget’s
#PG_Event_Drawhandler.Loading and Saving: Skins can be programmatically constructed using SkinSetValue(), dumped to a structured CSS file format using SaveSkin(), and loaded from disk at runtime using LoadSkin().
Example
The following example demonstrates how to define skin properties programmatically, differentiating between generic class styles and specific ID-based overrides, and handling states and transitions.
IncludeFile "ProGUI_PB.pbi"
StartProGUI()
; Define a skin property specifically targeting a widget named "mySpecialButton"
; Note the use of the '#' prefix!
SkinSetValue("#mySpecialButton", "", "", "background-color", "gold")
SkinSetValue("#mySpecialButton", "hover", "", "background-color", "orange")
SkinSetValue("#mySpecialButton", "", "", "transition", "background-color 0.3s")
; Define a generic class style to show the difference
SkinSetValue("genericButton", "", "", "background-color", "silver")
SkinSetValue("genericButton", "", "", "border", "blue 2px")
SkinSetValue("genericButton", "", "", "color", "black")
SkinSetValue("genericButton", "", "", "font-family", "Arial")
SkinSetValue("genericButton", "", "", "font-size", "12pt")
SkinSetValue("genericButton", "", "", "text-align", "center")
SkinSetValue("genericButton", "", "", "vertical-align", "center")
Procedure DrawWidgetHandler(Widget, EventType, *EventData.PG_EventDraw, *UserData)
; Draw a text label
DrawSkinText(Widget, "", 0, 0, *EventData\width, *EventData\height, PeekS(*UserData))
EndProcedure
Procedure MouseHandler(Widget, EventType, *EventData.PG_EventMouse, *UserData)
Select EventType
Case #PG_Event_MouseEnter
WidgetSetSkinState(Widget, "hover")
Case #PG_Event_MouseLeave
WidgetSetSkinState(Widget, "")
EndSelect
EndProcedure
MyWindow = CreateWindow(0, 0, 300, 200, "WidgetSetName Example", #PG_Window_Default | #PG_Window_LayoutFlex)
If MyWindow
LayoutSetPadding(#Null, 20)
LayoutFlexSetDirection(#Null, #PG_Flex_Direction_Column)
LayoutFlexSetAlignItems(#Null, #PG_Flex_AlignItems_Center)
; --- Widget 1: Using a generic class ---
Widget1 = CreateWidget(0, 0, 150, 40)
WidgetSetClass(Widget1, "genericButton")
WidgetSetMarginBottom(Widget1, 10)
AddEventHandler(Widget1, #PG_Event_Draw, @DrawWidgetHandler(), @"Generic Button")
; --- Widget 2: Using a specific Name (ID) ---
Widget2 = CreateWidget(0, 0, 150, 40)
; Use the "genericButton" base class to inherit from
WidgetSetClass(Widget2, "genericButton")
; Set the widget's name to link it with the CSS #mySpecialButton style
WidgetSetName(Widget2, "mySpecialButton")
AddEventHandler(Widget2, #PG_Event_Draw, @DrawWidgetHandler(), @"mySpecialButton")
AddEventHandler(Widget2, #PG_Event_MouseEnter, @MouseHandler())
AddEventHandler(Widget2, #PG_Event_MouseLeave, @MouseHandler())
WindowShow(MyWindow, #True, #PG_WindowShow_ScreenCentered)
Repeat
Event = WaitWindowEvent()
Until Event = #PB_Event_CloseWindow
EndIf
StopProGUI()