Cross-plattform Apps mit Electron und React Teil 2

Im letzten Teil haben wir eine Electron-Anwendung geschrieben, die auf der GTK-Demo-App basiert. Wir haben die grundsätzliche Struktur der Anwendung erstellt und die Funktionalität, die die Anwendung bereitstellt hinzugefügt.

In diesem Teil möchten wir nur nun die Optik der Anwendung entsprechend anpassen. Dazu werden wir als Erstes herausfinden, auf welchem Betriebssystem und mit welchem Fenstermanager die Anwendung läuft.

Ausgestattet mit dieser Information werden wir dann das jeweilige passende Stylesheet laden, um die Anwendung nativ aussehen zu lassen.

Das Ziel ist es nicht, dass die Anwendung 100 % so aussieht, wie eine Anwendung, die mit dem Window-Toolkit des Betriebssystems geschrieben ist. Unser Ziel ist es, dass die Anwendung mit vertretbarem Aufwand so aussieht, dass sie nicht als Fremdkörper wahrgenommen wird.

In welcher Umgebung läuft die App?

Und herauszufinden, in welcher Umgebung die App läuft, müssen wir eine kleine Javascript-Abfrage beim Start der Anwendung einbauen. Mit dieser Laden wird dann, damit wir ein spezifisches Stylesheet laden können.

Dazu öffnen wir die index.html-Datei, laden style.css für plattformunabhängige Styles und fügen folgendes Javascript ein.

<link rel="stylesheet" type="text/css" href="./styles/style.css">
  <script type="text/javascript"> 
    let cssUrl = '';
    if (process.platform === 'linux') {
      const execSync = require('child_process').execSync;
      const theme = execSync('gsettings get org.gnome.desktop.interface gtk-theme').toString().toLowerCase()
      
      if (theme.includes('adwaita')) {
        cssUrl = 'adwaita.css'
      } else if (theme.includes('arc')) {
        cssUrl = 'arc.css'
      } else if (theme.includes('OSC') || theme.includes('mac')) {
        cssUrl = 'mac.css';
      } else if (theme.includes('win')) {
        cssUrl = 'win.css';
      }
      
    } else if (process.platform === 'darwin') {
        cssUrl = 'mac.css';
    } else if (process.platform === 'win32') {
        cssUrl = 'win.css';
    }
    if (cssUrl) {
        document.write('<link rel="stylesheet" type="text/css" href="./styles/'+ cssUrl +'">');
        }
  </script>

Dabei nutzen wir die process-Bibliothek von NodeJS und schauen als Erstes auf welchem Betriebssystem wir uns befinden. Befinden wir uns auf einem Mac- oder einem Windows-Rechner, nutzen wir die CSS-Datei mac.css oder win.css.

Für den Fall, dass wir uns auf einem Linux Rechner befinden haben wir noch eine kleine Besonderheit eingebaut: Wir führen mit NodeJS eine Kommandozeilen-Abfrage durch, die uns den Namen des aktuellen Themes zurückgibt. Dementsprechend verwenden wir nun die passende CSS-Datei.

Die Auswahl des Themes lässt sich beliebig erweitern, da ich persönlich das Arc-Theme verwende, werden wir uns auf dieses konzentrieren. An dieser Stelle muss jeder selbst entscheiden, mit wie viel Aufwand er Linux-User zu unterstützen möchte. Ich könnte mir vorstellen, dass ein Theme für Ubuntu-Nutzer auch noch sinnvoll wäre. Dies könnte eine gute Übung sein.

Cross-platfform Styling

Um nun ein plattform-gerechtes Styling der Einzelkomponenten zu kreieren, müssen wir diverse CSS-Dateien anlegen. Dazu erstellen wir den Ordner styles im src-Verzeichnis. Für jede angegebene CSS-Dateien müssen wir nun eine anlegen.

Als Erstes entfernen wir jegliche Inline-Styles aus unseren Komponenten. Sonst können wir diese nur mühsam überschreiben.

Im neu geschaffenen styles-Verzeichnis legen wir nun die Datei style.css an. In diese Datei kommen alle Style-Informationen, die wir vorher inline eingefügt hatten. Für die Farben legen wir CSS-Variablen fest. 

:root{
    --fg_color: #5c7080;
    --text_color: #182026;
    --bg_color: rgb(245, 248, 250);
    --borders: rgb(206, 217, 224)
}

#Layout {
    display: flex;
    flex-direction: column;
    height: 100vh;
}

.bp3-button { cursor: default; }

#TextField {
    height: 100%;
    flex-grow: 1;
    overflow: auto;
    border: none;
    resize: none;
    outline: none;
}

#StatusBar {
    background-color: var(--bg_color); 
    border-top: 1px solid var(--borders);
    color: var(--text_color);
    height: 50px;
    padding: 12px;
}

Das hat den Vorteil, dass man nur durch den Austausch der Farben schon unterschiedliche Themes erstellen kann. Soweit so gut. 

Um nun das Arc-Theme nachzubauen, benötigt man nur wenige Zeilen CSS. Zumindest für unsere Beispielanwendung.

Als Erstes überschreiben wir die CSS-Variablen für die verwendeten Farben. Dann passen wir noch Details der einzelnen Komponenten an.

:root {
    --window_bg: #e7e8eb;
    --window_fg: hsla(222, 18%, 39%, 0.8);
    --button_hover_bg: #fdfdfd;
    --button_hover_border: #D1D3DA;
    --selected_bg_color: #5294e2;
    --selected_fg_color: #ffffff;
}

.bp3-navbar {
    background-color: var(--window_bg);
    padding: 0 5px;
    height: 40px;
}
.bp3-navbar-group {
    height: 40px;
}
.bp3-button .bp3-icon {
    color: var(--window_fg)
}
.bp3-button {
    min-width: 35px;
    min-height: 30px;
}
.bp3-button.bp3-minimal:hover {
    background-color: var(--button_hover_bg);
    border: 1px solid var(--button_hover_border);
}
.bp3-menu-item { padding: 1px 7px; }
.bp3-menu-item:hover, .bp3-button.bp3-minimal:active, .bp3-button.bp3-minimal.bp3-active  { 
    background-color: var(--selected_bg_color); 
    color: var(--selected_fg_color);
}
:focus {
    outline: none;
}

#StatusBar {
    max-height: 38px;
    padding: 10px;
}

Das Ergebnis sieht wie folgt aus:

Das Gleiche machen wir noch für das MacOS- und für das Windows-Theme. Wir brauchen dafür jeweils nur wenige Zeilen CSS.

:root {
    --window_bg: #e7e8eb;
    --window_fg: hsla(222, 18%, 39%, 0.8);
    --button_hover_bg: #fdfdfd;
    --button_hover_border: #D1D3DA;
    --selected_bg_color: #5294e2;
    --selected_fg_color: #ffffff;
}

.bp3-navbar {
    background-color: var(--window_bg);
    padding: 0 5px;
    height: 40px;
}
.bp3-navbar-group {
    height: 40px;
}
.bp3-button .bp3-icon {
    color: var(--window_fg)
}
.bp3-button {
    min-width: 35px;
    min-height: 30px;
}
.bp3-button.bp3-minimal:hover {
    background-color: var(--button_hover_bg);
    border: 1px solid var(--button_hover_border);
}
.bp3-menu-item { padding: 1px 7px; }
.bp3-menu-item:hover, .bp3-button.bp3-minimal:active, .bp3-button.bp3-minimal.bp3-active  { 
    background-color: var(--selected_bg_color); 
    color: var(--selected_fg_color);
}
:focus {
    outline: none;
}

#StatusBar {
    max-height: 38px;
    padding: 10px;
}
:root {
    --window_bg: #fffff;
    --window_fg: #000000;
    --button_hover_bg: #cce8ff;
    --button_hover_border: #0078d7;
    --selected_bg_color: #cce8ff;
    --selected_fg_color: #000000;
    --bg_color: #ffffff;
    --borders: rgba(16, 22, 26, 0.1);
}

.bp3-navbar {
    background-color: var(--window_bg);
    padding: 0 10px;
    height: 40px;
    box-shadow: 0 0 0 1px var(--borders);
}
.bp3-navbar-group {
    height: 40px;
}
.bp3-button .bp3-icon {
    color: var(--window_fg)
}
.bp3-button {
    min-width: 35px;
    min-height: 30px;
    border-radius: 0;
}
.bp3-button.bp3-minimal:hover, .bp3-button.bp3-minimal:active, .bp3-button.bp3-minimal.bp3-active  {
    background-color: var(--button_hover_bg);
    border: 1px solid var(--button_hover_border);
}
.bp3-menu-item { padding: 1px 7px; }
.bp3-menu-item:hover, .bp3-button.bp3-minimal:active, .bp3-button.bp3-minimal.bp3-active  { 
    background-color: var(--selected_bg_color); 
    color: var(--selected_fg_color);
}
:focus {
    outline: none;
}

#StatusBar {
    max-height: 45px;
    padding: 15px;
}

Fairerweise muss man dazu sagen, dass alle drei Theme ohne Gradienten auskommen und somit nur wenig Änderungen brauchen. Das Gnome Standard-Theme Adwaita nachzubauen, wäre deutlich aufwendiger.

Du findest den Source-Code für dieses Tutorial auf Github: https://github.com/rockiger/electron-react-example

Im nächsten Teil werden wir das Menü der gtk3-demo-application nachbauen und noch ein wenig Feinschliff betreiben.

Created with Dictandu