Développer pour le Librem 5 [part 6] – Modularisation du template

de | décembre 14, 2019

Pour cette partie, le moins que l’on puisse dire c’est que j’ai galéré. Le manque de documentations ou d’exemples est flagrant. J’ai quand même réussi à me dépatouiller. Voici en premier lieu les ressources documentaires que j’ai réussi à trouver.

Barre de titre

Template

Commençons par dissocier notre barre de titre du reste du template. Pour cela ouvrons le Gnome builder. Ajoutons le répertoire ui dans src et créons les deux fichiers titlebar.js et titlebar.ui.

Dans le fichier windows.ui couper tout le bloc commençant par :

 <child type="titlebar">
      <object class="HdyTitleBar" id="main-menu">
      ...
      </object>
 </child>

et coller le dans le fichier titlebar.ui. Une fois cela fait nous allons devoir y apporter quelques modifications :

  • Le fichier doit commencer par le tag <interface>
  • Le parent doit être notre composant HdyTitleBar
<?xml version="1.0" encoding="UTF-8"?>
<interface>
      <template parent="HdyTitleBar" class="Titlebar">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="selection_mode">True</property>
        <child>
          <object class="HdyLeaflet" id="header">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="visible_child">main_header</property>
            <property name="mode_transition_type">slide</property>
            <property name="child_transition_type">slide</property>
            <child>
              <object class="GtkHeaderBar" id="sidebarHeader">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <child>
                  <object class="GtkButton" id="sidebar-action">
                    <property name="visible">True</property>
                    <property name="can_focus">True</property>
                    <property name="receives_default">True</property>
                    <property name="relief">none</property>
                    <child>
                      <object class="GtkImage" id="sidebar-action-icon">
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="icon_name">view-grid-symbolic</property>
                      </object>
                    </child>
                  </object>
                  <packing>
                    <property name="position">2</property>
                  </packing>
                </child>
                <child>
                  <object class="GtkButton" id="sidebar-search">
                    <property name="visible">True</property>
                    <property name="can_focus">True</property>
                    <property name="receives_default">True</property>
                    <property name="relief">none</property>
                    <child>
                      <object class="GtkImage" id="side-search-icon">
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="icon_name">edit-find-symbolic</property>
                      </object>
                    </child>
                  </object>
                  <packing>
                    <property name="pack_type">end</property>
                    <property name="position">1</property>
                  </packing>
                </child>
                <child>
                  <object class="GtkButton" id="keyboard">
                    <property name="visible">True</property>
                    <property name="can_focus">True</property>
                    <property name="receives_default">True</property>
                    <property name="relief">none</property>
                    <child>
                      <object class="GtkImage" id="keyboard-icon">
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="icon_name">input-keyboard-symbolic</property>
                      </object>
                    </child>
                  </object>
                  <packing>
                    <property name="pack_type">end</property>
                    <property name="position">1</property>
                  </packing>
                </child>
              </object>
              <packing>
                <property name="name">sidebar</property>
              </packing>
            </child>
            <child>
              <object class="GtkSeparator" id="separator_header">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <style>
                  <class name="sidebar"/>
                </style>
              </object>
              <packing>
                <property name="name">separator</property>
              </packing>
            </child>
            <child>
              <object class="GtkHeaderBar" id="main_header">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="hexpand">True</property>
                <property name="title">Kodimote</property>
                <property name="subtitle" translatable="yes" context="Display the state of the kodi media center">kodi_state</property>
                <property name="spacing">10</property>
                <property name="show_close_button">True</property>
                <child>
                  <object class="GtkRevealer">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                    <property name="transition_type">crossfade</property>
                    <property name="transition_duration">0</property>
                    <property name="reveal_child">True</property>
                    <child>
                      <object class="GtkButton" id="menu-toogle">
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="receives_default">False</property>
                        <property name="relief">none</property>
                        <signal name="clicked" handler="on_menu_clicked" swapped="no"/>
                        <child>
                          <object class="GtkImage" id="menu-toogle-icon">
                            <property name="visible">True</property>
                            <property name="can_focus">False</property>
                            <property name="icon_name">open-menu-symbolic</property>
                            <property name="icon_size">1</property>
                          </object>
                        </child>
                        <style>
                          <class name="image-button"/>
                        </style>
                      </object>
                    </child>
                  </object>
                </child>
                <child>
                  <object class="GtkButton" id="wol-button">
                    <property name="visible">True</property>
                    <property name="can_focus">True</property>
                    <property name="receives_default">True</property>
                    <property name="relief">none</property>
                    <property name="image_position">right</property>
                    <child>
                      <object class="GtkImage">
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="icon_name">system-shutdown-symbolic</property>
                      </object>
                    </child>
                  </object>
                  <packing>
                    <property name="pack_type">end</property>
                    <property name="position">1</property>
                  </packing>
                </child>
              </object>
              <packing>
                <property name="name">content</property>
              </packing>
            </child>
          </object>
          </child>
  </template>
</interface>

Comme vous le voyez le tag <child type="titlebar"> disparaît. Il sera ajouté automatiquement lors de l’ajout de ce template à notre composant window parent.

Les lignes 1 et 2 définisse ce fichier comme un fichier template.

La ligne 3 est la plus important et permet de dire que le composant parent de ce template sera notre HdyTitleBar. Nous reviendrons là-dessus dans le fichier titlebar.js

Code

Le fonctionnement des classes JS avec GJS est un peu particulier. Oublier les classes ES6 standard. Vous obtiendriez un joli message d’erreur comme ceci :

Some code accessed the property 'Titlebar' on the module 'titlebar'. That property was defined with 'let' or 'const' inside the module. This was previously supported, but is not correct according to the ES6 standard. Any symbols to be exported from a module must be defined with 'var'. The property access will work as previously for the time being, but please fix your code anyway.

Au lieu de cela, une classe se construit de la manière suivante :

#!/usr/bin/gjs
const { GObject, Handy } = imports.gi;


var Titlebar = GObject.registerClass({
    GTypeName: 'Titlebar',
    Template: 'resource:///info/scandi/kodimote/ui/titlebar.ui',
    InternalChildren: [
        'sidebarHeader',
    ]
}, class Titlebar extends Handy.TitleBar {
  _init(params) {
    super._init(params);
  }
});
  • !/usr/bin/gjs permet de dire que nous avons à faire à un fichier GJS.
  • const { GObject, Handy } = imports.gi; On importe les bibliothèques nécessaires depuis le namespace gi.
  • var Titlebar = GObject.registerClass({}) Déclare la classe Titlebar
  • GTypeName: 'Titlebar' Donne le type de la classe. Cela est directement relié à la valeur class dans le template <template parent)"HdyTitleBar" class="Titlebar">
  • Template: 'resource:///info/scandi/kodimote/ui/titlebar.ui' définit le template de la classe.
  • InternalChildren: [ ] Définit les éléments qui devront être accessibles dans la classe sous la forme this._element ici nous définissons l’élément sidebarHeader.
  • class Titlebar extends Handy.TitleBar. Il s’agit en fait ici de notre classe au sens ES6. Voici la raison pour laquelle notre HdyTitleBar doit être déclarée en tant que template dans notre fichier ui. Attention notre classe s’appelle HdyTitleBar dans notre template mais Handy.TitleBar dans notre fichier js. Le préfixe Hdy disparait.

Définition de l’emplacement de nos ressources

Pour que Builder puisse compiler notre projet nous devons lui indiquer l’emplacement de nos deux nouveaux fichiers. Pour cela modifions le fichier src/info.scandi.kodimote.data.gresource.xml comme ceci :

<?xml version="1.0" encoding="UTF-8"?>
<gresources>
  <gresource prefix="/info/scandi/kodimote">
    <file>window.ui</file>
    <file>ui/titlebar.ui</file>
  </gresource>
</gresources>

Et le fichier src/info.scandi.kodimote.src.gresource.xml comme ceci :

<?xml version="1.0" encoding="UTF-8"?>
<gresources>
  <gresource prefix="/info/scandi/kodimote/js">
    <file>main.js</file>
    <file>window.js</file>
    <file>ui/titlebar.js</file>
  </gresource>
</gresources>

Les lignes surlignées contiennent l’emplacement de nos deux fichiers. À noter l’attribut prefix définissant l’espace de nom utilisé pour nos fichier et directement relié au chemin utilisé pour pointer vers ces mêmes fichiers : resource:///info/scandi/kodimote/ui/titlebar.ui.

À noter aussi que j’ai essayé d’utiliser un espace de nom comme celui-ci

<?xml version="1.0" encoding="UTF-8"?>
<gresources>
  <gresource prefix="/info/scandi/kodimote/ui">
    <file>titlebar.ui</file>
  </gresource>
</gresources>

Cela ne marche pas car ui est un dossier. L’espace de nom fera toujours référence au dossier src.

Import de la barre de titre dans notre fenêtre principale

Ajoutons maintenant notre titlebar dans notre fenêtre d’application. Vous allez le voir cela est très simple.

var { GObject, Gtk } = imports.gi;
var { Titlebar } = imports.ui.titlebar;

var KodimoteWindow = GObject.registerClass({
    GTypeName: 'KodimoteWindow',
    Template: 'resource:///info/scandi/kodimote/window.ui',
    InternalChildren: [],
}, class KodimoteWindow extends Gtk.ApplicationWindow {
    _init(application) {
        super._init({ application });
        const titlebar = new Titlebar();
        this.set_titlebar(titlebar);
    }
});

Ligne 2, nous importons notre classe Titlebar. L’import est toujours de la forme imports.chemin_vers_fichier.

Ligne 11 et 12, nous instancions notre classe et l’ajoutons à notre fenêtre ApplicationWindow grâce à la méthode set_titlebar(tiltlebar).

Si vous exécutez l’application nous obtenons ceci :

Plutôt pas mal. Nous voyons aussi que nous avons une erreur :

Error building template class 'KodimoteWindow' for an instance of type 'KodimoteWindow': .:50:38 Object with ID sidebarHeader not found

Il s’agit de notre élément GtkGroupSize qui ne trouve plus notre titlebar. En effet GtkGroupSize est maintenant instancié avant notre titlebar. Mettons de côté cette erreur pour le moment. Nous verrons comment la corriger dans le prochain article et nous ajouterons aussi notre contenu principal.

00

Une réflexion au sujet de « Développer pour le Librem 5 [part 6] – Modularisation du template »

  1. Ping : Développer pour le Librem 5 [part 1] – Installation – Le blog de Scandi

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.