Italian B4J - Menu dinamici

ivanomonti

Expert
Licensed User
Longtime User
Notte fonda e mi chiedevo perchè indexOff mi da sempre -1 ?

B4X:
Public Sub mMenu_add(mMenuBar As MenuBar,menu As MenuItem)
    If mMenuBar.Menus.IndexOf("Resume") = -1 Then
        Dim mMenu As Menu
        mMenu.Initialize("Resume","")
        mMenuBar.Menus.Add(mMenu)
        mMenu.MenuItems.Add(menu)
    Else
        Dim mMenu As Menu =  mMenuBar.Menus.Get(mMenuBar.Menus.IndexOf("Resume"))
        mMenu.MenuItems.Add(menu)
    End If
End Sub
 

LucaMs

Expert
Licensed User
Longtime User
Notte fonda e mi chiedevo perchè indexOff mi da sempre -1 ?
In effetti è un po' ingeneroso; almeno la sufficienza potrebbe dartela ?

Su cosa esegui la funzione IndexOf? Su una List; ma di che tipo, stringa, per cui cerchi (chissà perché) "Resume"? No, è una lista di oggetti MenuItem:

1588487130404.png



Mi sveglio, bevo un caffè e te l'aggiusto.
 
Last edited:

LucaMs

Expert
Licensed User
Longtime User
Mi sveglio, bevo un caffè e te l'aggiusto.
Eh, te lo aggiusto... si fa per dire, in quanto la cosa non funziona come immaginavo.
Stavo scrivendo una routine in questo modo (che già in partenza non mi "piaceva" in quanto dovrebbe poter aggiungere nuovi menu non solo ad una MenuBar ma anche a dei MenuItem):

B4X:
' Qui, anziché passare un MenuBar, si dovrebbe passare un MenuItem,
' ovvero dare la possibilità di aggiungere un menu non soltanto ad un MenuBar
' ma anche ad un MenuItem, ovvero un sottomenu.
Public Sub AddMenu(Menu As MenuBar, NewMenuText As String)
    Dim MenuAlreadyExists As Boolean
   
    For Each Item As MenuItem In Menu.Menus
        If Item.Text = NewMenuText Then
            MenuAlreadyExists = True
            Exit
        End If
    Next
   
    If Not(MenuAlreadyExists) Then
        ' Il menu con testo NewMenuText non esiste, lo creo e lo aggiungo.
        Dim NewMenuItem As MenuItem
        NewMenuItem.Initialize(NewMenuText, NewMenuText)
        ' Menu. <--- qui avrei aggiunto NewMenuItem alla lista Menu.Menus; peccato che sia una lista in sola lettura!
    End If
End Sub

Ergo, si dovrà fare in modo diverso, ancora da scovare...
 

LucaMs

Expert
Licensed User
Longtime User
E' molto più incasinato di quanto pensassi!

Nell'immagine che ho aggiunto al mio primo post, in effetti si vede che il tipo è Menu, ma pensavo fosse un piccolo errore nella descrizione e che fosse MenuItem.
Beh, non è così. Esiste il tipo Menu, che ha una lista di MenuItem cui se ne possono aggiungere altri, mentre il tipo MenuBar ha una lista Menus che però è read-only!

Un po' un casino; anche perché non si può fare casting tra i vari tipi.
 

ivanomonti

Expert
Licensed User
Longtime User
@LucaMs il problema non è che è read-only! perche devo solo vedere se esiste, se non esiste lo creo, ma non lo legge nemmeno dopo creato..

li ceo cosi al via del main

qui li imposto

B4X:
    If Global.ToastMessage(mFooter,"Welcome back, I'm getting organized for work.") = True Then
        Global.mMenu_menu(mMenuBar,"Actions",Array As String("Login","New user","Close"))
        Global.mMenu_menu(mMenuBar,"Modules",Array As String("Organizer","Calendar"))
        Global.mMenu_menu(mMenuBar,"Additional modules",Array As String(""))
        Global.mMenu_menu(mMenuBar,"Resume",Array As String(""))
    End If

qui li istanzio e creo

B4X:
Public Sub mMenu_menu(mMenuBar As MenuBar,title As String, Args() As String)
    Dim mMenu As Menu
    mMenu.Initialize(title,"")
    mMenu.Tag=title
    mMenuBar.Menus.Add(mMenu)
    If Args(0)="" Then Return
    For i=0 To Args.Length-1
        Dim mi As MenuItem
        mi.Initialize(Args(i),"Actions")
        mMenu.MenuItems.Add(mi)
    Next
End Sub

qui aggiungo poi le voci mancanti

B4X:
Public Sub mMenu_add(mMenuBar As MenuBar,menu As MenuItem)
    If mMenuBar.Menus.IndexOf("Resume") = -1 Then
        Dim mMenu As Menu
        mMenu.Initialize("Resume","")
        mMenuBar.Menus.Add(mMenu)
        mMenu.MenuItems.Add(menu)
    Else
        Dim mMenu As Menu =  mMenuBar.Menus.Get(mMenuBar.Menus.IndexOf("Resume"))
        mMenu.MenuItems.Add(menu)
    End If
End Sub

nulla di che, ma se poi leggo il suo contenuto con Log(mMenuBar.Menus)

B4X:
(ObservableListWrapper) [Menu[id=null, styleClass=[menu-item, menu]], Menu[id=null, styleClass=[menu-item, menu]], Menu[id=null, styleClass=[menu-item, menu]], Menu[id=null, styleClass=[menu-item, menu]], Menu[id=null, styleClass=[menu-item, menu]]]
 

LucaMs

Expert
Licensed User
Longtime User
nulla di che,
"nulla di che" lo dici tu! Ripeto che quella tua routine non può funzionare (e oltretutto scritta così non ha senso, perché, se funzionasse e non può, aggiungerebbe sempre e solo un menu col testo "Resume", che hai scritto nella routine stessa:
B4X:
Public Sub mMenu_add(mMenuBar As MenuBar,menu As MenuItem)
    If mMenuBar.Menus.IndexOf("Resume") = -1 Then
        Dim mMenu As Menu
        mMenu.Initialize("Resume","")
        mMenuBar.Menus.Add(mMenu)
        mMenu.MenuItems.Add(menu)
    Else
        Dim mMenu As Menu =  mMenuBar.Menus.Get(mMenuBar.Menus.IndexOf("Resume"))
        mMenu.MenuItems.Add(menu)
    End If
End Sub

Tu stesso hai scritto che quella IndexOf ti restituisce sempre -1 e ti ho spiegato il motivo, perché tu gli fai cercare "Resume", che è una stringa, in una lista, Menus, che non è una lista contenente stringhe, ma oggetti di tipo Menu!
 

ivanomonti

Expert
Licensed User
Longtime User
2020-05-03_100025.jpg


Il problema che continua ad aggiungere menu resume ad ogni record che e da resumare hahahahah

B4X:
Public Sub LoadResumeItem(idlogin As String)
    
    If Global.SQL_Exists = False Then Return
    
    Dim sqlite As SQL
    sqlite.InitializeSQLite(File.DirApp,"data.sqlite", False)
    
    Dim SenderFilter As Object = sqlite.ExecQueryAsync("SQL", "SELECT fullname,id FROM item where userid='" & idlogin & "' and hide='false' order by fullname",Null)
    
    Wait For (SenderFilter) SQL_QueryComplete (Success As Boolean, rs As ResultSet)
    Dim cm As ContextMenu
    cm.Initialize("")
    
    If Success Then
        Dim i As Int
        Do While rs.NextRow
            i = i + 1
            Dim it As MenuItem
            it.Initialize(rs.GetString("fullname"),"resume")
            Dim lsTemp As List
            lsTemp.Initialize
            lsTemp.Add(rs.GetString("fullname"))
            lsTemp.Add(rs.GetString("id"))
            lsTemp.Add(Global.idlogin)
            it.Tag=lsTemp
            Global.mMenu_add(mMenuBar,it)
        Loop
        If i = 0 Then
            'nessun risultato trovato
            Log($"No results found"$)
        End If
        rs.Close
    Else
        Log(LastException)
    End If
End Sub

questo e solo il codice di quando seleziono la voce menu, tutto funziona perfettamente, ma crea tanti menu("Resume")

B4X:
Sub resume_Action
    Dim mi As MenuItem = Sender
    Dim lstemp As List = mi.Tag
    Dim sqlite As SQL
    sqlite.InitializeSQLite(File.DirApp,"data.sqlite", False)
    Dim query As String = "UPDATE associates SET hide = 'true'  WHERE idmaster='" & lstemp.Get(1) & "' OR idassociates='" & lstemp.Get(1) & "' AND userid='" & Global.idlogin & "';"
    sqlite.ExecNonQuery(query)
    query = "UPDATE item SET hide = 'true'  WHERE id='" & lstemp.Get(1) & "'AND userid='" & Global.idlogin & "';"
    sqlite.ExecNonQuery(query)
    mi.Enabled=False
    mClassOrganizer.loadItem
End Sub
 

ivanomonti

Expert
Licensed User
Longtime User
ho risolto in altro modo ma questa e il solito cerotto, a volte si dice "Funziona!!","allora che te frega", in realtà voglio gestirlo in maniera più corretta ma non so che cosa vuol dire corretta.

@LucaMs ho cercato come stringa e come menu (oggetto) il risultato e sempre lo stesso

2020-05-03_102702.jpg


B4X:
Public Sub LoadResumeItem(idlogin As String)
   
    If Global.SQL_Exists = False Then Return
   
    Dim sqlite As SQL
    sqlite.InitializeSQLite(File.DirApp,"data.sqlite", False)
   
    Dim SenderFilter As Object = sqlite.ExecQueryAsync("SQL", "SELECT fullname,id FROM item where userid='" & idlogin & "' and hide='false' order by fullname",Null)
   
    Wait For (SenderFilter) SQL_QueryComplete (Success As Boolean, rs As ResultSet)
    Dim cm As ContextMenu
    cm.Initialize("")

    Dim lsMenuItem As List
    lsMenuItem.Initialize
   
    If Success Then
        Dim i As Int
        Do While rs.NextRow
            i = i + 1
            Dim it As MenuItem
            it.Initialize(rs.GetString("fullname"),"resume")
            Dim lsTemp As List
            lsTemp.Initialize
            lsTemp.Add(rs.GetString("fullname"))
            lsTemp.Add(rs.GetString("id"))
            lsTemp.Add(Global.idlogin)
            it.Tag=lsTemp
            lsMenuItem.Add(it)
        Loop
        If i = 0 Then
            'nessun risultato trovato
            Log($"No results found"$)
        End If
        rs.Close
    Else
        Log(LastException)
    End If
   
    Global.mMenu_Resume(mMenuBar,"Resume",lsMenuItem)
   
End Sub


B4X:
Public Sub mMenu_Resume(mMenuBar As MenuBar, title As String, lsMenuItem As List)
   
    Dim mMenu As Menu
    mMenu.Initialize(title,"")
    mMenu.Tag=title
    mMenuBar.Menus.Add(mMenu)
    mMenu.MenuItems.AddAll(lsMenuItem)
   
End Sub
 
Last edited:

LucaMs

Expert
Licensed User
Longtime User
In realtà, quando devo risolvere un problemino, poi mi metto a sviluppare qualcosa che possa essere riutilizzato e che si il più generico ed ampio possibile.
Ecco perché avevo scritto:
C'è da lavorare un'oretta, fare una classe come si deve e in questo momento non mi va proprio.
In effetti, una classe (o modulo) che servisse a gestire una MenuBar, con possibilità di aggiungere, rimuovere, abilitare/disabilitare, salvare, etc. potrebbe essere utile.
Per il momento "mi accontento" di darti una mano.

Ti serve poter aggiungere "singole voci di menu" (ovvero senza sottomenu di queste) ad uno dei menu già presenti nella MenuBar1?

Questo lo capirei, per la voce del MenuBar1 "Additional modules" e sarebbe semplice.
Anche volendo fare la stessa cosa per il menu "Resume" (*).

Se invece vuoi poter aggiungere runtime qualunque "tipo" di menu, a qualunque livello, la cosa è più complicata e servirebbe appunto una classe (o modulo di codice) come detto all'inizio.


(*) A che serve il menu "Resume"?
 

LucaMs

Expert
Licensed User
Longtime User
Che poi, tra l'altro, tutti i menu, compresi quelli della menu bar principale (unica, direi) andrebbero creati a runtime, per un semplice motivo: poter "localizzare" il sw, ovvero poter scrivere le voci in varie lingue.
 

ivanomonti

Expert
Licensed User
Longtime User
@LucaMs i menu sono creati tutti a Runtime (guarda il codice) il menubar e di layout (che è vuoto) e a codice creo i menu dinamici con i sottomenu

MenuBar > Menus > MenuItem

Pare ovvio che quando creo iMenuItemper ilMenu "Resume" lo faccio dopo il login (caricare i dati a secondo il login che mi restituisce idlogin (member), quindi mediante la query richiamo ii record che sono in stato hide (nascosti) e quindi non visibili, in questo modo avendo il menu posso resumarli (facciamo una croce) hahahah che è diveso dal elimina, che li elimina in modo permanente.

Resume: lista di record messi nascosti con un hide=false
 

udg

Expert
Licensed User
Longtime User
Non vorrei complicarvi la vita, ma i Menu posti direttamente sulla MenuBar sono in numero predefinito (o hanno un max predefinito calcolato sulla lunghezza media della label e lo spazio disponibile)? In caso contrario, la MenuBar è (o si appoggia su) una horizontal scroll view di qualche genere?
 

LucaMs

Expert
Licensed User
Longtime User
Non vorrei complicarvi la vita, ma i Menu posti direttamente sulla MenuBar sono in numero predefinito (o hanno un max predefinito calcolato sulla lunghezza media della label e lo spazio disponibile)? In caso contrario, la MenuBar è (o si appoggia su) una horizontal scroll view di qualche genere?
Mo tel dico (provo a creare una 20na di menu direttamente nel Designer; se scoppia, significa che c'è un numero massimo ?)
 

LucaMs

Expert
Licensed User
Longtime User
Non vorrei complicarvi la vita, ma i Menu posti direttamente sulla MenuBar sono in numero predefinito (o hanno un max predefinito calcolato sulla lunghezza media della label e lo spazio disponibile)? In caso contrario, la MenuBar è (o si appoggia su) una horizontal scroll view di qualche genere?
Cmq, anche se io ed Ivano parliamo due lingue diverse ?, suppongo che lui voglia aggiungere sotto-menu a runtime, non menu della MenuBar.
Il che non significa che non si debba impostare un limite anche per questi, comunque!
 

udg

Expert
Licensed User
Longtime User
Dagli esempi mi sembrava che gli interessassero entrambe le possibilità.
In Android cè il menu di overflow (i tre puntini impilati che si vedevano sulla destra), ma non è che mi faccia impazzire. Uno scrolling orizzontale (XCLV o HSV) credo sia migliore, ma bisogna ricordarsi di segnalare graficamente la presenza di altre voci e poi "sincronizzare" la posizione del sub-menu con il menu selezionato.
Sempre che, come dicevi, il numero di Menu principali non sia limitato e calcolato in modo che rientri pienamente della MenuBar.
 

ivanomonti

Expert
Licensed User
Longtime User
ragazzi quello che volevo l'ho ottenuto giocando sul ricrea menu nella menubar1

MenuBar
MenuBar > Menus voce 1 | voce 2 | voce 3 |

MenuBar > Menus > ManuItem (dinamico che legge i record nel database)

voce 3.add(child 1)
voce 3.add(child 2)
voce 3.add(child 3)

a secondo dei record trovati

dovrebbe essere semplice in quanto i menu sono object, quindi si dovrebbero rintracciare con un get(i) is menu .... ma non è così come lo è per tutto il resto degli object, poi con calma trovo la soluzione corretta invece del cerotto
 
Top