Quand la technique ne suffit plus

De part mes expériences en tant que développeur logiciel, les divers articles et ouvrages que je lis, les conférences auxquelles j’assiste, je me forge ma propre opinion sur l’importance de la technique en tant que développeur. Au fil de ces expériences, ouvrages, conférences, j’en suis arrivé à une conclusion toute personnelle que la technique ne suffit plus. L’article de Quentin Adam, CEO de CleverCloud, “Je n’embauche jamais de poisson pané. Alors arrêtez de vous présenter comme tel.” a fait écho dans mes réflexions ; si vous ne l’avez pas encore lu, je vous le recommande.

Cette conclusion va pourtant à l’encontre de notre époque, où les candidats se présentent comme fullstack (souvent en ne sachant pas réellement le sens de ce terme, s’il en a vraiment un), les entreprises publient des offres d’emploi où elles recherchent des ninjas du développement, des awesome developers tout en insistant sur le fait qu’elles utilisent des technologies à la pointe comme J2EE et VBScript… Sans vouloir dénigrer l’une ou l’autre partie, j’estime que cela est une perte de temps pour 1°/ le développeur tenté d’y postuler pour se rendre compte que l’esprit de l’entreprise ne lui correspond pas, 2°/ pour l’entreprise elle-même qui réalise durant un entretien avec le candidat qu’il ne correspond pas humainement à ses attentes et 3°/ car les technologies sont en constantes évolutions. Il nous suffit de constater que les outils JavaScript mutent tous les six mois, que les langages de programmation ne cessent de se multiplier et que les technologies de virtualisation varient du tout au tout.

Si en tant que développeur je souhaite être à la page, je n’aurai d’autres choix que d’être attentif aux nouveautés, à tester celles pour lesquelles je développe un intérêt plus particulier que pour d’autres, et cela de manière continuelle et assidue.

Je ne suis pas en train d’affirmer que la technique a un intérêt négligeable, simplement qu’elle n’a peut-être pas le monopole, selon moi, dans notre métier (de développeur).

La maîtrise de sa langue

Peut-être avons-nous tendance à oublier qu’un développeur ne fait pas qu’écrire du code mais il écrit de la documentation (ou du moins devrait), rédige des emails, participe à des réunions, interagis avec d’autres personnes. Malgré toutes ses interactions, je ne cesse de constater à quel point il est de plus en plus difficile de maîtriser correctement sa langue maternelle: phrases écrites en langage texto, contenant sept fautes d’orthographes pour quatre mots, composées de langage parlé, composées d’anglais et de français, etc. Bien que la langue française soit l’une des plus compliquée au monde, les correcteurs orthographiques sont de plus en plus performants et présents quasiment dans tous nos outils de communication tels que notre navigateur web, notre client email, notre traitement de texte. Alors j’ai de plus en plus de mal à accepter des phrases du genre “Nous avons constaté votre problème. Pensé-vous qu’il est possible de que cela vient de votre server ?”.

A titre personnel, cela ne me donne absolument pas confiance en mon interlocuteur. Cette mauvaise maîtrise de la langue française ne devrait pas remettre en cause les compétences de ce dernier, pourtant j’estime que cela n’est pas professionnel.

Depuis quelques années j’ai repris goût à la langue française et je m’efforce de l’utiliser au mieux :

  • j’essaie, tant que faire se peut, de ne pas employer de termes anglais au beau milieu de mes discours/documents :
    • je n’écris pas server mais serveur ;
    • je n’écris pas feature mais fonctionnalité ;
    • je ne forward pas un email, je le transfère ;
    • je ne reboot pas un serveur, je le redémarre.
  • j’essaie d’éviter les expressions orales, le parlé, à l’écrit :
    • “J’ai oublié de faire la doc” devient “J’ai oublié d’écrire la documentation”
  • j’essaie de faire des phrases grammaticalement correctes :
    • “J’ai pas envoyé le mail” devient “Je n’ai pas envoyé l’email”
  • j’essaie d’avoir un langage correct, non familier

Cela peut paraître ridicule, trop strict, mais m’apprend à mieux communiquer avec mes collègues, à éviter les quiproquos, à éviter de perdre trop de temps à tenter de me faire comprendre.

La communication

On évoque régulièrement la communication comme un art. Un art qu’il est bon de savoir maîtriser pour être sûr de transmettre convenablement nos idées, nos valeurs, nos points de vue, mais aussi afin de comprendre ce que l’on attend de nous.

J’ai l’occasion de régulièrement me rendre compte qu’il m’arrive de ne pas exposer l’ensemble d’un contexte lors d’une discussion, résultant à une incompréhension de mon interlocuteur. Cela peut également se produire lors d’un choix technique où une option parfaitement claire dans mon esprit, aboutissant au fait que je n’évoque pas pourquoi elle l’est, et à une incompréhension de la part de mon interlocuteur.

Il est aussi important de parfois savoir quand interrompre une discussion afin de se rendre compte que les deux parties n’ont pas la même définition d’un terme, d’un concept, ou qu’elles ne traitent pas du même sujet et que cela mène à un quiproquos. Nombre de fois j’ai préféré interrompre temporairement une discussion afin de m’assurer que mon interlocuteur et moi-même évoquions le même sujet, pour demander une clarification sur un terme ou un concept ou simplement pour simplifier le débat. En effet, une discussion peut rapidement se diversifier et aborder des sujets ou problématiques qui n’en étaient pas le sujet.

Enfin, il est bon de savoir être attentif à la discussion à laquelle nous prenons part afin de bien cerner l’objet de cette dernière, de s’assurer que l’on comprenne bien ce que souhaite nous transmettre notre interlocuteur. Cette écoute est aussi une preuve de respect envers notre interlocuteur et un signe de professionnalisme.

Cette maîtrise de la communication, permet bien souvent d’éviter une frustration comme une incompréhension de la part des parties y prenant part, mais aussi d’éviter une perte de temps superflue alors que ce dernier est précieux.

Le discernement

Dans notre métier de développeur, les technologies évoluent à une vitesse vertigineuse et nous sommes très souvent tenté de les adopter. Mais cette passion de notre métier doit aussi être accompagnée de discernement. La nouvelle version d’un outil est-elle vraiment indispensable à mon besoin ? Qu’apporte-t-elle ? Corrige-t-elle un dysfonctionnement que j’ai dû contourner ? Quel sera son impact dans mon projet ? Voici quelques exemples de questions que j’aime à me poser. Dans certains cas, elles m’aident à décider de ne pas utiliser cette nouvelle version pour diverses raisons : ne pas introduire de fragilités dans mon projet, m’éviter de requalifier cet outil.

Afin de réaliser une nouvelle fonctionnalité, nous pouvons être amené à choisir entre plusieurs outils : l’un très à la mode, convenant à mon besoin mais amenant des fonctionnalités inutiles à mon besoin, l’autre plus adapté à mon besoin mais moins populaire. Ni l’un ni l’autre ne peut être un mauvais choix car nous pouvons nous baser sur d’autres points pour arrêter notre choix : la communauté autour de l’un ou l’autre est plus importante et pourra nous aider à le mettre en place, en tirer pleine partie, la régularité des mises à jour (trop de mises à jour rapprochées ne témoignent-t-elles pas une instabilité de l’outil ?), la réactivité du support.

Faire preuve de discernement permet d’essayer de choisir les outils répondant au mieux à mon besoin, mon cœur de métier et de parfois éviter des écueils dû à une analyse trop brève de mon besoin et des outils disponibles.

Le courage

Lorsque l’on pense au métier de développeur, le courage n’est pas la première qualité que nous attribuerions à ce dernier. Pourtant il s’agit de l’une des qualités essentielle à mes yeux en tant que développeur. Lors de réunions de préparatifs aux développement, il nous est parfois demandé si une fonctionnalité (souvent des plus importantes) est réalisable ou non. Il m’est déjà arrivé (aux balbutiements de ma carrière) de penser que non, elle ne sera pas réalisable, et de ne pas affirmer mon opinion mais plutôt de le nuancer par un peut-être. A l’arrivée de la date butoir des développements, la fonctionnalité n’a pas pu être réalisée et terminée. Cela n’a fait qu’apporter frustration aux équipes ayant porté ce développement, et s’en est suivi une phase où une solution a dû être trouvée.

La lecture de l’ouvrage Clean Code de Robert C. Martin (que je vous recommande grandement) m’a fait prendre conscience de l’importance de savoir dire non. Il ne s’agit pas de dire non sans aucune raison valable mais d’avoir le courage de le dire tout en expliquant nos raisons : les délais impartis sont trop courts, les technologies actuelles ne le permettent pas, les compétences nécessaires à la faisabilité ne sont pas maîtrisées. Cela pourra engendrer une déception de la part de votre interlocuteur, mais ne signifie pas qu’il vous en tiendra rigueur.

Le courage doit aussi nous permettre de dire oui là où peut-être d’autres diront non. Oui parce que nous entrevoyons une solution, nous avons une idée, nous avons des compétences, nous avons des connaissances, nous arrivons à convaincre.

Des situations délicates peuvent également survenir au sein d’une équipe et d’une entreprise et il peut-être nécessaire de se saisir de courage afin d’en informer, d’en discuter avec ses collègues et sa hiérarchie. Ce courage permet d’éviter des situations de malaise, de mal-être nuisant à l’intérêt de son équipe ou de son entreprise. Cependant nous pouvons avoir des craintes à les évoquer pour diverses raisons (des représailles, des remarques inappropriées) et les surmonter peut-être difficile.

Le courage est une force démontrant notre intérêt pour le produit, pour notre équipe, pour notre entreprise. Il appuie notre investissement de contribuer à développer un produit robuste, innovant et éthique.

L’attitude

Chaque être humain a ses forces et faiblesses, son histoire, ses opinions. Le travail en équipe nécessite d’adapter son attitude à l’entreprise au sein de laquelle on évolue. Au cours de notre vie nous surmontons des épreuves, nos collègues également. Nous devrions savoir prendre sur nous et ne pas en pénaliser nos collègues par des sautes d’humeur, des remarques désobligeantes. Ce genre d’attitude ne nous permettra pas de surmonter plus aisément les épreuves de notre vie et risquera d’instaurer une ambiance délicate avec ceux avec qui nous passons la majeur partie de notre journée.

Une attitude aimable, serviable et attentionnée permet de renvoyer une image positive de nous même et participe à la bonne entente générale ainsi qu’à la cohésion de notre équipe : nos collègues n’hésitent pas à nous demander de l’aide tout en n’hésitant pas à nous recommander en cas de besoin. Elle est également source d’apprentissage pour nous même en nous offrant l’opportunité de découvrir des méthodologies, des outils. Elle permet également de faire en sorte que nous soyons et nous nous sentions un membre à part entière de l’équipe et non un développeur marginal.

Avoir une attitude et une discipline respectueuse des procédures, du traitement de l’information qui nous est fournie, permet de renvoyer une image positive de nous même, démontrant notre aptitude à nous adapter, à être concentré. Une attitude positive nous aide à trouver des solutions dans des situations semblant compromises car nous ne nous contentons pas d’être focalisés sur un problème mais au contraire de le surmonter.

Le développeur moderne

Il est important à mes yeux que le métier de développeur soit un peu moins stigmatisé : un développeur n’est pas nécessairement une personne à lunette, s’habillant avec des t-shirt amples avec des inscriptions incompréhensibles, ayant un vocabulaire limité, ne parlant qu’à une poignée de connaissances et passant ses soirées à jouer à des jeux vidéos. Il n’y a pas besoin d’être développeur pour ça.

Il est également important de se rendre compte qu’actuellement beaucoup de développeurs maîtrisent tel ou tel langage, telle ou telle technologie. Si en tant que développeur vous souhaitez être compétitif, choisi lors d’un entretien d’embauche, la différence ne se fera peut-être pas sur vos compétences techniques mais sur vos compétences humaines.

C’est cela qui m’amène à penser que la technique à elle seule ne suffit plus.

Advertisements

Partial HTML generation from textile using eclipse Mylyn standalone

The title is pretty much explicit. For SlideshowFX, I needed to generate HTML content from textile, in order to define slide’s content. Looking for a lib, I found eclipse Mylyn that can be used in a standalone way. The library is pretty powerful but I have to look a lot at the sources in order to get the things done how I wanted to (#LukeAtTheSource power). Generally to convert a markup string into a HTML one, you create a MarkupLanguage as well as a MarkupParser and do the following:

final MarkupLanguage language = new TextileLanguage();
final MarkupParser parser = new MarkupParser(language);
parser.parseToHTML("h1. My little String");

The thing is that this code will create a whole HTML document (with html, head, body and so on tags). Me needs were to just get the HTML code corresponding to my string, e.g.

<h1>My little String</h1>

So the code should be changed to use a DocumentBuilder, like the following one:

final StringWriter writer = new StringWriter();
final DocumentBuilder builder = new HtmlDocumentBuilder(writer);
final MarkupLanguage language = new TextileLanguage();
final MarkupParser parser = new MarkupParser(language, builder);

// false indicates to not produce a whole HTML document
parser.parse("h1. My little String", false);

writer.flush();
writer.close();

String htmlContent = writer.toString();

This is almost done except that Mylyn generates IDs (which is kind of normal right?) by using the content of the markup. In short, I wanted to avoid IDs’ generation but I could’t find a right and efficient way to do it. So I decided to ensure uniqueness of IDs by always getting the current timestamp. Maybe it’s not a wonderful solution, but for the purpose of this little tutorial it will be perfect. The main idea is to change the ID generation and for doing this, you have to override some classes, because the default ID generation strategy is stored as a static and final variable, and no setters are available. The following example demonstrates how to do it:

final StringWriter writer = new StringWriter();

// The generation strategy generates IDs using the current timestamp
final IdGenerationStrategy idGenerationStrategy = new IdGenerationStrategy() {
  @Override
  public String generateId(String s) {
    return System.currentTimeMillis() + "";
  }
};

final IdGenerator idGenerator = new IdGenerator();
idGenerator.setGenerationStrategy(idGenerationStrategy);

final TextileContentState contentState = new TextileContentState() {
  @Override
  public IdGenerator getIdGenerator() {
    return idGenerator;
  }
};

// Override the language to return the created contentState used for the ID generation
final MarkupLanguage language = new TextileLanguage() {
  @Override
  protected ContentState createState() {
    return contentState;
  }
};

final DocumentBuilder builder = new HtmlDocumentBuilder(writer);

final MarkupParser parser = new MarkupParser(language, builder);

parser.parse(markupString, false);

writer.flush();
writer.close();

final String htmlContent = writer.toString();

This is it. Enjoy.

Update your Scene in JavaFX

Usually it is very simple to update your scene in JavaFX. For example if you want to update the X and Y position of your nodes, you can often simply do some stuff like:

node.setLayoutX(100d);
node.setLayoutY(150d);

But this isn’t a good way of doing it. In JavaFX you have to be sure you are in the JavaFX application thread. But how can you know this?

Platform.isApplicationFxThread();

But what if you’re not in that thread?

Platform.runLater(Runnable r);

So each time you would like to update your scene manually, you should check if you’re or not in the FX application thread. So let’s make a really simple helper class:

/**
 * @author Thierry Wasylczenko
 */
public class PlatformHelper {

    public static void run(Runnable treatment) {
        if(treatment == null) throw new IllegalArgumentException("The treatment to perform can not be null");

        if(Platform.isFxApplicationThread()) treatment.run();
        else Platform.runLater(treatment);
    }
}

The method run does the check for you. Finally you can use a lambda expression to execute that code:

final Node node = ... ;
PlatformHelper.run(() -> {
  node.setLayoutX(100d);
  node.setLayoutY(150d);
});

That’s it.

PathTextField: quick example of property usage in JavaFX

Context

Properties and binding in JavaFX is a really interesting feature that sometimes is hard to illustrate. Recently I just faced a problem in CompilerFX: file paths entered by the user could be problematic. Indeed on some platforms path separator is \ and on others it is /. In Java you can often use / as path separator.

Practice

So in our example, you can choose many solutions like for example:

  • When the user submit the path in a text field, replace all \ by /;
  • When the user enter a \, use a key event to replace it;
  • Choose a more JavaFX way.

Let’s deal with the third solution. Have this ones in mind:

  • In JavaFX you have the properties, allowing you to listen for a change using a ChangeListener;
  • The text of a TextField is store in the text property.

You could implement the second solution listen previously but let’s work with a custom text field that will replace replace all \ by /.

public class PathTextField extends TextField {

  {
    textProperty().addListener(new ChangeListener<String>() {
      @Override
      public void changed(ObservableValue<? extends String> observableValue, String s, String s2) {
        if(!textProperty().isBound() && s2 != null) {
          textProperty().set(s2.replaceAll("\\\\", "/"));
        }
      }
    });
  }
}

In this component, we place a ChangeListener on the text property in order to replace all \ by /. Then you can use it in your FXML files for example.

TableView: column’s width using percentage in JavaFX

In my previous post you’ve successfully learned how to simply use a TableView. It is obvious that a tables has columns and columns have a size. Sometimes you would like to specify the width of your columns using a percentage. Unfortunately it’s not a native feature of JavaFX 2. Well this ain’t a problem because, again, it is very simple to implement it yourself by creating a custom control.

PTableColumn

Creating a custom control in our case is very simple because what we need to do it basically already done. Indeed what we need is to know the size of our table and then compute our column’s width according a given percentage. All others behaviours are already managed by the TableColumn class. So we will create a class named PTableColumn (‘P’ standing obviously for percentage) and which extends the TableColumn class.

public class PTableColumn<S, T> extends javafx.scene.control.TableColumn<S, T> {

  private final DoubleProperty percentageWidth = new SimpleDoubleProperty(1);

  public PTableColumn() {
    tableViewProperty().addListener(new ChangeListener<TableView<S>>() {

      @Override
      public void changed(ObservableValue<? extends TableView<S>> ov, TableView<S> t, TableView<S> t1) {
        if(PTableColumn.this.prefWidthProperty().isBound()) {
          PTableColumn.this.prefWidthProperty().unbind();
        }

        PTableColumn.this.prefWidthProperty().bind(t1.widthProperty().multiply(percentageWidth));
      }
    });
  }
    
  public final DoubleProperty percentageWidthProperty() {
    return this.percentageWidth;
  }
    
  public final double getPercentageWidth() {
    return this.percentageWidthProperty().get();
  }
    
  public final void setPercentageWidth(double value) throws IllegalArgumentException {
    if(value >= 0 && value <= 1) {
      this.percentageWidthProperty().set(value);
    } else {
      throw new IllegalArgumentException(String.format("The provided percentage width is not between 0.0 and 1.0. Value is: %1$s", value));
    }
  }
}

As you can notice, we add a listener to the TableView property of the TableColumn. So each time the table changes, you just update the column’s width. The prefWidthProperty is bound to our percentageWidthPropery using a NumberBinding. So each time our property changes, the width is recalculated.

Use it

Now the only thing left to do is use our custom column in our FXML. Don’t forget to import it!

<!-- ... -->
<?import com.twasyl.envviewer.controls.*?>
<!-- ... -->
<TableView fx:id="propertiesTable">
  <columns>
    <PTableColumn text="Type" fx:id="typeColumn" percentageWidth="0.08">
      <!-- ... -->
    </PTableColumn>
    <!-- ... -->
  </columns>
</TableView>

Well as I said, really easy.

The source code is available here.

TableView: display tables in JavaFX

If you were used to use JTable in Swing in order to display tables in your UI, you probably know that was kind of difficult to use and customise. In JavaFX you have what is called a TableView which is complex component allowing you to do a lot of stuff. In this post we will try to cover some basics about the TableView.

Main classes

In order to display tables in JavaFX you will use a TableView. The TableView will host TableColumns. That are the two main classes you will use in order to create a table. The TableRow class also exists but you won’t use it much, except if you want to create your own rowFactory. But that is a bit out of the scope of this post.

Bring up some Generics

A table is a container that will host some kind of data: a product, an account, an item, etc. So your TableView will host some kind of data. So the following declaration makes sense:

TableView<Property> propertiesTable = new ...

Columns will host a specific value of an item in the table, like the name of the product for example. And the name is a string. So declaring columns will be like this:

TableColumn<Property, String> nameColumn;
TableColumn<Property, String> valueColumn;

The power of properties

One of the great concept of JavaFX 2 is the properties and binding (read about here). And it is very useful in our case. Indeed we know what to display in our table, we have the data but I don’t want to say each time I need a new entry in my table that the name is in the first column, the value in the second, and so on. So let’s have a look. First of all I’m going to create a JavaBean with properties:

public class Property implements Serializable {
  public enum Type {
    SYSTEM, ENVIRONMENT
  }

  private final StringProperty name = new SimpleStringProperty();
  private final StringProperty value = new SimpleStringProperty();
  private final ObjectProperty<Type> type = new SimpleObjectProperty<>();
    
  public Property() {
  }
    
  public Property(String name, String value, Type type) {
    this.name.set(name);
    this.value.set(value);
    this.type.set(type);
  }
    
  public final StringProperty nameProperty() {
    return this.name;
  }
    
  public final String getName() {
    return this.nameProperty().get();
  }
    
  public final void setName(String name) {
    this.nameProperty().set(name);
  }
    
  public final StringProperty valueProperty() {
    return this.value;
  }
    
  public final String getValue() {
    return this.valueProperty().get();
  }
    
  public final void setValue(String value) {
    this.valueProperty().set(value);
  }
    
  public final ObjectProperty<Type> typeProperty() {
    return this.type;
  }
    
  public final Type getType() {
    return this.typeProperty().get();
  }

  public final void setType(Type type) {
    this.typeProperty().set(type);
  }
}

This quite simple. But now the real power will be revealed. We will create our table completely in FXML, without anything else. But you can of course create your table completely using pure Java code (just have a look on the Oracle website).

<BorderPane prefHeight="800" prefWidth="1000" fx:id="rootPane"
    xmlns:fx="http://javafx.com/fxml"
    fx:controller="com.twasyl.envviewer.controllers.EnvViewerController">
  <center>
    <TableView fx:id="propertiesTable">
      <columns>
        <TableColumn text="Type" fx:id="typeColumn">
          <cellValueFactory>
            <PropertyValueFactory property="type" />
          </cellValueFactory>
        </TableColumn>
        <TableColumn text="Name" fx:id="nameColumn">
          <cellValueFactory>
            <PropertyValueFactory property="name" />
          </cellValueFactory>
        </TableColumn>
        <TableColumn text="Value" fx:id="valueColumn">
          <cellValueFactory>
            <PropertyValueFactory property="value" />
          </cellValueFactory>
        </TableColumn>
      </columns>
    </TableView>
  </center>
</BorderPane>

And now take a look at the controller:

public class EnvViewerController implements Initializable {
    
  @FXML
  private Pane rootPane;
  @FXML
  private TableView<Property> propertiesTable;
  @FXML
  private TableColumn<Property, String> nameColumn;
  @FXML
  private TableColumn<Property, String> valueColumn;
  @FXML
  private TableColumn<Property, Type> typeColumn;
    
  @Override
  public void initialize(URL url, ResourceBundle rb) {
  }
}

As you can read, each TableColumn tag has a cellValueFactory one, which have a PropertyValueFactory. And this last one has an attribute named property. So what about everything? That simply means the column is bound to a property of the main content of the table. In other words, our table contains some Property objects (our JavaBean class) and the first column is bound to the type property, the second to the name one and the third to the value. Easy right? Here is a little screenshot:
Empty EnvViewer
Now we will modify our controller class in order to add some items to our table:

@Override
public void initialize(URL url, ResourceBundle rb) {
        
  for(Map.Entry entry : System.getProperties().entrySet()) {
    propertiesTable.getItems().add(new Property(entry.getKey().toString(), entry.getValue().toString(), Type.SYSTEM));
  }
        
  for(Map.Entry entry : System.getenv().entrySet()) {
    propertiesTable.getItems().add(new Property(entry.getKey().toString(), entry.getValue().toString(), Type.ENVIRONMENT));
  }
}

And this is what you get:
EnvViewer filled
And the TableView comes with nice features like having sortable columns by default, you can move columns, and so on. These features were long to develop in Swing so JavaFX brings some really nice feature natively.

Of course you can do a lot more with TableViews, like editing the data, respond to user’s clicks, dynamically add data, customise the look & feel and so on. Maybe I’ll blog about some other features.

The code is from a little app I’ve done and that I’ll update to provide new features. You can find it here.

PS: this blog post took me much more time to write than coding the app, just saying!

Keyboard shortcuts and mnemonic in JavaFX

Nowadays you won’t find an application without keyboard shortcuts and mnemonic, that’s a fact. This article is about to deal with them in a menu in a JavaFX 2 application. The way to do it is very simple and powerful.

Set up a context

In order to introduce you the concepts, let’s set up the context I use in TweetWallFX.

FXML menubar

I have an FXML that contains my application menubar which is the following one:

<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<MenuBar id="menuBar" fx:id="menuBar" xmlns:fx="http://javafx.com/fxml" fx:controller="com.twasyl.tweetwallfx.controllers.MenuController">
  <menus>
    <Menu text="File" mnemonicParsing="true">
      <items>
        <MenuItem fx:id="accountMenuItem" text="Account ..." onAction="#displayAccount" />
      </items>
    </Menu>
    <Menu text="View">
      <items>
        <MenuItem fx:id="fullScreenMenuItem" text="Fullscreen" onAction="#displayFullscreen" />
      </items>
    </Menu>
    <Menu text="_Tools" mnemonicParsing="true">
      <items>
        <MenuItem fx:id="musicPlayerMenuItem" text="Music player" onAction="#playBackgroundSongAction" />
      </items>
    </Menu>
  </menus>
</MenuBar>

Controller

And the needed code for the controller:

public class MenuController implements Initializable {
  @FXML private MenuBar menuBar;
  @FXML private MenuItem fullScreenMenuItem;
  @FXML private MenuItem musicPlayerMenuItem;
  @FXML private MenuItem accountMenuItem;

  @Override
  public void initialize(URL url, ResourceBundle rb) {
    // TODO create the keyboard shortcuts here
  }
}

Mnemonics

The easy one is the mnemonic because you don’t need to write a lot of stuff … ! The mnemonic, on Windows, is when underscored letter of a menu of a menubar. When you press Alt+<the letter> the menu will be opened. It gives you a quick access to the menu. In order to define a mnemonic, in the text of your menu, place the character ‘_’ and set the mnemonicParsingProperty() to true either in your FXML or either in your backing code. And automatically, JavaFX will compute and set the mnemonic for your menu. That’s it! Easy, isn’t it?

Keyboard shortcut

Defining a keyboard shortcut for a menu entry isn’t really though when you know where to look at. In JavafX, it’s called an accelerator. An accelerator is composed of a combination of keys:

  • a main key: A, B, C, …
  • a modifier: Ctrl, Meta, Shift, Alt

. In order to define an accelerator, there are, for me, two main classes:

  • KeyCombination
  • KeyCode

Once you got them defining an accelerator is easy because it is a … KeyCombination. A keyboard shortcut is usually something like Ctrl+<a key> on Windows and Meta+<a key> on Mac, which is called the modifier. The first thing is to manage both system in order to know which modifier (Ctrl or Meta) to use … JavaFX already manages this for you by providing KeyCombination.SHORTCUT_ANY and KeyCombination.SHORTCUT_DOWN. So use them instead of CTRL or META if you want to be platform independent.
So if I want to create a combination for (Ctrl | Meta) + F as accelerator of a menu item I can use this piece of code:

this.fullScreenMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.F, KeyCombination.CONTROL_DOWN, KeyCombination.SHORTCUT_DOWN));

Nothing more, nothing less. Of course you can combine multiple modifiers like Shortcut and Shift for example to get Shortcut+Shift+F.