Der fpGUI Layouting-Algorithmus

Sebastian Günther, 2001-02-12

Initialisierung eines Fensters (Forms)

Wenn sich ein Fenster zum ersten Mal darstellt, und keine Standardgröße vorgegeben wurde, dann muß es diese selbst berechnen. Dies ist ein rekursiver Prozeß, bei dem allen Kindern des Fensters das Ereignis TCalcSizesEventObj von oben nach unten im Widget-Baum zugestellt wird (beginnend beim Fenster selbst). TWidget reagiert auf den Empfang dieses Ereignisses folgendermaßen (in TWidget.EvCalcSizes):

  1. Das Ereignis wird mit TWidget.DistributeEvent an alle Kinder weitergeleitet
  2. Die virtuelle geschützte Methode TWidget.DoCalcSizes wird aufgerufen. Abgeleitete Widgets überschreiben diese Methode, um ihre Größen (Minimum, Maximum, Optimum) neu zu berechnen.
  3. Die Ergebnisse von DoCalcSizes werden ggf. korrigiert, z.B. darf die Maximalgröße nicht kleiner als die Minimalgröße sein.
Wenn der Code für das Fenster den Versand dieses Ereignisses fertiggestellt hat, haben alle Widgets im Fenster gültige Größenangaben. Nun kann es seine eigene, initiale Größe setzen (dies ist die vorher berechnete Optimum-Größe des Fensters). Dies wird per TWidget.SetBounds durchgeführt.

Zuweisung einer neuen Position und Größe mit TWidget.SetBounds

SetBounds dient zwei Zwecken: Dem Setzen einer neuen Position, und dem Setzen einer neuen Größe für ein Widget. Zunächst werden die neuen Daten ins Widget übernommen. SetBounds überprüft anschließend, ob eine Größenänderung vorliegt - wenn ja, wird ein TApplySizeEventObj-Ereignis ausgelöst. Der Default-Handler in TWidget führt nun zwei einfache Schritte durch:

  1. Aufruf der virtuellen geschützten Methode TWidget.DoApplySize
  2. Weiterleitung des Ereignisses an alle Kinder per TWidget.DistributeEvent
DoApplySize dürfte von allen Widgets überschrieben werden, die Kinder besitzen - denn dies ist der einzig richtige Ort, um die Kinder zu layouten (also ihre Position und Größe festzulegen.)

Das TApplySizeEventObj-Ereignis führt ein wichtiges Flag mit: ForcedSize gibt an, ob die nun folgende Größenänderung 'erzwungen' ist oder nicht. Erzwungen bedeutet, daß Änderungen an untergeordneten Widgets (s.u.) keinen erneuten Layout-Vorgang auslösen sollen. Dies wird beispielsweise in folgenden Fällen genutzt:

Der aktuelle 'Gezwungenheits-Zustand' wird über das Flag wsSizeIsForced in TWidget.WidgetState angezeigt.

Forms behandeln dieses Ereignis auf etwas andere Art und Weise: Der Wunsch nach einer Größenänderung wird an das unterliegende fpGFX-Fenster weitergeleitet; dieses liefert irgendwann die Nachricht, daß nun die neue Größe aktiv ist. Als Reaktion ruft es nun TWidget.SetBounds für sich selbst auf - also die geerbte Methode. Diese sorgt dann, wie bei anderen Widgets auch, für ein korrektes Layouting.

Änderungen eines Widgets

Wenn sich bestimmte Eigenschaften eines Widgets ändern, kann sich dadurch auch dessen Größe ändern. Bei Verdacht auf Größenänderung sollten Widgets intern immer die Methode TWidget.Update aufrufen. Ist die Größe des aktuellen Widgets erzwungen, so bricht diese Methode sofort ab. Ansonsten wird zunächst eine neue Berechnung der Größen per TCalcSizesEventObj-Ereignis veranlaßt. Sollten diese nun von den alten Größen abweichen, so wird das Ereignis TUpdateEventObj ausgelöst. Dieses wird nicht an Kinder weitergeleitet, stattdessen ruft der Default-Handler in TWidget die Update-Methode des Eltern-Widgets auf. Der Handler für Forms reagiert auf dieses Ereignis allerdings mit einer Anpassung der Fenstergröße mit Hilfe der SetBounds-Methode.

Widget ändert seine Sichtbarkeit

Wenn ein normales Widget sichtbar oder unsichtbar wird, und diese Änderung vom Widget selbst (und nicht seinem Eltern-Widget) ausgelöst wurde, dann wird für das Eltern-Widget die TWidget.Update-Methode aufgerufen. Dieses prüft nun den Einfluß dieser Änderung auf das Layout, und löst ggf. ein Relayouting aus.