B4J Question (Solved) How to calculate the Main Form depending on the screen ratio (16:9, 4:3)?

BlueVision

Active Member
Licensed User
Longtime User
I have a problem with the screen ratio in B4J. Primarily it is about the format adjustment between a 16:9 and a 4:3 display.
The screen sizes can be determined without any problems:
1920 x 1280 on a 4:3 display.
1366 x 728 on a 16:9 display
My development environment is the PC with the 16:9 display.

The problem:
I define the size of the app in the sub Appstart in the main. There I define top, left, width and height. The rest of the scaling is done by anchors and the designer script and works very well.
Due to the different ratio, the app will of course be distorted on a 4:3 display. An element displayed square on the 16:9 display then becomes an upright rectangle on the 4:3 display. I have not yet found a way to define the ratio determined at the start of the app in any way in the main form according to the display size. I can get the screen dimensions, assign values, but dynamic calculations to adjust are not possible in this sub.

Does anyone have a solution to this problem?
 

emexes

Expert
Licensed User
What is the difference between "dynamic calculations" and normal a = b + c calculations?

Or maybe I am misunderstanding "I can get the screen dimensions". Does this mean you are manually looking up the specifications for your phone's display eg www.gsmarena.com, rather than getting the current device values eg 100%x and 100%y ?

If you are using designer scripts and working in DIPs, then keeping square elements square should be no problem.

If you post or message what the layout should look like (bonus: on two devices with different display ratios) and the designer script, there are many keen layout artists here who will probably jump on the challenge for fun. Your biggest problem might then be that layouts are like economics: if you ask n experts for an opinion, you generally receive n + m responses ? but too many is better than none, right? ?
 
Upvote 0

emexes

Expert
Licensed User
After thinking about other stuff, but with your aspect ratio query simmering away in the background...

is the question how to fit a square something into a rectangular (maybe square, maybe not) area?

in which case maybe try something like:

B4X:
SquareSize = Minimum(AreaWidth, AreaHeight)
    'or if no Minimum function, then:
    'If AreaWidth < AreaHeight Then
    '    SquareSize = AreaWidth
    'Else
    '    SquareSize = AreaHeight
    'End If
SquareTop = AreaTop + (AreaHeight - SquareSize) / 2
SquareLeft = AreaLeft + (AreaWidth - SquareSize) / 2
 
Upvote 0

BlueVision

Active Member
Licensed User
Longtime User
First of all, thank you for your answer. I'm afraid I need to explain my problem better.

Primarily, the 16:9 layout should fit both a 16:9 screen and a 4:3 screen.
In the case of 16:9, 1366/725 gives a ratio of 1.89.
In the case of 4:3, 1920/1280 gives a ratio of 1.54.
These values are not absolutely built into the layout, they only served me for understanding.
The designer script calculates with percentages. With different screen formats, distortions naturally occur on a 4:3 display if the app was created on a 16:9 display.
Now you could determine the current ratio on the device in the designer script during programme execution and "straighten out" these distortions by means of a calculation in the script itself.
With a large designer script (currently about 600 lines due to many elements and a complex app), it would then be very time-consuming to create approximately square shapes with the help of a ratio formula in the script.
With the amount of code, however, I don't want to create a second layout variant, which would then also have to be updated with every change.

So what can the solution look like? The actual task is to display an app in 16:9 format also in 16:9 format on a 4:3 display without distortion.

My approach is to adapt the shape definition of the app to the current display right at the start of the programme (app start in the main screen) because of the dimensions of the screen. In other words: the app is programmed in 16:9, on a 4:3 display the height of the shape is reduced and I save myself all the wild calculations with the ratio in the script because the form itself then has the format 16:9 on a 4:3 display.

I'm not quite 100% sure, but the following code showed decent results on various screens:

Preserving 16:9 formatted app dimensions on a 4:3 screen:
Sub AppStart (Form1 As Form, Args() As String)
    MainForm = Form1
    Dim PagesManager As B4XPagesManager
    Dim PS As Screen = FX.PrimaryScreen
'    shrinking width to 90% of the screen
    Dim Width = PS.MaxX*.9 As Double
'    calculating 16:9 height depending on shrinked width
    Dim Height = Width*.56 As Double
'    calculating ratio only for comparison
    Dim Ratio = Height/Width As Double
    Log ("Ratio   : " & Ratio)
'    calculating gap to screen top/bottom for defining top of app
    Dim Gap = (PS.MaxY-Height)/2 As Double
    MainForm.BackColor = FX.Colors.Transparent
'    centering left/right
    MainForm.WindowLeft = PS.MinX + PS.MaxX*.05
'    setting formwidth
    MainForm.WindowWidth = Width
'    setting top of form   
    MainForm.WindowTop = PS.MinY + Gap
'    setting formheight   
    MainForm.WindowHeight = Height
'    limitation of downsizing the form below 75%   
    MainForm.SetWindowSizeLimits(MainForm.WindowWidth*.75, MainForm.WindowHeight*.75, MainForm.WindowWidth, MainForm.WindowHeight)
    PagesManager.Initialize(MainForm)
    MainForm.Show
End Sub
 
Upvote 0

emexes

Expert
Licensed User
The designer script calculates with percentages. With different screen formats, distortions naturally occur on a 4:3 display if the app was created on a 16:9 display.

1/ I had a project a few years back that would fit a single layout, which I think I'd done with an aspect ratio halfway between 4:3 and 16:9 = 1.5396, and then expanded that to fit maximally in the current screen, leaving a bit of black on the sides on a 4:3 display (ie layout thinner than 4:3) or at the top and bottom on a 16:9 display (ie layout shorter than 16:9). I will dig that code up. I vaguely remember it involved adding an otherwise-unused panel sized to represent the nominal 1.5396 layout area.

2/ heck, I've forgotten what the second thought was :rolleyes: oh yeah: The designer script can calculate with percentages. But it can calculate in absolutes too, ie what the view dimensions actually are in pixels (or is it DIPs?) with same size/scale vertically and horizontally (cf 1%x is probably different size to 1%y)
 
Last edited:
Upvote 0

emexes

Expert
Licensed User
I will dig that code up.

Uh, it was for B4A and B4I, not B4J.

So it's going to need some work to get it going on B4J.

On the bright side, at least in B4J the window can be resized on the fly, instead of needing multiple devices with different screen aspect ratios to test.
 
Upvote 0

BlueVision

Active Member
Licensed User
Longtime User
On the bright side, at least in B4J the window can be resized on the fly, instead of needing multiple devices with different screen aspect ratios to test.

This can be true horror! And this here was only for a square, honestly I am new to B4J and have to learn about the differences compared to B4A. I remember programming my compass app for android. The circled compass was driving me nuts because of the different screen sizes used on the mobile phones.
 
Upvote 0

emexes

Expert
Licensed User
I re-enacted the basics of my resizer. It works pretty good, resizes text too. But it definitely looked better on phones - I think because on black phones with black screen backgrounds, the padding wasn't as obvious. Plus, of course, the B4J window can be varied heaps, from super narrow to super wide, whereas phone displays occupy a more limited range of aspect ratios that don't change on the fly in front of you.

In action, it looks like this:

1673059215523.png


1673059260265.png


1673059235979.png


1673059951243.png
 
Last edited:
Upvote 0

emexes

Expert
Licensed User
What the resizer needs is a pane that represents the area to be fitted to the screen, with a tag "AspectSizer" so that the resizer can easily and reliably find it.

Ideally it should be at the top of the tree, ie the bottom layer, and I usually make it invisible (so it doesn't hide anything) and disable it (so it doesn't hijack any clicks).

1673059517911.png


Then click the copy icon at the top-right of the code box below, then go to B4J, click in code window, Ctrl+A to select all, Ctrl+V to paste, F5 to compile and run. What could possibly go wrong?

Copy-and-paste this over top of fresh B4J UI project:
#Region Project Attributes
    #MainFormWidth: 600
    #MainFormHeight: 600
#End Region

Sub Process_Globals
    Private fx As JFX
    Private MainForm As Form
    Private xui As XUI
    Private Button1 As B4XView
  
    Type LayoutCopyType(Node As Node, Width As Int, Height As Int, Left As Int, Top As Int, Tag As String, TextSize As Float)
    Dim LayoutCopy As List
  
End Sub

Sub AppStart (Form1 As Form, Args() As String)
    MainForm = Form1
    MainForm.RootPane.LoadLayout("Layout1")
    CopyLayout
    MainForm.Show
  
    ResizeLayoutToFitWindow
  
End Sub

Sub Button1_Click
    '''xui.MsgboxAsync("Hello World!", "B4X")
    ResizeLayoutToFitWindow
End Sub

Sub MainForm_Resize (Width As Double, Height As Double)
    ResizeLayoutToFitWindow 
End Sub

Sub CopyLayout

    LayoutCopy.Initialize
  
    Dim FoundAspectFixerFlag As Boolean = False 
    For Each n As Node In MainForm.RootPane.GetAllViewsRecursive
        If n.PrefWidth > 0 And n.PrefHeight > 0 And n.Left >= 0 And n.Top >= 0 Then
            Dim ThisNode As LayoutCopyType
            ThisNode.Node = n
            ThisNode.Width = n.PrefWidth
            ThisNode.Height = n.PrefHeight
            ThisNode.Left = n.Left
            ThisNode.Top = n.Top
            ThisNode.Tag = n.Tag
          
            'text resizing
            If n Is Button Then
                ThisNode.TextSize = n.As(Button).TextSize
            else if n Is Label Then
                ThisNode.TextSize = n.As(Label).TextSize
            Else
                ThisNode.TextSize = 1    'just in case
            End If
              
            If ThisNode.Tag.Trim.ToLowerCase = "aspectfixer" Then
                LayoutCopy.InsertAt(0, ThisNode)
                FoundAspectFixerFlag = True
            Else
                LayoutCopy.Add(ThisNode)
            End If
        End If
    Next

    If FoundAspectFixerFlag = False Then
        Log("add full-size pane with tag = AspectFixer")
        LayoutCopy.Clear
    End If
  
End Sub

Sub ResizeLayoutToFitWindow

    If LayoutCopy.Size = 0 Then
        Return 
    End If

    Dim ThisNode As LayoutCopyType = LayoutCopy.Get(0)
  
    Dim LayoutWidth As Int = ThisNode.Width
    Dim LayoutHeight As Int = ThisNode.Height
      
    Dim WindowWidth As Int = MainForm.Width
    Dim WindowHeight As Int = MainForm.Height
  
    Dim ScaleX As Float = WindowWidth / LayoutWidth
    Dim ScaleY As Float = WindowHeight / LayoutHeight
      
    Dim Scale As Float
    Dim OffsetX As Int = 0
    Dim OffsetY As Int = 0
    If ScaleX < ScaleY Then
        Scale = ScaleX
        OffsetY    = (WindowHeight - LayoutHeight * Scale) / 3    'biased towards top
    Else
        Scale = ScaleY
        OffsetX    = (WindowWidth - LayoutWidth * Scale) / 2
    End If

    Dim TextScale As Float = Sqrt(Scale)    'enlarge/shrink text slower than everything else
  
    For I = 0 To LayoutCopy.Size - 1
        ThisNode = LayoutCopy.Get(I)
      
        ThisNode.Node.PrefWidth = ThisNode.Width * Scale
        ThisNode.Node.PrefHeight = ThisNode.Height * Scale
        ThisNode.Node.Left = OffsetX + ThisNode.Left * Scale
        ThisNode.Node.Top = OffsetY + ThisNode.Top * Scale

        'text resizing
        If ThisNode.Node Is Button Then
            ThisNode.Node.As(Button).TextSize = ThisNode.TextSize * TextScale
        else if ThisNode.Node Is Label Then
            ThisNode.Node.As(Label).TextSize = ThisNode.TextSize * TextScale
        End If
    Next
  
End Sub
 
Upvote 0

BlueVision

Active Member
Licensed User
Longtime User
You can create layouts with different variant in Designer.
I know...
Hello Aeric. The task was somewhat imprecisely defined, my mistake. I didn't want to go down this route, because the Designer script is very extensive. The task was rather to create and define an app in 16:9 and then display it on a 4:3 screen in 16:9 without any major distortions.
With two layout variants, both variants would then always have to be updated when changes are made. I wanted to save myself this work, so I looked for another way here in the thread and found it (also thanks to some inspiration from Emexes).
 
Upvote 0

Elric

Well-Known Member
Licensed User
I think you have different solutions:
1. You may study this: https://www.b4x.com/android/forum/threads/b4x-dse-designer-script-extensions.141312/
2. You may work with designer script
3. You may work with proportion, as per following example:
B4X:
Private Sub AdaptViewsToScreen
   
    Dim f As Form = B4XPages.GetNativeParent(Me)
   
    Private pnl As B4XView
    Private lbl As B4XView
    Private ImgVw As B4XImageView
    'Etc.'
   
    For i = 0 To 1 'or the total number of similar views - you may find a more elegant style for such cycle
        Select i
            Case 0
                pnl = Pane1
                lbl = Label1
                ImgVw = B4XImageView1
            Case 1
                pnl = Pane2
                lbl = Label2
                ImgVw = B4XImageView2
        End Select
       
        Dim fH As Double = f.WindowHeight
       
        Dim myPaneHeight As Double = fH * (0.33144) ' 0.33144 is just an example - parenthesis is just for my confort
       
        pnl.Width = pnl.Height * (0.7428) ' 0.7428 is just an example  - parenthesis is just for my confort
           
        lbl.Width = pnl.Width * (0.923) ' This is just an example  - parenthesis is just for my confort
        ImgVw.mBase.Width = pnl.Width * (0.846)
           
        lbl.Height = pnl.Height * (0.143)
        ImgVw.mBase.Height = pnl1.Height * (0.600)
           
        lbl.Left = pnl.Width * (0.038)
        ImgVw.mBase.Left = pnl1.Width * (0.077)
           
        lbl.Top = pnl.Height * (0.029)
        ImgVw.mBase.Top = pnl1.Height * (0.200)
    Next
End Sub

You may also combine 2 and 3.
 
Upvote 0
Top