Dark Theme Manager

I have created a theme manager for use in Access. It uses two tables, one with different themes and their colors for various controls and one with user settings and preferred themes. It is not just limited to dark and light, but unlimited themes. This works fine in Access.

I have tried to convert this to Clarion (11) and it uses two procedures: LoadTheme and ApplyTheme. LoadTheme complies fines, but ApplyTheme will not. It starts to complain already at the start with ‘wnd’ . Can somebody give me a suggestion on how to proceed. I have used Copilot to build the procedures, but must admit it is not great with Clarion. Good with Access and VBA. Not with Clarion. The two procedures:

LoadTheme            PROCEDURE                             ! Declare Procedure

CODE
  ! Finn aktivt tema i AppUserSettings (SettingID = 1)
  CLEAR(AppUserSettings)
  AppUserSettings.SettingID = 1
  GET(AppUserSettings, AppUserSettings.SettingID)

  IF ERRORCODE()
     MESSAGE('AppUserSettings: Fant ikke aktivt tema.')
     RETURN
  END

  ! Hent selve temaet fra AppTheme
  CLEAR(AppTheme)
  AppTheme.ThemeID = AppUserSettings.ThemeID
  GET(AppTheme, AppTheme.ThemeID)

  IF ERRORCODE()
     MESSAGE('AppTheme: Fant ikke tema med ID ' & AppUserSettings.ThemeID)
     RETURN
  END

  ! Kopier alle felter inn i ThemeRecord
  ThemeRecord.ThemeID                   = AppTheme.ThemeID
  ThemeRecord.ThemeName                 = AppTheme.ThemeName
  ThemeRecord.PrimaryColor              = AppTheme.PrimaryColor
  ThemeRecord.SecondaryColor            = AppTheme.SecondaryColor
  ThemeRecord.BackgroundColor           = AppTheme.BackgroundColor
  ThemeRecord.FormBackColor             = AppTheme.FormBackColor
  ThemeRecord.AccentColor               = AppTheme.AccentColor
  ThemeRecord.TextBoxBackColor          = AppTheme.TextBoxBackColor
  ThemeRecord.TextBoxForeColor          = AppTheme.TextBoxForeColor
  ThemeRecord.LabelForeColor            = AppTheme.LabelForeColor
  ThemeRecord.LabelBackColor            = AppTheme.LabelBackColor
  ThemeRecord.ButtonBackColor           = AppTheme.ButtonBackColor
  ThemeRecord.ButtonHoverColor          = AppTheme.ButtonHoverColor
  ThemeRecord.ButtonPressedColor        = AppTheme.ButtonPressedColor
  ThemeRecord.ButtonWidth               = AppTheme.ButtonWidth
  ThemeRecord.ButtonHeight              = AppTheme.ButtonHeight
  ThemeRecord.FontName                  = AppTheme.FontName
  ThemeRecord.FontSizeLabel             = AppTheme.FontSizeLabel
  ThemeRecord.FontSizeTextBox           = AppTheme.FontSizeTextBox
  ThemeRecord.FontSizeHeader            = AppTheme.FontSizeHeader
  ThemeRecord.MarginOuter               = AppTheme.MarginOuter
  ThemeRecord.MarginInner               = AppTheme.MarginInner
  ThemeRecord.TabActiveColor            = AppTheme.TabActiveColor
  ThemeRecord.TabInactiveColor          = AppTheme.TabInactiveColor
  ThemeRecord.DetailBackColor           = AppTheme.DetailBackColor
  ThemeRecord.DetailAlternateBackColor  = AppTheme.DetailAlternateBackColor
  ThemeRecord.HeaderBackColor           = AppTheme.HeaderBackColor
  ThemeRecord.FooterBackColor           = AppTheme.FooterBackColor
  ThemeRecord.HeaderLabelBackColor      = AppTheme.HeaderLabelBackColor
  ThemeRecord.FooterLabelBackColor      = AppTheme.FooterLabelBackColor
  ThemeRecord.DetailLabelBackColor      = AppTheme.DetailLabelBackColor
  ThemeRecord.HeaderLabelForeColor      = AppTheme.HeaderLabelForeColor
  ThemeRecord.FooterLabelForeColor      = AppTheme.FooterLabelForeColor
  ThemeRecord.DetailLabelForeColor      = AppTheme.DetailLabelForeColor

  RETURN

```LoadTheme            PROCEDURE                             ! Declare Procedure



CODE

  ! Finn aktivt tema i AppUserSettings (SettingID = 1)

  CLEAR(AppUserSettings)

  AppUserSettings.SettingID = 1

  GET(AppUserSettings, AppUserSettings.SettingID)



  IF ERRORCODE()

     MESSAGE('AppUserSettings: Fant ikke aktivt tema.')

     RETURN

  END



  ! Hent selve temaet fra AppTheme

  CLEAR(AppTheme)

  AppTheme.ThemeID = AppUserSettings.ThemeID

  GET(AppTheme, AppTheme.ThemeID)



  IF ERRORCODE()

     MESSAGE('AppTheme: Fant ikke tema med ID ' & AppUserSettings.ThemeID)

     RETURN

  END



  ! Kopier alle felter inn i ThemeRecord

  ThemeRecord.ThemeID                   = AppTheme.ThemeID

  ThemeRecord.ThemeName                 = AppTheme.ThemeName

  ThemeRecord.PrimaryColor              = AppTheme.PrimaryColor

  ThemeRecord.SecondaryColor            = AppTheme.SecondaryColor

  ThemeRecord.BackgroundColor           = AppTheme.BackgroundColor

  ThemeRecord.FormBackColor             = AppTheme.FormBackColor

  ThemeRecord.AccentColor               = AppTheme.AccentColor

  ThemeRecord.TextBoxBackColor          = AppTheme.TextBoxBackColor

  ThemeRecord.TextBoxForeColor          = AppTheme.TextBoxForeColor

  ThemeRecord.LabelForeColor            = AppTheme.LabelForeColor

  ThemeRecord.LabelBackColor            = AppTheme.LabelBackColor

  ThemeRecord.ButtonBackColor           = AppTheme.ButtonBackColor

  ThemeRecord.ButtonHoverColor          = AppTheme.ButtonHoverColor

  ThemeRecord.ButtonPressedColor        = AppTheme.ButtonPressedColor

  ThemeRecord.ButtonWidth               = AppTheme.ButtonWidth

  ThemeRecord.ButtonHeight              = AppTheme.ButtonHeight

  ThemeRecord.FontName                  = AppTheme.FontName

  ThemeRecord.FontSizeLabel             = AppTheme.FontSizeLabel

  ThemeRecord.FontSizeTextBox           = AppTheme.FontSizeTextBox

  ThemeRecord.FontSizeHeader            = AppTheme.FontSizeHeader

  ThemeRecord.MarginOuter               = AppTheme.MarginOuter

  ThemeRecord.MarginInner               = AppTheme.MarginInner

  ThemeRecord.TabActiveColor            = AppTheme.TabActiveColor

  ThemeRecord.TabInactiveColor          = AppTheme.TabInactiveColor

  ThemeRecord.DetailBackColor           = AppTheme.DetailBackColor

  ThemeRecord.DetailAlternateBackColor  = AppTheme.DetailAlternateBackColor

  ThemeRecord.HeaderBackColor           = AppTheme.HeaderBackColor

  ThemeRecord.FooterBackColor           = AppTheme.FooterBackColor

  ThemeRecord.HeaderLabelBackColor      = AppTheme.HeaderLabelBackColor

  ThemeRecord.FooterLabelBackColor      = AppTheme.FooterLabelBackColor

  ThemeRecord.DetailLabelBackColor      = AppTheme.DetailLabelBackColor

  ThemeRecord.HeaderLabelForeColor      = AppTheme.HeaderLabelForeColor

  ThemeRecord.FooterLabelForeColor      = AppTheme.FooterLabelForeColor

  ThemeRecord.DetailLabelForeColor      = AppTheme.DetailLabelForeColor

  RETURN

And the other procedure:

MEMBER()



INCLUDE('ABWINDOW.INC'),ONCE



                     MAP

ApplyTheme PROCEDURE(Wnd Window)

                     END




ApplyTheme PROCEDURE(Wnd WINDOW)

FieldNum LONG



CODE

  Wnd{PROP:Background} = ThemeRecord.BackgroundColor



  FieldNum = 0

  LOOP

    FieldNum = Wnd{PROP:NextField, FieldNum}

    IF FieldNum = 0 THEN BREAK.



    CASE FieldNum{PROP:Type}



    OF 1   ! Prompt

      FieldNum{PROP:Color}      = ThemeRecord.LabelForeColor

      FieldNum{PROP:Background} = ThemeRecord.LabelBackColor



    OF 2   ! Entry

      FieldNum{PROP:Color}      = ThemeRecord.TextBoxForeColor

      FieldNum{PROP:Background} = ThemeRecord.TextBoxBackColor



    OF 3   ! Button

      FieldNum{PROP:Color}      = ThemeRecord.ButtonForeColor

      FieldNum{PROP:Background} = ThemeRecord.ButtonBackColor



    OF 7   ! List

      FieldNum{PROP:Color}            = ThemeRecord.LabelForeColor

      FieldNum{PROP:Background}       = ThemeRecord.DetailBackColor

      FieldNum{PROP:Alternate}        = ThemeRecord.DetailAlternateBackColor

      FieldNum{PROP:HeaderBackColor}  = ThemeRecord.HeaderBackColor

      FieldNum{PROP:HeaderTextColor}  = ThemeRecord.HeaderLabelForeColor

      FieldNum{PROP:FooterBackColor}  = ThemeRecord.FooterBackColor

      FieldNum{PROP:FooterTextColor}  = ThemeRecord.FooterLabelForeColor



    OF 14  ! Tab

      FieldNum{PROP:Background} = ThemeRecord.TabInactiveColor

      IF FieldNum{PROP:Selected}

         FieldNum{PROP:Background} = ThemeRecord.TabActiveColor

      END



    END

  END

Hi Werner
Load up THEMER APP ( NT Server number 88). This is used to play with and make your own themes. Play with the themes. There is a Dark theme in there.
Ron

Hi Ron,

Werner’s code is not for a NetTalk application, it’s a themer for desktop applications, and so NT themer wouldn’t be useful here.

Mark

Hi Werner,

you wrote:

ApplyTheme PROCEDURE(Wnd WINDOW)

Clarion prototypes are in the opposite order than many languages, clarion expects
(DataType ParameterLabel )

So you want
ApplyTheme PROCEDURE(*Window Wnd)


Also FWIW, I would use the create:* equates for the PROP:Type
you may need to add an INCLUDE('equates.clw'),ONCE

CASE FieldNum{PROP:Type}   
  OF CREATE:BUTTON

I’m not sure of the data types of ThemeRecord and AppTheme
but there’s a good chance you can make all of the assignments with 1 line of code
either ThemeRecord = AppTheme or ThemeRecord :=: AppTheme the 2nd assignment is called a Deep Assign in clarion, it matches the fields in any order and makes assignments to matching labels, but if the structures are the same then the first simple assign is more efficient (not that you’d ever notice the difference)


one last thing, when using PROP:NextField, please be aware that pulldown menus and toolbars will be enumerated as well, you can easily identify them as they will have negative FieldNums (Field Equates)

If the default FEQ assignment is used, that is.

Mark,
thanks for your comments . The code now accepts the prototype for the procedure.

The code now looks as follows in included handwritten file ThemeModule.clw
!====================================
MEMBER()

INCLUDE(‘ABWINDOW.INC’),ONCE

                 MAP

ApplyTheme PROCEDURE(Window Wnd)
END

ApplyTheme PROCEDURE(Window Wnd)

FieldNum LONG

CODE
Wnd{PROP:Background} = ThemeRecord.BackgroundColor

FieldNum = 0
LOOP
FieldNum = Wnd{PROP:NextField, FieldNum}
IF FieldNum = 0 THEN BREAK.

CASE FieldNum{PROP:Type}

OF 1   ! Prompt
  FieldNum{PROP:Color}      = ThemeRecord.LabelForeColor
  FieldNum{PROP:Background} = ThemeRecord.LabelBackColor

!==============================================

The logic is like this: In the dictionary I have a tps file named AppTheme. In the Application I have a group named ThemeRecord. I use the LoadTheme procedure to load the data in the tps file into the group and the ApplyTheme to apply themecolors to the various types of control. In each window I will need to start the procedure ApplyTheme in Window Open (I think). The current problem is that in the LoadTheme procedure the procedure is not able to find identifier Themerecord and also error ‘Field not find: BackgroundColor’ (as an example).
I am a bit surprised about this as it is a global group.
Any suggestions?

Feq{PROP:InToolbar} is a good check.

For the Menus he is checking PROP:Type so can avoid the Menus & Items.

Congratulations on fixing the prototype

Global data is NOT visible from modules that have an empty MEMBER() statement
to see the global data you need to provide the global module MEMBER('MyGlobalModule')


Personally, I would write this as a class, which I’ll be happy to help you with if you’re interested
The data would be held as properties. Which means you could then avoid needing to tie this code to a particular global module.

I see you’re doing an INCLUDE('abWindow.inc'),ONCE
so, you might consider writing this as a derived window manager
or more flexibly as a WIndowComponent


As a side note, posts are easier to read when source code is tagged to render as source code
New Clarion Syntax Highlighting on ClarionHub! - Announcements - ClarionHub

Mark,
I have tried to create a class (together with copilot) , but it is not working. It consists mainly of ThemeClass.inc and ThemeClass.clw and it is to be implemented in a form as follows:


!==================================
!Use in a form:
!INCLUDE('ThemeClass.inc'),ONCE
!==================================

!Embed point
!==================================
! Event:OpenWindow - Embed
! Theme ThemeClass
! Theme.LoadTheme(1)
! Theme.ApplyTheme(SELF.Window)
!==================================

The ThemeClass.inc code looks as follows:

ThemeClass            CLASS,TYPE

ThemeRecord           GROUP
ThemeID                   LONG
ThemeName                 STRING(30)
PrimaryColor              LONG
SecondaryColor            LONG
BackgroundColor           LONG
FormBackColor             LONG
AccentColor               LONG
TextBoxBackColor          LONG
TextBoxForeColor          LONG
LabelForeColor            LONG
LabelBackColor            LONG
ButtonForeColor           LONG
ButtonBackColor           LONG
ButtonHoverColor          LONG
ButtonPressedColor        LONG
ButtonWidth               LONG
ButtonHeight              LONG
FontName                  STRING(50)
FontSizeLabel             LONG
FontSizeTextBox           LONG
FontSizeHeader            LONG
MarginOuter               LONG
MarginInner               LONG
TabActiveColor            LONG
TabInactiveColor          LONG
DetailBackColor           LONG
DetailAlternateBackColor  LONG
HeaderBackColor           LONG
FooterBackColor           LONG
HeaderLabelBackColor      LONG
FooterLabelBackColor      LONG
DetailLabelBackColor      LONG
HeaderLabelForeColor      LONG
FooterLabelForeColor      LONG
DetailLabelForeColor      LONG
                        END

ApplyTheme            PROCEDURE(Window Wnd)
LoadTheme             PROCEDURE(LONG ThemeID)

                      END
!The ThemeClass.clw look as follows:

                     MEMBER()

INCLUDE('ThemeClass.inc'),ONCE

AppTheme FILE,DRIVER('TPS'),NAME('AppTheme.tps'),PRE(TM)
Record   RECORD
  ThemeID                   LONG
  ThemeName                 STRING(30)
  PrimaryColor              LONG
  SecondaryColor            LONG
  BackgroundColor           LONG
  FormBackColor             LONG
  AccentColor               LONG
  TextBoxBackColor          LONG
  TextBoxForeColor          LONG
  LabelForeColor            LONG
  LabelBackColor            LONG
  ButtonForeColor           LONG
  ButtonBackColor           LONG
  ButtonHoverColor          LONG
  ButtonPressedColor        LONG
  ButtonWidth               LONG
  ButtonHeight              LONG
  FontName                  STRING(50)
  FontSizeLabel             LONG
  FontSizeTextBox           LONG
  FontSizeHeader            LONG
  MarginOuter               LONG
  MarginInner               LONG
  TabActiveColor            LONG
  TabInactiveColor          LONG
  DetailBackColor           LONG
  DetailAlternateBackColor  LONG
  HeaderBackColor           LONG
  FooterBackColor           LONG
  HeaderLabelBackColor      LONG
  FooterLabelBackColor      LONG
  DetailLabelBackColor      LONG
  HeaderLabelForeColor      LONG
  FooterLabelForeColor      LONG
  DetailLabelForeColor      LONG
         END
KEY(KeyPrimary,ThemeID),PRIMARY
       END
!----------------------------------------------------------
! LoadTheme: short version

!----------------------------------------------------------
ThemeClass.LoadTheme PROCEDURE(LONG pThemeID)
CODE
  CLEAR(SELF.ThemeRecord)

  OPEN(AppTheme)
  IF ERRORCODE() THEN RETURN.

  CLEAR(AppTheme)
  ATH:ThemeID = pThemeID
  GET(AppTheme,ThemeKey)

  IF ERRORCODE() THEN
    CLOSE(AppTheme)
    RETURN
  END

  SELF.ThemeRecord.ThemeID                  = TM:ThemeID
  SELF.ThemeRecord.ThemeName                = TM:ThemeName
  SELF.ThemeRecord.PrimaryColor             = TM:PrimaryColor
  SELF.ThemeRecord.SecondaryColor           = TM:SecondaryColor
  SELF.ThemeRecord.BackgroundColor          = TM:BackgroundColor
  SELF.ThemeRecord.FormBackColor            = TM:FormBackColor
  SELF.ThemeRecord.AccentColor              = TM:AccentColor
  SELF.ThemeRecord.TextBoxBackColor         = TM:TextBoxBackColor
  SELF.ThemeRecord.TextBoxForeColor         = TM:TextBoxForeColor
  SELF.ThemeRecord.LabelForeColor           = TM:LabelForeColor
  SELF.ThemeRecord.LabelBackColor           = TM:LabelBackColor
  SELF.ThemeRecord.ButtonForeColor          = TM:ButtonForeColor
  SELF.ThemeRecord.ButtonBackColor          = TM:ButtonBackColor
  SELF.ThemeRecord.ButtonHoverColor         = TM:ButtonHoverColor
  SELF.ThemeRecord.ButtonPressedColor       = TM:ButtonPressedColor
  SELF.ThemeRecord.ButtonWidth              = TM:ButtonWidth
  SELF.ThemeRecord.ButtonHeight             = TM:ButtonHeight
  SELF.ThemeRecord.FontName                 = TM:FontName
  SELF.ThemeRecord.FontSizeLabel            = TM:FontSizeLabel
  SELF.ThemeRecord.FontSizeTextBox          = TM:FontSizeTextBox
  SELF.ThemeRecord.FontSizeHeader           = TM:FontSizeHeader
  SELF.ThemeRecord.MarginOuter              = TM:MarginOuter
  SELF.ThemeRecord.MarginInner              = TM:MarginInner
  SELF.ThemeRecord.TabActiveColor           = TM:TabActiveColor
  SELF.ThemeRecord.TabInactiveColor         = TM:TabInactiveColor
  SELF.ThemeRecord.DetailBackColor          = TM:DetailBackColor
  SELF.ThemeRecord.DetailAlternateBackColor = TM:DetailAlternateBackColor
  SELF.ThemeRecord.HeaderBackColor          = TM:HeaderBackColor
  SELF.ThemeRecord.FooterBackColor          = TM:FooterBackColor
  SELF.ThemeRecord.HeaderLabelBackColor     = TM:HeaderLabelBackColor
  SELF.ThemeRecord.FooterLabelBackColor     = TM:FooterLabelBackColor
  SELF.ThemeRecord.DetailLabelBackColor     = TM:DetailLabelBackColor
  SELF.ThemeRecord.HeaderLabelForeColor     = TM:HeaderLabelForeColor
  SELF.ThemeRecord.FooterLabelForeColor     = TM:FooterLabelForeColor
  SELF.ThemeRecord.DetailLabelForeColor     = TM:DetailLabelForeColor

  CLOSE(AppTheme)
!----------------------------------------------------------
! ApplyTheme: colors windows and controls
!----------------------------------------------------------
ThemeClass.ApplyTheme PROCEDURE(Window Wnd)
FieldNum LONG
CtrlType LONG

CODE
  Wnd{PROP:Background} = SELF.ThemeRecord.BackgroundColor

  FieldNum = 0
  LOOP
    FieldNum = Wnd{PROP:NextField, FieldNum}
    IF FieldNum = 0 THEN BREAK.

    CtrlType = Wnd{PROP:Type, FieldNum}

    CASE CtrlType

    OF CREATE:Label
      Wnd{PROP:TextColor, FieldNum} = SELF.ThemeRecord.LabelForeColor
      Wnd{PROP:BackColor, FieldNum} = SELF.ThemeRecord.LabelBackColor

    OF CREATE:Entry
      Wnd{PROP:TextColor, FieldNum} = SELF.ThemeRecord.TextBoxForeColor
      Wnd{PROP:BackColor, FieldNum} = SELF.ThemeRecord.TextBoxBackColor

    OF CREATE:Button
      Wnd{PROP:TextColor, FieldNum} = SELF.ThemeRecord.ButtonForeColor
      Wnd{PROP:BackColor, FieldNum} = SELF.ThemeRecord.ButtonBackColor

    END
  END

I cannot get it to work as it is and I feel that using Copilot is sometimes like to going down the rabbithole. It start nice and easy with a inc file and a clw file, and whence it doesn’t work it comes up with all these fancy explanations why.

Any comments to the code would be welcome as I have some applications I would like to give the possibility to use alternative color schemes.

Brgds
Werner

Congrats on getting your first class this far.

This reply is just about Classes in general vs. this particular example
but I’ll use your class name and filenames to talk about what needs to be done.

As you mentioned, the class is split between two files
ThemeClass.inc and ThemeClass.clw


For the .INC CoPilot wrote
ThemeClass CLASS,TYPE

You’ll want
ThemeClass CLASS,TYPE,MODULE('ThemeClass.clw'),LINK('ThemeClass.clw)

This tells the compiler that any methods will appear in ThemeClass.clw and please compile ThemeClass.clw too, you do NOT need to add ThemeClass.clw to your project (.CWProj)

Note: if you want to compile the class in a DLL, and use it in other DLLs or the EXE then you’ll want to use the 2nd argument of the Link attribute, and add a ,DLL(YadaDllMode) to make the same code compile correctly in both the DLL and EXE where YadaDllMode is a project define.

Here’s a similar line for ABC’s WindowManager

WindowManager     CLASS,TYPE,MODULE('ABWINDOW.CLW'),LINK('ABWINDOW.CLW',_ABCLinkMode_),DLL(_ABCDllMode_)     

As you mentioned in your comment, the idea is to be able to write
INCLUDE('ThemeClass.inc'),ONCE
Now that the class is written to use a ,MODULE attribute
the INCLUDE('ThemeClass.inc'),ONCE must appear at the global scope (recommended) or a module scope. Otherwise your program will GPF at runtime when you first “touch” the class, even though it compiles just fine (at least that was the case the last I checked)


In the .CLW CoPilot failed to add a MAP;END
It’s often important to add one, even when the MAP is empty

 MAP
 END

because while the map looks empty, it’s treated as follows.

 MAP
    INCLUDE('builtins.clw'),once
 END

Giving the compiler important prototypes for quite a few commands that we consider part of the Run-Time Library (RTL)


If you’re not familiar with the ,TYPE attribute
think of it as a notation for telling the compiler about a new kind of thing
But do NOT allocate any RAM for it

many classes are written with a ,TYPE attribute
and then are instantiated (allocated ram, and run the .construct if present) where they’re needed.

You may find it useful to create GROUP,TYPE and QUEUE,TYPE

This Reply talks about the specifics of the initial source you posted above
I’ll try to focus on just getting things working vs. ways to enhance the class
I tried, but I just had mention some changes :slight_smile:

I doubt the posted code compiles

File declaration is off

  1. ,DRIVER('TPS') should be ,DRIVER('TopSpeed')

  2. The field labels in the file, need to be in the first column
    this is a requirement for all labels in clarion

  3. The Key components must use the Prefix
    as much as I wish that weren’t the case

  4. The Key needs a label

  5. I’ve never seen the KEY below the record, that might be ok, but I don’t see an advantage to placing it after the record

Note: there is an assumption that the Topsped file driver has been added this project, there are ways to add it into the INC or CLW - but let’s defer that “trick” for now

I also noticed a mix of prefixes in the code
The file is declared with ,PRE(TM) but ATH:ThemeID is used ThemeClass.LoadTheme before the GET

Side note: I would either have a method that’s called when there’s an error in .LoadTheme, and/or have it RETURN ERRORCODE() so the calling code would have a chance to handle problems


.ApplyTheme has needs to correct the property syntax

Wnd{PROP:TextColor, FieldNum} = SELF.ThemeRecord.LabelForeColor
should be
FIeldNum{PROP:TextColor, FieldNum} = SELF.ThemeRecord.LabelForeColor
or if you prefer
Wnd $ FieldNum{PROP:TextColor, FieldNum} = SELF.ThemeRecord.LabelForeColor

Assuming the passed window is the current Target (which is likely)
then the two places where Wnd{PROP: is correct
Wnd{PROP:Background} = SELF.ThemeRecord.BackgroundColor
FieldNum = Wnd{PROP:NextField, FieldNum}

the Wnd in Wnd{PROP: could be replaced with 0
as in 0{PROP:NextField, FieldNum}

So there’s no need to pass the Window into ApplyTheme


Assuming that all windows will using the same ThemeID
then I would move the include to the global scope
and just call .LoadTheme( n ) once at program startup

There are a few things to work through if you want to support changing the theme or selecting a different theme when there are windows that are open

You will need to call .ApplyTheme after you open a window


Later, after we have this functional as a class
Then I’d like to explore some ways it can be improved upon

Good feedback.
FYI: I often use FILE declarations without a prefix hence the keys don’t have a prefix either.