JavaFX-2.0-Integration mit Eclipse 4.x

Eine JavaFX-basierende Rendering Engine

Was braucht man nun alles, um eine Rendering Engine für ein neues UI Toolkit bereitzustellen? Im Allgemeinen braucht man zwei neue Projekte: eines für eine neue Workbench, die das UI Toolkit berücksichtigt, und eines für die neuen Renderer. Bei e(fx)clipse sind das die Projekte at.bestsolution.efxclipse.runtime.workbench und at.bestsolution.efxclipse.runtime.workbench.renderers. Das Workbench-Projekt stellt das Interface IRenderFactory für den JavaFX-basierten Renderer und die abstrakte Klasse AbstractPartRenderer zur Verfügung, die alle Renderer als Superklasse haben müssen. Weiterhin wird eine neue E4Application-Klasse bereitgestellt. Sie sollte bei allen Eclipse Launchern als Applikation angegeben beziehungsweise in Produktdefinitionen/Extensions verwendet werden. Die umfangreichste Klasse ist PartRenderingEngine, die das Interface IPresentationEngine implementiert. Die PartRenderingEngine implementiert das Gerüst der Bindung des Workbench-Modells an JavaFX. Hier wir die RendererFactory für die JavaFX-basierten Renderer und der ThemeManager, der für das setzten des CSS Stylings zuständig ist, erzeugt. Weiterhin befindet sich hier sämtlicher Code, der das Modell sowohl beim Startup als auch bei dynamischen Änderungen zu den entsprechenden JavaFX Renderern bringt. Beim Vergleich der Rendering Engine für SWT (Klasse org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine) sieht man, dass ein großer Teil des Codes nur vom Workbench-Modell und den Methoden von AbstractPartRenderer abhängig ist. Für die Zukunft würde ich mir wünschen, dass es eine gemeinsame Oberklasse für alle PartRendering Engines gibt, die nur die UI-Toolkit-unabhängigen Teile enthalten. Das würde die Anpassung an verschiedene UI Toolkits deutlich vereinfachen. Die eigentlichen Renderer und die oben erwähnte RendererFactory befinden sich im Projekt at.bestsolution.efxclipse.runtime.workbench.renderers. Die Klasse WorkbenchRendererFactory implementiert das Interface IRendererFactory, das nur die Methode public AbstractPartRenderer getRenderer(MUIElement uiElement, Object parent) vorgibt. Listing 2 zeigt die Implementierung dieser Methode, allerdings nur für MWindow und MPartSashContainer. Die reale Implementierung berücksichtigt alle relevanten Workbench-Modellelemente, die mit JavaFX gerendert werden sollen.

Listing 2
public AbstractPartRenderer getRenderer(MUIElement uiElement, Object parent) {
if (uiElement instanceof MWindow) {
  if (workbenchWindowRenderer == null) {
     workbenchWindowRenderer = 
ContextInjectionFactory.make(WorkbenchWindowRenderer.class, context);
  }
  return workbenchWindowRenderer;
} else if (uiElement instanceof MPartSashContainer) {
  if (sashRenderer == null) {
          sashRenderer = ContextInjectionFactory.make(SashRenderer.class, context);
  }
  return sashRenderer;
}
// und so weiter... 
CSS Styling für JavaFX

Ein meiner Meinung nach echtes Killerfeature von JavaFX ist, dass alle Elemente von Haus aus mit CSS gestylt werden können. Das CSS ist so strukturiert, dass jeder CSS Selector einer JavaFX-Klasse entspricht. Die zu stylenden Properties der Java-Klasse bekommen im CSS den Präfix fx-. Ein paar Beispiele zeigt Tabelle 1. e(fx)clipse enthält einen sehr schönen Editor für das CSS Styling, inklusive Syntax-Highlighting und Content Assist. Abbildung 5 zeigt diesen Editor, Listing 3 zeigt das komplette Styling für die dunkle Version der JavaFX-Contacts-Demo (Abb. 4). Für die JavaFX-Contacts-Demo habe ich noch einen blauen und einen grauen Style entworfen, die Sie in den Abbildungen 6 und 7 sehen können. Natürlich lässt sich das CSS Styling auch zur Laufzeit ändern, die Demo bietet dafür ein eigenes Menü beziehungsweise auch drei Toolbar Icons. Die Möglichkeit, das Styling zur Laufzeit zu ändern, ist wichtig, wenn man einige Aspekte von Barrierefreiheit (Accessibility) implementieren möchte, zum Beispiel das Umschalten auf ein Hoch-Kontrast-Farbschema.

Tabelle 1: Beispiele für CSS Properties

JavaFX-Klasse Property CSS Property
javafx.scene.Node Cursor -fx-cursor
javafx.scene.text.Text textAlignment -fx-text-alignment
javafx.scene.text.Font Font -fx-font

Abb. 5: e(fx)clipse-JavaFX-CSS-Editor
Listing 3
.root {
    -fx-base: rgb(50, 50, 50);
    -fx-background: rgb(50, 50, 50);
    -fx-control-inner-background:  rgb(50, 50, 50);
}

.tab {
  -fx-background-color: 
        linear-gradient(to top, -fx-base, derive(-fx-base,30%));
}

.menu-bar {
    -fx-background-color: 
         linear-gradient(to bottom, -fx-base, derive(-fx-base,30%));
    
}

.tool-bar:horizontal {
    -fx-background-color:
         linear-gradient(to bottom, derive(-fx-base,+50%), 
         derive(-fx-base,-40%), derive(-fx-base,-20%));
}

.tool-bar:vertical {
    -fx-background-color:  linear-gradient(to bottom, derive(-fx-base,-50%),
                           derive(-fx-base,-40%), derive(-fx-base,-20%));
}

.tool-bar .separator:vertical .line {
    -fx-border-style: solid;
    -fx-background-color: null;
    -fx-border-color:  
         transparent transparent derive(-fx-base,-35%) derive(-fx-base,+15%);
}

.tool-bar .separator:horizontal .line {
    -fx-border-style: solid;
    -fx-background-color: null;
    -fx-border-color:  
         transparent transparent derive(-fx-base,+15%) transparent;
}

.tooltip {
     -fx-background-color:-fx-shadow-highlight-color, -fx-outer-border,
     -fx-inner-border, -fx-body-color;
}

.button {
    -fx-background-color: transparent;
    -fx-padding: 4;
}

.button:hover {
    -fx-background-color: -fx-shadow-highlight-color, -fx-outer-border, 
                          -fx-inner-border, -fx-body-color;
    -fx-color: -fx-hover-base;
}

.table-view {
    -fx-table-cell-border-color:derive(-fx-base,+10%);
    -fx-table-header-border-color:derive(-fx-base,+20%);   
}

.table-row-cell:filled:selected:focused, .table-row-cell:filled:selected {
    -fx-background-color: derive(-fx-base,+60%);
}

 .table-row-cell:filled:hover {
    -fx-background-color: derive(-fx-base,+40%);
}

.split-pane:horizontal > * > .split-pane-divider {
   -fx-border-color: transparent -fx-base transparent -fx-base;
   -fx-background-color: transparent, derive(-fx-base,20%);
   -fx-background-insets: 0, 0 1 0 1;
}

.my-gridpane {
  -fx-background-color: radial-gradient(radius 100%, derive(-fx-base,20%),       
                         derive(-fx-base,-20%));
}

.separator-label {
   -fx-text-fill: orange;
}

Abb. 6: e4-Contacts-Demo mit JavaFX Renderer und grauem CSS Styling

Abb. 7: e4-Contacts-Demo mit JavaFX Renderer mit blauem CSS Styling
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.