B4J Library [B4J][B4Xlib] WiLXLayouts - a Cross-platform Layout Conversion Library

Currently, you can do cross-platform layout conversion, for example xyz.bjl to xyz.bal, by:
1. open a new B4J project
2. copy xyz.bjl to its Files folder
3. open the projects .b4j in the B4J IDE
4. use the Files tab to add xyz.bjl
5. go to the internal designer, open the xyz layout file
6. select all views and copy them to the clipboard
7. close the B4J IDE and open the target B4A IDE
8. go to internal designer and open a new empty layout
9. paste the clipboard content to the layout and save as xyz.bal

You need to do steps 2, 4, 5, 8 and 9 for every layout and every variant you want to convert.
If you forget to check the XUI views and BCEngine libraries in the B4A IDE Library panel
you'll find you have to do things over again.

The Internal Designer does its conversion magic in a black box, so we can't see what changes it made.
This process is cumbersome and invites errors that can remain hidden until some expected event happens.

There are ways to convert layout files (.bjl, .bal, .bil) to .json files and back.
But these different platform-specific .json files are not compatible with each other.
The .json files contain all the layout information but they are overly bulky and difficult to review.
Moreover, the layout and .json files have many elements that are unrelated to the salient features of layout design.

So here is my solution.
1. Extract the salient information from the .json files and use that to make a generic layout .txt file
2. Generate any of the three platform layouts from the generic layout .txt file
This way any of (.bjl, .bal, .bil) can be converted to any of (.bjl, .bal, .bil)

Consider a layout file named 'complex' as displayed in the following screenshot:
complex.png


If we can achieve this:

complex_bjl.json → complex_bjl.txt=complex_bal.txt=complex_bil.txt → complex_bjl.json
complex_bal.json → complex_bjl.txt=complex_bal.txt=complex_bil.txt → complex_bal.json
complex_bil.json → complex_bjl.txt=complex_bal.txt=complex_bil.txt → complex_bil.json

Then we will have reached our final goal if and only if for each of the three lines above:
the first json equals the last json and the middle layer files are all the same.

I have attached the WiLXLayouts.b4xlib and an example app that uses it.
Don't forget to copy WiLXLayouts.b4xlib to your Additional folder
In the next few posts I will describe in more detail how to use it.
 

Attachments

  • WiLXLayouts.zip
    278.8 KB · Views: 43
  • WiLXLayouts.b4xlib
    47.7 KB · Views: 43
Last edited:

William Lancee

Well-Known Member
Licensed User
Longtime User
This project was introduced in This Thread

At the end of this post is the complete middle layer text file for the layout in post #1
Here are things to note:
  1. The file is a plain .txt file.
  2. Some lines have multiple key-value pairs, but that is to group the items into natural clusters to make it more readable.
    The items could be put on separate lines with the same effect
  3. The order of key-value pairs is not important
  4. Keys and non-quoted values are case-insensitive.
  5. Delimiters can be space, comma, or TAB (which the converter uses to generate .txt files).
  6. Comments are denoted by a single quote and are ignored by the parser.


B4X:
'________________________________________
name=Pane1    type=Panel
eventName=Pane1
enabled=true    visible=true
   
width=130    fromLeft=10
height=80    fromTop=10
   
baseColor=0xFFE6E6FF
borderWidth=2    borderColor=Black    borderRadius=3
'________________________________________
name=Label1    type=Label
parent=Pane1    eventName=Label1
enabled=true    visible=true
   
width=*    fromLeft=10    fromRight=10
height=*    fromTop=10    fromBottom=10
   
baseColor=DEFAULT
   
textContent="12pt\nTop Left"
fontColor=Black    fontSize=12
vAlign=CENTER    hAlign=CENTER    wordWrap=false
'________________________________________
name=Pane2    type=Panel
eventName=Pane2
enabled=true    visible=true
   
width=130    fromRight=10
height=80    fromTop=10
   
baseColor=0xFFE6E6FF
borderWidth=2    borderColor=Black    borderRadius=3
'________________________________________
name=Label2    type=Label
parent=Pane2    eventName=Label2
enabled=true    visible=true
   
width=*    fromLeft=10    fromRight=10
height=*    fromTop=10    fromBottom=10
   
baseColor=DEFAULT
   
textContent="14pt\nTop Right"
fontColor=Black    fontSize=14
vAlign=CENTER    hAlign=CENTER    wordWrap=false
'________________________________________
name=Pane3    type=Panel
eventName=Pane3
enabled=true    visible=true
   
width=130    fromLeft=10
height=80    fromBottom=10
   
baseColor=0xFFE6E6FF
borderWidth=2    borderColor=Black    borderRadius=3
'________________________________________
name=Label3    type=Label
parent=Pane3    eventName=Label3
enabled=true    visible=true
   
width=*    fromLeft=10    fromRight=10
height=*    fromTop=10    fromBottom=10
   
baseColor=DEFAULT
   
textContent="16pt\nBottom Left"
fontColor=Black    fontSize=16
vAlign=CENTER    hAlign=CENTER    wordWrap=false
'________________________________________
name=Pane4    type=Panel
eventName=Pane4
enabled=true    visible=true
   
width=130    fromRight=10
height=80    fromBottom=10
   
baseColor=0xFFE6E6FF
borderWidth=2    borderColor=Black    borderRadius=3
'________________________________________
name=Label4    type=Label
parent=Pane4    eventName=Label4
enabled=true    visible=true
   
width=*    fromLeft=10    fromRight=10
height=*    fromTop=10    fromBottom=10
   
baseColor=DEFAULT
   
textContent="18pt\nBottom Right"
fontColor=Black    fontSize=18
vAlign=CENTER    hAlign=CENTER    wordWrap=false
'________________________________________
name=Pane5    type=Panel
eventName=Pane5
enabled=true    visible=true
   
width=500    fromLeft=50    hcentered=ignoreFromLeft
height=80    fromTop=250    vcentered=ignoreFromLeft
   
baseColor=DEFAULT
borderWidth=1    borderColor=Black    borderRadius=3
'________________________________________
name=Label5    type=Label
parent=Pane5    eventName=Label5
enabled=true    visible=true
   
width=*    fromLeft=0    fromRight=0
height=*    fromTop=0    fromBottom=0
   
baseColor=DEFAULT
   
textContent="22pt\nThe Quick Brown Fox Jumped Over The Lazy Dog"
fontColor=Black    fontSize=22
vAlign=CENTER    hAlign=CENTER    wordWrap=false
'________________________________________
name=Pane6    type=Panel
eventName=Pane6
enabled=true    visible=true
   
width=60    fromLeft=280    hcentered=ignoreFromLeft
height=100    fromTop=10
   
baseColor=DEFAULT
borderWidth=1    borderColor=Black    borderRadius=3
'________________________________________
name=Label6    type=Label
parent=Pane6    eventName=Label6
enabled=true    visible=true
   
width=*    fromLeft=5    fromRight=5
height=*    fromTop=5    fromBottom=5
   
baseColor=DEFAULT
   
textContent="E"
fontSize=65
vAlign=CENTER    hAlign=CENTER    wordWrap=false
'________________________________________
name=Pane7    type=Panel
eventName=Pane7
enabled=true    visible=true
   
width=60    fromLeft=280    hcentered=ignoreFromLeft
height=100    fromBottom=10
   
baseColor=DEFAULT
borderWidth=1    borderColor=Black    borderRadius=3
'________________________________________
name=Label9    type=Label
parent=Pane7    eventName=Label9
enabled=true    visible=true
   
width=*    fromLeft=5    fromRight=5
height=*    fromTop=5    fromBottom=5
   
baseColor=DEFAULT
   
textContent="E"
fontSize=65    fontStyle=BOLD
vAlign=CENTER    hAlign=CENTER    wordWrap=false
'________________________________________
name=Pane8    type=Panel
eventName=Pane8
enabled=true    visible=true
   
width=110    fromLeft=260
height=110    fromTop=350
   
file="tomatoes.jpg"    gravity=Fill
borderWidth=1    borderColor=Black    borderRadius=3
'________________________________________
name=Pane9    type=Panel
eventName=Pane9
enabled=true    visible=true
   
width=120    fromLeft=250
height=90    fromTop=140
   
gradient=LEFT_RIGHT    firstColor=Blue    secondColor=Red
borderWidth=1    borderColor=Black    borderRadius=3
 
Last edited:

William Lancee

Well-Known Member
Licensed User
Longtime User
Take a look at the middle layer .txt file shown in post 2. If you scroll down to Label1, you'll see how I have implemented anchors.
Widths and heights have either fixed or inherited values.
If fixed then fromLeft or fromRight have the usual meaning for left and right anchors
If width is inherited then the value is "*" and there will be BOTH fromLeft and fromRight values
If height is inherited then the value is "*" and there will be BOTH fromTop and fromBottom values

I found this to be the clearest way to capture the notion of anchors in readable way.

Scroll down to Pane5.
You'll see:
width=500 fromLeft=50 hcentered=ignoreFromLeft​
height=80 fromTop=250 vcentered=ignoreFromTop​

These centered items are derived from the designer script and will be converted to target designer script
 
Last edited:

William Lancee

Well-Known Member
Licensed User
Longtime User
This is the middle layer .txt script for a simple CustomListView.
Note that there is no need to show that it is a CustomView, it is handled automatically.
This also show what happens to view specific key-value pairs. They are listed without alteration.
If a key-value pair is left out, it will receive the default value.
Color values are by name (case-insensitive) if the code is on the official 140 HTML web color names list:
https://www.w3schools.com/tags/ref_colornames.asp
example: borderColor=Black
If a color is not on the list then it is in B4X hexidecimal format, for example: dividerColor=0xFFD9D7DE

B4X:
'________________________________________
name=CustomListView1    type=CustomListView
eventName=CustomListView1
enabled=true    visible=true
   
width=100    fromLeft=100
height=100    fromTop=100
   
baseColor=DEFAULT
borderWidth=1    borderColor=Black    borderRadius=3
   
textContent=""
fontSize=15
vAlign=CENTER    hAlign=CENTER    wordWrap=false
   
dividerColor=0xFFD9D7DE
dividerHeight=2
insertAnimationDuration=300
listOrientation=Vertical
pressedColor=0xFF7EB4FA
showScrollBar=true
 

William Lancee

Well-Known Member
Licensed User
Longtime User
Property values are all of the string type, object types (i.e. ValueType: Value maps, B4A padding [5,5,5,5]) are handled automatically.

The structure of the layout (i.e. which view contains other views) is derived from the parent=viewName property.
This avoids trying to navigate the .txt file. The parent reference is all that is needed to define the tree.

WiLXLayouts can handle multiple variants - these generate multiple .txt files in the middle layer, which are then re-combined in the final layer
This process includes Designer Scripts involved in centering views - but not any other script (these are not necessarily meaningful cross platform)

The whole conversion process relies heavily on resource tables that have information about structure, colors, value type, and
a complete catalog of all default properties of all views valid in all three platforms. These resource tables are included in the library.

If a view is not present in the catalog entry for a target platform, a placeholder label view (same size and position) will be generated.
It will have text describing what it is replacing, so you can decide what to do about it - most often the best policy is to use
cross-platform XUI Views instead - they are present in all platforms.

I should admit that this project has taken a lot more effort than I anticipated.
B4J, B4A, B4i have evolved over time and it this shows in the layout formats.
Backward compatibility required that these diverging formats could not be refactored.
The shining light are the cross-platform views from the XUI Views library, they are 100% consistent.

If you unzip the WiLXLayouts.b4xlib, you'll see three modules:
"..\WiLConverter.bas"
"..\WiLLayoutWriter.bas"
"..\WiLLayoutReader.bas"

The last two are directly taken from @Erel BalConverter library. I separated out the writer from the reader, changed a few names,
and added a log of missing file references. Otherwise it is the same.
I would not have attempted this project without this library and the openness that @Erel provides. Thank you.
 

William Lancee

Well-Known Member
Licensed User
Longtime User
Opportunities:

1. The generic text file is useful for its own sake - it provides a quick catalogue of views in a layout
2. It can also be generated from other non-B4X layouts, like HTML, or 3rd party design software
3. You can create simple layouts in a text editor and convert them to any B4X layout

Special B4i layout limitations:
1. There are no BitmapDrawable and GradientDrawable backgrounds​
2. Vertical text alignments defaults to "Center"​
3. B4i Layout files have a series of standard subviews in the Main panel toolBar0 to toolBar4 and topRight0 to topRight4​
They are automatically added when converting to .bil, but any modificaton that you made to the standard "Main" are NOT converted​
4. I don't have any iOS devices to test the B4i implementation fully - but I have made sure that the test .bil layouts are faithfully converted​
A special thanks to @Mashiane who gracefully provided me with .bil file that contains all the default views​
(Ultimately , I did have to get my own copy of the B4i IDE)​
5. Don't expect chained conversions to work. The final layout of: complex_bjl.json → complex_bal.json → complex_bjl.json​
will have missing views and missing properties.​

Why are the three text files generated by WiLXLayouts not the same if the original .bil, .bal, and .bil layouts are the same?

Good Question, Expected differences:
1. Name of the root view, by default "Main" for .bjl and .bil, but "Activity" for .bal.
2. Platform specific properties, e.g ellipsize in .bal and autoCorrection in .bil
3. Platform specific ommissions or implemenations of views, such as Slider, ToggleButton

I am still always surprised at the efficiency of the B4X system. I used hundreds of B4X maps to account for all the variations.
I use a map for clarity and convenience of coding and reading instead of optimizing for speed (using indexed arrays).
The converter module has just over 2000 lines of code.
On my CPU, a full conversion of a layout with multiple variants and multiple output files takes typically only 30 to 100 msecs in Release mode.
 
Last edited:

William Lancee

Well-Known Member
Licensed User
Longtime User
An important note.

Custom views other than from the XUI Views library will be shown as placeholders.

As stated earlier, the conversion depends on a catalog of defaults for each platform.
I is beyond the scope of the project to get defaults for customs views that are not in the catalog.
Moreover it would be a moving target as new custom views are created and old custom views are retired.

The best I could do was to put a Label with the same left, top, width, height, hanchor, and vanchor as the custom view.
 
Last edited:

William Lancee

Well-Known Member
Licensed User
Longtime User
You may have noticed that "Main" and "Activity" are NOT in the middle layer .txt files.
After trying different scenarios, I concluded that for X-Platform conversions, any changed properties in Main and Activity would be meaningless.
Therefore the target final layout files will have their own platform default Main or Activity root views.

edit: I will make some changes to WiLXlayouts because during testing I realized that by removing "main"
backgroundColor and title are not captured. (These changes will be posted in a day or two)


Edit: Version 2.20 puts a skeleton Main section in the output layout text file

To make the .txt files consistent, parent references to "Activity" are changed to "Main" (since both B4J and B4i have this as their root name).

B4J has TextArea and TextField
B4A has EditText (single line, and multi line)
B4i has TextView and TextField

I kept TextArea and TextField as a .txt types, but changed single line EditText to TextField and multiLine EditText to TextArea
I also changed TextView to TextArea. These changes affect only the middle layer. The changes are reversed as needed for their targets.

Similarly:
Pane in B4J is changed to Panel
ScrollPane in B4J is changed to ScrollView
ProgressView in B4A is changed to ProgressBar
SeekBar in B4A is changed to Slider

B4i does not have a CheckBox or ToggleButton. They are mapped to Switch.
However, I recommend using XUI Views equivalents. They will work in all three platforms in a consistent way.

This is especially so for ListView. Its behaviour has platform specific limitations. CustomListView is a much better choice

When all is said and done, after using WiLXLayouts, you will still need to check and test the results.
If you're new to X-platform work, remember that you also need modify event handling for the target platform (except for XUI Views).
 
Last edited:

William Lancee

Well-Known Member
Licensed User
Longtime User
V2.01

I have added missing comments for the Public subs in the WilConverter module.
I have also changed the names of the two major subs (now: 'toTextFile' and 'toLayoutFile') to be more natural and consistent.

I have attached the new versions of the b4xlib and the example app to post #1.
 

William Lancee

Well-Known Member
Licensed User
Longtime User
V2.10 (I skipped a few versions)

I have added some feature that accesses the converter without generating intermediate files.
It lets you convert lists of generic views (i.e. the like the middle layer .txt files) directly to target layouts.
You could create your own generic scripts and generate the desired layout file.
You could also process layout formats from other sources (HTML, third party designer software) and generate layout files for any of the three platforms.
One other technique may be to convert a layout to generic in code and edit it it and then convert the new generic back to the layout.

The new subs are 'toTextMap' and 'mapToLayout'

I also added a parameter to the initialize sub to disable the writing of intermediate .json files.
These files were used heavily during testing but may not be of interest anymore.

The new version of the WiLXLayouts in post #1. I have added the new parameter to the original example in post #1
The example showing these new features is attached below.

B4X:
    convert.Initialize(False)    'True will copy the input and output json files to the input and output folders respectively

    'This will be a map of variants - the key will be the variant id and the value will be list of generic views
    Dim generic As Map = convert.toTextMap(inLayoutsDir, "complex.bjl")
   
    For Each kw As String In generic.Keys
        Dim genericViews As List = generic.Get(kw)
        For Each line As String In genericViews
            Log(line)
        Next
    Next
   
    convert.mapToLayout(generic, outLayoutsDir, "complex.bjl")
 

Attachments

  • WiLXLayoutsV2_10.zip
    271.8 KB · Views: 51
Last edited:

William Lancee

Well-Known Member
Licensed User
Longtime User
V2.20

This is a important change to V2.10.
After experimenting with converting B4X layout file to Generic Text File to HTML, I realized that
I was wrong in leaving out the Main/Activity view.

It holds two pieces of commonly customized layout information - baseColor and title.

Therefore, I modified the generic text layout to convey this info.

It looks like this example (note absence of name and all the default elements of platform specific Mains):
B4X:
#if layout_text

'________________________________________
type=Main
eventName=MainForm
    
width=600
height=600
    
baseColor=0xFFAFF9BD
    
title="Test Translation to HTML"


#end if

The width and height info is taken from the variant information.
These are ignored when creating B4X layout files, since the designer knows the variants and can use anchors.
However in my experiment with HTML I used these for computing position and shape when anchors are specified.

I have updated the previous posts to reflect this change. You need to download V2.20 in post #1 and update your Additional folder.
The example in #1 is also updated to use the new writeJsonFile parameter in .Initialize.

I attach a proof-of-concept example of translating a B4J Designer layout to an HTML file.
The attached example draws heavily on the excellent MiniHTML library created by @aeric

You can download the html example and review its code to see how it is done.
To run the example the MiniHTML library needs to be downloaded and copied to your Additional folder.

testHTML.png
 

Attachments

  • tryMiniHTML.zip
    20.6 KB · Views: 44
Top