Créer un navigateur personnalisé - 5ème partie

Amélioration de l'interface utilisateur avec Bootstrap

31 janvier 2016

Introduction

Ce projet est open source et accessible depuis GitHub GL-Browser.

Cet article est le 5ème d'une série d'articles concernant la création d'un navigateur internet incluant par défaut des fonctionnalités que l'on retrouve habituellement sous forme d'extensions dans Chrome ou Firefox.

Exemples de fonctionnalités :

Nous souhaitons :

Nous utilisons Electron qui mixe le navigateur open source Chromium avec la richesse du framework Node.js.

Objectifs

Dans ce 5ème article, nous allons améliorer l'interface utilisateur à l'aide de Bootstrap, notamment en utilisant des onglets pour afficher plusieurs pages internet simultanément.

Onglets

Résumé des articles précédents

Article 1 : initialisation du projet avec Electron.
Article 2 : injection du CSS et du JavaScript dans la page de Google.
Article 3 : injection de scripts JavaScript et de styles CSS suivant l'URL.
Article 4 : bloquer les requêtes de suivi/tracking des utilisateurs.

Pour récupérer directement les sources du 4ème article :

git clone https://github.com/emmanuelroecker/GL-Browser
git checkout article4

jQuery et Bootstrap

jQuery est une librairie JavaScript qui facilite la manipulation d'un document HTML.

Bootstrap est une bibliothèque libre de composants visuels CSS et JavaScript qui facilite le développement de l'interface utilisateur d'une page web.
Bootstrap nécessite jQuery.

Installer

Ajouter au fichier package.json les nouvelles dépendances :

{
    "devDependencies": {
        "electron-prebuilt": "^0.36.0",
        "js-yaml": "^3.5.0",
        "match-pattern": "^0.0.1",
        "bootstrap": "^3.3.0",
        "jquery": "^1.9.1"
    }
}

Pour installer :

npm install

Intégration

Pour mieux structurer le projet :

Ajouter à index.html le style de Bootstrap et les liens vers les nouveaux fichiers :

<link rel="stylesheet" type="text/css" href="../node_modules/bootstrap/dist/css/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="css/glbrowser.css">
<script src="./js/glbrowser.js" type="text/javascript"></script>

Ajouter au début de glbrowser.js les déclarations de jQuery et Bootstrap :

window.$ = window.jQuery = require('jquery');
require('bootstrap');

Améliorer le code avec jQuery en remplaçant par exemple les appels du type document.getElementById("main"); par $('#main'), etc

Les onglets

Les onglets sont gérés à l'aide du composant tab de Bootstrap.

Le contenu d'un onglet

Placer dans le fichier page.html le code HTML du composant qui sera dupliqué dans chaque onglet, c'est à dire la zone de saisie de l'URL, les bouttons de navigation, l'élément webview, ... :

<div class="gl-header input-group">
  <div class="input-group-btn">
    <a class="gl-goback btn btn-default" role="button"><span class="glyphicon glyphicon-arrow-left"></span></a>
  </div>
  <input class="gl-urltext form-control" type="text" placeholder="URL">
  <span class="gl-indicator input-group-addon"></span>
  <div class="input-group-btn">
    <a class="gl-refresh btn btn-default" role="button"><span class="glyphicon glyphicon-repeat"></span></a>
    <a class="gl-dev btn btn-default" role="button"><span class="glyphicon glyphicon-wrench"></span></a>
  </div>
</div>
<webview class="gl-webview">
</webview>

Pour afficher une icône indiquant l'état de chargement d'une page internet, ajouter à glbrowser.js :

let webview = $('.tab-pane.active .gl-webview');
let indicator = $('.tab-pane.active .gl-indicator');
webview.on('did-start-loading', () => {
    indicator.toggleClass('glyphicon glyphicon-refresh');
});
webview.on('did-stop-loading', () => {
    indicator.toggleClass('glyphicon glyphicon-refresh');
});

La gestion des onglets

Un onglet + est dédié à l'ajout d'un nouvel onglet, compléter index.html avec :

<ul class="nav nav-tabs">
    <li><a role="button" class="add-url" data-toggle="tab">+</a></li>
</ul>
<div class="tab-content">
</div>

Le code correspondant est ajouté à glbrowser.js :

const webComponent = fs.readFileSync('page.html', encoding); //lecture du code html du composant à injecter dans chaque onglet
let elemid = 'url' + $('.nav-tabs').children().length;  //générer un identifiant unique suivant le nombre d'onglets déjà présent
$(this).closest('li').before(`<li><a href="#${elemid}" data-toggle="tab">${elemid}</a><span>x</span></li>`); //ajouter le boutton d'accès à l'onglet
$('.tab-content').append(`<div class="tab-pane fade" id="${elemid}">${webComponent}</div>`); //injecter le composant dans le nouvel onglet

Un onglet par défaut est créé au démarrage du navigateur :

window.onload = function () {
    $('.add-url').click();
};

Pour cacher un onglet inactif, Bootstrap utilise display: none mais le webview ne récupère pas les bonnes dimensions de l'onglet lors de son réaffichage.
Pour corriger cela, surcharger le CSS de Bootstrap afin de remplacer display: none par height: 0; overflow-y: hidden.

.tab-content > .tab-pane {
    display: block;
    height: 0;
    overflow-y: hidden;
}
.tab-content > .active {
    height: auto;
} 

Résultat

AvantAprès
Capture d'écran avantCapture d'écran après

Envoyer sur GitHub

git add -A
git commit -m "article 5"
git push

Prochain article

Le prochain article traitera de l'encapsulation des composants à l'aide de Riot.