So, nun nehme ich mir mal ein paar Minuten um das Konzept meiner selbst gezeichneten Komponenten zu erklären. Das ist vor FireMonkey entstanden. Im Moment sieht es allerdings so aus als ob das in der Praxis wegen FireMonkey nie zum Einsatz kommen wird und wir stattdessen FireMonkey einsetzen werden.
Deshalb kommt das ganze größtenteils aus der Erinnerung und einem kurzen Blick in den Quelltext, denn das ist schon eine Weile her...
Das Grundprinzip:
Beim Zeichnen bekommt die Komponente eine Zeichenfläche mit übergeben. Dort zeichnet sie sich selbst einfach drauf. Ob die Zeichenfläche der Bildschirm oder eine Bitmap ist, interessiert die Komponente nicht.
Jede Komponente besitzt eine Methode, die eine Region erstellt, die die Grenzen der Komponente festlegt. Vor dem Zeichnen wird die Zeichenfläche mit ExtSelectClipRgn auf diese Region in Kombination der Region der Elternkomponente beschränkt. Man kann also z.B. günstige Gradientmethoden usw. nutzen, die eigentlich über die Komponente hinaus zeichnen würden, statt diese manuell auf die konkrete Region anzupassen.
Muss eine Komponente neu gezeichnet werden und ist Transparenz aktiv, wird ein umgebendes Rechteck der Region benutzt um alle Komponenten, die im selben Bereich liegen, zu finden und ebenfalls neu zu zeichnen. Der Vergleich der Regionen kostete bei vielen Komponenten zu viel Zeit, ebenso die Sichtbarkeitsprüfung um nur nicht verdeckte und somit sichtbare Teile neu zu zeichnen.
Dazu kommt nun, dass eine Komponente gedreht, gezerrt, gedrückt usw. werden kann. Dies weiß die Komponente selbst aber nicht, sondern wird über eine Matrixtransformation der Zeichenfläche realisiert. Die Komponente zeichnet sich selbst also ganz normal, sieht aber auf der realen Zeichenfläche entsprechend transformiert aus. Die Transformation geschieht mit SetWorldTransform, die reine Versetzung um einen gedrückten Zustand darzustellen mit SetViewportOrgEx (womit dies vor die weitere Transformation gesetzt wird ohne dass das normale Zeichnen diesen Extraoffset kennt), Transparenz kommt am Ende mit AlphaBlend dazu.
Für den Designmodus wird ebenfalls das umgebende Rechteck verwendet und daran die Henkel zum Anfassen, drehen, zerren usw. gezeichnet. Das funktioniert natürlich direkt in Delphi wie man es gewohnt ist. Für den Objektinspektor war ein wenig Tools
API Trickserei nötig, aber es funktioniert gut und war nicht weiter kompliziert.
Quelltext kann ich wie gesagt nicht viel zeigen, daher nur in sehr groben Ausschnitten:
Delphi-Quellcode:
var
TransForm: TXForm;
TransForm.eM11 := Cos(RadAngle);
TransForm.eM12 := Sin(RadAngle);
TransForm.eM21 := -Sin(RadAngle);
TransForm.eM22 := Cos(RadAngle);
TransForm.eDx := OffsetX;
TransForm.eDy := OffsetY;
Auf diese Weise kann man das Bild z.B. drehen und versetzen. Mehr zu diesen Berechnungen findest du hier in der Doku zu SetWorldTransform:
http://msdn.microsoft.com/en-us/libr...(v=vs.85).aspx
bzw. in der Doku zu ExtCreateRegion, mit der Funktion lässt sich eine Region analog transformieren:
http://msdn.microsoft.com/en-us/libr...(v=vs.85).aspx
Das Zeichnen selbst ist daher sehr gradlinig:
Delphi-Quellcode:
procedure T...Element.Paint(ACanvas: TCanvas);
var
RegionRect: TRect;
DisplayRegion, OldRegion: HRGN;
begin
DisplayRegion := AutoClipElement(ACanvas, RegionRect, OldRegion);
try
PaintTo(ACanvas, RegionRect);
finally
SelectClipRgn(ACanvas.Handle, OldRegion);
DeleteObject(OldRegion);
DeleteObject(DisplayRegion);
end;
end;
Sprich es wird immer entsprechend geclipped und auch bei Fehlern das alte Clipping wiederhergestellt. Das try..finally ist hier sehr wichtig.
Das mal als kurze Übersicht...