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.