TUI Layout
Anchoring-First Responsive Layout für TUI-Anwendungen.
Anchoring ist der Default-Modus - jedes Control verhält sich korrekt bei Terminal-Resize ohne spezielle Container.
Grundprinzip
Anchoring-First bedeutet:
- Terminal Resize darf Layout, Focus und Rendering nicht brechen
- Controls behalten stabile räumliche Beziehungen
- Vorhersagbare Expansion/Shrinking-Regeln
Anchor-Semantik
Verfügbare Anchors
| Anchor | Bedeutung |
| Left | Abstand zum linken Rand bleibt konstant |
| Top | Abstand zum oberen Rand bleibt konstant |
| Right | Abstand zum rechten Rand bleibt konstant |
| Bottom | Abstand zum unteren Rand bleibt konstant |
Kombinationen
Default: Left + Top
→ Position bleibt stabil, Größe konstant
Left + Right:
→ Breite dehnt/schrumpft mit Parent-Breite
Top + Bottom:
→ Höhe dehnt/schrumpft mit Parent-Höhe
Alle vier (Left + Top + Right + Bottom):
→ Rect dehnt sich in beide Richtungen
Keine Anchors:
→ Behandeln wie Left + Top (keine "floating" Ambiguität)
PXAML Beispiele
<!-- Feste Position oben links -->
<Button Anchors="Left,Top" Text="OK" />
<!-- Volle Breite, feste Höhe -->
<TextEdit Anchors="Left,Top,Right" Height="3" />
<!-- Füllt gesamten verfügbaren Bereich -->
<ListView Anchors="Left,Top,Right,Bottom" />
<!-- Statusleiste unten -->
<StatusBar Anchors="Left,Right,Bottom" Height="1" />
Resize-Verhalten
Ablauf bei Resize
1. Resize Event empfangen
└─ Root Available Rect aktualisieren
2. Layout invalidieren
└─ Measure/Arrange Pass durchführen
3. Focus erhalten
└─ Gleicher logischer Control behält Focus
4. Neu rendern
└─ Paint → DiffFlush
Deterministisch
PFLICHT:
- Gleicher Input → Gleiches Layout
- Kein Flicker bei schnellem Resize
- Kein Cursor-Corruption
- Focus bleibt auf logischem Control
Breakpoints
Optionale aber empfohlene Resize-Schwellwerte:
Standard-Breakpoints
| Breakpoint | Breite | Typisches Layout |
| Narrow | < 80 | Single-Column, gestapelt |
| Medium | 80-119 | Sidebar collapsed zu Tabs |
| Wide | >= 120 | Sidebar + Content nebeneinander |
PXAML Konfiguration
<Panel ResponsiveMode="Auto" Breakpoints="80,120">
<!-- Automatisches Reflow bei Breakpoint-Überschreitung -->
</Panel>
Breakpoint-Regeln
PFLICHT:
- Breakpoints NICHT "magic" - in PXAML oder Theme Config definieren
- Breakpoints dokumentieren
- Verhalten bei jedem Breakpoint klar definieren
Layout-Container
Stack
Orientierung: Vertikal oder Horizontal
Kinder werden nacheinander angeordnet
<Stack Orientation="Vertical">
<Label Text="Name:" />
<TextEdit />
<Label Text="Email:" />
<TextEdit />
</Stack>
Dock
Positionen: Left, Top, Right, Bottom, Fill
Kinder docken an Seiten, Rest füllt Mitte
<Dock>
<MenuBar Dock="Top" />
<StatusBar Dock="Bottom" />
<Sidebar Dock="Left" Width="20" />
<Content Dock="Fill" />
</Dock>
Overlay
Für Popups, Modals, Tooltips
Überlagert andere Controls
Muss innerhalb Screen Bounds bleiben (clamped)
<Overlay>
<Dialog X="10" Y="5" Width="40" Height="10">
<!-- Modal Content -->
</Dialog>
</Overlay>
Grid (später)
Zeilen und Spalten definieren
Controls in Zellen platzieren
Anchoring-Precedence
Reihenfolge der Layout-Berechnung:
1. Container Layout (wenn explizit)
└─ Stack/Grid/Dock/Overlay berechnet Child-Rects
2. Anchors anwenden
└─ Innerhalb des Container-Rects verfeinern
└─ Min/Max beachten
└─ Margins/Padding erhalten
3. Clipping zuletzt
└─ Finale Bounds beschneiden
Wenn Content größer als Bereich
BEVORZUGT:
- ScrollViewer Container verwenden
- Content scrollbar machen
NICHT als Default:
- Text "shrink to fit"
PFLICHT:
- Controls können in kleinerem Rect rendern (Clip + Ellipsis)
- State darf nicht verloren gehen
<ScrollViewer Anchors="Left,Top,Right,Bottom">
<Stack Orientation="Vertical">
<!-- Lange Liste von Controls -->
</Stack>
</ScrollViewer>
Focus unter Reflow
PFLICHT:
- Focus wird über Control-Identität getrackt (nicht Screen-Position)
- Nach Reflow:
- Wenn fokussiertes Control noch fokussierbar und sichtbar → behalten
- Sonst: Fallback auf nächstes fokussierbares Sibling in Focus-Order
Focus-Stabilität
procedure TLayoutEngine.ApplyReflow;
var
PreviousFocus: TWvdSControl;
begin
(* Focus merken *)
PreviousFocus := FocusManager.FocusedControl;
(* Layout neu berechnen *)
MeasureAndArrange(RootControl);
(* Focus wiederherstellen *)
if (PreviousFocus <> nil) and
PreviousFocus.CanFocus and
PreviousFocus.IsVisible then
FocusManager.SetFocus(PreviousFocus)
else
FocusManager.FocusNextAvailable;
end;
Verifikation
Resize-Tests
[ ] Narrow ↔ Wide wiederholt: kein Flicker
[ ] Cursor nicht korrupt nach Resize
[ ] Focus bleibt auf gleichem Control
[ ] Anchored Stretch Controls überlappen nicht
[ ] Popups/Overlays bleiben in Screen Bounds
Breakpoint-Tests
[ ] Layout wechselt korrekt bei Breakpoint-Überschreitung
[ ] Kein Flicker beim Breakpoint-Wechsel
[ ] Alle Breakpoints dokumentiert
Container-Tests
[ ] Stack ordnet Kinder korrekt an
[ ] Dock füllt korrekt (Left, Top, Right, Bottom, Fill)
[ ] Overlay bleibt in Bounds
[ ] Nested Containers funktionieren
PXAML Referenz
Control-Attribute
| Attribut | Werte | Bedeutung |
| Anchors | Left,Top,Right,Bottom | Anchor-Kombinationen |
| Margin | „l,t,r,b“ oder „all“ | Äußerer Abstand |
| Padding | „l,t,r,b“ oder „all“ | Innerer Abstand |
| MinWidth | Integer | Minimale Breite |
| MaxWidth | Integer | Maximale Breite |
| MinHeight | Integer | Minimale Höhe |
| MaxHeight | Integer | Maximale Höhe |
Container-Attribute
| Attribut | Werte | Bedeutung |
| ResponsiveMode | Auto, Manual | Automatisches Breakpoint-Handling |
| Breakpoints | „w1,w2,…“ | Breakpoint-Breiten |
Siehe auch