Page 1 of 1

Onboarding and Guided Tour Module

Posted: Wed Feb 11, 2026 2:39 am
by mrbungle
I wrote this for some of my apps. It lets you display an onboarding window that can have multiple steps that you can use to guide first time users on how to use your app or do things like ask for donations.

It is cross-platform. You define each steps using a list element. An example is included below. It also supports images though it is primarily for displaying text.

Code: Select all

DeclareModule Tour
  Structure Screen
    Title.s
    Description.s
    ImageHandle.i
  EndStructure

  Declare.i Start(List Screens.Screen(), ParentWindowID.i = 0)
EndDeclareModule

Module Tour
  Structure State
    WindowID.i
    CurrentPage.i
    TotalPages.i
    List Screens.Screen()
    TitleGadget.i
    DescGadget.i
    ImageGadget.i
    BtnBack.i
    BtnNext.i
    ChkDismiss.i
    Result.i
  EndStructure

  Procedure UpdateUI(*State.State)
    SelectElement(*State\Screens(), *State\CurrentPage)
    
    SetGadgetText(*State\TitleGadget, *State\Screens()\Title)
    SetGadgetText(*State\DescGadget, *State\Screens()\Description)
    
    If *State\Screens()\ImageHandle <> -1 And IsImage(*State\Screens()\ImageHandle)
      SetGadgetState(*State\ImageGadget, ImageID(*State\Screens()\ImageHandle))
      HideGadget(*State\ImageGadget, #False)
      ResizeGadget(*State\DescGadget, 20, 200, 360, 80)
    Else
      HideGadget(*State\ImageGadget, #True)
      ResizeGadget(*State\DescGadget, 20, 90, 360, 200)
    EndIf
    
    DisableGadget(*State\BtnBack, Bool(*State\CurrentPage = 0))
    
    If *State\CurrentPage = *State\TotalPages - 1
      SetGadgetText(*State\BtnNext, "Finish")
    Else
      SetGadgetText(*State\BtnNext, "Next")
    EndIf
    
    SetWindowTitle(*State\WindowID, "Onboarding Step " + Str(*State\CurrentPage + 1) + " of " + Str(*State\TotalPages))
  EndProcedure

  Procedure.i Start(List UserScreens.Screen(), ParentWindowID.i = 0)
    Protected State.State
    
    CopyList(UserScreens(), State\Screens())
    State\TotalPages = ListSize(State\Screens())
    If State\TotalPages = 0 : ProcedureReturn #False : EndIf
    
    State\WindowID = OpenWindow(#PB_Any, 0, 0, 400, 380, #Empty$, #PB_Window_ScreenCentered, ParentWindowID)
    StickyWindow(State\WindowID, #True)
    LoadFont(10, "Arial", 24, #PB_Font_Bold)
    State\TitleGadget = TextGadget(#PB_Any, 20, 20, 360, 26, #Empty$, #PB_Text_Center)
    SetGadgetFont(State\TitleGadget, FontID(10))
    
    State\ImageGadget = ImageGadget(#PB_Any, WindowWidth(State\WindowID) / 2 - 20, 75, 120, 120, 0)
    State\DescGadget  = TextGadget(#PB_Any, 20, 210, 360, 200, #Empty$)
    
    State\ChkDismiss  = CheckBoxGadget(#PB_Any, 20, 342, 200, 25, "Don't show this again")
    State\BtnBack     = ButtonGadget(#PB_Any, 210, 340, 80, 30, "Back")
    State\BtnNext     = ButtonGadget(#PB_Any, 300, 340, 80, 30, "Next")
    
    UpdateUI(@State)
    
    Repeat
      Select WaitWindowEvent()
        Case #PB_Event_CloseWindow
          Break
        Case #PB_Event_Gadget
          Select EventGadget()
            Case State\BtnNext
              If State\CurrentPage < State\TotalPages - 1
                State\CurrentPage + 1
                UpdateUI(@State)
              Else
              	State\Result = GetGadgetState(State\ChkDismiss)
              	If IsFont(10)
              		FreeFont(10)
              	EndIf
                CloseWindow(State\WindowID)
                Break
              EndIf
            Case State\BtnBack
              If State\CurrentPage > 0
                State\CurrentPage - 1
                UpdateUI(@State)
              EndIf
          EndSelect
      EndSelect
    ForEver
    
    ProcedureReturn State\Result
  EndProcedure
EndModule



NewList MyTour.Tour::Screen()

; Sample image
Define imgSample = CreateImage(#PB_Any, 120, 120)
StartDrawing(ImageOutput(imgSample))
Box(0,0,120,120, RGB(200, 200, 200))
Circle(60,60,40, RGB(50, 50, 50))
StopDrawing()

AddElement(MyTour())
MyTour()\Title = "Introduction"
MyTour()\Description = "This is step one of your modular tour."
MyTour()\ImageHandle = -1 ; Use -1 for no image

AddElement(MyTour())
MyTour()\Title =  "Did You Know?"
MyTour()\Description = "Simply call Tour::Start() and it returns the checkbox result."
MyTour()\ImageHandle = -1

AddElement(MyTour())
MyTour()\Title =  "Almost Finished!"
MyTour()\Description =  "One more step to go. Check the box below if you want to skip this in the future."
MyTour()\ImageHandle = -1

AddElement(MyTour())
MyTour()\Title =  "Ready to Launch!"
MyTour()\Description =  "Click 'Finish' to close the tour and start using the application!"
MyTour()\ImageHandle = imgSample

; Capture the user's preference 
Define DontShowAgain = Tour::Start(MyTour())

If DontShowAgain
  Debug "User wants to skip this in the future. Save this to your config file!"
EndIf

Re: Onboarding and Guided Tour Module

Posted: Wed Feb 11, 2026 2:46 am
by miso
Hello. I'm on Windows7, the \title text shows only the top half of the characters (6.40 alpha 1)
Regardless this it's interesting. Thanks for sharing it.

Re: Onboarding and Guided Tour Module

Posted: Wed Feb 11, 2026 3:38 pm
by mrbungle
I use a Mac so did not test it on Windows, but it uses standard gadgets, so with a little bit of tweaking, it should work correctly! :D

Re: Onboarding and Guided Tour Module

Posted: Wed Feb 11, 2026 8:19 pm
by Erlend
Works perfectly, nice "nag" sample even on Linux..

BR
Erlend

Re: Onboarding and Guided Tour Module

Posted: Wed Feb 11, 2026 9:09 pm
by mrbungle
👍

Re: Onboarding and Guided Tour Module

Posted: Wed Feb 11, 2026 9:46 pm
by mrbungle
The image assumes you're using Hgh DPI settings. I also suggest expanding the height of the textgadget() so you can use more of the available space for text. This my first module, so glad it works OK.

Assume it's under GPL, anyone can include it in their programs and is free to modify it how they see fit. I use a slightly more advanced version for my own purposes.