fpGUI Layouting

# 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 Anwender hat ein Fenster manuell auf eine bestimmte Größe gebracht
- Eine ScrollBox löscht üblicherweise dieses Flag für ihre Kinder auf jeden Fall, da der Inhalt der ScrollBox meist unabhängig von dem 'Drumherum' ist.

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.