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:

max123

Well-Known Member
Licensed User
Longtime User
I suppose this is wrong, seem it convert arguments to string:
B4X:
oCmd = oCmd & CM & o

But how do it? I need to wrap command and arguments in the same list item but without use a concatenation &.

Here I test it and it looks that it is a rectangle:
B4X:
Private Sub InvalidateCommand(cmd As String, Args As List)
    If mAutoInvalidate Then
        ParseCommand(cmd, Args)
    Else
        Dim oCmd As Object = cmd
        For Each o As Object In Args
            If o Is B4XRect Then
                Log("(Point 1) Found rect") ' Good. Here is B4XRect
            End If
            oCmd = oCmd & CM & o
        Next
        DrawList.Add(oCmd)
    End If
End Sub

But after I manage it and add to the draw list with DrawList.Add(oCmd) and call a Case,
it is not recognized as B4XRect, but as string, something like this line show:
B4X:
Log(Rect)

Here the code:
B4X:
        Case "DrawRoundRectRotated" '    DrawRoundRectRotated(Rect As B4XRect, Color As Int, Filled As Boolean, StrokeWidth As Float, CornerRadius As Float, Degrees As Float)

            If Args.Get(0) 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 = Args.Get(0)
            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, Args.Get(4))
            cvs.DrawPathRotated(path, Args.Get(1), Args.Get(2), Args.Get(3), Args.Get(5), rect.CenterX, rect.CenterY)
 
Last edited:
Upvote 0

teddybear

Well-Known Member
Licensed User
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) '<=This is wrong'
            Dim left As Float = rect.Left
B4X:
            Dim oCmd As Object = cmd
            For Each o As Object In Args
                oCmd = oCmd & CM & o
            Next
            DrawList.Add(oCmd)
Dim rect As B4XRect = Args.Get(0).As(B4XRect) is wrong, because you have casted args to String in sub InvalidateCommand .
so the log is java.lang.RuntimeException: Method: getLeft not found in: java.lang.String
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
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) '<=This is wrong'
            Dim left As Float = rect.Left
B4X:
            Dim oCmd As Object = cmd
            For Each o As Object In Args
                oCmd = oCmd & CM & o
            Next
            DrawList.Add(oCmd)
Dim rect As B4XRect = Args.Get(0).As(B4XRect) is wrong, because you have casted args to String in sub InvalidateCommand .
so the log is java.lang.RuntimeException: Method: getLeft not found in: java.lang.String
Yes thanks for reply @teddybear , it should not be a string, but object.

This line is wrong:
B4X:
oCmd = oCmd & CM & o
because it cast to a String, any argument should be an object and I need to combine cmd (That is a string DrawRoundRectRotated) that is a string and can be casted as Object, with arguments in a single line item... any as Object, even with separators each one.

A final item should be like that: DrawRoundRectRotated-,-Arg0-,-Arg1-,-etc...

How concatenate it ?
 
Last edited:
Upvote 0

teddybear

Well-Known Member
Licensed User
Yes thanks for reply @teddybear , it should not be a string, but object.

This line is wrong:
B4X:
oCmd = oCmd & CM & o
because it cast to a String, any argument should be an object and I need to combine cmd (That is a string DrawRoundRectRotated) with arguments in a single line item... with separators each one.

A final item should be like that: DrawRoundRectRotated-,-Arg0-,-Arg1-,-etc...

How concatenate it ?
If you get Arg0 is like (200.0, 200.0, 350.0, 350.0) , you can use regex to split it to left, top....
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
If you get Arg0 is like (200.0, 200.0, 350.0, 350.0) , you can use regex to split it to left, top....
Mmmmmm, not good..... I've a lots like these..... and I not happy with the idea, I need to pass inalterated objects to avoid problems.
 
Upvote 0

teddybear

Well-Known Member
Licensed User
Mmmmmm, not good..... I've a lots like these..... and I not happy with the idea, I need to pass inalterated objects to avoid problems.
So you have to redesign the oCmd structure/type
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
Yes, and how change this part ?
B4X:
            Dim oCmd As Object = cmd
            For Each o As Object In Args
                oCmd = oCmd & CM & o ' << Is wrong
            Next
            DrawList.Add(oCmd)

After I fill the list and call Invalidate command I already call this that use split... it extract each command on DrawList and call a sub with Select Case:
B4X:
Sub ExecuteDrawList(cmdList As List)
    Dim msg As String
 
    For cnt = 0 To cmdList.Size-1 ' For all draw commands
 
        msg = cmdList.Get(cnt)
 
'        Log("ExecuteDrawList:  (" & (cnt+1) & ")  " & msg )
  
        Dim ListItems As List
        ListItems.Initialize2(Regex.Split(CM, msg))
        Dim cmd As String = ListItems.Get(0)
        Dim Args As List : Args.Initialize
        For i = 1 To ListItems.Size-1
            Args.Add(ListItems.Get(i))
        Next
 
        ParseCommand(cmd, Args)
    Next
 
    DrawList.Clear
End Sub

May this is wrong too and I should convert to Object not String ?
But here only cmd is String, arguments added to the list are treated as Objects is previous sub send List as objects, or I'm wrong ?
This worked on another project, where the list is received fron UDP socket.

In substance in my code I check if AutoInvalidate is True, if it is True, it draw the Canvas and Invalidate each command (the normal B4J JFX Canvas use), If user set AutoRepaint False, as default on B4A Canvas, then the code put all consecutive commands to the List.

Next when the user call Invalidate, this sub I posted here is called and every command in the list is executed one by one, at the end I clear the list to be used next cycle.

I still create a wrapper for B4XCanvas, but with (or without) autorepaint function, the user can activate/deactivate it by code.
 
Last edited:
Upvote 0

Daestrum

Expert
Licensed User
Longtime User
I would just have a list within a list small example based on your code

B4X:
Sub Process_Globals
    Private fx As JFX
    Private MainForm As Form
    Private xui As XUI 
    Private Button1 As B4XView
    Dim argList As List
    Dim DrawList As List
    Dim myRect As B4XRect
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, 5.0)
End Sub


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, xArgs As List)

    Dim tempList As List
    tempList.Initialize
    
    tempList.Add(cmd)
    tempList.Add(xArgs)
    
    DrawList.Add(tempList)
    
    Log("Draw List = " & DrawList)
    
    ' read it back to check - this would be where you use it to draw probably
    Dim retCmd As String = DrawList.Get(0).As(List).Get(0)
    Dim xtempList As List = DrawList.Get(0).As(List).Get(1)
    
    Log("Command = " & retCmd)
    
    For Each item As Object In xtempList
        Log("item   " & GetType(item) & " --> " &item)
    Next

End Sub
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
Thanks @teddybear I will try it in my project ...
And post here results... ;)

I don't know much this piece of code:
B4X:
    ' read it back to check - this would be where you use it to draw probably
    Dim retCmd As String = DrawList.Get(0).As(List).Get(0)
    Dim xtempList As List = DrawList.Get(0).As(List).Get(1)
 
    Log("Command = " & retCmd)
 
    For Each item As Object In xtempList
        Log("item   " & GetType(item) & " --> " &item)
    Next

So use nested Lists o_O ?
Is not possible to avoid this... and just use the object itself as is ?
I would avoid to do all this work on around 30-40 use Cases in Select.
 
Last edited:
Upvote 0

Daestrum

Expert
Licensed User
Longtime User
B4X:
DrawList.Get(0).As(List).Get(0)

This reads the first item in DrawList as a list, and then reads the first item in that list. Which would be your Command
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
This worked on another project where I just send all the msg over wifi (UDP socket) from an ESP8266 microcontroller that never know B4X classes, just as plain string text... but then I regex.split it and not manipulate argumets.
so should be another way.... I want avoid nested Lists
 
Upvote 0

Daestrum

Expert
Licensed User
Longtime User
Ok, was just a suggestion. No problem.
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
Yes, many thanks to @teddybear for great support and time, and you @Daestrum for your suggestion.
I perfectly know that is just a suggestion, no problem at all... :D

I need a simpler solution, may use arrays when manipulate arguments ? List worked very fast.
 
Last edited:
Upvote 0

Daestrum

Expert
Licensed User
Longtime User
How about this way
B4X:
Sub newDrawRoundRectRotated(Rect As B4XRect, Color As Int, Filled As Boolean, StrokeWidth As Float, CornerRadius As Float, Degrees As Float)
    DrawList.add(Array("DrawRoundRectRotated",Rect, Color, Filled, StrokeWidth, CornerRadius, Degrees))
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
Upvote 0

Daestrum

Expert
Licensed User
Longtime User
Obviously I had get(0) because I only had one item in the list, but you would have it in a forEach loop
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
This seem cannot work I think, I need an intermediate sub where I check if AutoInvalidate is true or false:
B4X:
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
            If o Is B4XRect Then
                Log("(Point 1) Found rect") ' Good. Here is B4XRect
            End If
            oCmd = oCmd & CM & o
        Next
        DrawList.Add(oCmd)
    End If

'    Catch
'        Log("ERROR: " & LastException)
'    End Try
End Sub
 
Upvote 0

Daestrum

Expert
Licensed User
Longtime User
What does ParseCommand do ?
 
Upvote 0

max123

Well-Known Member
Licensed User
Longtime User
ParseCommands just have a long Select Case, it is called every command if AutoInvalidate is True, or it is called when AutoInvalidate is False and user do Invalidate, in this situation ExecuteDrawCommands will be called, extract every command and arguments in the list item and call ParseCommand, do it for all collected items in the list.... if you see I iterate all list items, each contain a draw command, on this the first argument always is the command name, eg "DrawRoundRectRotated", then there are arguments (as objects) separated by separator that regex.split use,.

After a list is fully read and every command processed, I will clear the list for next cycle.

Here is how ParseCommand looks....
B4X:
Private Sub ParseCommand(cmd As String, Args As List)
    count = count + 1
'    Log("ParseCommand: " & cmd & "    ARGS: " & Args)

'    Try

    Select cmd
       Case "Clear"
            Dim rect As B4XRect
            rect.Initialize(0, 0, pnl.Width, pnl.Height)
            cvs.DrawRect(rect, Args.Get(0), True, 0)
        Case "ClearRect"
            cvs.ClearRect(Args.Get(0))
'        Case "ClipPath"
'            cvs.ClipPath(Args.Get(0))
        Case "DrawBitmap"
            cvs.DrawBitmap(Args.Get(0), Args.Get(1))
        Case "DrawBitmapRotated"
            cvs.DrawBitmapRotated(Args.Get(0), Args.Get(1), Args.Get(2))
        Case "DrawCircle"
            cvs.DrawCircle(Args.Get(0), Args.Get(1), Args.Get(2), Args.Get(3), Args.Get(4), Args.Get(5))
        Case    "DrawLine"
            cvs.DrawLine(Args.Get(0), Args.Get(1), Args.Get(2), Args.Get(3), Args.Get(4), Args.Get(5))
'        Case "DrawPath"
'            cvs.DrawPath(Args.Get(0), Args.Get(1), Args.Get(2), Args.Get(3))
'        Case "DrawPathRotated"
'            cvs.DrawPathRotated(Args.Get(0), Args.Get(1), Args.Get(2), Args.Get(3), Args.Get(4), Args.Get(5), Args.Get(6))
        Case "DrawRect"
            cvs.DrawRect(Args.Get(0), Args.Get(1), Args.Get(2), Args.Get(3))
         
        Case "DrawRectRotated"
            Dim path As B4XPath
            Dim rect As B4XRect = Args.Get(0)
            rect.Initialize(rect.Left, rect.Top, rect.Right, rect.Bottom)
            path.InitializeRoundedRect(rect, 0)
            cvs.DrawPathRotated(path, Args.Get(1), Args.Get(2), Args.Get(3), Args.Get(4), rect.CenterX, rect.CenterY)

        Case "DrawRoundRect"   '    DrawRoundRect(Rect As B4XRect, Color As Int, Filled As Boolean, StrokeWidth As Float, CornerRadius As Float)
         
            If Args.Get(0) 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 = Args.Get(0)
            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, Args.Get(4))
            cvs.DrawPathRotated(path, Args.Get(1), Args.Get(2), Args.Get(3), 0, rect.CenterX, rect.CenterY) ' 0 degrees
     
        Case "DrawRoundRectRotated" '    DrawRoundRectRotated(Rect As B4XRect, Color As Int, Filled As Boolean, StrokeWidth As Float, CornerRadius As Float, Degrees As Float)

            If Args.Get(0) 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 = Args.Get(0)
            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, Args.Get(4))
            cvs.DrawPathRotated(path, Args.Get(1), Args.Get(2), Args.Get(3), Args.Get(5), rect.CenterX, rect.CenterY)
     
     
        Case "DrawText"
            cvs.DrawText(Args.Get(0), Args.Get(1), Args.Get(2), Args.Get(3), Args.Get(4), "LEFT")
        Case "DrawTextRotated"
            cvs.DrawTextRotated(Args.Get(0), Args.Get(1), Args.Get(2), Args.Get(3), Args.Get(4), Args.Get(5), Args.Get(6))
        Case ""
             
         
    End Select
     
'    Catch
'        Log("ERROR -> " & LastException)
'    End Try
 
End Sub
There are other functions to add, eg. DrawOval, DrawOvalRotated, and others.... I already coded on another project.
 
Last edited:
Upvote 0
Top