Então......
Pense em recursividade, no que precisa pro menu, e pode criar um de níveis ilimitados.
Não é tão difícil, é só pensar passo a passo, e na vida útil das variáveis.
Tem que pensar no tipo de variável que não vai causar confusão no entendimento do que ela representa.
Isso é importantíssimo, principalmente no codeblock.
Código: Selecionar todos
FOR nCont = 1 TO 10
? nCont
bCode := { || nCont }
NEXT
? nCont
Supondo que o codeblock fosse uma ACTION de menu.
O valor vai ser 11.
Tá errado ?
Lógico que não.
O que está lá no codeblock ?
nCont
O codeblock vai usar a variável nCont
Coisas vão, coisas vém, até que chega o momento de executar o codeblock.
Na execução do codeblock, será usado o valor de nCont no momento da execução.
Quanto será o valor de nCont ?
Quando termina o FOR/NEXT, nCont contém 11, então esse será o retorno.
Supondo um array de programas:
Código: Selecionar todos
aList := { "prog1", "prog2", "prog3" }
FOR nCont = 1 TO 3
bAction := { || aList[ nCont ] }
NEXT
Mesma coisa.
O codeblock é PRA VARIÁVEL aList[ nCont ]
Qual programa será executado ?
Nenhum, vai dar erro.
Porque ?
O codeblock é alist[ nCont ]
o conteúdo de nCont será 4
aList[ nCont ] vai dar erro porque não existe elemento 4.
Entendeu a dificuldade ?
O codeblock é o que ele contém, NÃO É o que VOCÊ PENSA que ele contém
O codeblock é um pedaço de fonte.
esse pedaço de fonte contém aList[ nCont ]
esse pedaço de fonte NÃO contém alist[1], nem aList[2], nem aList[3]
É só essa a dificuldade de criar menu, ou coisas parecidas.
Mas tem outra coisa que atrapalha, que não dá pra perceber, na MINIGUI.
O arquivo CH da minigui transforma isso em codeblock.
Isso é transformado em { || Eval( bCode ) }
Entendeu ? voltamos ao mesmo problema anterior.
vai ser executada a variável bCODE.
Qual o valor de BCODE?
Ainda não se sabe, com certeza não é o que você acabou de colocar.
Vai ser o valor da variável BCODE de quando for feita a execução.
ACTION é uma coisa do futuro, vai ser executada no futuro.
Você precisa tomar todas as precauções para que quando chegue o futuro, esse conteúdo seja o mesmo.
E na minigui isso se multiplica, porque mesmo tomando todo cuidado com a variável, tem esse detalhe do CH não deixar ver essa transformação em codeblock.
Em último caso, o mais prático seria uma função:
Código: Selecionar todos
CriaOpcaoMenu( cTexto, cCodeBlock )
MENUITEM cTexto ACTION Eval( cCodeBlock )
RETURN Nil
O que acontece criando função assim ?
A variável é única, interna da função, o conteúdo dela nunca vai mudar.
Outro jeito que inventei de fazer, e deu certo, é usando FOR/EACH
A variável do FOR/EACH também é única.
Pode usar uma ou mais.
Note que o que o valor pro FOR/EACH será um array, um pra cada variável.
Código: Selecionar todos
FOR nCont = 1 TO 10 // esta variável muda
FOR EACH A IN { nCont }
bCode := { || A } // como a variável A é do FOR/EACH, o valor está garantido
NEXT
NEXT
Desse jeito criamos uma variável A, cujo conteúdo está garantido de ser único a cada passagem for FOR/EACH.
Precisa mais de uma ? Tudo bem....
Código: Selecionar todos
FOR EACH A, B, C, D IN { Nil }, { Nil }, { Nil }, { Nil }
NEXT
Desse jeito criamos 4 variáveis únicas A, B, C, D.
Apenas simbólico, seria parecido com isto, SE ISTO FOSSE POSSÍVEL FAZER:
É APENAS SIMBÓLICO. O FOR/EACH é como criar uma variável local dentro dele.
Código: Selecionar todos
FOR EACH A, B, C, D IN ...
LOCAL A
LOCAL B
LOCAL C
LOCAL D
NEXT
Pro codeblock isso é o que precisa, isolar o conteúdo do codeblock eternamente.
Tem horas que é mais prático fazer isso do que criar milhares de funções pra resolver todas as situações do aplicativo.
E interessante isso tudo acima.
Apesar de situações específicas, voltamos ao básico da programação: VARIÁVEIS, TIPOS DE VARIÁVEIS, etc.
É usar uma variável, e não deixar que o valor dela seja diferente.
Isso resolve problemas pra ACTION de menus, buttons, e tudo que puder ser clicado ou ter ACTION, ou coisa parecida.
E o que é melhor: sem precisar ficar inventando uma infinidade de nomes diferentes, e complicar tudo ainda mais.
Como é que se cria o menu ? é parecido em todas as LIBs.
Um título principal e as subopções
Código: Selecionar todos
MENUITEM TituloPrincipal
FOR nCont = 1 TO 10
Subopcao x
NEXT
ENDMENU
Uma rotina simples resolve o menu.
E se tem vários níveis ?
Talvez intessante recursividade, se for um array com sub-elementos.
O que eu uso:
Se tem subopções, um array no segundo elemento, com mesma estrutura.
Só pensar.... se tem array cheio é um conjunto, senão é opção normal.
O que mudou pra função anterior ? Testar se tem array, e fazer a mesma coisa pra esse nível.
Vai fazer a mesma coisa que a função já faz ? Então só chamar ela mesma.
Código: Selecionar todos
FUNCTION CriaMenu( aOpcao )
BEGIN MENU
FOR EACH aItem IN aOpcao
IF Len( aItem[ 2 ] ) > 0 // segundo elemento é o array de sub-opções, tem subopção?
MENUITEM aItem[ 1 ] // primeiro elemento é o nome do conjunto
CriaMenu( aItem[ 2 ] ) // vai fazer tudo de novo pro sub-array
ENDMENU
ELSE
MenuItem aItem[1] ACTION aItem[3] // se é opção sem nível, tem ACTION
ENDIF
NEXT
MENUEND
RETURN Nil
Fica simples.
aItem é um array interno do FOR/EACH, é único, não vai ter problema de valor alterado no futuro ou na recursividade.
Só trocar a criação de cada item/conjunto pelo jeito de fazer em cada lib gráfica.
Se for tab com tabpages, mesma coisa.
É praticamente o jeito de fazer manual, mas automático.
A atenção principal é sobre ter a variável única em cada execução.
O menu não saiu do jeito que queria ?
Altere a rotina pra mostrar em formato de fonte, algo como gerador de fontes.