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.