Cloud with rain
.:G
G:.
0 and 1 serie, black on white
pulled card
myjsp.feelinglinux.com
ver. 1.1.9-4
Hallo, welcome to my world.
Here you can find some stuff about computer science.
<<< Enjoy your visit! >>>
0 and 1 serie, white on black

Kylix: Creazione nuovi componenti grafici visuali

        Scritto: Giansante Gabriele, 2002     

REQUISITI
E' stato usato Kylix 2 professional, installato su Linux RedHat 7.2.
Teoricamente dovrebbe andare bene anche qualsiasi altra versione di Kylix e Delphi.
Un componente visuale e' un oggetto che ha una immagine grafica statica (es. figura) o dinamica (es. edit box).
Pubblicita'
Creare un nuovo componente visuale significa creare un controllo da inserire nelle proprie form con una grafica e delle proprieta' generalmente diverse da quelle dei componenti gia' esistenti.

La creazione del nuovo componente richiede l'estensione di una determinata classe.
I componenti visuali possono dividersi in due categorie: quelli che accettano un input e che vengono selezionati a runtime (focus), mediante TAB, click del mouse, ecc., e quelli che non ne hanno bisogno, pur continuando a rispondere agli eventi di sistema.
Nel primo caso si puo' estendere la classe TCustomControl, nel secondo caso, invece, la classe TGraphicControl.
Entrambe queste classi derivano direttamente o indirettamente da un'altra piu' generale che e' TControl.
TControl e' la classe da cui derivano tutti i componenti visibili a runtime, fornendo metodi, proprieta' ed eventi per il loro controllo.
Puo' essere interessante dare un'occhiata alla gerarchia completa delle classi di cui si e' parlato fino ad ora:
                  TObject
                     |
                  TPersistent 
                     |
                  TComponent
                     |
                  TControl
	             |
            +--------+--------+
	    |                 |
    TGraphicControl ---> TWidgetControl
                              |
			 TCustomControl
Supponiamo di voler creare un componente rappresentante una freccia.

Componente da realizzare come esempio
fig. 1 "esempio di componente"

Eccone le specifiche:
  1. deve essere una freccia orientabile nei 4 versi principali (0, 90, 180, 270 gradi);
  2. gli 0 gradi coincidono con il lato destro del componente e la rotazione e' da intendersi nel senso antiorario. Un esempio di freccia orientato a 0 gradi e' rappresentato nella figura 1;
  3. il componente deve avere forma rettangolare, con al centro disegnata la freccia secondo opportuni parametri;
  4. i parametri modificabili sono i seguenti:
    • orientamento della freccia;
    • colore del bordo della freccia;
    • colore di fondo della freccia;
    • dimensione lato verso cui e' rivolta la punta della freccia, per default 49 (50-1, perche' il numero deve essere dispari per vedere le linee della punta perfettamente dritte);
    • distanza in pixel fra la punta della freccia e la coda (incluse), per default 100;
    • le coordinate dell'angolo in alto a sinistra.

  5. la pendenza dei due lati aventi come estremo comune la punta della freccia devono essere inclinati di 45 gradi (valore assoluto).
  6. si devono poter catturare almeno il singolo click ed il click doppio del mouse sul componente.
Dal menu principale, si crea il nuovo componente (Component -> New Component...). Si dovrebbe aprire una finestra di dialogo richiedente alcune informazioni necessarie per la creazione dello scheletro di codice su cui basarsi.
Le informazioni richieste sono:
  • Ancestor type, ovvero la classe da cui far discendere il nuovo componente. In questo caso scegliamo TGraphicControl;
  • Class Name, ovvero il nome della nostra nuova classe. Es. TFeelingArrow (ricordarsi che come convenzione, tutti i tipi in ObjectPascal Hanno una "T" davanti al nome;
  • Palette Page, ovvero il nome della pagina dei componenti all'interno di Kylix (editor) dove inserire il nuovo componente. Puo' essere una pagina gia' esistente (es. Standard, Additional, ecc.) oppure una pagina non esistente (es. FeelingLinux). In quest'ultimo caso, la pagina viene creata;
  • Unit file name, cioe' il nome della unit che conterra' la dichiarazione della classe (es. QFeelingArrow);
  • Search path, cioe' il percorso di ricerca standard, piu' quello della nuova eventuale directory contenente la unit del componente. Questo campo dovrebbe essere automaticamente aggiornato, sollevando il programmatore dalla necessita' di modificarlo.
Diamo un'occhiata al codice generato da Kylix:
  unit QFeelingArrow;

  interface

  uses
    SysUtils, Types, Classes, QGraphics, QControls, QForms, QDialogs;

  type
    TFeelingArrow = class(TGraphicControl)
    private
      { Private declarations }
    protected
      { Protected declarations }
    public
      { Public declarations }
    published
      { Published declarations }
    end;

  procedure Register;

  implementation

  procedure Register;
  begin
    RegisterComponents('FeelingLinux', [TFeelingArrow]);
  end;

  end.
La unit contiene una semplice estensione di classe, con una piccola aggiunta: viene generato il metodo Register che consente l'inserimento del componente nella palette indicata ("FeelingLinux", in questo caso).
I passi per completare la creazione del componente, a grandi linee sono i seguenti:
  1. pubblicazione delle proprieta' dell'oggetto.
    Per pubblicazione delle proprieta' si intende il rendere disponibile nell'object inspector alcune proprieta', come la dimensione del componente, il colore, e tutte le altre espresse nelle specifiche. Inoltre si possono rendere disponibili anche altre proprieta' proprie del controllo grafico che altrimenti verrebbero nascoste. Le proprieta' da pubblicare vanno specificate nella sezione published della classe;
  2. disegno del componente.
    Il disegno del componente, in generale, comporta la definizione di alcuni oggetti come la penna ed il pennello, necessari a modificare le proprieta' di disegno da parte dell'utente (es. colore). Sara' necessario ridefinire alcuni metodi per la modifica del canvas (superficie di disegno) del componente;
  3. dichiarazione di eventi che si potrebbero voler catturare (come il click del mouse ed altri);
  4. inserimento di un'immagine per la "component palette";
  5. registrazione del componente e test.

Esempio di risultato finale da raggiungere
fig.2 "Arriveremo a poter fare questo!"


Pubblicazione delle proprieta' dell'oggetto

Cominciamo con l'orientamento della freccia. Definiamo un tipo enumerato contenente i quattro orientamenti possibili, 0, 90, 180, 270 gradi. Prima della definizione della classe TFeelingArrow inseriamo
  TOrientation = (deg_0, deg_90, deg_180, deg_270);
Tali valori devono essere pubblicati e resi disponibili all'utente per la scelta. Sara' necessario definire all'interno di TFeelingArrow sia una variabile di tipo TOrientation che un metodo per la sua modifica. Eventualmente si puo' inserire un metodo anche per la lettura, ma nella maggior parte dei casi la lettura della variabile coincide con la restituzione del valore della variabile stessa.
Nella sezione private inseriamo
  _Orientation: TOrientation;
  procedure SetOrientation(v: TOrientation);
con la seguente implementazione per SetOrientation:
  procedure TFeelingArrow.SetOrientation(v: TOrientation);
  begin
    if _Orientation <> v then begin
      _Orientation := v;
      SetRealDimension;
      Invalidate;
    end;
  end;
In pratica, il nuovo orientamento viene salvato solo se e' diverso dal precedente. Nel caso in cui l'orientamento scelto sia effettivamente un nuovo orientamento, con il metodo Invalidate ereditato da TControl, viene ridisegnata completamente l'immagine del componente. Cambiando l'orientamento, cambiano anche le dimensioni dell'oggetto, che devono quindi essere modificate. Di cio' si occupa il metodo SetRealDimension che verra' introdotto piu' avanti.
Per finire con la proprieta' Orientation, dobbiamo pubblicarla e definire un valore di default. Nella sezione published inseriamo la riga
  property Orientation: TOrientation read _Orientation write SetOrientation;
Scrivere read _Orientation significa che il contenuto di Orientation viene letto direttamente dalla variabile privata _Orientation. Scrivere write SetOrientation, invece, significa usare la procedura SetOrientation per modificare il valore di Orientation (la stessa cosa poteva essere fatta anche con read). Ovviamente, il parametro accettato da SetOrientation deve essere dello stesso tipo della proprieta'.
Il valore di default lo possiamo indicare nel costruttore che va quindi ridefinito.
Nella sezione public inseriamo
  constructor Create(AOwner: TComponent); override;
con cui specifichiamo che stiamo ridefinendo il costruttore.
L'implementazione del nuovo costruttore e' la seguente:
  constructor TFeelingArrow.Create(AOwner: TComponent);
  begin
    inherited Create(AOwner);
    _Orientation := deg_0;
  end;
Con inherited Create(AOwner); viene creato il costruttore che abbiamo sovrascritto. E' necessario farlo perche' servono comunque le inizializzazioni di default.

Analogamente procediamo per le altre proprieta', con piccole variazioni.

Le coordinate dell'angolo in alto a sinistra vengono pubblicate gia' di default come proprieta' Top e Left, derivate da TControl.

Chiamiamo ArrowWidth la distanza in pixel fra la punta della freccia e la coda (incluse).
Chiamiamo ArrowHeight la dimensione del lato verso cui e' rivolta la punta della freccia.
Per pubblicare le proprieta' ArrowWidth ed ArrowHeight, inserire nella sezione private:
  _Height: Integer;
  _Width: Integer;
  _ArrowWidth: Integer;
  _ArrowHeight: Integer;

  procedure SetArrowWidth(v: Integer);
  procedure SetArrowHeight(v: Integer);
  procedure SetRealDimension;
Il metodo SetRealDimension aggiusta le dimensioni grafiche del componente in base all'orientamento della freccia.
Ad esempio, se la freccia e' orientata verso l'alto (90 gradi), allora la altezza reale del componente deve coincidere con la larghezza della freccia, mentre la larghezza reale del componente deve coincidere con la altezza della freccia (vedere definizione di ArrowHeight e di ArrowWidth).
Dal momento che l'altezza e la larghezza del componente devono essere regolate in base ad ArrowHeight ed ArrowWidth, si rende necessario nascondere le proprieta' di default Height e Width, che verranno modificate automaticamente.
Vengono nascoste con un piccolo "trucco": si ridichiarano sia la proprieta' Height che la Width come "read only", redirigendo la lettura alle variabili private _Height e _Width.

Nella sezione published:
  property ArrowWidth: Integer read _ArrowWidth write SetArrowWidth;
  property ArrowHeight: Integer read _ArrowHeight write SetArrowHeight;
  property Height: Integer read _Height;
  property Width: Integer read _Width;
Essendo sia Height che Width a sola lettura, non appariranno nell'Object Inspector.
All'interno del costruttore:
  _ArrowWidth := 100;
  _Width := 100;
  inherited Width := _Width;
  _ArrowHeight := 49;
  _Height := 49;
  inherited Height := _Height;
Dal momento che devono variare le dimensioni reali dell'oggetto, bisogna ricorrere alle proprieta' Height e Width originarie usando la parola chiave inherited. Se non lo facessimo, le dimensioni del componente non potrebbero variare.
A 0 gradi, come a 180, l'altezza reale coincide con quella della freccia e cosi' anche per la larghezza.

Infine nella parte di implementazione:
  procedure TFeelingArrow.SetRealDimension;
  begin
    {Aggiusto la reale altezza e larghezza del componente in base
     all'orientamento}
    case _Orientation of
      deg_0, deg_180: begin
        Height := _ArrowHeight;
        Width := _ArrowWidth;
      end;
      deg_90, deg_270: begin
        Height := _ArrowWidth;
        Width := _ArrowHeight;
      end;
    end;
    inherited Width := _Width;
    inherited Height := _Height;
  end;

  procedure TFeelingArrow.SetArrowWidth(v:integer);
  begin
    if (_ArrowWidth <> v) and (v > _ArrowHeight) then begin
      _ArrowWidth := v;
      SetRealDimension;
      Invalidate;
    end;
  end;

  procedure TFeelingArrow.SetArrowHeight(v:integer);
  begin
    if (_ArrowHeight <> v) and (_ArrowWidth > v) then begin
      _ArrowHeight := v;
      SetRealDimension;
      Invalidate;
    end;
  end;
Le condizioni "(v > _ArrowHeight)" ed "(_ArrowWidth > v)" ci assicurano che esista sempre una coda "decente" per la freccia.

Pubblicita'
Rimangono da sistemare il colore di fondo ed il colore del bordo della freccia.
Il disegno del componente avviene sul canvas del componente. Le funzioni definite nella classe TCanvas (da cui discende il canvas) usano due oggetti di tipo TPen e TBrush. TPen serve a disegnare i bordi (penna), mentre TBrush serve a disegnare in genere il fondo (pennello). Ad esempio, nel disegno di un rettangolo, l'oggetto di tipo TPen viene usato per definire le proprieta' del bordo, mentre l'oggetto di tipo TBrush viene usato per definire le proprieta' della parte interna (riempimento del rettangolo). Ancora un esempio, il metodo TCanvas.FillRect usa il pennello (TBrush) corrente.

Questa volta, non abbiamo dei tipi semplici come proprieta', ma degli oggetti. Il procedimento di creazione della proprieta' cambia un po'. Innanzitutto, gli oggetti contenenti la penna ed il pennello li abbiamo gia' all'interno del Canvas, ereditato da TGraphicsControl.
Ogni volta che si cambiano le proprieta' di penna e pennello, la freccia deve essere ridisegnata. Sia TPen che TBrush prevedono l'evento OnChange che si attiva quando vengono modificati. Basta quindi modificare la gestione dell'evento assegnando una funzione che si occupa di invalidare la grafica del componente.
Nella sezione private, aggiungiamo
  procedure SetBrush(v: TBrush);
  procedure SetPen(v: TPen);
  function GetPen: TPen;
  function GetBrush: TBrush;
  procedure ChangeEvent(Sender: TObject);
Le due funzioni Set hanno la stessa funzione vista in precedenza con le altre proprieta'.
Le due funzioni Get si rendono necessarie perche', nella pubblicazione della penna e del pennello non si riesce ad ottenere direttamente le proprieta' Canvas.Pen e Canvas.Brush.
La procedura ChangeEvent non fa altro che chiamare Invalidate e serve da gestore degli eventi onChange.
Ecco l'implementazione dei 5 metodi:
  procedure TFeelingArrow.ChangeEvent(Sender: TObject);
  begin
    {Metodo pensato per gli eventi di cambiamento di proprieta' che
     comportano il ridisegno del componente}
    Invalidate;
  end;

  function TFeelingArrow.GetPen: TPen;
  begin
    Result := Canvas.Pen;
  end;

  function TFeelingArrow.GetBrush: TBrush;
  begin
    Result := Canvas.Brush;
  end;

  procedure TFeelingArrow.SetBrush(v: TBrush);
  begin
    Canvas.Brush.Assign(v);
  end;

  procedure TFeelingArrow.SetPen(v: TPen);
  begin
    Canvas.Pen.Assign(v);
  end;
Nelle due funzioni Set, attenzione al fatto che viene chiamato il metodo Assign che non distrugge l'oggetto gia' esistente, ma ne riassegna i campi in base a quelli dell'oggetto passato.
L'assegnazione dell'evento onChange va fatta nel costruttore:
  Canvas.Pen.OnChange := ChangeEvent;
  Canvas.Brush.OnChange := ChangeEvent;
Infine, la pubblicazione del pennello e della penna avviene nella sezione published con le seguenti righe:
  property Pen: TPen read GetPen write SetPen;
  property Brush: TBrush read GetBrush write SetBrush;
Notare come questa volta siano stati usati dei metodi anche per la lettura della variabile.

Disegno del componente

Avendo definito tutte le proprieta' del componente freccia, abbiamo tutto cio' che serve a disegnarlo.
Per disegnare il componente si deve sovrascrivere il metodo Paint derivato da TGraphicsControl. L'implementazione standard, infatti non fa niente.
Nella sezione Protected, inseriamo la dichiarazione di sovrascrittura
  procedure Paint; override;
con questa implementazione:
  procedure TFeelingArrow.Paint;
  begin
    case _Orientation of
      deg_0: Draw0;
      deg_90: Draw90;
      deg_180: Draw180;
      deg_270:  Draw270;
    end;
  end;
Per non creare un metodo troppo lungo e per facilitarne la lettura, il disegno e' stato spostato nei quattro metodi "Draw", uno per ogni angolo possibile.
Nella sezione private, inseriamo le seguenti dichiarazioni:
  procedure Draw0;
  procedure Draw90;
  procedure Draw180;
  procedure Draw270;
la cui implementazione sara':
  procedure TFeelingArrow.Draw0;
  var a: array[1..7] of TPoint;
  begin
    a[1].X := _ArrowWidth - 1; a[1].Y := Round(_ArrowHeight/2);
    a[2].X := a[1].X - a[1].Y; a[2].Y := 0;
    a[3].X := a[2].X; a[3].Y := Round(_ArrowHeight/4);
    a[4].X := 0; a[4].Y := a[3].Y;
    a[5].X := 0; a[5].Y := 3*a[3].Y;
    a[6].X := a[2].X; a[6].Y := a[5].Y;
    a[7].X := a[2].X; a[7].Y := _ArrowHeight - 1;
    Canvas.Polygon(a);
  end;

  procedure TFeelingArrow.Draw90;
  var a: array[1..7] of TPoint;
  begin
    a[1].X := Round(_ArrowHeight/2); a[1].Y := 0;
    a[2].X := 0; a[2].Y := a[1].X;
    a[3].X := Round(_ArrowHeight/4); a[3].Y := a[1].X;
    a[4].X := a[3].X; a[4].Y := _ArrowWidth - 1;
    a[5].X := 3*a[3].X; a[5].Y := a[4].Y;
    a[6].X := a[5].X; a[6].Y := a[1].X;
    a[7].X := _ArrowHeight - 1; a[7].Y := a[1].X;
    Canvas.Polygon(a);
  end;

  procedure TFeelingArrow.Draw180;
  var a: array[1..7] of TPoint;
  begin
    a[1].X := 0; a[1].Y := Round(_ArrowHeight/2);
    a[2].X := a[1].Y; a[2].Y := _ArrowHeight - 1;
    a[3].X := a[1].Y; a[3].Y := 3*Round(_ArrowHeight/4);
    a[4].X := _ArrowWidth - 1; a[4].Y := a[3].Y;
    a[5].X := a[4].X; a[5].Y := Round(_ArrowHeight/4);
    a[6].X := a[1].Y; a[6].Y := a[5].Y;
    a[7].X := a[1].Y; a[7].Y := 0;
    Canvas.Polygon(a);
  end;

  procedure TFeelingArrow.Draw270;
  var a: array[1..7] of TPoint;
  begin
    a[1].X := Round(_ArrowHeight/2); a[1].Y := _ArrowWidth - 1;
    a[2].X := _ArrowHeight - 1; a[2].Y := a[1].Y - a[1].X;
    a[3].X := 3*Round(_ArrowHeight/4); a[3].Y := a[2].Y;
    a[4].X := a[3].X; a[4].Y := 0;
    a[5].X := Round(_ArrowHeight/4); a[5].Y := 0;
    a[6].X := a[5].X; a[6].Y := a[3].Y;
    a[7].X := 0; a[7].Y := a[3].Y;
    Canvas.Polygon(a);
  end;
Per come e' stato implementato il componente, il Canvas contiene gia' il pennello e la penna corretti, quindi non serve altro che disegnare il poligono rappresentante la freccia. Il metodo Polygon, definito nell'istanza di TCanvas, crea un poligono avente il bordo con le proprieta' della penna e l'interno con le proprieta' del pennello.

Inserimento di eventi

Molti eventi sono gia' definiti in TControl e basta solo renderli disponibili tramite la sezione published.
Inseriamo le seguenti linee nella sezione published:
  property OnClick; {click del mouse}
  property OnDblClick; {doppio click del mouse}
  property OnMouseEnter; {il mouse comincia a passare sulla freccia}
  property OnMouseLeave; {il mouse non e' piu' sopra la freccia}
  property OnMouseMove; {il mouse si sta muovendo sulla freccia}
  property OnMouseDown; {pulsante del mouse premuto sulla freccia}
  property OnMouseUp; {pulsante del mouse non piu' premuto sulla freccia}
Pubblicando le proprieta', queste diventano automaticamente disponibili ed utilizzabili come in tutti gli altri componenti gia' esistenti.

Inserimento immagine per la "Component palette"

Ogni componente nella Component palette ha un'immagine con cui e' faclimente distinguibile dagli altri. Se non esiste un'immagine appropriata, ne viene usata una di default.
La stessa cosa avviene per i nuovi componenti creati. Assegnare un'immagine e' semplice e comporta i seguenti passi:
  1. creazione del file bitmap contenente l'immagine. Puo' essere usato Gimp, ma attenzione che si dovra' avere un bitmap a 256 colori e di dimensioni 24x24. La restrizione dei 256 colori puo' cadere nel caso si utilizzi un tool che ne supporta di piu'. Quello di Delphi 6 supporta solo 256 colori. Il nome dell'immagine deve essere lo stesso del componente, ma a lettere maiuscole (non e' proprio obbligatorio, ma cosa costa farlo?). Ad esempio, "TFEELINGARROW.bmp";
  2. inserimento del file bitmap in un file di risorse (es. "dcr"). Il nome del file di risorse dovra' essere identico al nome della unit con il codice di implementazione del componente (es. "QFeelingArrow.dcr");
  3. il file di risorse dovra' essere posizionato nella stessa directory dell'unita'. Una volta compilato il file di risorse, l'immagine bitmap non e' piu' necessaria.
Il passo 2) merita un piccolo approfondimento. In Delphi, un file di risorse e l'immagine bitmap possono essere creati direttamente attraverso il tool di disegno fornito con l'IDE. In Kylix non esiste un editor simile (o almeno io non ne ho trovato traccia). Il tutto va fatto a mano. Una volta creata l'immagine bitmap, questa puo' essere inserita in un file di risorse con uno dei tool disponibili in giro (ce ne e' uno nel "Companion tools CD").



Registrazione del componente

La registrazione del componente avviene in modo semplice. Basta definire all'interno dell'unita' la procedura Register che chiama la procedura RegisterComponents come gia' visto all'inizio con lo scheletro del componente. RegisterComponents accetta come parametri la pagina dove inserire il componente nella Component palette (nel codice mostrato e' "FeelingLinux") ed un elenco di componenti da inserire in tale pagina (in questo caso abbiamo un solo componente, ovvero "FeelingArrow").


Registrazione componente - fase 1
fig.3 "Prima fase della registrazione del componente"

Dal menu, scegliere "Component" -> "Install Component..." (figura 3). Apparira' la schermata di installazione, dove richiede il nome dell'unita' contenente il componente, il path dove trovare i file necessari (dovrebbe essere automaticamente aggiornato), il nome del package dove inserire il componente ed un commento al package.
Per installare il componente in un nuovo package, basta cambiare pagina cliccando sul tab "Into new package".


Registrazione componente - fase 2
fig.4 "Seconda fase della registrazione del componente"

Dando l'Ok, si arriva al form di gestione del package (figura 4). Si puo' notare come sia stata aggiunta la unit con il codice della freccia. Selezionando Install, viene compilato il package ed il componente viene inserito all'interno della palette dei componenti di Kylix. Se il componente gia' era stato inserito, si dovra' selezionare Compile e l'unica variazione sara' nel comportamento dell'oggetto (rispecchiante le modifiche apportate).

Il codice completo della unit "QFeelingArrow"

Come riepilogo finale, ecco il codice completo della unit, contenente tutti i pezzi di codice gia' visti.
Manca solo l'immagine del componente, che comunque verra' inclusa in un package FeelingLinux di prossima realizzazione.
  {
        Author:    Gabriele Giansante (c) 2002
        Package:   FeelingLinux
        Component: TFeelingArrow
        License:   Do whatever you want with this code, 
	           but specify this copyright notes.
  }
  unit QFeelingArrow;

  interface

  uses
    SysUtils, Types, Classes, QGraphics, QControls, QForms, QDialogs;

  type

    TOrientation = (deg_0,deg_90,deg_180,deg_270);

    TFeelingArrow = class(TGraphicControl)
    private
      _Height: Integer;
      _Width: Integer;
      _ArrowWidth: Integer;
      _ArrowHeight: Integer;
      _Orientation: TOrientation;

      procedure SetArrowWidth(v: Integer);
      procedure SetArrowHeight(v: Integer);
      procedure SetRealDimension;
      procedure SetOrientation(v: TOrientation);
      procedure SetBrush(v: TBrush);
      procedure SetPen(v: TPen);
      function GetPen: TPen;
      function GetBrush: TBrush;
      procedure ChangeEvent(Sender: TObject);

      procedure Draw0;
      procedure Draw90;
      procedure Draw180;
      procedure Draw270;

    protected
      procedure Paint; override;

    public
      constructor Create(AOwner: TComponent); override;

    published
      property Orientation: TOrientation read _Orientation write SetOrientation;
      property ArrowWidth: Integer read _ArrowWidth write SetArrowWidth;
      property ArrowHeight: Integer read _ArrowHeight write SetArrowHeight;
      property Pen: TPen read GetPen write SetPen;
      property Brush: TBrush read GetBrush write SetBrush;
      property Height: Integer read _Height;
      property Width: Integer read _Width;

      property OnClick;
      property OnDblClick;
      property OnMouseEnter;
      property OnMouseLeave;
      property OnMouseMove;
      property OnMouseDown;
      property OnMouseUp;

    end;

  procedure Register;

  implementation

  procedure Register;
  begin
    RegisterComponents('FeelingLinux', [TFeelingArrow]);
  end;

  constructor TFeelingArrow.Create(AOwner: TComponent);
  begin
    inherited Create(AOwner);
    {Con l'orientamento a 0 gradi la larghezza reale coincide con la larghezza
     della freccia e cosi' anche per l'altezza}
    _Orientation := deg_0;
    _ArrowWidth := 100;
    _Width := 100;
    _ArrowHeight := 49;
    _Height := 49;
    inherited Height := _Height;
    inherited Width := _Width;

    Canvas.Pen.OnChange := ChangeEvent;
    Canvas.Brush.OnChange := ChangeEvent;
  end;

  procedure TFeelingArrow.Paint;
  begin
    case _Orientation of
      deg_0: Draw0;
      deg_90: Draw90;
      deg_180: Draw180;
      deg_270: Draw270;
    end;
  end;

  procedure TFeelingArrow.Draw0;
  var a: array[1..7] of TPoint;
  begin
    a[1].X := _ArrowWidth - 1; a[1].Y := Round(_ArrowHeight/2);
    a[2].X := a[1].X - a[1].Y; a[2].Y := 0;
    a[3].X := a[2].X; a[3].Y := Round(_ArrowHeight/4);
    a[4].X := 0; a[4].Y := a[3].Y;
    a[5].X := 0; a[5].Y := 3*a[3].Y;
    a[6].X := a[2].X; a[6].Y := a[5].Y;
    a[7].X := a[2].X; a[7].Y := _ArrowHeight - 1;
    Canvas.Polygon(a);
  end;

  procedure TFeelingArrow.Draw90;
  var a: array[1..7] of TPoint;
  begin
    a[1].X := Round(_ArrowHeight/2); a[1].Y := 0;
    a[2].X := 0; a[2].Y := a[1].X;
    a[3].X := Round(_ArrowHeight/4); a[3].Y := a[1].X;
    a[4].X := a[3].X; a[4].Y := _ArrowWidth - 1;
    a[5].X := 3*a[3].X; a[5].Y := a[4].Y;
    a[6].X := a[5].X; a[6].Y := a[1].X;
    a[7].X := _ArrowHeight - 1; a[7].Y := a[1].X;
    Canvas.Polygon(a);
  end;

  procedure TFeelingArrow.Draw180;
  var a: array[1..7] of TPoint;
  begin
    a[1].X := 0; a[1].Y := Round(_ArrowHeight/2);
    a[2].X := a[1].Y; a[2].Y := _ArrowHeight - 1;
    a[3].X := a[1].Y; a[3].Y := 3*Round(_ArrowHeight/4);
    a[4].X := _ArrowWidth - 1; a[4].Y := a[3].Y;
    a[5].X := a[4].X; a[5].Y := Round(_ArrowHeight/4);
    a[6].X := a[1].Y; a[6].Y := a[5].Y;
    a[7].X := a[1].Y; a[7].Y := 0;
    Canvas.Polygon(a);
  end;

  procedure TFeelingArrow.Draw270;
  var a: array[1..7] of TPoint;
  begin
    a[1].X := Round(_ArrowHeight/2); a[1].Y := _ArrowWidth - 1;
    a[2].X := _ArrowHeight - 1; a[2].Y := a[1].Y - a[1].X;
    a[3].X := 3*Round(_ArrowHeight/4); a[3].Y := a[2].Y;
    a[4].X := a[3].X; a[4].Y := 0;
    a[5].X := Round(_ArrowHeight/4); a[5].Y := 0;
    a[6].X := a[5].X; a[6].Y := a[3].Y;
    a[7].X := 0; a[7].Y := a[3].Y;
    Canvas.Polygon(a);
  end;

  procedure TFeelingArrow.ChangeEvent(Sender: TObject);
  begin
    {Metodo pensato per gli eventi di cambiamento di proprieta' che
     comportano il ridisegno del componente}
    Invalidate;
  end;

  procedure TFeelingArrow.SetOrientation(v: TOrientation);
  begin
    if _Orientation <> v then begin
      _Orientation := v;
      SetRealDimension;
      Invalidate;
    end;
  end;

  procedure TFeelingArrow.SetRealDimension;
  begin
    {Aggiusto la reale altezza e larghezza del componente in base
     all'orientamento}
    case _Orientation of
      deg_0, deg_180: begin
        _Height := _ArrowHeight;
        _Width := _ArrowWidth;
      end;
      deg_90, deg_270: begin
        _Height := _ArrowWidth;
        _Width := _ArrowHeight;
      end;
    end;
    inherited Height := _Height;
    inherited Width := _Width;
  end;

  procedure TFeelingArrow.SetArrowWidth(v:integer);
  begin
    if (_ArrowWidth <> v) and (v > _ArrowHeight) then begin
      _ArrowWidth := v;
      SetRealDimension;
      Invalidate;
    end;
  end;

  procedure TFeelingArrow.SetArrowHeight(v:integer);
  begin
    if (_ArrowHeight <> v) and (_ArrowWidth > v) then begin
      _ArrowHeight := v;
      SetRealDimension;
      Invalidate;
    end;
  end;

  function TFeelingArrow.GetPen: TPen;
  begin
    Result := Canvas.Pen;
  end;

  function TFeelingArrow.GetBrush: TBrush;
  begin
    Result := Canvas.Brush;
  end;

  procedure TFeelingArrow.SetBrush(v: TBrush);
  begin
    Canvas.Brush.Assign(v);
  end;

  procedure TFeelingArrow.SetPen(v: TPen);
  begin
    Canvas.Pen.Assign(v);
  end;

  end.


Hai trovato utile questo articolo?
Aiutami a condividerlo o metti un "mi piace".
Grazie mille!


Gli strumenti di condivisione (Google+, Facebook) sono visibili in alto a destra solo dopo aver accettato la policy di utilizzo dei cookie per questo sito.
FAQ - Come faccio a cambiare la mia scelta?

 

Strumenti (myjsp.feelinglinux.com)
Gioco: allenamento con la tastiera Strumenti di codifica/decodifica URI (%-encoding) e Base64 Strumenti di calcolo online per IP e Reti
QUIZ GAME
Quiz game

Cerca @myjsp.feelinglinux.com

Pubblicita'