Skinning & Theming

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:

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()