B4J Question Cannot recreate B4XRect after put it inside a List [SOLVED]

max123

Well-Known Member
Licensed User
Longtime User
Hi all,

I've this piece of code where there is a Select Case.

I pass to the sub a List of arguments as objects, the first argument Args.Get(0) is a B4XRect.
The problem here is that I cannot recreate a B4XRect passed as Object and the error occours
on the line Dim left As Float = rect.Left
B4X:
 Case "DrawRoundRectRotated"   ' DrawRoundRectRotated(Rect As B4XRect, Color As Int, Filled As Boolean, StrokeWidth As Float, CornerRadius As Float, Degrees As Float)
            Dim path As B4XPath
            Dim rect As B4XRect = Args.Get(0).As(B4XRect)
            Dim left As Float = rect.Left
            Dim top As Float = rect.Top
            Dim right As Float = rect.Right
            Dim bottom As Float = rect.Bottom
            rect.Initialize(left, top, right, bottom)
            path.InitializeRoundedRect(rect, Args.Get(4))
            cvs.DrawPathRotated(path, Args.Get(1), Args.Get(2), Args.Get(3), Args.Get(5), rect.CenterX, rect.CenterY) ' 0 degrees

On this line in Debug mode I can see the passed rectangle dimensions, but may it is transformed to string when iI put in the list ?
B4X:
Dim rect As B4XRect = Args.Get(0).As(B4XRect)

I construct the list this way, CM is a command and arguments separator, I cannot use just a single character like a comma
because need to be a pattern because I use some commands that accept strings and the split command confuse it.
Note that separator is not a problem, I've used it in other commands and in other projects and it works.
B4X:
Private Sub Class_Globals
    Private fx As JFX
    Private xui As XUI

    Private Const CM As String = "-,-" ' Separator
    Private DrawList As List    ' List of all drawings
    Private ArgList As List  ' List of all arguments

    Private mAutoInvalidate As Boolean = False
End Sub

'Similar to DrawRoundRect. Draw a rectangle with round corners and rotated by Degrees angle.
Sub DrawRoundRectRotated(Rect As B4XRect, Color As Int, Filled As Boolean, StrokeWidth As Float, CornerRadius As Float, Degrees As Float)
    ArgList.Initialize2(Array(Rect, Color, Filled, StrokeWidth, CornerRadius, Degrees))
    InvalidateCommand("DrawRoundRectRotated", ArgList)
End Sub

Private Sub InvalidateCommand(cmd As String, Args As List)
'    Try
        If mAutoInvalidate Then
            ParseCommand(cmd, Args)
        Else
            Dim oCmd As Object = cmd
            For Each o As Object In Args
                oCmd = oCmd & CM & o
            Next
            DrawList.Add(oCmd)
        End If
'    Catch
'        Log("ERROR: " & LastException)
'    End Try
End Sub

Here is how B4XRect looks inside the Args list:
(200.0, 200.0, 350.0, 350.0)

Attached the error log.

Thanks

Waiting for debugger to connect...
Program started.
600
600
STARTED
Resize
Error occurred on line: 208 (AsyncCanvas)
java.lang.RuntimeException: Method: getLeft not found in: java.lang.String
at anywheresoftware.b4a.shell.Shell$MethodCache.getMethod(Shell.java:891)
at anywheresoftware.b4a.shell.Shell.getMethod(Shell.java:539)
at anywheresoftware.b4a.shell.Shell.runMethod(Shell.java:628)
at anywheresoftware.b4a.shell.Shell.raiseEventImpl(Shell.java:234)
at anywheresoftware.b4a.shell.Shell.raiseEvent(Shell.java:167)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:111)
at anywheresoftware.b4a.shell.ShellBA.raiseEvent2(ShellBA.java:100)
at anywheresoftware.b4a.objects.Timer$TickTack$1.run(Timer.java:135)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:428)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:427)
at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:174)
at java.base/java.lang.Thread.run(Thread.java:834)
 
Last edited:

Daestrum

Expert
Licensed User
Longtime User
I recoded invalidateCommand does this work better for you?
B4X:
Sub Process_Globals
    Private fx As JFX
    Private MainForm As Form
    Private xui As XUI 
    Private Button1 As B4XView
    Dim DrawList As List
    Dim myRect As B4XRect
    Dim mAutoInvalidate As Boolean = False
End Sub

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

Sub Button1_Click
    myRect.Initialize(100,100,150,150)
    DrawRoundRectRotated(myRect,0xff0000,False,2.0,10.0,45.0)
    displayDrawList
End Sub

Sub DrawRoundRectRotated(Rect As B4XRect, Color As Int, Filled As Boolean, StrokeWidth As Float, CornerRadius As Float, Degrees As Float)
    InvalidateCommand(Array("DrawRoundRectRotated",Rect, Color, Filled, StrokeWidth, CornerRadius, Degrees))
End Sub

Private Sub InvalidateCommand(x() As Object)
    If mAutoInvalidate Then
        parseCommand(x)
    Else
        If x(1) Is B4XRect Then   ' command is x(0)  - Rect is x(1)
            Log("(Point 1) Found rect") ' Good. Here is B4XRect
        End If
        DrawList.Add(x)
    End If
End Sub

Sub parseCommand(x() As Object)
    ' no idea what goes here  lol
    Log("in ParseCommand")
End Sub

Sub displayDrawList
    Dim x() As Object = DrawList.Get(0)
    For Each item In x
        Log("item = " & GetType(item) & " --> " & item)
    Next
End Sub
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
I recoded invalidateCommand does this work better for you?
B4X:
Sub Process_Globals
    Private fx As JFX
    Private MainForm As Form
    Private xui As XUI
    Private Button1 As B4XView
    Dim DrawList As List
    Dim myRect As B4XRect
    Dim mAutoInvalidate As Boolean = False
End Sub

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

Sub Button1_Click
    myRect.Initialize(100,100,150,150)
    DrawRoundRectRotated(myRect,0xff0000,False,2.0,10.0,45.0)
    displayDrawList
End Sub

Sub DrawRoundRectRotated(Rect As B4XRect, Color As Int, Filled As Boolean, StrokeWidth As Float, CornerRadius As Float, Degrees As Float)
    InvalidateCommand(Array("DrawRoundRectRotated",Rect, Color, Filled, StrokeWidth, CornerRadius, Degrees))
End Sub

Private Sub InvalidateCommand(x() As Object)
    If mAutoInvalidate Then
        parseCommand(x)
    Else
        If x(1) Is B4XRect Then   ' command is x(0)  - Rect is x(1)
            Log("(Point 1) Found rect") ' Good. Here is B4XRect
        End If
        DrawList.Add(x)
    End If
End Sub

Sub parseCommand(x() As Object)
    ' no idea what goes here  lol
    Log("in ParseCommand")
End Sub

Sub displayDrawList
    Dim x() As Object = DrawList.Get(0)
    For Each item In x
        Log("item = " & GetType(item) & " --> " & item)
    Next
End Sub
Many Thanks, I will try it when I return to PC.
 
Upvote 0

Daestrum

Expert
Licensed User
Longtime User
I think the parse would look like this - some bits I was unsure of which values go where. Also only did DrawRoundRectRotated as thats all I have lol

B4X:
Sub parseCommand(x() As Object)
    ' x(0) is cmd - x(1) ... x(n) is the args
    Select x(0)
    Case "DrawRoundRectRotated" '    DrawRoundRectRotated(Rect As B4XRect, Color As Int, Filled As Boolean, StrokeWidth As Float, CornerRadius As Float, Degrees As Float)

        If x(1) Is B4XRect Then
            Log("(Point 2) Found rect")
        Else
            Log("(Point 2) Is not a rect") ' Bad. It is not recognized as B4XRect and this line will show on the log
        End If
       
        Dim path As B4XPath
        Dim r As B4XRect = x(1)
        Dim left As Float = r.Left
        Dim top As Float = r.Top
        Dim right As Float = r.Right
        Dim bottom As Float = r.Bottom
        r.Initialize(left, top, right, bottom)
        path.InitializeRoundedRect(r, x(5)) 
' not sure about next line - I'll leave it to you :)
        'cvs.DrawPathRotated(path, Args.Get(1), Args.Get(2), Args.Get(3), Args.Get(5), rect.CenterX, rect.CenterY) ' not sure of the args here
    End Select
End Sub
 
Last edited:
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
I recoded invalidateCommand does this work better for you?
B4X:
Sub Process_Globals
    Private fx As JFX
    Private MainForm As Form
    Private xui As XUI
    Private Button1 As B4XView
    Dim DrawList As List
    Dim myRect As B4XRect
    Dim mAutoInvalidate As Boolean = False
End Sub

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

Sub Button1_Click
    myRect.Initialize(100,100,150,150)
    DrawRoundRectRotated(myRect,0xff0000,False,2.0,10.0,45.0)
    displayDrawList
End Sub

Sub DrawRoundRectRotated(Rect As B4XRect, Color As Int, Filled As Boolean, StrokeWidth As Float, CornerRadius As Float, Degrees As Float)
    InvalidateCommand(Array("DrawRoundRectRotated",Rect, Color, Filled, StrokeWidth, CornerRadius, Degrees))
End Sub

Private Sub InvalidateCommand(x() As Object)
    If mAutoInvalidate Then
        parseCommand(x)
    Else
        If x(1) Is B4XRect Then   ' command is x(0)  - Rect is x(1)
            Log("(Point 1) Found rect") ' Good. Here is B4XRect
        End If
        DrawList.Add(x)
    End If
End Sub

Sub parseCommand(x() As Object)
    ' no idea what goes here  lol
    Log("in ParseCommand")
End Sub

Sub displayDrawList
    Dim x() As Object = DrawList.Get(0)
    For Each item In x
        Log("item = " & GetType(item) & " --> " & item)
    Next
End Sub
@Daestrum this may can work but separators are missing here:
B4X:
InvalidateCommand(Array("DrawRoundRectRotated",Rect, Color, Filled, StrokeWidth, CornerRadius, Degrees))

May it should be:
B4X:
InvalidateCommand(Array("DrawRoundRectRotated", CM, Rect, CM, Color, CM, Filled, CM, StrokeWidth, CM, CornerRadius, CM, Degrees))
 
Upvote 0

Daestrum

Expert
Licensed User
Longtime User
Why do you need the separators? you can access the data directly without the need of regex
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
I think the parse would look like this - some bits I was unsure of which values go where. Also only did DrawRoundRectRotated as thats all I have lol

B4X:
Sub parseCommand(x() As Object)
    ' x(0) is cmd - x(1) ... x(n) is the args
    Select x(0)
    Case "DrawRoundRectRotated" '    DrawRoundRectRotated(Rect As B4XRect, Color As Int, Filled As Boolean, StrokeWidth As Float, CornerRadius As Float, Degrees As Float)

        If x(1) Is B4XRect Then
            Log("(Point 2) Found rect")
        Else
            Log("(Point 2) Is not a rect") ' Bad. It is not recognized as B4XRect and this line will show on the log
        End If
  
        Dim path As B4XPath
        Dim r As B4XRect = x(1)
        Dim left As Float = r.Left
        Dim top As Float = r.Top
        Dim right As Float = r.Right
        Dim bottom As Float = r.Bottom
        r.Initialize(left, top, right, bottom)
        path.InitializeRoundedRect(r, x(5))
' not sure about next line - I'll leave it to you :)
        'cvs.DrawPathRotated(path, Args.Get(1), Args.Get(2), Args.Get(3), Args.Get(5), rect.CenterX, rect.CenterY) ' not sure of the args here
    End Select
End Sub
List can converted to regular arrays and viceversa, because do not use this ?
B4X:
Sub parseCommand(x As List)
    ' x.Get(0) is cmd - x.Get(1) ... x.Get(n) are the args
    Select x.Get(0)
        Case "DrawRoundRectRotated" '    DrawRoundRectRotated(Rect As B4XRect, Color As Int, Filled As Boolean, StrokeWidth As Float, CornerRadius As Float, Degrees As Float)

            If x.Get(1) Is B4XRect Then
                Log("(Point 2) Found rect")
            Else
                Log("(Point 2) Is not a rect") ' Bad. It is not recognized as B4XRect and this line will show on the log
            End If
    
            Dim path As B4XPath
            Dim r As B4XRect = x.Get(1)
            Dim left As Float = r.Left
            Dim top As Float = r.Top
            Dim right As Float = r.Right
            Dim bottom As Float = r.Bottom
            r.Initialize(left, top, right, bottom)
            path.InitializeRoundedRect(r, x.Get(5))
            ' not sure about next line - I'll leave it to you :)
            'cvs.DrawPathRotated(path, x.Get(1), x.Get(2), x.Get(3), x.Get(5), rect.CenterX, rect.CenterY) ' not sure of the args here
    End Select
End Sub

So just mantain original array list without need to combine draw command and arguments that seem the issue here.
' x.Get(0) is cmd - x.Get(1) ... x.Get(n) are the args
 
Upvote 0

Daestrum

Expert
Licensed User
Longtime User
its just x(1) not x.get(1)

All the data passed is in one array
The command is x(0)
the values are in x(1) .. x(n)
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
Note that I pass List as sub argument instead of regular array of Objects type, changed from your code.
 
Last edited:
Upvote 0

Daestrum

Expert
Licensed User
Longtime User
I missed where you had list, but I wonder why you have a list when you could just use the array.

x(0) produces far less code (probably) than x,get(0) (unless the compiler is being super efficient)
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
I missed where you had list, but I wonder why you have a list when you could just use the array.

x(0) produces far less code (probably) than x,get(0) (unless the compiler is being super efficient)
I can't tell you this, only Erel know it.

I think that List are regulars java ArrayList, so powerful and efficient, for sure wrapped by Erel adapted to B4X in a certain way...
I think but I'm not sure for this ...

Sometime I've exchanged ArrayList in some my B4X java libraries and if I remember, B4X accept it as regular List.

To know better it we have a way, just use a timer or Wait For to do 1.000.000 write/read using List and using Array, and calculate elapsed milliseconds with DateTime now for both and compare results.
 
Last edited:
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
You don't have apologize, I'm not sure, may you are right and I'm wrong.

Now I will try to adapt it, just I use a list as it is (original) without divide command name and arguments, but passing to subs as just a single List (or array).

If it is working I will release this library on the forum, may as b4xlib, if you see jAsyncCanvas it is my library, if you do not see it, I cannot solve some issues. I had the same issue with B4XPath, probably because it read as String instead of object.
 
Last edited:
Upvote 0

Daestrum

Expert
Licensed User
Longtime User
Good luck, hope you solve it.

( x(0) is about 75% less bytecode than x.get(0) )
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
So it looks like I will start with arrays. Thanks
I will post here if I encounter some other similar problems.

I want to Thank You @teddybear, even if I do not used your code, because nested lists, I really appreciated your help. and time you spent to help me with your code and suggestions.

Many Many Thanks both
 
Last edited:
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
Hi,

I started converting all my code, for now I just will use List to test and mantain some part of original code, next I will switch to simple arrays.

I completely removed the double argument in subs, so simplified a lot, even I do not use separator and regex.split at all.

The code now looks like that (that seem much better) and true objects are passed to final functions,
The argument list now contains as first argument the command name and starting from index 1 contains all arguments:
B4X:
'Draws the text at the given position. For alignment use one of following values "LEFT", "CENTER", "RIGHT".
Sub DrawText (Text As String, x As Double, y As Double, Font As B4XFont, Color As Int, Alignment As Object)
    ArgList.Initialize2(Array("DrawText", Text, x, y, Font, Color, Alignment))
    CommitCommand(ArgList)
End Sub

Private Sub CommitCommand(Args As List)
'    Try
        If mAutoInvalidate Then
            ParseCommand(Args)
        Else
            DrawList.Add(Args)
        End If
'    Catch
'        Log("ERROR: " & LastException)
'    End Try
End Sub

Private Sub ParseCommand(Args As List)
    count = count + 1
'    Log("ParseCommand: " & cmd & "   ARGS: " & Args)

'    Try
    Dim cmd As String = Args.Get(0) ' The command name
    Select cmd
        Case "Clear"
            Dim rect As B4XRect
            rect.Initialize(0, 0, pnl.Width, pnl.Height)
            cvs.DrawRect(rect, Args.Get(1), True, 0)
        Case "DrawText"
            cvs.DrawText(Args.Get(1), Args.Get(2), Args.Get(3), Args.Get(4), Args.Get(5), "LEFT")
    End Select
End Sub

Private Sub ExecuteDrawList(cmdList As List)

'    For cnt = 0 To cmdList.Size-1 ' For all draw commands
'        ParseCommand(cmdList.Get(cnt))
'    Next
 
    For Each o As Object In cmdList ' For all draw commands
        ParseCommand(o)
    Next
 
    DrawList.Clear  ' Finally clear the list
End Sub

'Commits the drawings.
' If AutoInvalidate is False this must be called for the drawings to be updated.
Sub Invalidate
    If mAutoInvalidate = False Then
        '    Log("(" & DateTime.Now & ")   -  Repaint")
        ExecuteDrawList(DrawList)
    End If
End Sub
And this worked.

Now I've another problem here, see DrawText last argument, if I put "LEFT", "CENTER", "RIGHT" in the ParseCommand sub it works, but if I put these in the calling functions I get an Exception, they are recognized as String because double quotes and not real costant.
B4X:
cvs.DrawText("MyName", 50, 300, xui.CreateDefaultBoldFont(80), xui.Color_Yellow, "LEFT")
This line does not works.

I pass Align as Object, but it is treated as String, but it should be javafx.scene.text.TextAlignment

All other objects are now treated as Objects, I do not yet tried B4XRect, but now it should work, I will test it now.

Please, how I can cast Alignment it the right way ?
 
Last edited:
Upvote 0

Daestrum

Expert
Licensed User
Longtime User
B4X:
Dim tAlign As JavaObject
tAlign.InitializeStatic("javafx.scene.text.TextAlignment")
...
cvs.DrawText("MyName", 50, 300, xui.CreateDefaultBoldFont(80), xui.Color_Yellow, tAlign.GetField("LEFT"))
...
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
Many thanks...

I need to put it inside a library sub.
What is best, pass it as string eg. "LEFT" or just create 3 costants, so you have:

cvs.AlignLEFT
cvs.AlignCENTER
cvs.AlignRIGHT

or may just...

cvs.LEFT
..........
..........

but can be confused with properties, just change to uppercase.

???
 
Upvote 0

Daestrum

Expert
Licensed User
Longtime User
or just use the string value from the list
B4X:
cvs.DrawText("MyName", 50, 300, xui.CreateDefaultBoldFont(80), xui.Color_Yellow, tAlign.GetField( yourArgumentFromList.toUpperCase ))
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
No, I mean to use costants like that in user side, and adapt it in the library using your JavaObject code.
B4X:
cvs.DrawText("MyName", 50, 300, xui.CreateDefaultBoldFont(80), xui.Color_Yellow, cvs.AlignLEFT)
or just....
B4X:
cvs.DrawText("MyName", 50, 300, xui.CreateDefaultBoldFont(80), xui.Color_Yellow, cvs.LEFT)   ' May not too good
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
or just use the string value from the list
B4X:
cvs.DrawText("MyName", 50, 300, xui.CreateDefaultBoldFont(80), xui.Color_Yellow, tAlign.GetField( yourArgumentFromList.toUpperCase ))
Now I know what you mean.... you mean to pass just a string eg. "LEFT" on user side....

What is best for your opinion ?
Use a costant or a string ?
Personally I prefer costant to avoid false strings typing, but this require to use cvs.mycostant notation.

May just have LEFT, CENTER, RIGHT as costants can be a solution.... but how, custom Type so it is viewed globally ?
May can conflict with other code ?
 
Upvote 0
Top