B4J Tutorial [ABMaterial] Better Delegating the creation of complex components

[EDIT: This thread started as my approach to delegating complex components creation, including events.
@alwaysbusy then proposed a better system, that he needs us to say if we want it implemented on the upcoming release of v 3.75, SO let's create a Poll!
Hi Gurus...

This time I come Not with questions, but with MY answer to one of my biggest problems with this amazing framework : ABMaterial!

And to me, the main problem is, a simple webapp can get quite complex very quickly UI wise, so maintainability can be a very hard business...

So I thought... what if I could divide my code into "complex components" and treat them as a unit, instead of going through all of a pages lines to find a prop to change, and risk breaking the code beyond repair!
So I obviously thought of a ClassModule and treat it like a custom view... and it almost worked...
I only had 2 issues with it... both related to theming...
Going the Class route I just couldn't figure out how to theme the "complex component", since a class needs to be first initialized, and the theme must be created BEFORE!
Then, as I typed this question I started thinking about a CodeModule...
Main advantages : doesn't need declaring nor initializing!

So, my basic steps were:
Create a Code Module, in my case, I called it "TopBar2"

then declared all that was needed to my Complex Component
B4X:
Sub Process_Globals
    Private ABM As ABMaterial 'ignore
    Private MyTheme As ABMTheme

    Private TargetPage As ABMPage
    Private ObjectId As String
    Private LogoFilename As String
    Private TopBarThemeName As String

    Private TopBarContainer As ABMContainer
    Private TopBarSubContainer As ABMContainer
    Private TopBarLogo As ABMImage
    Private TopBarSearch As ABMInput
    Private TopBarBtn1 As ABMButton
    Private TopBarBtn2 As ABMButton
    Private TopBarBtn3 As ABMButton

    Private LogoTheme As String
    Private SearchTheme As String
    Private BtnTheme As String
    Private Btn3Theme As String
End Sub

Then the "real construction work" takes place at the "CreateTopBar" sub:
B4X:
Public Sub CreateTopBar(page As ABMPage, id As String, filename As String, Theme As String) As ABMContainer
    TargetPage = page
    ObjectId = id
    LogoFilename = filename
    TopBarThemeName = Theme
    LogoTheme = TopBarThemeName & "Logo"
    SearchTheme = TopBarThemeName & "Search"
    BtnTheme = TopBarThemeName & "Btn"
    Btn3Theme = TopBarThemeName & "Btn3"

    TopBarContainer.Initialize ( TargetPage, ObjectId, "" )
    TopBarSubContainer.Initialize ( TargetPage, ObjectId & "SubCntr", "")
    TopBarLogo.Initialize ( TargetPage, ObjectId & "Logo", LogoFilename, 1 )
    TopBarSearch.Initialize ( TargetPage, ObjectId & "Search", ABM.INPUT_SEARCH, "Pesquizar", False, SearchTheme )
    TopBarBtn1.InitializeRaised ( TargetPage, ObjectId & "Btn1", "", "", "Registar", BtnTheme )
    TopBarBtn2.InitializeRaised( TargetPage, ObjectId & "Btn2", "", "", "Login", BtnTheme)
    TopBarBtn3.InitializeFlat ( TargetPage, ObjectId & "Btn3", "settings", ABM.ICONALIGN_CENTER, "", Btn3Theme)

    LogError ("LogoTheme : " & LogoTheme)
    LogError ("SearchTheme : " & SearchTheme)
    LogError ("BtnTheme : " & BtnTheme)
    LogError ("Btn3Theme : " & Btn3Theme)

    'then we build the grid

    TopBarContainer.AddRowsMV(1, False,7,7, ABM.VISIBILITY_SHOW_ON_LARGE_ONLY,"").AddCellsOS(1,0,0,0,2,2,2,"").AddCellsOS(1,0,0,0,7,7,7,"").AddCellsOS(1,0,0,0,3,3,3,"")
    TopBarContainer.BuildGrid ' IMPORTANT!

    TopBarSubContainer.AddRowsMV(1, False,0,0, ABM.VISIBILITY_SHOW_ON_LARGE_ONLY,"").AddCellsOSMP(1,0,0,0,5,5,5,0,0,0,5,"").AddCellsOSMP(1,0,0,0,5,5,5,0,0,5,0,"").AddCellsOS(1,0,0,0,1,1,1,"")
    TopBarSubContainer.BuildGrid ' IMPORTANT!

    'and we set the cell height
    TopBarContainer.Row(1).SetFixedHeight(70, False)
'    TopBarSubContainer.Row(1).SetFixedHeight(60, False)

    'we then add the picture Logo and set its theme
    TopBarContainer.Cell(1,1).AddComponent(TopBarLogo)
    TopBarContainer.Cell(1,1).UseTheme(LogoTheme)

    'Now we add the search field and set its theme
    TopBarSearch.Narrow = True
    TopBarContainer.Cell(1,2).AddComponent(TopBarSearch)
    TopBarContainer.Cell(1,2).UseTheme(SearchTheme)

    'now we add the SubContainer for Buttons 1 & 2
    TopBarContainer.Cell(1,3).AddComponent(TopBarSubContainer)

    'finaly we start adding the buttons
    TopBarBtn1.UseFullCellWidth =True
    TopBarSubContainer.Cell(1,1).AddComponent(TopBarBtn1)

    TopBarBtn2.UseFullCellWidth =True
    TopBarSubContainer.Cell(1,2).AddComponent(TopBarBtn2)
    TopBarContainer.Cell(1,3).UseTheme(BtnTheme)

    TopBarSubContainer.Cell(1,3).AddComponent(TopBarBtn3)

    Return TopBarContainer
End Sub
(yeah, I know, not the best coding technique, but I'm getting there!)
The more aware of you will notice that I have 2 containers...
The main Container has 3 cells, and inside the 3rd cell we have a SubContainer with another 3 cells...
I did it this way to arrange the positioning of the components more efficiently (in other words, to my liking)
Then we do what we always do, assemble the LEGO! and we return the main Container to the calling sub!

the call resembles something like this:
B4X:
Page.Cell(1,1).AddComponent(TopBar2.CreateTopBar(AppPage,"TopBar","../web/images/topbarlogo60.gif","TopBar"))

The way I solved my theming issue was, I first created a "BuildTheme" public sub:
B4X:
Public Sub BuildTheme
    MyTheme =ABMShared.MyTheme

    MyTheme.AddCellTheme("TopBarLogo")
    MyTheme.Cell("TopBarLogo").Align = ABM.CELL_ALIGN_CENTER
    MyTheme.Cell("TopBarLogo").VerticalAlign = True

    MyTheme.AddInputTheme("TopBarSearch")
    MyTheme.Input("TopBarSearch").BackColor = ABM.COLOR_GREY
    MyTheme.Input("TopBarSearch").BackColorIntensity = ABM.INTENSITY_LIGHTEN2
    MyTheme.Input("TopBarSearch").ForeColor = ABM.COLOR_BLUE
    MyTheme.Input("TopBarSearch").ForeColorIntensity = ABM.INTENSITY_DARKEN4
    MyTheme.Input("TopBarSearch").FocusForeColor = ABM.COLOR_BLUE
    MyTheme.Input("TopBarSearch").FocusForeColorIntensity = ABM.INTENSITY_DARKEN4
    MyTheme.Input("TopBarSearch").AutoCompleteBackColor = ABM.COLOR_DEEPPURPLE
    MyTheme.AddCellTheme("TopBarSearch")
    MyTheme.Cell("TopBarSearch").Align = ABM.CELL_ALIGN_CENTER
    MyTheme.Cell("TopBarSearch").VerticalAlign = True

    MyTheme.AddCellTheme("TopBarBtn")

    MyTheme.Cell("TopBarBtn").Align = ABM.CELL_ALIGN_CENTER
    MyTheme.Cell("TopBarBtn").VerticalAlign = True
    MyTheme.AddButtonTheme("TopBarBtn")
    MyTheme.Button("TopBarBtn").WavesCircle = True

    MyTheme.AddCellTheme("TopBarBtn3")
    MyTheme.Cell("TopBarBtn3").Align = ABM.CELL_ALIGN_CENTER
    MyTheme.Cell("TopBarBtn3").VerticalAlign = True
    MyTheme.AddButtonTheme("TopBarBtn3")
    MyTheme.Button("TopBarBtn3").BackColor = ABM.COLOR_GREY
    MyTheme.Button("TopBarBtn3").BackColorIntensity = ABM.INTENSITY_LIGHTEN2
    MyTheme.Button("TopBarBtn3").WavesCircle = False
    MyTheme.Button("TopBarBtn3").WavesEffect = ABM.WAVESEFFECT_NONE
    MyTheme.Button("TopBarBtn3").ForeColor = ABM.COLOR_BLUE
    MyTheme.Button("TopBarBtn3").ForeColorIntensity = ABM.INTENSITY_DARKEN4
End Sub
But the real magic in this was the very first line... that extended the existing theme into my Code Module
All I had the to do was call this sub from within the Shared CodeModule BuildTheme sub.
The only issue I still have is, I can't figure out how to theme the subcontainer.

Then, to make things a bit cleaner on the events side...
B4X:
Sub Page_ParseEvent(Params As Map)
    Dim eventName As String = Params.Get("eventname")
    Dim eventParams() As String = Regex.Split(",",Params.Get("eventparams"))
...
    If eventName = "topbarbtn1_clicked" Then
        CallSub2 (myTopBar, "TopBarBtn1_Clicked",Params.Get(eventParams(0)))
    End If
This way we can have the events declared inside our CodeModule keeping it all nice and neat!

Hope this helps somebody!
 
Last edited:

alwaysbusy

Expert
Licensed User
Longtime User
I think some bubbling is in order! (not the champagne unfortunately)

I think it should be possible to set a parents EventHandler to be propagated to all its children so we can replace:
B4X:
' NEW PART: we set for each component that we want the handler of the events here to this class
 TopBarContainer.EventHandler = Me
 TopBarSubContainer.EventHandler = Me
 TopBarLogo.EventHandler = Me
 TopBarSearch.EventHandler = Me
 TopBarBtn1.EventHandler = Me
 TopBarBtn2.EventHandler = Me
 TopBarBtn3.EventHandler = Me

by:
B4X:
' NEW PART: we set the handler of the events here to this class, and all 'child' components too
 TopBarContainer.EventHandler = Me

and still catch the btn1_clicked in the class set by its parent, the TopBarcontainer. Else, it would be hard to keep track of when to set the EventHandler.
 
Top