Artigo Clube Delphi Edição 10 - Invisíveis e Transparentes

Artigo da Revista Clube Delphi Edição 10.

Peter Norton foi o guru de muitos programadores de minha geração. Com suas obras especializadas, contribuiu para formar muitos de meus conceitos, alguns renovados com as novas tecnologias, outros resistentes ao tempo.

A chuva de obras publicadas em seu nome, me deixaram confuso, será que foi ele quem realmente escreveu todos? Baseado em assuntos bem variados, eu comecei a fazer algumas contas: Pelo menos 1 ano para formar conceitos sólidos sobre o assunto e mais seis meses no mínimo para escrever um livro com 250 a 300 páginas. Seria impossível comparado ao número de títulos. Já seus aplicativos, temos certeza que foi uma grande equipe.

Com a revolução visual pós Windows 95, muitos aplicativos começaram a chamar a atenção pelas telas de splash (tela inicial enquanto o programa é carregado). O Norton Crash Guard, com seu splash em forma de escudo me fascinou. Pensei: Tenho que imitá-lo!

A forma mais simples seria arrumar um bitmap com fundo transparente, na forma desejada, e colocar o form transparente também alterando sua propriedade brush, e finalmente esconder o caption do form.

Colocando o Form transparente

            Form1.Brush.Style := bsclear;
        

Trabalhoso, mas mais trabalhoso ainda é fazer isto utilizando funções APIs. Porém, este é o nosso desafio!

O problema do código acima é que ele não atualiza a área transparente, e não adianta dar um simples Invalidate em um evento qualquer - chega de "gambiarras", vamos escrever uma solução definitiva. O efeito do form transparente é executado pelo princípio de Regiões. Primeiro crie uma região que cerca o form inteiro. Então, ache a área de cliente da forma (Client vs. non-Client). Após isto basta mostrar ou esconder o form, preocupando-se com o refresh da parte invisível, ou seja, atualizar o fundo em caso de você mover o form. Vamos para a prática !


            Var

            Form1: TForm1;
          
            FullRgn, ClientRgn, CtlRgn : THandle;
          
          procedure TForm1.DoInvisible;
          
          var
          
            AControl : TControl;
          
            A, Margin, X, Y, CtlX, CtlY : Integer;
          
          begin
          
            Margin := ( Width - ClientWidth ) div 2;
          
            //Pegamos a região do form
          
            FullRgn := CreateRectRgn(0, 0, Width, Height);
          
            //Procuramos a área Client
          
            X := Margin;
          
            Y := Height - ClientHeight - Margin;
          
            ClientRgn := CreateRectRgn( X, Y, X + ClientWidth, Y + ClientHeight );
          
            CombineRgn( FullRgn, FullRgn, ClientRgn, RGN_DIFF );
          
            //Agora procuramos nossos controles no form
          
           for A := 0 to ControlCount - 1 do begin
          
              AControl := Controls[A];
          
              if ( AControl is TWinControl ) or ( AControl is TGraphicControl )
          
                  then with AControl do begin
          
                if Visible then begin
          
                  CtlX := X + Left;
          
                  CtlY := Y + Top;
          
                  CtlRgn := CreateRectRgn( CtlX, CtlY, CtlX + Width, CtlY + Height );
          
                  CombineRgn( FullRgn, FullRgn, CtlRgn, RGN_OR );
          
                end;
          
              end;
          
            end;
          
            //Depois de todas as regiões registradas, executamos o efeito
          
            SetWindowRgn(Handle, FullRgn, TRUE);
          
          end;
        

Obs.: Primeiro crie a rotina para esconder o form, aproveite e declare as variáveis locais ao form (não a procedure, pois utilizaremos elas em outros procedimentos também).

Apesar de a primeira vista parecer confuso, uma análise mais calma demonstra claramente o passo a passo do código, Aonde procuramos delimitar as regiões do form, sua área total, sua área cliente e a área preenchida com seus controles.

Como todo veneno tem seu antídoto, vamos agora desfazer, torna-lo visível:


procedure TForm1.DoVisible;

begin

// devolve a visibilidade para a região

FullRgn := CreateRectRgn(0, 0, Width, Height);

CombineRgn(FullRgn, FullRgn, FullRgn, RGN_COPY);

SetWindowRgn(Handle, FullRgn, TRUE);

end;
        

Bem mais simples, né? Talvez. Na verdade precisamos entender o vilão dos dois procedimentos, a função API CombineRgn e seus parâmetros.

A função CombineRgn, combina duas regiões e retorna seu valor numa terceira região. Veja a sintaxe:


            Int CombineRgn(

                HRGN hrgnDest, // handle to destination            region
            
                HRGN hrgnSrc1,   // handle to source               region
            
                HRGN hrgnSrc2,   // handle to source
            
               region
            
                int fnCombineMode // region combining
            
                mode
            
               );
        

Os três primeiros parâmetros são intuitivos, o macete está no quarto parâmetro, que possui uma lista de opções e responsável diretamente pelo efeito desejado, veja algumas destas opções abaixo:

Revelamos o motivo da mágica, as demais APIs utilizadas podem ter uma explicação mais breve:

Aproveitarei para fazer meu comercial. Precisando de maiores explicações sobre estas ou outras APIs, basta mandar um email para: Visual Books Editora e encomendar meu novo livro, entitulado "Delphi, API's e Sockets - A caixa preta das tecnologias".

Voltando ao nosso problema, ainda não terminamos, precisamos criar um botão para mostrar e esconder o form. Ao finalizar nosso aplicativo, devemos destruir as regiões criadas (que são consideradas objetos).


procedure TForm1.FormDestroy(Sender: TObject);

begin

  DeleteObject(ClientRgn);

  DeleteObject(FullRgn);

  DeleteObject(CtlRgn);

end;

procedure TForm1.Button1Click(Sender: TObject);

begin

  if Button1.Caption = 'Mostra Form' then begin

  DoVisible;

  Button1.Caption := 'Esconde Form';

end

else begin

   DoInvisible;

   Button1.Caption := 'Mostra Form';

end;

end;
        

Para finalizar nosso projeto com qualidade, inclua também um procedimento para o evento FormResize:


            procedure TForm1.FormResize(Sender: TObject);

            begin
            
             if Button1.Caption = 'Mostra Form' then
            
                DoInvisible
            
              else
            
                DoVisible;
            
            end;
        

Vamos agora testar sua lógica de programador: Porque o primeiro código (...Brush.Style := bsClear;) não atualiza a área invisível de nosso form quando ele é movido? A resposta é: Window Region!!!

Vou te contar uma coisa impressionante, quando o a área Client do form está invisível, e você tentar clicar nela com o mouse, ela simplesmente não existe e o foco passa para o aplicativo que esta por baixo, ou seja, ela esta invisível aos olhos e ao toque - Gostei!


Esse artigo faz parte da revista Clube Delphi edição 10. Clique aqui para ler todos os artigos desta edição
Ebook exclusivo
Dê um upgrade no início da sua jornada. Crie sua conta grátis e baixe o e-book

Artigos relacionados