B4J Question Context menu for multiple nodes

DonManfred

Expert
Licensed User
Longtime User
It doesn't indicate which button or label was clicked on.
You are responsible to get a reference between the Button and a Contextmenu respectively the MenuItem you added.

Look this Code. Read the Comments.

B4X:
Sub Class_Globals
    Private Root As B4XView
    Private xui As XUI
    Private Button1 As Button
    Private ctxMenues As Map ' Global Map to hold the References
End Sub

Public Sub Initialize
    B4XPages.GetManager.LogEvents = True
    ctxMenues.Initialize ' Initialize the Map
End Sub

'This event will be called once, before the page becomes visible.
Private Sub B4XPage_Created (Root1 As B4XView)
    Root = Root1
    Root.LoadLayout("MainPage")
    Dim ctxMenu As ContextMenu  
    ctxMenu.Initialize("ctxm")
    Dim MT() As String = Array As String("MI1","MI2","MI3")
    For i = 0 To MT.Length - 1
        Dim Mi As MenuItem
        Mi.Initialize(MT(i),"ctxm")
        ctxMenu.MenuItems.Add(Mi)
        ctxMenues.Put(Mi,Button1) ' These Contextmenuitem belongs to Button1

    Next
 
    Button1.As(JavaObject).RunMethod("setContextMenu",Array(ctxMenu))
End Sub

'You can see the list of page related events in the B4XPagesManager object. The event name is B4XPage.
Sub ctxm_Action
    Dim Mi As MenuItem = Sender
    Log(Mi.Text)
    Dim btn As Button = ctxMenues.Get(Mi) ' Get the Button based on the MenuItem used. Remember. All Menuitems you added are refering to Button1
    Log($"This Menuitem belongs to Button ${btn.Text}"$)
End Sub
Private Sub Button1_Click
    xui.MsgboxAsync("Hello world!", "B4X")
End Sub
 
Upvote 0

Tim Chapman

Active Member
Licensed User
Longtime User
I have gotten it to here if this helps anyone:
B4J code:
Sub Process_Globals
    Private MainForm As Form
    Private xui As XUI
    Private Button1 As Button
    Dim Label1 As Label
    Private ctxMenues As Map ' Global Map to hold the References
    Dim ctxMenu As ContextMenu
    Dim NodeList As List
    
End Sub

Public Sub AppStart (Form1 As Form, Args() As String)
    MainForm = Form1
    MainForm.RootPane.LoadLayout("Layout1") 'Load the layout file
    MainForm.Show
    MainForm.Title = "OpenAI Toolbox"
    ctxMenues.Initialize ' Initialize the Map
    NodeList.Initialize
    'NodeList = File.ReadList("D:\Downloads\B4J\_OpenAIUtils\Files", "NodeList.txt")
    
    NodeList.Add(Button1)
    NodeList.Add(Label1)
    
    For Each Node As JavaObject In NodeList
        ctxMenu.Initialize("ctxm")
        Dim MT() As String = Array As String("MI1","MI2","MI3")
        For i = 0 To MT.Length - 1
            Dim Mi As MenuItem
            Mi.Initialize(MT(i),"ctxm")
            ctxMenu.MenuItems.Add(Mi)
            ctxMenues.Put(Mi,Node) ' These Contextmenuitem belongs to Button1
        Next
 
        Node.As(JavaObject).RunMethod("setContextMenu",Array(ctxMenu))
    Next
End Sub

'You can see the list of page related events in the B4XPagesManager object. The event name is B4XPage.
Sub ctxm_Action
    Dim Mi As MenuItem = Sender
    Log(Mi.Text)
    Dim SenderType As String = ctxMenues.Get(Mi)
    Log("SenderType = " & SenderType)
    
    Dim startIndex As Int = SenderType.IndexOf("'") + 1 ' +1 to move past the first quote
    Dim endIndex As Int = SenderType.Length -1
    Dim tag As String = SenderType.SubString2(startIndex, endIndex) ' Get text between the quotes
    Log("Tag = " & tag)
End Sub

Private Sub Button1_Click
    xui.MsgboxAsync("Hello world!", "B4X")
End Sub
 
Last edited:
Upvote 0

Tim Chapman

Active Member
Licensed User
Longtime User
After much fiddling, I stumbled upon how to get the tag for the clicked node. Pretty funky!
The full project is attached.
Funky Code!:
Sub Process_Globals
    Private MainForm As Form
    Private xui As XUI
    Private Button1 As Button
    Dim Label1 As Label
    Private ctxMenues As Map ' Global Map to hold the References
    Dim ctxMenu As ContextMenu
    Dim NodeList As List
    Dim NodeNames As List
    
End Sub

Public Sub AppStart (Form1 As Form, Args() As String)
    MainForm = Form1
    MainForm.RootPane.LoadLayout("Layout1") 'Load the layout file
    MainForm.Show
    MainForm.Title = "OpenAI Toolbox"
    ctxMenues.Initialize ' Initialize the Map
    NodeList.Initialize
    NodeNames.Initialize
    'NodeList = File.ReadList("D:\Downloads\B4J\_OpenAIUtils\Files", "NodeList.txt")
    
    NodeList.Add(Button1)
    NodeList.Add(Label1)
    
    NodeNames.Add("Button1")
    NodeNames.Add("Label1")
    
    For Each Node As JavaObject In NodeList
        Dim NodeText As String = NodeNames.Get(NodeList.IndexOf(Node))
        Log("NodeText = " & NodeText)
        ctxMenu.Initialize("ctxm")
        Dim MT() As String = Array As String("MI1","MI2","MI3")
        For i = 0 To MT.Length - 1
            Dim Mi As MenuItem
            Mi.Initialize(MT(i),"ctxm")
            'Log("Node as String = " & Node.As(String))
            
            'Setting UserData for each MenuItem here, before it's added to the context menu\
            'NOTE:  This actually sets the Tag for the MenuItem.  An unexpected benefit!!!!
            Mi.As(JavaObject).RunMethod("setUserData", Array(NodeText))
            
            ctxMenu.MenuItems.Add(Mi)
            ctxMenues.Put(Mi,Node)
        Next
        Node.As(JavaObject).RunMethod("setContextMenu",Array(ctxMenu))
        
    Next
End Sub

Sub ctxm_Action
    Dim Mi As MenuItem = Sender
    Log(Mi.Text)

    Log("Sender.Tag = " & Mi.Tag)
    
End Sub

Private Sub Button1_Click 'NOTE:  This has to be _Click instead of _MouseClicked(EventData As MouseEvent).  If it is _MouseClicked(EventData As MouseEvent), the normal button click is triggered when the button is right clicked.  I don't yet know a way to fix this.
    xui.MsgboxAsync("Hello world!", "B4X")
End Sub
 

Attachments

  • ContextMenu.zip
    2.6 KB · Views: 9
Last edited:
Upvote 0

Tim Chapman

Active Member
Licensed User
Longtime User
Now, I want to get it work using a list of nodes from a text file. This code below won't work. Does anyone know how to make it work?
I want to be able to read the list of nodes from the text file and reference the objects they pertain to.
The nodes are already defined in the Process_Globals (Button1 and Label1).
They are already initialized in Sub AppStart


Non-working code.:
Sub Process_Globals
    Private MainForm As Form
    Private xui As XUI
    Private Button1 As Button
    Dim Label1 As Label
    Private ctxMenues As Map ' Global Map to hold the References
    Dim ctxMenu As ContextMenu
    Dim NodeList As List
    Dim NodeNames As List
   
End Sub

Public Sub AppStart (Form1 As Form, Args() As String)
    MainForm = Form1
    MainForm.RootPane.LoadLayout("Layout1") 'Load the layout file
    MainForm.Show
    MainForm.Title = "OpenAI Toolbox"
    ctxMenues.Initialize ' Initialize the Map
    NodeList.Initialize
    NodeNames.Initialize
    NodeList = File.ReadList("D:\Downloads\B4J\_OpenAIUtils\Files", "NodeList.txt")
   
    'NodeList.Add(Button1)
    'NodeList.Add(Label1)
   
    'NodeNames.Add("Button1")
    'NodeNames.Add("Label1")
   
    For Each Node As JavaObject In NodeList
        Dim NodeText As String = NodeNames.Get(NodeList.IndexOf(Node))
        Log("NodeText = " & NodeText)
        ctxMenu.Initialize("ctxm")
        Dim MT() As String = Array As String("MI1","MI2","MI3")
        For i = 0 To MT.Length - 1
            Dim Mi As MenuItem
            Mi.Initialize(MT(i),"ctxm")
            'Log("Node as String = " & Node.As(String))
           
            'Setting UserData for each MenuItem here, before it's added to the context menu\
            'NOTE:  This actually sets the Tag for the MenuItem.  An unexpected benefit!!!!
            Mi.As(JavaObject).RunMethod("setUserData", Array(NodeText))
           
            ctxMenu.MenuItems.Add(Mi)
            ctxMenues.Put(Mi,Node)
        Next
        Node.As(JavaObject).RunMethod("setContextMenu",Array(ctxMenu))
       
    Next
End Sub

Sub ctxm_Action
    Dim Mi As MenuItem = Sender
    Log(Mi.Text)

    Log("Sender.Tag = " & Mi.Tag)

End Sub

Private Sub Button1_Click 'NOTE:  This has to be _Click instead of _MouseClicked(EventData As MouseEvent).  If it is _MouseClicked(EventData As MouseEvent), the normal button click is triggered when the button is right clicked.  I don't yet know a way to fix this.
    xui.MsgboxAsync("Hello world!", "B4X")
End Sub
 

Attachments

  • ContextMenu.zip
    2.8 KB · Views: 12
Last edited:
Upvote 0

William Lancee

Well-Known Member
Licensed User
Longtime User
The following is a re-write of your code in post #6. In B4X nodes are called views.
Views can be cast to B4XViews, irrespective of what kind they are. B4XViews are also cross platform.

Views (and b4j nodes) are complex objects and can not be stored in a .txt file. But their names can be stored and re-attached to
their objects.

The MouseEvent object has methods to detect which button is pressed.
Because we use B4XView, we can attach right click menus to any type View.

Does the following (see attached zip) do what you want?

B4X:
Sub Process_Globals
    Private MainForm As Form
    Private xui As XUI
    Private Button1 As Button
    Private Label1 As Label
    Private ctxMenues As Map     'Global Map to hold the References
    Private ViewLookup As Map     'Global map to associate view names with view objects
End Sub

Public Sub AppStart (Form1 As Form, Args() As String)
    MainForm = Form1
    MainForm.RootPane.LoadLayout("Layout1") 'Load the layout file
    MainForm.Show
    MainForm.Title = "OpenAI Toolbox"

    ViewLookup.Initialize
    ViewLookup.Put("Btn1", Button1)
    ViewLookup.Put("Lbl1", Label1)
    
    PrepareMenuFile
    ctxMenues.Initialize ' Initialize the Map
    Dim mdata As List = File.ReadList(File.DirApp, "menu_data.txt")

    Dim currentViewName As String
    Dim ctxMenu As ContextMenu
    Dim View As B4XView
    For Each s As String In mdata
        If Not(s.StartsWith(TAB)) Then
            If ctxMenu.IsInitialized Then View.As(JavaObject).RunMethod("setContextMenu",Array(ctxMenu))
            Dim ctxMenu As ContextMenu
            ctxMenu.Initialize("ctxm")
            currentViewName = s
        Else
            Dim Mi As MenuItem
            Mi.Initialize(s.trim, "ctxm")
            Log(currentViewName & TAB & ViewLookup.ContainsKey(currentViewName))
            View = ViewLookup.Get(currentViewName)
            Mi.Tag = View
            ctxMenu.MenuItems.Add(Mi)
            ctxMenues.Put(Mi, View)
        End If
    Next
    If ctxMenu.IsInitialized Then View.As(JavaObject).RunMethod("setContextMenu",Array(ctxMenu))
End Sub

Private Sub PrepareMenuFile
    Dim menuData As String = $"
Btn1
    Item1 text 123
    Item2 text 456
    Item3 text 789
Lbl1
    Item1 text abc
    Item2 text def
    Item3 text ghi
    "$
    Dim output As List
    output.Initialize
    For Each s As String In Regex.split(CRLF, menuData)
        If s.Trim.Length > 0 Then output.Add(s)
    Next
    File.WriteList(File.DirApp, "menu_data.txt", output)
End Sub

Private Sub ctxm_Action
    Dim mi As MenuItem = Sender
    Log(mi.Tag.As(B4XView).text & TAB & mi.Text)
End Sub

Private Sub Button1_MouseClicked(Ev As MouseEvent)
    If Ev.PrimaryButtonPressed Then  xui.MsgboxAsync("Hello world!", "B4X")
End Sub
 

Attachments

  • ContextMenu.zip
    2.8 KB · Views: 10
Upvote 0

Tim Chapman

Active Member
Licensed User
Longtime User
Thank you both for your replies. I am trying to find a way to do this without have to make individual entries for each node (view).
I have 230 nodes to make this work for. I have implemented this here in my example code:
 
Upvote 0
Top