Criando Pacotes
Origem: A Bíblia do Lazarus, a enciclopédia livre.
Introdução
A Criação em si de componentes no Lazarus, o Código Pascal, não se diferencia muito do Código Delphi, porém sempre tem recursos a mais a serem aprendidos e utilizados.
Neste Capítulo você aprenderá mais do que simplesmente as diferenças de pacotes em Delphi/Lazarus, você aprenderá a criar componentes do zero, caso não saiba criar componentes no Delphi.
Recomenda-se um conhecimento básico sobres classes, componentes, propriedades, métodos e eventos, antes de começar aqui!
Antes de Começar
Antes de Começar devem ser ressaltados alguns conceitos de pacotes de componentes, contidos e não-contidos em Delphi:
- O Lazarus/Free Pascal somente suporta "static packages", pacotes estáticos, ou seja, para serem instalados na IDE, os seguintes passos devem ser dados: Criação do Pacote->Compilação do Pacote->Instalação do Pacote->Recriação da IDE;
A Recriação da IDE, conhecido como "Rebuild Lazarus" consiste em compilar todos os pacotes (inclusive LCL, SynEdit) que "estão instalados" e recriar a IDE com os pacotes ja instalados nela;
- Os pacotes no Lazarus podem, assim como no Delphi, necessitar/requerir pacotes previamente instalados;
- Os pacotes no Lazarus podem ser independentes de Sistema Operacional (Win32, Linux, MacOs X ...) ou podem ser especificos para um Sistema Operacional em especial;
- Os pacotes no Lazarus tem, por padrão, extensão .lpk, diferente dos pacotes Delphi, que contém extensão .dpk;
- Outras modificações básicas foram feitas, como algumas procedures "Registers" existentes, mas serão discutidas mais adiante...
Começando a criar um Componente
Um Componente para ser utilizado não precisa ser necessariamente um pacote, você pode criar um componente TFormAbout derivado de TForm no seu próprio programa;
Na verdade o "componente empacotado" serve, na maioria das vezes, para ser utilizado na IDE, em tempo de design e com as vantagens das ferramentas RAD que o Delphi e o Lazarus são.
Existem várias formas de se escreverem componentes para o Lazarus, entre as quais:
- Criação em Tempo de Execução:
Criar o Componente em um Programa Lazarus, e criá-lo em tempo de Execução (Componente1:=MeuComponente.Create(Self); Esta técnica é boa, porém leve em consideração que um programa demora um bocado para ser compilado, e isto pode realmente encher o saco :-(;
- Migração de Componentes Delphi:
Nesta parte existem duas formas de migração:
1 - Você migra um componente de outra pessoa, que já está escrito;
2 - Você escreve ele em Delphi, com a vantagem da compilação super-rápida, e o migra para lazarus (mas você tem que saber exatamente o que você vai conseguir migrar com certeza no Lazarus);
Esta técnica é realmente boa, tendo em vista que o Delphi compila qualquer programa/componente com uma velocidade verdadeiramente estupenda;
Alguns Detalhes sobre Componentes
Todos componentes devem descender de outros componentes. Isso é uma regra do Pascal orientado à objetos.
Em geral todos os componentes descendem do componente TComponent.
TComponent por sua vez descende de TPersistent que por sua vez deriva de TObject, a classe "raiz".
Um Exemplo de um componente derivado é o TComboBox que deriva de TCustomComboBox e assim por diante.
Caso decida criar um componente não visual, é recomendado que ele derive de TComponent ou um de seus derivados, não visual.
Já se você se decidir em criar um componente visual, escolha um que derive de TControl ou TWinControl (mesmo para Linux ou MacOX X) ou um de seus derivados.
Criando um Componente Simples: TMsgBox
Para começar, vamos ao que interessa: a criação de um componente.
Vamos criá-lo primeiro em tempo de execução, e depois vamos transformá-lo em um componente...
O Componente será criado na mesma Unit do Programa, junto com o TForm1.
O nosso componente é tão simples que poderiamos chamá-lo de: TInutil :-).
Este componente apenas exibe uma mensagem, utilizado ShowMessage.
Como só exibe uma mensagem, não precisa ser visual, o que significa que podemos derivá-lo de TComponent.
Vamos ao componente: Inicie um projeto no Lazarus e siga os seguintes passos:
Antes da declaração de TForm1, ainda na seção interface, insira:
{ TMsgBox }
TMsgBox = class(TComponent)
private
FMensagem: String;
public
procedure Execute;
published
property Mensagem: String read FMensagem write FMensagem;
end;
E antes dos códigos de TForm1, na seção implementation, insira:
{ TMsgBox }
procedure TMsgBox.Execute;
begin
ShowMessage(FMensagem);
end;
No evento OnCreate de TForm1 insira:
procedure TForm1.FormCreate(Sender: TObject); begin MsgBox1:=TMsgBox.Create(nil); MsgBox1.Mensagem:='Bem-Vindo ao Meu Programa'; MsgBox1.Execute; end;
E no evento OnDestroy de TForm1 insira:
procedure TForm1.FormDestroy(Sender: TObject); begin MsgBox1.Destroy; end;
Você pode verificar o código completo de unit1.pas Aqui!
Compile e Execute o Programa! O Resultado será uma mensagem "Bem-Vindo ao Meu Programa" Mas antes de você se lamentar que não entendeu nada, vamos as explicações do código:
TMsgBox foi declarado como um componente descendente de TComponent: TMsgBox = class(TComponent)
Depois foram declaradas uma variável, uma procedure e uma propriedade, em "aréas especiais". Existem quatro "aréas especiais": Protected, Private, Public e Published. Vamos detalhar:
Protected: Todas as propriedades, procedures, funções e variáveis declaradas aqui poderão ser utilizadas e modificadas apenas por este componente, e somente por ele, e mais em nenhum outro código.
Private: Todas as propriedades, procedures, funções e variáveis declaradas aqui poderão ser utilizadas e modificadas apenas por este componente, e para outros que descenderem dele.
Public: Todas as propriedades, procedures, funções e variáveis declaradas aqui poderão ser utilizadas e modificadas em qualquer lugar: na unit atual, em outras units/programas que utilize este componente e em componentes que descenderem deste componente.
Published: Todas as propriedades, procedures, funções e variáveis declaradas aqui poderão ser utilizadas e modificadas em qualquer lugar, como em Public, com a diferença de que serão publicados no Object Inspector, quando o componente for instalado na IDE.
Agora vamos supor que tivesse um código do tipo:
{ TMsgBox }
TMsgBox = class(TComponent)
FTestaVariavel: String;
end;
FTestaVariavel seria considerada public, pois não esta em nenhuma "seção especial"...
Entendendo um pouco sobre o Property
Vamos estudar um pouco o property:
property Mensagem: String read FMensagem write FMensagem;
Neste trecho de código temos Mensagem, uma variável publicada que aparecerá no Object Inspector caso o componente seja instalado na IDE. Vemos que Mensagem é uma variável do tipo String, quando o Valor de Mensagem foi lido, na verdade será lido o valor de FMensagem e quando for escrito será escrito em FMensagem... Por que isso ocorre? Simples. Propriedades são o que podemos chamar de variáveis de 'somente-leitura', pelo menos no projeto final. Você não pode escrever em Mensagem, mas pode escrever em FMensagem que é o "valor real" de Mensagem... Quando você desenvolver mais e mais componentes em Delphi/Lazarus você entenderá esse conceito por completo.
Outro fator que é sempre bom citar é que podemos colocar o ponto de leitura(read) e escrita(write) em procedures, mas isso veremos mais adiante...
Empacotando TMsgBox
Vimos como criar um componente TMsgBox, mas como transformar realmente esse componente em um Componente? Para estar disponível na IDE, em modo de design, devem ser seguidos os seguintes passos: Empacotar componente/compilar/compilar a IDE/recriar a IDE. Isso se deve ao fato de que o Lazarus/Free Pascal não aceita pacotes dinâmicos como o Delphi®, somente pacotes estáticos, leia o tutorial sobre Pacotes no diretório Lazarus/Docs/Packages.txt e você entenderá mais sobre essa "parte chata do negócio". Isso é temporário, com a evolução do Lazarus/Free Pascal as coisas vão melhorar. Enquanto isso, temos que nos virar com o que temos, agora vamos à primeira etapa: empacotar o componente.
Dica:
Você pode utilizar o pacote/componente sem ter que recriar a IDE, caso você utilize o pacote apenas em RunTime (Tempo de Execução). Você aprenderá a utilizar o componente sem recriar a IDE mais adiante.
Primeiro você deve criar uma "unit exclusiva" para o componente. Nesta unit poderá ter mais de um componente sem problemas, como ocorrem em units do Lazarus como Dialogs por exemplo que contém vários componentes entre os quais: TOpenDialog e TSaveDialog. Vamos então ao que interessa, inicie um novo projeto e vá até File->New->Unit. Pegue toda a estrutura de TMsgBox e coloque nessa Unit. O código completo deve ser similar à Aqui. Salve a unit Como UMsgBox.pas em um diretório como lazarus/components/testes/uMsgBox.pas. Lembre-se: Estamos inserindo o "U" na frente apenas por que o Free Pascal já contém uma unit chamada MsgBox e caso você nomeie ela como MsgBox.pas gerará um erro na compilação de arquivo ambíguo. Caso fosse outro componente como TMeuComponentedeTeste, poderíamos nomear a unit como MeuComponentedeTeste.pas normalmente. Agora que você já tem a unit para o componente TMsgBox. Perceba que, diferente de programas, por enquanto, essa unit não tem uma diretiva final {$ xxx.lrs} pelo fato de esta unit não utilizar recursos Lazarus, ainda! Breve será utilizado os recursos nela.
Agora vamos à criação do pacote para esta unit:
- Vá até File -> New -> Packages -> Standard Packages e clique em OK;
- Salve o pacote no mesmo diretório da unit, de preferência, e com o nome de LazMsgBox;
- Clique em Add e siga para Add Unit. Navegue até UMsgBox.pas e clique no botão Add Unit;
- Clique em Add e siga para Add Requirement. Selecione o pacote LCL e clique em OK;
- Compile o Pacote. Se tudo der certo, o seu componente está quase pronto para ser instalado na IDE, faltando apenas um ícone e registrar ele na paleta de componentes.
Criou o Pacote??? Agora vamos utilizá-lo, sem instalar na IDE:
- Inicie um Novo Projeto (File -> New -> Project -> Application);
- Vá até Project -> Project Inspector -> e Selecione Add. Adicione em Novo Requerimento o Pacote LazMsgBox (que você criou acima);
- Adicione na clausula Uses a unit UMsgBox (unit do componente TMsgBox);
- Abaixo de Implementation você tem uma parte como:
Form1: TForm1;
Adicione abaixo:
MsgBox1: TMsgBox;
- No Evento OnCreate do Form insira:
MsgBox1:=TMsgBox.Create(nil); //Deixe como Nil, o Componente não será mostrado MsgBox1.Mensagem:='Mensagem:='Bem-Vindo ao Meu Programa'; MsgBox1.Execute;
- No Evento OnDestroy do Form insira:
MsgBox1.Destroy;
Pronto! Você já pode utilizar seu programa sem ter ele instalado na IDE. Existem, é claro, várias vantagens em instalá-lo na IDE, principalmente se o componente for visual porque o usuário vê em tempo de Design o resultado das modificações nas propriedades do Componente. Porém, para um desenvolvedor de Pacotes/Componentes, seria bem díficil ficar recriando a IDE a cada modificação no código. Qual a Solução então? Crie o componente em um código de um programa (Como no primeiro exemplo dado) ou crie o componente, empacote-o e não recrie a IDE (como no último exemplo). Depois que seu componente for testado 100% por você, instale ele na IDE e verifique se ficou como você realmente queria (às vezes, mesmo depois de muitos testes, você ainda tem problemas a serem corrigido). Depois de Instalado na IDE, se você recompilar o pacote do componente, as modificações também aparecerão na IDE.
Instalando o Componente na IDE
Vamos instalar TMsgBox na IDE, só para detalharmos essa parte antes de continuarmos.
Lembre-se de que você não necessita ficar recriando a IDE a cada modificação no código, mas somente quando este estiver realmente pronto, depois basta recompilar o pacote, como foi dito acima, e as modificações serão vista também em modo de Design.
Estamos instalando aqui só para uma visão geral de instalação na IDE.
Existem alguns detalhes a serem discutidos na instalação na IDE.
Antes de instalar na IDE você deve lembrar de duas coisas a serem feitas:
- Registrar o Componente - Necessário, caso você queira que seja apresentado na paleta de componentes;
- Criar um ícone para o Componente - Não Necessário! Caso não seja criado, o ícone padrão será apresentado. Nesta etapa você deverá utilizar o utilitário LazRes, que será detalhado à frente, tendo em vista que a inserção de um ícone não funciona como no Delphi®.
Registrando o Componente
O "Registro" de um componente no Lazarus funciona de maneira semelhante à um registro em Delphi®. Você ainda deve utilizar a procedure Register em seu código, porém, o Lazarus adiciona uma nova unit no diretório do seu projeto que tem outra função, que não é necessário explicar sua utilização, tendo em vista que essa unit é criada automaticamente pelo Lazarus. Vamos registrar o Componente TMsgBox. Vamos inseri-lo na aba LazBib. Caso essa aba não exista, ela será automaticamente criada. Insira acima da palavra reservada implementation o seguinte código:
procedure Register;
Depois insira abaixo da palavra reservada implementation o seguinte código:
procedure Register;
begin
RegisterComponents('LazBib',[TMsgBox]);
end;
Depois disso, carregue o Pacote no Lazarus, selecione 'Files -> UMsgBox.pas e marque mais abaixo a opção Register Unit caso ela esteja desmarcada.
Pronto! Isso já irá adicionar seu componente à aba LazBib, após a recriação da IDE.
Criando um Ícone para o Componente
Esse passo é muito simples. Você não necessita obrigatoriamente ter um ícone do seu componente. O Lazarus, assim como o Delphi®, possui um Ícone padrão para componentes sem Ícones, mas é sempre bom ter personalização nos seus projetos. Para criar o Ícone, basta criar um arquivo de recurso e depois inseri-lo no código com a diretiva {$I}. Vamos aos passos:
- Caso não esteja compilado, compile o projeto lazres.lpi contido no diretório Tools do diretório Lazarus;
- Depois crie um bmp e nomeie-o como tmsgbox.bmp (o nome da classe a ser registrada).
No meu caso eu criei um BMP e desenhei nele um T de Teste. O Bitmap deve conter no máximo 24x24 de medida!. Você também pode utilizar como alternativa ao BMP o formato XPM;
- Execute o comando, sem as aspas: "lazres msgbox.lrs tmsgbox.bmp".
Se necessário vá, antes de executar o comando, até o diretório Lazarus/Tools com o comando CD;
- Criado o arquivo msgbox.lrs insira no diretório onde se encontra UMsgBox.pas;
- Descomente o final do código, onde possui a diretiva {$I }, da unit deixando-o assim:
{$I msgbox.lrs}
- Compile o componente e quando lhe for perguntando se deseja recriar a IDE, responda Yes/Sim.
Pronto! Seu componente já está na IDE.
Utilize-o visualmente e aproveite as vantagens da ferramenta RAD que é o Lazarus :-)!
A imagem que eu utilizei na criação de recursos deste componente foi:
(A figura esta em formato GIF, converta para BMP ou XPM antes de usá-la.)
Dica:
Na Janela onde você "gerencia" seu componente você pode clicar no botão Options e ir até Description. Nessa página você poderá inserir a descrição de seu componente, seu nome (Nome do Autor), licença do componente e versão.
Aprofundando-se na Criação de Componentes - Criando um segundo Componente
Agora que você já instalou seu componente, verifique como ficou ele. Agora vamos a criação de outro componente, um pouco mais complexo e que lhe dará uma visão mais geral sobre a criação de componentes no Lazarus. O nome do componente é "TMoveControle". Ele serve para movimentar controles (Componentes do Tipo TControl ou descendentes). Por exemplo, com esse componente você pode mover uma Imagem, apenas arrastando ela com o Mouse e com 3 linhas de código manual.
O código completo de movectrl.pas você pode pegar em TMoveControle! Agora veja se você consegue empacotar e instalar na IDE esse componente. Caso não consiga, continue lendo. Agora vamos à explicação passo-a-passo do código deste componente, e depois de um programa utilizando ele!
Vamos primeiro explicar a Interface do Componente:
{ TMoveControle }
TMoveControle = class(TComponent)
protected
S: TShape;
IniLeft, IniTop: Integer;
FPodeMover, FBloqueiaLados: Boolean;
FControle: TControl;
FOnControleMovido: TNotifyEvent;
public
constructor Create(TheOwner: TComponent); override;
procedure IniciarMov(X,Y: Integer);
procedure Mover(X,Y: Integer);
procedure TerminarMov;
destructor Destroy; override;
published
property PodeMover: Boolean read FPodeMover write FPodeMover default True;
property Controle: TControl read FControle write FControle;
property BloqueiaLados: Boolean read FBloqueiaLados write FBloqueiaLados;
property OnControleMovido: TNotifyEvent read FOnControleMovido write FOnControleMovido;
end;
Esse Componente descende de TComponent. Tendo em vista que ele não é um componente visual, é a melhor escolha! Na seção Protected temos algumas variáveis declaradas:
- S: TShape - Este será o componente TShape que o Componente usará para movimentar o Controle.
Na verdade o Controle em si não é Movido, e sim o TShape. Após soltar o TShape o Controle será movido para a posição do TShape. Criativo?;
- IniLeft, IniTop: Integer; - Estas serão as propriedades que guardarão a posição inicial da esquerda e Topo (Left e Top) do Controle a ser movido. Todo o cálculo para o movimento do TShape será feito apartir destas variáveis;
- FPodeMover, FBloqueiaLados: Boolean; - Nessas propriedades serão definidos dados importantes:
- FPodeMover - O Componente, e a animação com o TShape, só serão movido caso essa propriedade seja verdadeira;
- FBloqueiaLados - O Componente não poderá ser movido para fora do seu Componente Parente caso essa propriedade esteja como verdadeira. Caso contrário o compoennte poderá ser movido para qualquer lugar;
- FControle: TControl; - Esta propriedade armazena o componente a ser movido. Por exemplo, se o usuário do programa que utiliza o componente desejar mover Image1, definirá essa propriedade como Image1;
- FOnControleMovido: TNotifyEvent; - Esta será a propriedade, de tipo ponteiro, que armazenará o evento OnControlMovido, que é desencadeado após o componente ser movido.
Agora vamos estudar à seção Public
- constructor Create(TheOwner: TComponent); override; - Esse é o construtor do componente.
Esse é o mesmo construtor que o de TComponente, mas vamos adaptá-lo no nosso componente. Perceba a palavra chave override após a declaração do construtor, falaremos dela quando estudarmos esse construtor na seção implementation. Obs: Os construtores devem ser obrigatoriamente públicos, ou seja, devem estar na seção public do código-fonte;
- procedure IniciarMov(X,Y: Integer); - Esta procedure é a que vai iniciar o movimento.
Na verdade ela não movimenta nada, porém, ela deixa visivel o TShape que será mostrado quando o usuário Clicar no Componente a ser movido. Ela também fixa o valor de X e Y na variável IniLeft e IniTop. Veremos os detalhes dela quando estudarmos a seção implementation;
- procedure Mover(X,Y: Integer); - Essa procedure continua o movimento enquanto o usuário move o mouse sobre o Componente a ser movido.
Ela faz os cálculos necessários com X, Y, IniLeft e IniTop.
- procedure TerminarMov; - Essa procedure termina o movimento.
Ela nada mais faz do que esconder o TShape e movimentar o componente a ser movido para a posição do TShape.
- destructor Destroy; override; - O destruidor do Componenente, também herdado de TComponente, e ajustado ao nosso componente.
Os destruidores também devem ser públicos;
Agora vamos estudar à seção Published onde serão mostradas as propriedades no Object Inspector. Não há necessidade em estudar essa seção, tendo em vista que só passam das propriedades que serão mostradas no Object Inspector, e que são lidas e escritas nas variáveis declaradas na seção public.
- property PodeMover: Boolean read FPodeMover write FPodeMover default True;
- property Controle: TControl read FControle write FControle;
- property BloqueiaLados: Boolean read FBloqueiaLados write FBloqueiaLados;
- property OnControleMovido: TNotifyEvent read FOnControleMovido write FOnControleMovido;
Vamos estudar "O código em si" agora. Vamos à seção implementation:
constructor TMoveControle.Create(TheOwner: TComponent); begin Inherited Create(TheOwner); //TShape é Perfeito para Este Trabalho! S:=TShape.Create(nil); S.Visible:=False; S.Brush.Style:=BsClear; S.Pen.Width:=2; S.Pen.Color:=$00525A63; FPodeMover:=True; end;
Se lembra da palavra-chave override? Então, a palavra-chave override indica que vamos adaptar X procedure ou X função ou X construtor ou X destruidor ao nosso componente. Ou seja, indica ao compilador para não utilizar o método Create herdado do ancestor (no caso TComponent). Na prática isso significa que: TMoveControle não utilizará o método Create de TComponent, ele terá o seu próprio "personalizado". Isso será detalhado mais à frente quando estudarmos à respeito de métodos Virtuais e Dinâmicos. O construtor de TMoveControle faz o seguinte:
- Chama o método do ancestor (TComponent). (Inherited Create(TheOwner););
- Cria o componente TShape. (S:=TShape.Create(nil););
- Ajusta algumas propriedades do TShape (Invisível, Brush como Limpo e Caneta com largura igual à 2 e cor igual à $00525A63, um tom cinza (Veja o Capítulo sobre TCanvas para saber sobre TBrush e TPen));
- Ajusta a variável FPodeMover como True, transformando assim verdadeira a propriedade PodeMover
procedure TMoveControle.IniciarMov(X,Y: Integer);
begin
If ((FControle<>nil) and (FPodeMover=True)) then
begin
S.Parent:=FControle.Parent;
S.Left:=FControle.Left; S.Top:=FControle.Top;
S.Width:=FControle.Width; S.Height:=FControle.Height;
S.Visible:=True;
IniLeft:=X; IniTop:=Y;
end;
end;
O procedimento de Iniciar o Movimento Inicia o movimento, porém, só o inicia se FControle for diferente de nil (ou seja, se a proriedade Controle estiver associada à algum controle) e se a propriedade PodeMover for igual à True, ou seja, se o componente pode ser movido ou não. Ele faz o seguinte:
- Ajusta S como sendo "filho" do Parente do controle contido na propriedade Controle, ou seja, se por exemplo a propriedade Controle for igual à Image1 e Image1 estiver em um Painel (TPanel), ele irá inserir no Painel também o componente S;
- Ajusta para que S esteja na mesma posição e tenha o mesmo tamanho do controle contido na propriedade Controle para iniciar a simulação de movimento do controle;
- Mostra o componente S que até então estava escondido (S.Vibible:=True);
- Ajusta as propriedades IniLeft e IniTop igual à X e Y respectivamente, para que um posterior cálculo possa ser feito na procedure Mover;
procedure TMoveControle.Mover(X, Y: Integer);
var SLeft, STop: Integer;
begin
If ((FControle<>nil) and (FPodeMover=True)) then
begin
SLeft:=FControle.Left+(X-IniLeft);
STop:=FControle.Top+(Y-IniTop);
If FBloqueiaLados=True then //Código que Bloqueia os Lados
begin
If SLeft<0 then SLeft:=0;
If SLeft>(FControle.Parent.Width-FControle.Width) then
SLeft:=FControle.Parent.Width-FControle.Width;
If STop<0 then STop:=0;
If STop>(FControle.Parent.Height-FControle.Height) then
STop:=FControle.Parent.Height-FControle.Height;
end;
S.Left:=SLeft;
S.Top:=STop;
end;
end;
Essa sem dúvida é a mais complexa de todas as procedures deste componente. Ela, assim como IniciarMov, só fará todos os cálculos necessários, se a propriedade Controle estiver associada e se a propriedade PodeMover for igual à True; Ela faz alguns cálculos para movimentar o componente S e se a propriedade BloqueiaLados for igual à True ela também faz cálculos para "bloquear" o componente S não deixando ela passar das coordenadas de seu Parente. Se você está iniciando em Delphi® ou Lazarus não se preocupe caso não entenda os cálculos. Treine mais seus conhecimentos em Object Pascal e retorne à estudar este código. Vejamos como ela faz isso:
- SLeft:=FControle.Left+(X-IniLeft); e STop:=FControle.Top+(Y-IniTop); -
Esse cálculo funciona assim: SLeft, ou STop, será igual à Posição Esquerda, ou Superior, Controle que esteja definido na propriedade Controle mais a subtração da variável X, ou Y, recebida nos parâmetros menos a propriedade IniLeft, ou IniTop, onde o Mouse foi posicionado inicialmente;
- If FBloqueiaLados=True then (...)
Funciona somente se a propriedade BloqueiaLados estiver definida como True e funciona em quatro etapas:
- If SLeft<0 then SLeft:=0; -
Se SLeft for menor do que zero, deixamos SLeft como zero, para bloquear no lado esquerdo;
- If SLeft>(FControle.Parent.Width-FControle.Width) then SLeft:=FControle.Parent.Width-FControle.Width; -
Se SLeft for maior do que à subtração da Largura de seu Parente com a Largura do componente definido na propriedade Controle, definimos SLeft igual à subtração da Largura do Parente menos a Largura do componente definido na propriedade Controle;
- If STop<0 then STop:=0; -
Se STop for menor do que zero, deixamos STop como zero, para bloquear no lado superior;
- If STop>(FControle.Parent.Height-FControle.Height) then STop:=FControle.Parent.Height-FControle.Height; -
Se STop for maior do que à subtração da Altura de seu Parente com a Altura do componente definido na propriedade Controle, definimos STop igual à subtração da Altura do Parente menos a Altura do componente definido na propriedade Controle;
- S.Left:=SLeft; e S.Top:=STop; - Depois de Calculado SLeft e STop, e verificado se deve ou não ser bloqueado nos lados o componente, então S.Left e S.Top é definidas como SLeft e STop respectivamente.
procedure TMoveControle.TerminarMov;
begin
If ((FControle<>nil) and (FPodeMover=True)) then
begin
S.Hide;
FControle.Left:=S.Left;
FControle.Top:=S.Top;
If Assigned(FOnControleMovido) then FOnControleMovido(Self);
end;
end;
A procedure TerminarMov funciona de maneira fácil se der entendida. Como não é nenhuma excessão, ela também só funciona se FControle for diferente de nil e se a propriedade PodeMover for igual à True. Ela faz o seguinte:
- S.Hide; - Esconde o Componente S;
- FControle.Left:=S.Left; e FControle.Top:=S.Top; - Define a propriedade Left e Top do componente definido na propriedade Controle igual à S.Left e S.Top, o TShape que foi movimentado, respectivamente.
- If Assigned(FOnControleMovido) then FOnControleMovido(Self); - Se estiver associado o evento OnControleMovido ele é então desencadeado.
Perceba que a função Assigned retorna True se o Parâmetro recebido for diferente de nil. If Assigned(FOnControleMovido) seria igual à If FOnControleMovido<>nil.
destructor TMoveControle.Destroy;
begin
S.Free;
Inherited Destroy;
end;
Esse é o código mais fácil de todos. Ele simplesmente libera o componente, e consequentemente libera memória, S e chama o método Destroy do ancestor TComponent.
Verifique que no final há um código comentado:
//{$I movectrl.lrs}
Quando você for instalar o componente na IDE, descomente esse código, e antes de compilar crie o arquivo de recursos (movectrl.lrs).
Agora para utilizar esse componente, vamos à instalação na IDE.
Faça os passos que fez no componente acima.
No momento de criar a imagem para o arquivo de recurso, renomeie a imagem como tcontrolemove.bmp e pode criar o recurso (lazres ctrlmove.lrs tcontrolemove.bmp)
A Imagem que eu utilizei foi:
(A figura esta em formato GIF, converta para BMP ou XPM antes de usá-la)
Instale o componente na IDE e vamos ao Programa Exemplo para utilização do Componente:
- Inicie o Lazarus e vá até File -> New -> Application;
- Insira um componente do tipo TImage e um componente TMoveControle no formulário;
- Insira uma imagem qualquer no componente TImage
- Defina, no Object Inspector, MoveControle1.Controle igual à Image1 e MoveControle1.PodeMover igual à True;
- Insira respectivamente nos códigos OnMouseUp, onMouseMove e OnMouseDown de Image1 os seguintes códigos:
procedure TForm1.Image1MouseDown(Sender: TOBject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin MoveControle1.IniciarMov(X, Y); end; procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); begin MoveControle1.Mover(X, Y); end; procedure TForm1.Image1MouseUp(Sender: TOBject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin MoveControle1.TerminarMov; end;
Compile e execute a aplicação, depois clique a arraste a imagem. O Resultado final deve ser parecido com a imagem à seguir:
Pronto! O Seu segundo componente em ação. Você pode modificar a propriedade MoveControle1.Controle para outro controle como Edit1, Memo1 ou qualquer outro controle descendente de TControl; Se você definir MoveControle1.Controle sendo um componente do tipo TLabel, ocorrerá um erro na execução do programa e o TLabel não será movimentado. Isso se deve à um bug contido no evento OnMouseMove do TLabel, bug esse que será resolvido pela equipe de desenvolvedores do Lazarus o mais breve possível =). Repare outra coisa também: No construtor de TMoveControle você já define a propriedade TMoveControle.PodeMover=True não sendo necessário a modificação em tempo de Design no Object Inspector. O mais natural seria que a propriedade já iniciasse em True no Object Inspector certo? Sim, mas não é o que ocorre. Você pode definir um valor padrão a ser mostrado no Object Inspector utilizando a palavra-chave default, mas esse será um assunto abordade na próxima seção.
Aprofundando-se ainda mais na Criação de Componentes - Criando um terceiro Componente
Agora que você já desenvolveu dois componentes, vamos à criação de um terceiro, e mais complexo, componente.
[Tutorial em desenvolvimento. Se quiser ajudar, contacte ou abra uma discussão aqui]


