Ein Container ist eine Komponente, welche eine Liste von
                    Controls enthält. Die
                    Schnittstelle IContainer erweitert
                    IComponent
                    und somit auch
                    IWidget.
                    Die Controls eines Containers werden mit Hilfe eines
                    Layouters angeordnet. Es folgt
                    eine Beschreibung der wichtigsten Methoden.
                  
         
Folgende Methoden können verwendet werden, um Controls mit Hilfe eines BluePrints zu einem Container hinzuzufügen:
    <WIDGET_TYPE extends IControl> WIDGET_TYPE add(
        int index,
        IWidgetDescriptor<? extends WIDGET_TYPE> descriptor,
        Object layoutConstraints);
        
    <WIDGET_TYPE extends IControl> WIDGET_TYPE add(
        int index,
        IWidgetDescriptor<? extends WIDGET_TYPE> descriptor);
    <WIDGET_TYPE extends IControl> WIDGET_TYPE add(
        IWidgetDescriptor<? extends WIDGET_TYPE> descriptor, 
        Object layoutConstraints);
    <WIDGET_TYPE extends IControl> WIDGET_TYPE add(
        IWidgetDescriptor<? extends WIDGET_TYPE> descriptor);
                         Ein Control wird hinzugefügt, indem man ein BluePrint
                         hinzufügt. Jedes BluePrint implementiert die Schnittstelle
                         IWidgetDescriptor für einen konkreten
                         WIDGET_TYPE. Die add()
                         Methode liefert das erzeugte Control als Rückgabewert.
                       
            
Der Index bestimmt, an welcher Stelle das Control in die interne Liste eingefügt werden soll. Wird kein Index angegeben, wird das Control am Ende hinzugefügt.
                         Die layoutConstraints werden vom
                         verwendeten Layouter ausgewertet und sind somit
                         layout spezifisch. Da ein Layouter nicht
                         immer Constraints benötigt, können diese auch weggelassen
                         werden. Zudem ist es möglich, die Constraints nachträglich auf
                         dem Control zu setzen.
                       
            
                         Es folgt ein Beispiel für die Verwendung der
                         add() Methode:
                       
            
  1      final String growCC = "growx, w 0::";
  2          
  3      final IInputField<Date> dateField = container.add(BPF.inputFieldDate(), growCC);
  4  
  5      final IComboBoxSelectionBluePrint<Gender> genderCmbBp = BPF.comboBoxSelection(Gender.values());
  6      final IComboBox<Gender> genderCmb = container.add(genderCmbBp, growCC);
  7  
  8      final IButton okButton = container.add(BPF.buttonOk());
  9  
 10      final ICheckBoxBluePrint licenceCbBp = BPF.checkBox().setText("Accept");
 11      final ICheckBox licenceCb = container.add(2, licenceCbBp);
                         In Zeile 3-8 wird ein Datumsfeld, eine
                         ComboBox für die Enum
                         Gender sowie ein
                         OkButton hinzugefügt. Das Datumsfeld sowie
                         die Combobox haben ein
                         MigLayout Constraint, dass
                         sie den ganzen horizontalen Platz ausfüllen sollen
                         (growx).
                       
            
In Zeile 10-11 wird dann eine Checkbox an Position zwei (zwischen Combobox und Botton) eingefügt.
Will man selbst Controls erstellen, hat man folgende Möglichkeiten:
Man erstellt eine eigene Widget Bibliothek, siehe Erstellung eigener Widget Bibliotheken. Dabei wird auch ein BluePrint für das Control definiert welches man wie weiter oben beschrieben verwenden kann. Dies ist das empfohlene Vorgehen, wenn das Control wiederverwendet werden soll, da ein solch erstelltes Widget alle Vorzüge von jowidgets bietet.
Man leitet von einem Basis Widget ab, um ein Widget zu kapseln.
                                      Man kapselt ein Widgets mit Hilfe eines
                                      ICustomWidgetCreator.
                                    
                     
                         Folgende Methoden können verwendet werden, um Controls mit
                         Hilfe eines ICustomWidgetCreator zu einem
                         Container hinzuzufügen:
                       
            
    <WIDGET_TYPE extends IControl> WIDGET_TYPE add(
        int index, 
        ICustomWidgetCreator<WIDGET_TYPE> creator, 
        Object layoutConstraints);
        
    <WIDGET_TYPE extends IControl> WIDGET_TYPE add(
        int index, 
        ICustomWidgetCreator<WIDGET_TYPE> creator);
    <WIDGET_TYPE extends IControl> WIDGET_TYPE add(
        ICustomWidgetCreator<WIDGET_TYPE> creator, 
        Object layoutConstraints);
    <WIDGET_TYPE extends IControl> WIDGET_TYPE add(
        ICustomWidgetCreator<WIDGET_TYPE> creator);
                         Die Verwendung des index und der
                         layoutConstraints ist analog wie beider
                         Verwendung von BluePrints.
                       
            
                         Es folgt ein Beispiel für eine Implementierung der
                         ICustomWidgetCreator Schnittstelle:
                       
            
  1  public final class ErrorLabel implements ICustomWidgetCreator<ILabel> {
  2  
  3      private final String errorText;
  4  
  5      public ErrorLabel(final String errorText) {
  6          this.errorText = errorText;
  7      }
  8  
  9      @Override
 10      public ILabel create(final ICustomWidgetFactory widgetFactory) {
 11          final ILabelBluePrint labelBp = BPF.label();
 12          labelBp.setText(errorText).setIcon(IconsSmall.ERROR);
 13          final ILabel label = widgetFactory.create(labelBp);
 14          return label;
 15      }
 16  }
                         In Zeile 13 wird die ICustomWidgetFactory
                         verwendet, um ein ILabel Widget zu
                         erzeugen.
                       
            
                         Das ErrorLabel Control kann nun wie folgt
                         einem Container hinzugefügt werden:
                       
            
  1      final ILabel label = container.add(new ErrorLabel("Ups!!!"));Das folgende Beispiel Implementiert ein Control, welches den gesamten Platz mit schwarzer Farbe ausfüllt:
  1  public final class BlackBox implements ICustomWidgetCreator<IControl> {
  2  
  3      @Override
  4      public IControl create(final ICustomWidgetFactory widgetFactory) {
  5          final ICanvas canvas = widgetFactory.create(BPF.canvas());
  6  
  7          canvas.addPaintListener(new IPaintListener() {
  8              @Override
  9              public void paint(final IPaintEvent paintEvent) {
 10                  final IGraphicContext gc = paintEvent.getGraphicContext();
 11                  final Rectangle bounds = gc.getBounds();
 12                  gc.setBackgroundColor(Colors.BLACK);
 13                  gc.clearRectangle(
 14                      bounds.getX(), 
 15                      bounds.getY(), 
 16                      bounds.getWidth(), 
 17                      bounds.getHeight());
 18              }
 19          });
 20  
 21          return canvas;
 22      }
 23  }
                         Hier wird in Zeile 5 die
                         ICustomWidgetFactory verwendet, um ein
                         Canvas zu erzeugen welches im
                         IPaintListener ab Zeile 7 den gesammten
                         Bereich mit schwarzer Farbe ausfüllt.
                       
            
                         Das BlackBox Control kann dann die folgt
                         einem Container hinzugefügt werden:
                       
            
1 final IControl = frame.add(new BlackBox(), "growx, w 0::");
Controls können entweder auf dem Container mittels der folgenden Methoden entfernt werden:
    boolean remove(IControl control);
    
    void removeAll();
                         Oder man ruft auf dem Control selbst die
                         dispose() Methode auf:
                       
            
1 final IInputField<Date> dateField = container.add(BPF.inputFieldDate(), growCC); 2 dateField.dispose();
                         In Zeile 1 wird ein DateField zum Container
                         hinzugefügt, in Zeile 2 wird es wieder entfernt.
                       
            
Im folgenden Beispiel wird der gleiche Effekt erzielt, nur dass das Entfernen über den Container stattfindet.
1 final IInputField<Date> dateField = container.add(BPF.inputFieldDate(), growCC); 2 container.remove(dateField);
                         In beiden Fällen wird das DateField
                         disposed und kann anschließend nicht mehr verwendet werden.
                       
            
Folgende Methoden können verwendet werden, um das Layout für den Container festzulegen.
    void setLayout(ILayoutDescriptor layoutDescriptor);
    <LAYOUT_TYPE extends ILayouter> LAYOUT_TYPE setLayout(ILayoutFactory<LAYOUT_TYPE> layoutFactory);
                         Das Layout kann entweder über ein
                         ILayoutDescriptor oder über eine
                         ILayoutFactory festgelegt werden. Der
                         resultierende ILayouter ist dafür
                         verantwortlich, die Controls eines Containers in der
                         richtigen Größe an die
                         richtige Position zu Zeichnen.
                       
            
Mittels der folgenden Methoden kann der Layoutprozess manuell angestoßen werden:
    void layoutBegin();
    void layoutEnd();
    
    void layout();
    void layoutLater();
                         Die Methode layout() berechnet das Layout
                         für den Container neu.
                       
            
                         Die Methode layoutBegin() kann verwendet
                         werden, um anzuzeigen, dass mehrere Änderungen des Containers
                         folgen, welche das Layout beeinflussen. Dadurch wird
                         verhindert, dass der Container neu gezeichnet wird, bis die
                         Methode layoutEnd() aufgerufen wird. Diese
                         kann verwendet werden, um Flackereffekte
                         zu vermeiden.
                       
            
                         Die Methode layoutLater() führt ein Layout
                         in einem späteren UI Event durch.
                         Mehrere Aufrufe der Methode
                         layoutLater() im aktuellen Event bewirken
                         dabei nur einen späteren
                         Aufruf der Methode layout().
                       
            
                         Damit lässt sich ein in der Praxis gelegentlich auftretendes
                         Problem lösen, welches bewirken kann, dass viele
                         unabhängige Änderungen eines Containers
                         in einem Ui Event ein sofortiges layout()
                         nach sich ziehen. Dann wird in einem Event mehrfach ein
                         layout() durchgeführt, obwohl das Ergebnis nicht mehr als ein
                         Mal pro UI Event sichtbar werden kann. De facto wird also nur
                         der letzte layout() Aufruf für den Nutzer
                         sichtbar, die vielen anderen layout()
                         Aufrufe haben dabei unnötiger Weise den UI Thread blockiert.
                         Durch den mehrfachen Aufruf der Methode
                         layoutLater() anstatt
                         layout() tritt das Problem dann nicht mehr
                         auf, weil nur ein mal zu einem späteren Zeitpunkt ein
                         layout() durchgeführt wird.
                       
            
Um einen eigenen Layouter zu implementieren, sind die folgenden Methoden relevant:
    Rectangle getClientArea();
    Dimension computeDecoratedSize(Dimension clientAreaSize);
                         Die Methode getClientArea() gibt genau den
                         Bereich zurück, welcher für das Zeichnen der Controls zur
                         Verfügung steht.
                       
            
                         Die Methode computeDecoratedSize()
                         berechnet für eine gewünschte
                         clientAreaSize die notwendige Größe des
                         gesamten Containers.
                       
            
Für weitere Details zu diesem Thema sei auf den Abschnitt Layouting verwiesen.
Um die Reihenfolge festzulegen, in welcher durch die Controls durch Verwendung der Tabulator Taste navigiert wird, können die folgenden Methoden verwendet werden:
    void setTabOrder(Collection<? extends IControl> tabOrder);
    void setTabOrder(IControl... controls);Wird null übergeben, wird die default Reihenfolge verwendet
Mit Hilfe der folgenden Methode erhält man alle derzeit vorhanden Kind Controls eines Containers:
    List<IControl> getChildren();
                         Dabei handelt es sich um eine nicht modifizierbare
                         (unmodifieable) Kopie der aktuellen Liste der Kind Controls.
                         Dadurch ist zum Beispiel das folgende möglich, ohne eine
                         ConcurrentModificationException zu
                         bekommen:
                       
            
  1      container.layoutBegin();
  2      for (final IControl childControl : container.getChildren()) {
  3          if (!filter.accept(childControl)) {
  4              container.remove(childControl);
  5          }
  6      }
  7      container.layoutEnd();
                         Um sich darüber informieren zu lassen, wenn Kinder hinzugefügt
                         oder entfernt werden, kann ein
                         IContainerListener verwendet werden:
                       
            
    void addContainerListener(IContainerListener listener);
    void removeContainerListener(IContainerListener listener);Dieser hat die folgenden Methoden:
    void afterAdded(IControl control);
    void beforeRemove(IControl control);
                         Die Methode afterAdded() wird aufgerufen,
                         nachdem ein Control zum Container hinzugefügt wurde. Die
                         Methode beforeRemove() wird aufgerufen,
                         bevor ein Control vom Container entfernt wird. Dabei spielt es
                         keine Rolle, ob das Control mittels
                         dispose() oder mittels
                         remove() entfernt wird. Das Control ist zum
                         Zeitpunkt des Aufrufs der Methode
                         beforeRemove() noch
                               nicht disposed.
                       
            
                         Ein Container kann Controls enthalten, welche selbst wiederum
                         Container sein können. Dies ist zum Beispiel mit Hilfe eines
                         Composite möglich, welches
                         ein IContainer und ein
                         IControl zugleich ist.
                       
            
In der Praxis kann es vorkommen, dass zu einem eigenen Control Controls hinzugefügt werden, deren interner Aufbau einem über die Zeit (Controls können kommen und gehen) verborgen ist. Dennoch hätte man eventuell gerne Kenntnis über alle Kinder eines Containers (auch rekursiv), zum Beispiel um einen Listener hinzuzufügen, welcher ein PopupMenu öffnet.
                         Zu diesem Zweck kann eine
                         IContainerRegistry verwendet werden, welche
                         dem Container hinzugefügt wird:
                       
            
    void addContainerRegistry(IContainerRegistry registry);
    void removeContainerRegistry(IContainerRegistry registry);
                         Die Schnittstelle IContainerRegistry hat
                         die folgenden Methoden:
                       
            
    void register(IControl control);
    void unregister(IControl control);
                         Die Methode register() wird für alle
                         bereits vorhandenen (zum Zeitpunkt des Aufrufes von
                         addContainerRegistry()) Controls sowie für
                         alle in der Zukunft hinzugefügten Controls aufgerufen. Das
                         ganze wird rekursiv durchgeführt, wodurch
                         auch alle Kind Controls sowie deren Kinder usw. erfasst
                         werden.
                       
            
                         Werden Controls (oder Kind Controls, usw.) entfernt, wird die
                         Methode unregister() aufgerufen. Kind
                         Controls welche bereits vor dem Aufruf der Methode
                         addContainerRegistry() entfernt wurden,
                         werden dabei nicht berücksichtigt.
                       
            
Zum rekursiven hinzufügen von Listenern zu einem Container und dessen Kindern stehen folgende Methoden zur Verfügung:
    void addComponentListenerRecursive(IListenerFactory<IComponentListener> listenerFactory);
    void removeComponentListenerRecursive(IListenerFactory<IComponentListener> listenerFactory);
    void addFocusListenerRecursive(IListenerFactory<IFocusListener> listenerFactory);
    void removeFocusListenerRecursive(IListenerFactory<IFocusListener> listenerFactory);
    void addKeyListenerRecursive(IListenerFactory<IKeyListener> listenerFactory);
    void removeKeyListenerRecursive(IListenerFactory<IKeyListener> listenerFactory);
    void addMouseListenerRecursive(IListenerFactory<IMouseListener> listenerFactory);
    void removeMouseListenerRecursive(IListenerFactory<IMouseListener> listenerFactory);
    void addPopupDetectionListenerRecursive(IListenerFactory<IPopupDetectionListener> listenerFactory);
    void removePopupDetectionListenerRecursive(IListenerFactory<IPopupDetectionListener> listenerFactory);
                         Die Schnittstelle IListenerFactory sieht
                         wie folgt aus:
                       
            
public interface IListenerFactory<LISTENER_TYPE> {
    LISTENER_TYPE create(IComponent component);
}
                         Durch das Hinzufügen eines rekursiven Listeners wird für den
                         Container selbst und für jedes aktuelle und in der Zukunft
                         hinzugefügte Control die create() Methode
                         aufgerufen. Wird durch den Implementierer der
                         IListenerFactory Schnittstelle ein Listener
                         erzeugt, wird dieser der Component hinzugefügt, und vor dem
                         dispose der Component wieder entfernt. Die
                         create() Methode kann auch
                         null zurückgeben, wodurch für diese
                         Component kein Listener hinzugefügt wird.
                       
            
Das folgende Beispiel soll dies verdeutlichen:
  1      final IPopupMenu labelMenu = container.createPopupMenu();
  2      labelMenu.setModel(labelMenuModel);
  3  
  4      IListenerFactory<IPopupDetectionListener> listenerFactory;
  5      listenerFactory = new IListenerFactory<IPopupDetectionListener>() {
  6          @Override
  7          public IPopupDetectionListener create(final IComponent component) {
  8              if (component instanceof ILabel) {
  9                  final ILabel label = (ILabel) component;
 10                  return new IPopupDetectionListener() {
 11                      @Override
 12                      public void popupDetected(final Position position) {
 13                          labelMenuModel.setLabel(label);
 14                          labelMenu.show(label.toComponent(position, container));
 15                      }
 16                  };
 17              }
 18              return null;
 19          }
 20      };
 21          
 22      container.addPopupDetectionListenerRecursive(listenerFactory);
                         Für alle Labels eines Containers wird ein Kontextmenü
                         (labelMenu) hinzugefügt. Bevor das Menü
                         angezeigt wird, wird das Label auf dem zugehörigen Model
                         (Zeile 13) gesetzt, um etwaige Aktionen bezüglich des
                         ausgewählten Labels durchführen zu können.
                       
            
Das Kontextmenü könnte zum Beispiel Aktionen enthalten, welche den Inhalt des Labels in die Zwischenablage kopieren oder dessen Farbe ändern, etc.