No artigo de hoje, vou mostrar um macete para a criação de antigas Dialogs em AdvPL.
O PO-UI já está no mercado e sempre com inovações, mas as vezes precisamos criar uma Dialog simples e rápida.
Tendo essa premissa, vejo muitas pessoas me perguntando, como eu crio Dialogs tão rapidamente e posicionando os componentes corretamente na tela.
Pois bem jovens, existem duas dicas para isso, e hoje vou mostrar as duas, a primeira usando FWLayer e a segunda fazendo cálculos matemáticos.
Usando FWLayer
Nessa você deve separar a Dialog em “sub containers”, onde você pode usar a classe FWLayer e utilizar comandos como AddLine e AddColumn para fazer o controle da tela.
Então imaginamos uma tela, onde em cima quero exibir um texto de título e exibir alguns botões. E abaixo, no corpo, quero mostrar uma grid com grupos de produtos.
Para isso, será criado duas seções na minha Layer, uma chamada TIT (de título) e outra chamada COR (de corpo).
Depois na TIT, irei adicionar 3 colunas, uma que conterá o título com 50%, uma vazia que conterá 40% do espaço e uma com o botão de sair que terá o restante de 10%.
Já na COR, irei adicionar apenas 1 coluna contendo 100% do espaço. Agora em cada uma dessas columns, irei buscar o Panel, e ai irei vincular os objetos a esses painéis. Abaixo um print do resultado:
E abaixo, o código fonte do cenário acima:
//Bibliotecas
#Include "TOTVS.ch"
/*/{Protheus.doc} User Function zPulo1
Demonstração de como usar a FWLayer
@type Function
@author Atilio
@since 17/01/2021
@version 1.0
/*/
User Function zPulo1()
Local aArea := GetArea()
fMontaTela()
RestArea(aArea)
Return
Static Function fMontaTela()
Local nLargBtn := 50
//Objetos e componentes
Private oDlgPulo
Private oFwLayer
Private oPanTitulo
Private oPanGrid
//Cabeçalho
Private oSayModulo, cSayModulo := 'TST'
Private oSayTitulo, cSayTitulo := 'Pulo do Gato na Montagem de Dialogs'
Private oSaySubTit, cSaySubTit := 'Exemplo usando FWLayer'
//Tamanho da janela
Private aSize := MsAdvSize(.F.)
Private nJanLarg := aSize[5]
Private nJanAltu := aSize[6]
//Fontes
Private cFontUti := "Tahoma"
Private oFontMod := TFont():New(cFontUti, , -38)
Private oFontSub := TFont():New(cFontUti, , -20)
Private oFontSubN := TFont():New(cFontUti, , -20, , .T.)
Private oFontBtn := TFont():New(cFontUti, , -14)
Private oFontSay := TFont():New(cFontUti, , -12)
//Grid
Private aCampos := {}
Private cAliasTmp := "TST_" + RetCodUsr()
Private aColunas := {}
//Campos da Temporária
aAdd(aCampos, { "CODIGO" , "C", TamSX3("BM_GRUPO")[1], 0 })
aAdd(aCampos, { "DESCRI" , "C", TamSX3("BM_DESC")[1], 0 })
//Cria a tabela temporária
oTempTable:= FWTemporaryTable():New(cAliasTmp)
oTempTable:SetFields( aCampos )
oTempTable:Create()
//Busca as colunas do browse
aColunas := fCriaCols()
//Popula a tabela temporária
Processa({|| fPopula()}, "Processando...")
//Cria a janela
DEFINE MSDIALOG oDlgPulo TITLE "Exemplo de Pulo do Gato" FROM 0, 0 TO nJanAltu, nJanLarg PIXEL
//Criando a camada
oFwLayer := FwLayer():New()
oFwLayer:init(oDlgPulo,.F.)
//Adicionando 3 linhas, a de título, a superior e a do calendário
oFWLayer:addLine("TIT", 10, .F.)
oFWLayer:addLine("COR", 90, .F.)
//Adicionando as colunas das linhas
oFWLayer:addCollumn("HEADERTEXT", 050, .T., "TIT")
oFWLayer:addCollumn("BLANKBTN", 040, .T., "TIT")
oFWLayer:addCollumn("BTNSAIR", 010, .T., "TIT")
oFWLayer:addCollumn("COLGRID", 100, .T., "COR")
//Criando os paineis
oPanHeader := oFWLayer:GetColPanel("HEADERTEXT", "TIT")
oPanSair := oFWLayer:GetColPanel("BTNSAIR", "TIT")
oPanGrid := oFWLayer:GetColPanel("COLGRID", "COR")
//Títulos e SubTítulos
oSayModulo := TSay():New(004, 003, {|| cSayModulo}, oPanHeader, "", oFontMod, , , , .T., RGB(149, 179, 215), , 200, 30, , , , , , .F., , )
oSayTitulo := TSay():New(004, 045, {|| cSayTitulo}, oPanHeader, "", oFontSub, , , , .T., RGB(031, 073, 125), , 200, 30, , , , , , .F., , )
oSaySubTit := TSay():New(014, 045, {|| cSaySubTit}, oPanHeader, "", oFontSubN, , , , .T., RGB(031, 073, 125), , 300, 30, , , , , , .F., , )
//Criando os botões
oBtnSair := TButton():New(006, 001, "Fechar", oPanSair, {|| oDlgPulo:End()}, nLargBtn, 018, , oFontBtn, , .T., , , , , , )
//Cria a grid
oGetGrid := FWBrowse():New()
oGetGrid:SetDataTable()
oGetGrid:SetInsert(.F.)
oGetGrid:SetDelete(.F., { || .F. })
oGetGrid:SetAlias(cAliasTmp)
oGetGrid:DisableReport()
oGetGrid:DisableFilter()
oGetGrid:DisableConfig()
oGetGrid:DisableReport()
oGetGrid:DisableSeek()
oGetGrid:DisableSaveConfig()
oGetGrid:SetFontBrowse(oFontSay)
oGetGrid:SetColumns(aColunas)
oGetGrid:SetOwner(oPanGrid)
oGetGrid:Activate()
Activate MsDialog oDlgPulo Centered
oTempTable:Delete()
Return
Static Function fCriaCols()
Local nAtual := 0
Local aColunas := {}
Local aEstrut := {}
Local oColumn
//Adicionando campos que serão mostrados na tela
//[1] - Campo da Temporaria
//[2] - Titulo
//[3] - Tipo
//[4] - Tamanho
//[5] - Decimais
//[6] - Máscara
aAdd(aEstrut, {"CODIGO", "Código", "C", TamSX3('BM_GRUPO')[01], 0, ""})
aAdd(aEstrut, {"DESCRI", "Descrição", "C", TamSX3('BM_DESC')[01], 0, ""})
//Percorrendo todos os campos da estrutura
For nAtual := 1 To Len(aEstrut)
//Cria a coluna
oColumn := FWBrwColumn():New()
oColumn:SetData(&("{|| (cAliasTmp)->" + aEstrut[nAtual][1] +"}"))
oColumn:SetTitle(aEstrut[nAtual][2])
oColumn:SetType(aEstrut[nAtual][3])
oColumn:SetSize(aEstrut[nAtual][4])
oColumn:SetDecimal(aEstrut[nAtual][5])
oColumn:SetPicture(aEstrut[nAtual][6])
oColumn:bHeaderClick := &("{|| fOrdena('" + aEstrut[nAtual][1] + "') }")
//Adiciona a coluna
aAdd(aColunas, oColumn)
Next
Return aColunas
Static Function fPopula()
Local nAtual := 0
Local nTotal := 0
DbSelectArea("SBM")
SBM->(DbSetOrder(1))
SBM->(DbGoTop())
//Define o tamanho da régua
Count To nTotal
ProcRegua(nTotal)
SBM->(DbGoTop())
//Enquanto houver itens
While ! SBM->(EoF())
//Incrementa a régua
nAtual++
IncProc("Adicionando registro " + cValToChar(nAtual) + " de " + cValToChar(nTotal) + "...")
//Grava na temporária
RecLock(cAliasTmp, .T.)
(cAliasTmp)->CODIGO := SBM->BM_GRUPO
(cAliasTmp)->DESCRI := SBM->BM_DESC
(cAliasTmp)->(MsUnlock())
SBM->(DbSkip())
EndDo
Return
Usando Matemática
Esse segundo é mais braçal, mas funciona muito bem. Basicamente pessoal, 90% dos componentes gráficos em AdvPL, trabalham com uma escala dividida por 2 (quando se está internamente na Dialog).
Então, se criarmos uma tela com a dimensão de 200 de altura por 400 de largura, internamente nela, nós podemos declarar objetos, usando da linha 0 a 100 (metade de 200 de altura), e da coluna de 0 a 200 (metade de 400 de largura).
Com esse cenário, o centro da minha Dialog seria na linha 50 e na coluna 100. Agora basta eu pegar os componentes e fazer o cálculo da altura e largura deles, levando em conta o espaço que eu tenho na tela.
Por exemplo, um botão com 60 pixels de largura, para centralizar, eu preciso pegar a coluna do meio (nesse exemplo, 100) e subtrair metade do botão (nesse exemplo 30), então a coluna inicial do botão será 70.
Ai como é feito a matemática, se você aumentar a largura e a altura da janela (se estiverem em uma variável como nJanLarg e nJanAltu do exemplo), os componentes irão mudar sozinho também. Com essa lógica, se você quiser adicionar um componente no rodapé, basta pegar a altura da tela e subtrair a altura do componente. Se quiser adicionar um componente na direita, basta pegar a largura e subtrair. Existem inúmeras possibilidades.
Abaixo um print do exemplo acima:
Abaixo o código fonte desenvolvido:
//Bibliotecas
#Include "TOTVS.ch"
/*/{Protheus.doc} User Function zPulo2
Montando uma dialog com matemática para calcular o centro da tela
@type Function
@author Atilio
@since 17/04/2021
@version version
/*/
User Function zPulo2()
Local bInit := {|| }
//Fontes
Local cFontPad := "Tahoma"
Local oFontBtn := TFont():New(cFontPad, , -14)
//Objetos
Local oDlgPulo
Local oBtnOK
//Tamanho da janela
Local nJanLarg := 400
Local nJanAltu := 200
Local nColMeio := (nJanLarg / 2) / 2 // Primeiro dividimos a largura por 2, para termos o tamanho interno da dialog, ai dividimos por 2 novamente para descobrir o meio - ex.: 100 pixels
Local nLinMeio := (nJanAltu / 2) / 2 // Mesmo caso de acima
//Cria a janela
oDlgPulo := TDialog():New(0, 0, nJanAltu, nJanLarg, 'Pulo do Gato em Dialogs - Exemplo 2', , , , , CLR_BLACK, RGB(250, 250, 250), , , .T.)
//Como a largura do botão é 60 e a altura é 18, iremos pegar a coluna do meio menos 30 e a linha do meio menos 9
oBtnOK := TButton():New(nLinMeio - 9, nColMeio - 30, "Botão Centro", oDlgPulo, {|| Alert("teste")}, 060, 018, , oFontBtn, , .T., , , , , , )
//Ativa e exibe a janela
oDlgPulo:Activate(, , , .T., {|| .T.}, , bInit )
Return
Update (28/05/2021):
A pedido do leitor Marco Nagoa, fizemos o exemplo usando matemática, com a mesma tela do exemplo usando FWLayer, segue o fonte abaixo:
//Bibliotecas
#Include "TOTVS.ch"
/*/{Protheus.doc} User Function zPulo2
Demonstração de como usar matemática para criar telas
@type Function
@author Atilio
@since 28/05/2021
@version 1.0
/*/
User Function zPulo2()
Local aArea := GetArea()
fMontaTela()
RestArea(aArea)
Return
Static Function fMontaTela()
Local nLargBtn := 50
//Objetos e componentes
Private oPanGrid
Private oDlgPulo
//Cabeçalho
Private oSayModulo, cSayModulo := 'TST'
Private oSayTitulo, cSayTitulo := 'Pulo do Gato na Montagem de Dialogs'
Private oSaySubTit, cSaySubTit := 'Exemplo usando Matemática'
//Tamanho da janela
Private aSize := MsAdvSize(.F.)
Private nJanLarg := aSize[5]
Private nJanAltu := aSize[6]
//Fontes
Private cFontUti := "Tahoma"
Private oFontMod := TFont():New(cFontUti, , -38)
Private oFontSub := TFont():New(cFontUti, , -20)
Private oFontSubN := TFont():New(cFontUti, , -20, , .T.)
Private oFontBtn := TFont():New(cFontUti, , -14)
Private oFontSay := TFont():New(cFontUti, , -12)
//Grid
Private aCampos := {}
Private cAliasTmp := "TST_" + RetCodUsr()
Private aColunas := {}
//Campos da Temporária
aAdd(aCampos, { "CODIGO" , "C", TamSX3("BM_GRUPO")[1], 0 })
aAdd(aCampos, { "DESCRI" , "C", TamSX3("BM_DESC")[1], 0 })
//Cria a tabela temporária
oTempTable:= FWTemporaryTable():New(cAliasTmp)
oTempTable:SetFields( aCampos )
oTempTable:Create()
//Busca as colunas do browse
aColunas := fCriaCols()
//Popula a tabela temporária
Processa({|| fPopula()}, "Processando...")
//Cria a janela
DEFINE MSDIALOG oDlgPulo TITLE "Exemplo de Pulo do Gato" FROM 0, 0 TO nJanAltu, nJanLarg PIXEL
//Títulos e SubTítulos
oSayModulo := TSay():New(004, 003, {|| cSayModulo}, oDlgPulo, "", oFontMod, , , , .T., RGB(149, 179, 215), , 200, 30, , , , , , .F., , )
oSayTitulo := TSay():New(004, 045, {|| cSayTitulo}, oDlgPulo, "", oFontSub, , , , .T., RGB(031, 073, 125), , 200, 30, , , , , , .F., , )
oSaySubTit := TSay():New(014, 045, {|| cSaySubTit}, oDlgPulo, "", oFontSubN, , , , .T., RGB(031, 073, 125), , 300, 30, , , , , , .F., , )
//Criando os botões
oBtnSair := TButton():New(006, (nJanLarg/2-001)-((nLargBtn+2)*1), "Fechar", oDlgPulo, {|| oDlgPulo:End()}, nLargBtn, 018, , oFontBtn, , .T., , , , , , )
//Cria a grid
oPanGrid := tPanel():New(027, 001, "", oDlgPulo, , , , RGB(000,000,000), RGB(254,254,254), (nJanLarg/2)-1, (nJanAltu/2)-3)
oGetGrid := FWBrowse():New()
oGetGrid:SetDataTable()
oGetGrid:SetInsert(.F.)
oGetGrid:SetDelete(.F., { || .F. })
oGetGrid:SetAlias(cAliasTmp)
oGetGrid:DisableReport()
oGetGrid:DisableFilter()
oGetGrid:DisableConfig()
oGetGrid:DisableReport()
oGetGrid:DisableSeek()
oGetGrid:DisableSaveConfig()
oGetGrid:SetFontBrowse(oFontSay)
oGetGrid:SetColumns(aColunas)
oGetGrid:SetOwner(oPanGrid)
oGetGrid:Activate()
Activate MsDialog oDlgPulo Centered
oTempTable:Delete()
Return
Static Function fCriaCols()
Local nAtual := 0
Local aColunas := {}
Local aEstrut := {}
Local oColumn
//Adicionando campos que serão mostrados na tela
//[1] - Campo da Temporaria
//[2] - Titulo
//[3] - Tipo
//[4] - Tamanho
//[5] - Decimais
//[6] - Máscara
aAdd(aEstrut, {"CODIGO", "Código", "C", TamSX3('BM_GRUPO')[01], 0, ""})
aAdd(aEstrut, {"DESCRI", "Descrição", "C", TamSX3('BM_DESC')[01], 0, ""})
//Percorrendo todos os campos da estrutura
For nAtual := 1 To Len(aEstrut)
//Cria a coluna
oColumn := FWBrwColumn():New()
oColumn:SetData(&("{|| (cAliasTmp)->" + aEstrut[nAtual][1] +"}"))
oColumn:SetTitle(aEstrut[nAtual][2])
oColumn:SetType(aEstrut[nAtual][3])
oColumn:SetSize(aEstrut[nAtual][4])
oColumn:SetDecimal(aEstrut[nAtual][5])
oColumn:SetPicture(aEstrut[nAtual][6])
oColumn:bHeaderClick := &("{|| fOrdena('" + aEstrut[nAtual][1] + "') }")
//Adiciona a coluna
aAdd(aColunas, oColumn)
Next
Return aColunas
Static Function fPopula()
Local nAtual := 0
Local nTotal := 0
DbSelectArea("SBM")
SBM->(DbSetOrder(1))
SBM->(DbGoTop())
//Define o tamanho da régua
Count To nTotal
ProcRegua(nTotal)
SBM->(DbGoTop())
//Enquanto houver itens
While ! SBM->(EoF())
//Incrementa a régua
nAtual++
IncProc("Adicionando registro " + cValToChar(nAtual) + " de " + cValToChar(nTotal) + "...")
//Grava na temporária
RecLock(cAliasTmp, .T.)
(cAliasTmp)->CODIGO := SBM->BM_GRUPO
(cAliasTmp)->DESCRI := SBM->BM_DESC
(cAliasTmp)->(MsUnlock())
SBM->(DbSkip())
EndDo
Return
Bom pessoal, por hoje é só.
Abraços e até a próxima.


Bacana demais, melhor seria se os dois exemplos gerassem a mesma tela. Grato Dan.
Bom dia Marco.
Obrigado pelo comentário. Post atualizado, conforme sugerido, adicionei um fonte usando matemática, gerando a mesma tela do FWLayer.
Espero ter ajudado.
Abraços.
Parabéns Atílio. Muito bom!
Eu que agradeço pelo comentário jovem.
Grande abraço.
Nao encontrei no seu codigo a função fOrdena
Bom dia Harley, tudo joia?
Perdão, acabei deixando essa linha ao clicar no cabeçalho da coluna. Já removi desse exemplo, obrigado.
Caso queira ver um exemplo com bHeaderClick, segue o link: https://terminaldeinformacao.com/2023/06/12/criar-ordenacao-em-clique-de-colunas-com-fwbrowse-ti-responde-060/
Tenha uma ótima e abençoada quinta feira.
Um grande abraço.