Suche
NatTable Enhanced

Mit Theme-Styling zu modern aussehenden SWT-Anwendungen

Dirk Fauth

© Shutterstock.com/Nenov Brothers Images

Mit der Version 1.1.0 des beliebten Tabellen-/Grid-/Tree-Widgets wurden diverse Fehler behoben und einige neue Features hinzugefügt. Das Besondere an diesem Release ist, dass zum ersten Mal größere Contributions aus der Community dem Projekt hinzugefügt und veröffentlicht wurden. Die größten neuen Features in diesem Release sind ThemeConfigurations, über die das Styling einer NatTable-Instanz an einer Stelle definiert und zur Laufzeit ausgetauscht werden kann, sowie dynamisch berechnete Summenwerte im GroupBy-Feature, Hover-Support und Layer-Kompositionen mit mehreren Viewports. Ein Überblick über die wichtigsten Neuerungen und ein Ausblick auf die zukünftige Entwicklung im NatTable-Projekt.

Wie oben bereits erwähnt, sind einige der großen neuen Features und Anpassungen in dieser Version der NatTable Contributions aus der Community. Dazu gehören der überarbeitete Suchdialog (Abb. 1) und die Anpassung der Behandlung des aktiven Editors.

Abb. 1: Neuer NatTable-Suchdialog

Abb. 1: Neuer NatTable-Suchdialog

Der über CTRL+F zu öffnende Suchdialog ist jetzt an den Standardsuchdialog der Eclipse-IDE angelehnt und unterstützt erweiterte Suchfunktionen. Der aktive Editor in einer editierbaren NatTable wird nicht mehr in einem Singleton gehalten, sondern ist in der zugehörigen NatTable-Instanz als Eigenschaft zugreifbar. Dies ermöglicht die Verwendung der NatTable selbst als Editor-Control innerhalb einer NatTable. Auch die zur Laufzeit berechneten Summenwerte in Kombination mit der GroupBy-Funktion wurden durch die Community der NatTable beigesteuert. Während die ersten beiden Neuerungen ohne weitere Konfiguration für alle Benutzer verfügbar sind, müssen die GroupBy-Summenwerte konfiguriert werden.

Theme Styling

Das Standardstyling der NatTable ist in der heutigen Zeit nicht mehr attraktiv. Die Stylekonfigurationen sind daher die am häufigsten angepassten Einstellungen. Bisher mussten hierfür in den verschiedensten Layern entsprechende Stylekonfigurationen angepasst werden, was schnell zu sehr unübersichtlichen Codestrukturen führte. Um das Konfigurieren der Darstellung leichter zu gestalten, wurden daher ThemeConfigurations eingeführt. Über NatTable#setTheme(ThemeConfiguration) kann das Styling einer NatTable-Instanz zur Laufzeit gesetzt werden. Dabei ist zu beachten, dass die ThemeConfiguration erst nach NatTable#configure() gesetzt wird. Ansonsten kann es passieren, dass die Einstellungen durch layerspezifische Stylekonfigurationen überschrieben werden. Da eine ThemeConfiguration in der Vererbungshierarchie von IConfiguration erbt, ist es auch möglich, eine ThemeConfiguration über NatTable#addConfiguration(IConfiguration) zu setzen. Davon wird allerdings aufgrund von Nebeneffekten mit anderen Stylekonfigurationen abgeraten. In NatTable sind drei Standard-Themes enthalten:

  • DefaultNatTableThemeConfiguration (Abb. 2): das bekannte klassische Design
  • ModernNatTableThemeConfiguration (Abb. 3): ein moderneres Design
  • DarkNatTableThemeConfiguration (Abb. 4): ein auf dem modernen Design aufbauendes Dark Theme
Abb. 2: DefaultNatTableThemeConfiguration

Abb. 2: DefaultNatTableThemeConfiguration

Abb. 3: ModernNatTableThemeConfiguration

Abb. 3: ModernNatTableThemeConfiguration

Abb. 4: DarkNatTableThemeConfiguration

Abb. 4: DarkNatTableThemeConfiguration

Um eine eigene ThemeConfiguration zu erstellen, kann von der abstrakten Klasse ThemeConfiguration abgeleitet und es können die entsprechenden Methoden implementiert werden. Sinnvoller ist es allerdings, eine der bestehenden ThemeConfigurations zu erweitern und nur die dort definierten Attribute zu ändern, die angepasst werden sollen.

Um eine bestehende ThemeConfiguration zu erweitern, wurde das Konzept der IThemeExtension eingeführt. Dadurch können zusätzlich Konfigurationen, wie z. B. bedingte Formatierungen, einer bestehenden ThemeConfiguration hinzugefügt werden, ohne eine komplett neue Konfiguration erstellen zu müssen (Listing 1).

 class ConditionalStylingThemeExtension implements IThemeExtension {

  @Override
  public void registerStyles(IConfigRegistry configRegistry) {
    //add custom styling
    IStyle femaleStyle = new Style();
    femaleStyle.setAttributeValue(
      CellStyleAttributes.BACKGROUND_COLOR, 
      GUIHelper.COLOR_YELLOW);
    femaleStyle.setAttributeValue(
      CellStyleAttributes.FOREGROUND_COLOR, 
      GUIHelper.COLOR_BLACK);
    configRegistry.registerConfigAttribute(
        CellConfigAttributes.CELL_STYLE, 
        femaleStyle,
        DisplayMode.NORMAL,
        FEMALE_LABEL);
  }

  @Override
  public void unregisterStyles(IConfigRegistry configRegistry) {
    //unregister custom styling
    configRegistry.unregisterConfigAttribute(
        CellConfigAttributes.CELL_STYLE, 
        DisplayMode.NORMAL,
        FEMALE_LABEL);
  }
}
...
ThemeConfiguration conditionalDarkTheme = new DarkNatTableThemeConfiguration();
conditionalDarkTheme.addThemeExtension(new ConditionalStylingThemeExtension());
natTable.setTheme(conditionalDarkTheme);

Das GroupBy-Styling in der GlazedLists Extension ist z. B. über IThemeExtensions gelöst. Passend zu jedem Standard-Theme gibt es dort eine IThemeExtension, um keine Abhängigkeiten von NatTable Core zur GlazedLists Extension aufgrund von Styling aufzubauen.

Stylinganpassungen
Für die vollständige Umsetzung des Theme-Stylings war es notwendig, verschiedene Anpassungen und Änderungen an bestehenden Stylingklassen vorzunehmen
– Änderung von Paintern und Stylekonfigurationen, z. B. wurden im ColumnGroupHeaderTextPainter und im TreeImagePainter die Referenzen auf die Modellelemente entfernt. Stattdessen wird das Styling jetzt NatTable-typisch über Labels gesteuert. Benutzer, die an diesen Stellen eigene Labels in den Label-Stack schreiben, müssen darauf achten, dass durch diese Anpassung keine Änderung in der Darstellung auftritt.
– Einführung neuer Stylekonfigurationen, z. B. die Farbe der Grid-Linien oder ob Grid-Linien gezeichnet werden sollen.
– Anpassung der Implementierung des VerticalTextPainter. Die neue Implementierung nutzt SWT.Transform anstelle von gedrehten Images. Sollten bei der Benutzung Probleme auftauchen, ist es immer noch möglich, die alte Implementierung über den VerticalTextImagePainter zu verwenden.

GroupBy Enhancement

Wie bereits erwähnt, hat das GroupBy-Feature ein interessantes neues Feature spendiert bekommen. Die dynamische Berechnung von GroupBy-Summenwerten erlaubt es, zusätzliche Informationen zu einer Gruppierung anzuzeigen. Ähnlich der Verwendung des SummaryRowLayers muss hierfür lediglich ein IGroupBySummaryProvider (z. B. SummationGroupBySummaryProvider) für das Konfigurationsattribut GroupByConfigAttributes#GROUP_BY_SUMMARY_PROVIDER registriert werden (Listing 2). Hierfür können die neu eingeführten Labels verwendet werden:

  • GroupByDataLayer#GROUP_BY_SUMMARY: gilt für eine ganze GroupBy-Zeile
  • GroupByDataLayer#GROUP_BY_SUMMARY_COLUMN_PREFIX + <column_index>: gilt für die über <column_index> spezifizierte Spalte in einer GroupBy-Zeile
 configRegistry.registerConfigAttribute(
  GroupByConfigAttributes.GROUP_BY_SUMMARY_PROVIDER,
  new SummationGroupBySummaryProvider(columnPropertyAccessor),
  DisplayMode.NORMAL, 
  GroupByDataLayer.GROUP_BY_COLUMN_PREFIX + 3);

Zusätzlich ist es möglich, in der Gruppierungsspalte die Anzahl der Kindelemente anzeigen zu lassen. Hierfür muss ein Pattern, bestehend aus Platzhaltern, registriert werden (siehe folgende Codezeilen), das an das Ende des Gruppierungswerts angehängt wird. Der Platzhalter {0} wird dabei durch die Anzahl aller Kindelemente ersetzt (nur interessant bei mehrstufiger Gruppierung), während der Platzhalter {1} nur durch die Anzahl der direkten Kindelemente ersetzt wird.

 configRegistry.registerConfigAttribute(
  GroupByConfigAttributes.GROUP_BY_CHILD_COUNT_PATTERN,
  "[{0}] - ({1})");

Um die Summenwerte im GroupBy-Feature nutzen zu können, ist es erforderlich, den GroupByDataLayer-Konstruktor zu nutzen, der eine IConfigRegistry-Instanz als Parameter erwartet. Er wurde zusammen mit dem Feature eingeführt, um Zugriff auf die Konfigurationen zu erhalten. Abbildung 5 zeigt das GroupBy-Feature mit aktivierten Summenwerten.

Abb. 5: GroupBy-Summary-Beispiel

Abb. 5: GroupBy-Summary-Beispiel

Hover-Support

Der bislang fehlende Hover-Interaktionsmodus wurde über diverse Ergänzungen der NatTable hinzugefügt. Um spezielles Styling beim Hover über eine Zelle aktivieren zu können, wurden zwei neue DisplayModes eingeführt:

  • DisplayMode#HOVER: wird gesetzt, wenn der Mauszeiger über eine Zelle bewegt wird
  • DisplayMode#SELECT_HOVER: wird gesetzt, wenn der Mauszeiger über eine selektierte Zelle bewegt wird

Damit die neuen DisplayModes gesetzt und interpretiert werden, ist es erforderlich, den neu eingeführten HoverLayer in den Layer-Stack aufzunehmen. Neben dem Setzen des DisplayMode#HOVER übernimmt dieser auch das selektive Neuzeichnen der einzelnen Zellen, ohne dass ein kompletter Refresh notwendig ist.

Um Action Bindings zu ermöglichen, implementiert der interne IMouseModeEventHandler jetzt auch den MouseTrackListener. Dadurch können Aktionen auf mouseHover, mouseEnter und mouseExit-Events über die UiBindingRegistry registriert werden, wobei mouseEnter und mouseExit sich auf das NatTable-Control beziehen und nicht auf einzelne Zellen. Im Package org.eclipse.nebula.widgets.nattable.hover.config finden sich diverse Standardkonfigurationen, um verschiedenste Kompositionen zu unterstützen, z. B. die ColumnHeaderResizeHoverBindings, die die HoverStylingAction beim Bewegen der Maus über Zellen der ColumnHeader-Region registriert und sicherstellt, dass das Hover-Styling wieder entfernt wird, wenn sich der Mauszeiger aus der ColumnHeader-Region und der NatTable bewegt.

Multiple Viewports

Um komplexere NatTable-Kompositionen zu ermöglichen, wurde der ViewportLayer erweitert. Damit ist es möglich, mehrere Viewports in einer NatTable einzusetzen, um mehrere unabhängig voneinander scrollbare Bereiche konfigurieren zu können (Abb. 6).

Abb. 6: Split Viewport

Abb. 6: Split Viewport

Eine detaillierte Beschreibung dieses Features würde ein eigenes Tutorial füllen. Daher an dieser Stelle nur in Kürze die notwendigen Schritte, um mehrere Viewports in eine NatTable-Komposition einzubinden:

  • Erzeugen mehrerer ViewportLayer-Instanzen, die über einen CompositeLayer verbunden werden
  • Setzen der Minimum-/Maximumwerte der ViewportLayer, z. B. über ViewportLayer#setMaxColumnPosition(int) und ViewportLayer#setMinColumnPosition(int)
  • Definieren eines speziellen ClientAreaAdapters, der für die Scrollberechnungen des ersten ViewportLayers benötigt wird
  • Setzen von benutzerdefinierten Slidern, um die Scrollbalken der ViewportLayer darzustellen. Notwendig, da ein Scrollable Composite nur einen Scrollbalken unterstützt

Da es aktuell noch keine Tutorials hierzu gibt, bleibt an dieser Stelle nur der Verweis auf die NatTable-Examples-Anwendung, die von der NatTable-Webseite [2] über Webstart gestartet werden kann. Alternativ finden sich die Beispiele natürlich auch im NatTable-Sourcecode. Neben den hier aufgeführten Neuerungen gibt es noch eine Reihe weiterer nennenswerter Punkte. Dazu gehören neue Commands, um die Nodes einer Baumdarstellung auf einmal komplett zu- und aufzuklappen, Anpassungen an die Excel-Export-Funktionalität, neue Stylingkonfigurationen und Painter und die Lokalisierung diverser Konverter. Eine genauere Auflistung ist hier zu finden.

Ausblick

In den folgenden Monaten wird der größte Kritikpunkt an NatTable in Angriff genommen: die spärliche Dokumentation. Mit einer Reihe von Tutorials soll diese Lücke endlich gefüllt werden. In der überarbeiteten NatTable-Examples-Anwendung wurden hierfür bereits zahlreiche neue Beispiele unter dem Punkt Tutorial Examples hinzugefügt (Abb. 7). Diese werden den Tutorials als Codebeispiele dienen und können bereits jetzt als Referenz zu Rate gezogen werden.

Abb. 7: NatTable-Examples-Struktur

Abb. 7: NatTable-Examples-Struktur

Die Version 1.1 wurde zum Zeitpunkt ihres Erscheinens als finale Version der 1.x-Architektur bekannt gegeben. Aufgrund diverser Rückmeldungen aus der Community und des nicht unbeträchtlichen Aufwands einer Neuimplementierung wurde dies allerdings wieder verworfen. Stattdessen wird nun ein inkrementelles Update angestrebt, in dem einige Key-Features sukzessive eingebaut werden, wie z. B. die Unterstützung weiterer UI-Toolkits und die Einführung von Dependency Injection. Weitere Anpassungen, die die Architektur grundlegend beeinflussen, werden erst zu einem späteren Zeitpunkt in Angriff genommen, wie z. B. das Event Handling und die diversen internen Transformationen. Damit soll die Stabilität der NatTable gesichert werden und Benutzer, die ihre Anwendungen auf Basis von NatTable aufbauen, müssen diese nicht komplett neu schreiben. Dennoch muss mit diversen API-Änderungen gerechnet werden, z. B. im Bereich der Konfigurationen, um Farben oder Grafiken zu setzen. Außerdem wird durch dieses Vorgehen angestrebt, neue Beteiligte und Committer in das Projekt zu holen. Die Roadmap, die das Vorgehen beschreibt, ist über die Homepage von NatTable zu finden und befand sich zum Zeitpunkt, zu dem dieser Artikel entstand, noch in Arbeit.

Aufmacherbild: What´s your style? von Shutterstock / Urheberrecht: Nenov Brothers Images

Verwandte Themen:

Geschrieben von
Dirk Fauth
Dirk Fauth
Dirk Fauth ist Softwarearchitekt bei der Robert Bosch GmbH und seit mehreren Jahren im Bereich der Java-Entwicklung tätig. Er ist Committer und Project Lead im Nebula-NatTable-Projekt, Eclipse Platform Committer und Contributor in diversen weiteren Projekten im Eclipse-Universum.
Kommentare

Hinterlasse einen Kommentar

1 Kommentar auf "Mit Theme-Styling zu modern aussehenden SWT-Anwendungen"

avatar
400
  Subscribe  
Benachrichtige mich zu:
David W.
Gast

Ja, vor einigen Jahren hätte das durchaus modern ausgesehen.