Onboarding and Guided Tour Module

Share your advanced PureBasic knowledge/code with the community.
mrbungle
Enthusiast
Enthusiast
Posts: 188
Joined: Wed Dec 30, 2020 3:18 am

Onboarding and Guided Tour Module

Post 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
miso
Enthusiast
Enthusiast
Posts: 712
Joined: Sat Oct 21, 2023 4:06 pm
Location: Hungary

Re: Onboarding and Guided Tour Module

Post 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.
mrbungle
Enthusiast
Enthusiast
Posts: 188
Joined: Wed Dec 30, 2020 3:18 am

Re: Onboarding and Guided Tour Module

Post 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
User avatar
Erlend
Enthusiast
Enthusiast
Posts: 131
Joined: Mon Apr 19, 2004 8:22 pm
Location: NORWAY

Re: Onboarding and Guided Tour Module

Post by Erlend »

Works perfectly, nice "nag" sample even on Linux..

BR
Erlend
mrbungle
Enthusiast
Enthusiast
Posts: 188
Joined: Wed Dec 30, 2020 3:18 am

Re: Onboarding and Guided Tour Module

Post by mrbungle »

👍
mrbungle
Enthusiast
Enthusiast
Posts: 188
Joined: Wed Dec 30, 2020 3:18 am

Re: Onboarding and Guided Tour Module

Post 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.
Post Reply