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:

  • RGN_AND: Cria a interseção das duas regiões combinadas.
  • RGN_COPY: Cria uma cópia da região identificada por hrgnSrc1.
  • RGN_DIFF: Combina as partes de hrgnSrc1 sem a parte de hrgnSrc2.
  • RGN_OR: Cria a união de duas regiões combinadas.
  • RGN_XOR: Cria a união de duas regiões combinadas com exceção de qualquer área sobrepondo-a.

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

  • CreateRectRgn: Cria uma região retangular.
  • SetWindowRgn: Desenha, aplica uma região em uma janela.

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!

Revista Clube Delphi Edição 10
Esse artigo faz parte da revista Clube Delphi edição 10. Clique aqui para ler todos os artigos desta edição