Outils pour utilisateurs

Outils du site


issue175:inkscape

This month is the last part of our exploration of the new Selectors and CSS dialog (“Selectors dialog” for short). As with the previous parts, we’ll be working with this collection of shapes arranged as four groups of objects, one for each row. We’ve previously looked at using class and element selectors in the dialog, but, as you may recall from part 112, CSS allows for a variety of other selectors, and different ways to combine them. Inkscape’s CSS parser doesn’t support all the various possibilities, but it does manage some of them. Whether or not they’re actually useful to you is another question entirely, but, in this instalment, I’m going to take a look at some advanced selectors that do work, as well as some that unfortunately don’t. Starting with a blank Selectors dialog, I’ve created a class called “squares” that includes all the squares in our sample image, and a second class called “col-1” which contains all the elements in the first column of objects. As you can see, the “#rect31” element appears in both, as you would expect.

Ce mois-ci, c'est la dernière partie de notre exploration de la nouvelle boîte de dialogue Sélecteurs et CSS (« boîte de dialogue Sélecteurs » en abrégé). Comme dans les parties précédentes, nous allons travailler avec cette collection de formes disposées en quatre groupes d'objets, un pour chaque ligne.

Nous avons précédemment examiné l'utilisation des sélecteurs de classe et d'élément dans la boîte de dialogue, mais, comme vous vous en souvenez peut-être (CF la partie 112), CSS permet une variété d'autres sélecteurs et différentes façons de les combiner. L'analyseur CSS d'Inkscape ne prend pas en charge toutes ces possibilités, mais il en gère certaines. Qu'ils vous soient utiles ou non est une toute autre question, mais, dans cet épisode, je vais examiner certains sélecteurs avancés qui fonctionnent, ainsi que d'autres qui, malheureusement, ne fonctionnent pas.

En partant d'une boîte de dialogue de sélecteurs vierge, j'ai créé une classe appelée « squares » qui inclut tous les carrés de notre image échantillon, et une deuxième classe appelée « col-1 » qui contient tous les éléments de la première colonne d'objets. Comme vous pouvez le voir, l'élément « #rect31 » apparaît dans les deux, comme vous pouvez vous y attendre.

Deselecting everything in the drawing, and clicking the plus button at the bottom of the dialog, triggers the dialog for adding a new selector – pre-filled with “.Class1” as usual. As we saw last time, it’s also possible to enter an element name here, but, this time, we’ll create something even more complex: a selector that targets multiple classes. If we enter a value of “.squares.col-1” as a single string, with no spaces, the selector will target only those elements that have both the “squares” and “col-1” classes applied. In our case the only thing that matches that combination is the square at the top left. Alternatively, we can combine an element selector with a class selector. Entering “rect.col-1” for example, will only match those <rect> elements that also have a class of “col-1” applied. Again, the only thing that matches in this case is the square at the top left. Using these selectors, I’ve added rules to change the color of any object where both classes match, and the stroke of any <rect> with the “col-1” class. The effect is that the square at the top left has both the new rules applied, but no other elements are affected.

En désélectionnant tout ce qui se trouve dans le dessin et en cliquant sur le bouton plus en bas de la boîte de dialogue, on déclenche la boîte de dialogue permettant d'ajouter un nouveau sélecteur, pré-rempli avec « .Class1 » comme d'habitude. Comme nous l'avons vu la dernière fois, il est également possible d'entrer un nom d'élément ici, mais, cette fois, nous allons créer quelque chose d'encore plus complexe : un sélecteur qui cible plusieurs classes. Si nous saisissons la valeur « .squares.col-1 » comme chaîne unique, sans espace, le sélecteur ne ciblera que les éléments auxquels sont appliquées à la fois les classes « squares » et « col-1 ». Dans notre cas, le seul élément qui correspond à cette combinaison est le carré en haut à gauche.

Nous pouvons également combiner un sélecteur d'élément avec un sélecteur de classe. En saisissant « rect.col-1 », par exemple, seuls les éléments <rect> auxquels est appliquée la classe « col-1 » seront pris en compte. Encore une fois, la seule chose qui correspond dans ce cas est le carré en haut à gauche.

En utilisant ces sélecteurs, j'ai ajouté des règles pour changer la couleur de tout objet correspondant aux deux classes, et le trait de tout <rect> ayant la classe « col-1 ». L'effet est que le carré en haut à gauche se voit appliquer les deux nouvelles règles, mais aucun autre élément n'est affecté.

The ability to combine classes in this way could potentially be useful, especially if you want to use class names to categorize your elements. When producing game assets, for example, you might have multiple images in a single document, covering different types of landscape in different seasons, and with different assets. Need to quickly find the image for a wintery forest tile with a mine? Add a new selector for “.winter.forest.mine” – assuming you’ve already set the right classes on your images. Combining elements and classes is probably less useful, particularly given how many of Inkscape’s primitives are actually just <path> elements in the underlying SVG, and are therefore indistinguishable from each other via a simple element selector. If you can think of a good use case for this, however, it’s nice to know that Inkscape already supports the format. The fact that Inkscape piles its own internal attributes onto <path> elements in order to support some of its basic shapes does lead onto another type of CSS selector that’s worth further examination: the attribute selector. In the world of CSS it’s possible to select elements that have a particular attribute, or which have an attribute set to a particular value. The table below covers the main selectors that will work in a web browser.

La possibilité de combiner des classes de cette manière peut s'avérer utile, notamment si vous souhaitez utiliser des noms de classe pour catégoriser vos éléments. Lors de la production d'éléments de jeu, par exemple, vous pouvez avoir plusieurs images dans un seul document, couvrant différents types de paysages à différentes saisons, et avec différents éléments. Vous avez besoin de trouver rapidement l'image d'une tuile de forêt hivernale avec une mine ? Ajoutez un nouveau sélecteur pour « .winter.forest.mine » - en supposant que vous avez déjà défini les bonnes classes sur vos images.

La combinaison d'éléments et de classes est probablement moins utile, en particulier compte tenu du fait que de nombreuses primitives d'Inkscape ne sont en fait que des éléments <path> dans le SVG sous-jacent, et sont donc indiscernables les unes des autres via un simple sélecteur d'élément. Si vous pouvez penser à un bon cas d'utilisation pour cela, cependant, il est agréable de savoir qu'Inkscape supporte déjà le format.

Le fait qu'Inkscape empile ses propres attributs internes sur les éléments <path> afin de prendre en charge certaines de ses formes de base conduit à un autre type de sélecteur CSS qui mérite un examen plus approfondi : le sélecteur d'attributs. Dans le monde du CSS, il est possible de sélectionner des éléments qui possèdent un attribut particulier, ou dont l'attribut a une valeur particulière. Le tableau ci-dessous présente les principaux sélecteurs qui fonctionnent dans un navigateur Web.

There are other variations to find substrings only at the start or end of the value, or which force case sensitivity when matching, for example. If you’re a web developer who wants to know more about attribute selectors, I recommend looking them up on the Mozilla developer site (link at the end of the article). For our purposes, these should allow us to distinguish between different types of primitive in Inkscape. Let’s look at one of our star shapes in the XML editor (shown bottom right). That’s a long list of attributes, but the one we’re most interested in is the last one: “sodipodi:type” with a value of “star”. As I’ve discussed previously in this column, the “sodipodi” part is the namespace for this attribute, which is required because it’s not part of the SVG specification. In practical terms, however, I usually just refer to this as the “type” attribute. Suppose we want to target just the stars with a CSS rule. Based on the table of attribute selectors you might expect [type=“star”] to do the job, but it doesn’t. Not even a more basic selector of just [type] is accepted by Inkscape.

Il existe d'autres variantes pour trouver des sous-chaînes uniquement au début ou à la fin de la valeur, ou qui forcent la sensibilité à la casse lors de la correspondance, par exemple. Si vous êtes un développeur Web et que vous souhaitez en savoir plus sur les sélecteurs d'attributs, je vous recommande de les consulter sur le site des développeurs Mozilla (lien en fin d'article).

Pour nos besoins, ceux-ci devraient nous permettre de distinguer les différents types de primitives dans Inkscape. Regardons l'une de nos formes en étoile dans l'éditeur XML (en bas à droite).

C'est une longue liste d'attributs, mais celui qui nous intéresse le plus est le dernier : « sodipodi:type » avec une valeur de « star ». Comme je l'ai déjà dit dans cette rubrique, la partie « sodipodi » est l'espace de nom pour cet attribut, qui est nécessaire car il ne fait pas partie de la spécification SVG. En termes pratiques, cependant, je me réfère généralement à cet attribut comme étant l'attribut « type ».

Supposons que nous voulions cibler uniquement les étoiles avec une règle CSS. D'après le tableau des sélecteurs d'attributs, on pourrait s'attendre à ce que [type=“star”] fasse l'affaire, mais ce n'est pas le cas. Inkscape n'accepte même pas un sélecteur plus basique de [type].

Perhaps it’s that pesky namespace. How about [sodipodi:type] or [sodipodi:type=“star”]? No, they don’t work either. In fact namespaces in CSS are a bit of a pain, requiring you to redefine your prefixes in the CSS in addition to the definition in the XML itself. In the selector rule, the namespace is then separated from the value by a pipe character, not a colon. So, in theory, manually adding an “@namespace” rule to the stylesheet, then using [sodipodi|type=“star”] should do the job. But not in Inkscape. I’ve tried many, many different combinations of attribute selector, both with and without namespaces, but can’t get any of them to work within Inkscape. This is a real shame as it makes it impossible to target specific Inkscape primitives, or elements with other proprietary attributes. If you’re a web developer, then you may wish to know that these selectors do work as advertised in web browsers, provided you include the CSS namespace declaration and use a pipe separator. This <style> block, for example, will cause all the stars in the test document to appear with an orange fill within a web browser, but it doesn’t work when the same file is loaded into Inkscape.

C'est peut-être à cause de cet espace de noms. Que diriez-vous de [sodipodi:type] ou [sodipodi:type=“star”] ? Non, ils ne fonctionnent pas non plus. En fait, les espaces de noms en CSS sont un peu pénibles, car ils vous obligent à redéfinir vos préfixes dans le CSS en plus de la définition dans le XML lui-même. Dans la règle du sélecteur, l'espace de noms est ensuite séparé de la valeur par un caractère pipe, et non par deux points. Donc, en théorie, l'ajout manuel d'une règle « @namespace » à la feuille de style, puis l'utilisation de [sodipodi|type=“star”] devraient faire l'affaire. Mais pas dans Inkscape.

J'ai essayé de très nombreuses combinaisons différentes de sélecteurs d'attributs, avec ou sans espaces de noms, mais aucune d'entre elles ne fonctionne dans Inkscape. C'est vraiment dommage car il est impossible de cibler des primitives spécifiques d'Inkscape ou des éléments avec d'autres attributs propriétaires.

Si vous êtes un développeur Web, sachez que ces sélecteurs fonctionnent effectivement comme annoncé dans les navigateurs Web, à condition d'inclure la déclaration d'espace de nom CSS et d'utiliser un séparateur pipe. Ce bloc <style>, par exemple, fera apparaître toutes les étoiles du document de test avec un remplissage orange dans un navigateur Web, mais il ne fonctionne pas lorsque le même fichier est chargé dans Inkscape.

<style id=“style258”> @namespace sodipodi url(http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd); [sodipodi|type=“star”] { fill: orange !important; } </style> Although Inkscape doesn’t work with attribute selectors, there are a few other useful CSS rules that it does seem happy with. First, we have the descendent selector: simply enter two selectors with a space between them, and the rule will match only if the element matching the second selector is some descendent (in the XML structure) of an element matching the first selector. For example, in our test file, each row of objects is in a separate group, and I’ve set the ID for each group (using the Object Properties dialog) to “row-1”, “row-2” and so on. If I want to select all the <path> elements in row 3 (i.e. the star and Bézier path objects), then I can create a selector with the string “#row-3 path” to find every <path> element, but only if it’s a descendent of the element with the ID “row-3”.

<style id=“style258”>

 @namespace sodipodi url(http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd) ;
[sodipodi|type="star"] {
  fill : orange !important;
}

</style>

Bien qu'Inkscape ne fonctionne pas avec les sélecteurs d'attributs, il existe quelques autres règles CSS utiles dont il semble satisfait. Tout d'abord, nous avons le sélecteur descendant : il suffit de saisir deux sélecteurs séparés par un espace, et la règle ne correspondra que si l'élément correspondant au second sélecteur est un descendant (dans la structure XML) d'un élément correspondant au premier sélecteur.

Par exemple, dans notre fichier de test, chaque rangée d'objets se trouve dans un groupe distinct, et j'ai défini l'ID de chaque groupe (à l'aide de la boîte de dialogue Propriétés de l'objet) comme « row-1 », « row-2 » et ainsi de suite. Si je veux sélectionner tous les éléments <path> de la rangée 3 (c'est-à-dire les objets étoile et chemin de Bézier), je peux créer un sélecteur avec la chaîne « #row-3 path » pour trouver chaque élément <path>, mais seulement s'il est un descendant de l'élément avec l'ID « row-3 ».

Note that “descendent” in this case means any child, grandchild, great-grandchild and so on down the tree. If you want to select only immediate children, then use a “>” character between the individual parts of the selector (with optional white space around it for readability). Sometimes you might want a single set of CSS rules to apply to several different selectors. In that case, you can comma-separate the individual items. So, to create a rule that matches three specific elements by their IDs, use something like “#rect31, #rect1247, #path729”. The easiest way to achieve that is just to select them all before you press the “+” button at the bottom of the dialog: the field that opens for you to enter a selector will be pre-populated with a comma-separated list of the IDs. You can use commas to mix any types of selector, so creating a rule to match any immediate child of the second row plus any circle, would result in this: “#row-2 > *, circle”. Notice from this example that you can also use an asterisk (“*”) to match any element, regardless of its type or class. Comma-separated lists of selectors can easily become long and unwieldy when you need to match lots of different things. CSS has a couple of features in the form of the “:is()” and “:where()” pseudo-classes that can simplify many such rules. Although Inkscape does let you enter them into the dialog, unfortunately they don’t actually work in the program.

Notez que, dans ce cas, le terme « descendant » désigne tout enfant, petit-enfant, arrière-petit-enfant et ainsi de suite. Si vous souhaitez sélectionner uniquement les enfants immédiats, utilisez le caractère « > » entre les différentes parties du sélecteur (avec un espace blanc facultatif autour pour plus de lisibilité).

Parfois, vous pouvez souhaiter qu'un seul ensemble de règles CSS s'applique à plusieurs sélecteurs différents. Dans ce cas, vous pouvez séparer les différents éléments par des virgules. Ainsi, pour créer une règle qui correspond à trois éléments spécifiques par leurs ID, utilisez quelque chose comme « #rect31, #rect1247, #path729 ». Le moyen le plus simple d'y parvenir est de tous les sélectionner avant d'appuyer sur le bouton « + » en bas de la boîte de dialogue : le champ qui s'ouvre pour vous permettre de saisir un sélecteur sera prérempli avec une liste d'ID séparés par des virgules. Vous pouvez utiliser des virgules pour mélanger tous les types de sélecteurs. Ainsi, la création d'une règle permettant de faire correspondre tout enfant immédiat de la deuxième rangée et tout cercle donnerait le résultat suivant : « #row-2 > *, circle ». Remarquez dans cet exemple que vous pouvez également utiliser un astérisque (« * ») pour faire correspondre n'importe quel élément, quel que soit son type ou sa classe.

Les listes de sélecteurs séparés par des virgules peuvent facilement devenir longues et difficiles à manier lorsque vous devez faire correspondre de nombreux éléments différents. CSS dispose de quelques fonctionnalités sous la forme des pseudo-classes « :is() » et « :where() » qui peuvent simplifier de nombreuses règles de ce type. Bien qu'Inkscape vous permette de les saisir dans la boîte de dialogue, elles ne fonctionnent malheureusement pas dans le programme.

Also in the list of useful CSS rules that don’t work is the “:not()” pseudo-class. This should allow you to select elements that don’t match a particular rule. E.g. “:not(path)” for selecting all the non-path elements. Instead, Inkscape just swallows the new rule when you enter it – without it then appearing in the dialog at all. Prefixing it with a class selector (e.g. “.row-1:not(path)”) allows it to appear, but it certainly doesn’t work as it should. There are some pseudo-classes that do sort-of work with Inkscape, but not well enough to be genuinely useful. The “:first-child”, “:last-child” and “:nth-child()” selectors work, but only if they’re applied to a class or ID selector. For example, “.squares:first-child” will select any element with the “squares” class that is the first child of its parent. In the example file that will match the square in the top left, as it is the first child of the group element that holds the row. In theory you should be able to use just “:first-child” or “*:first-child” to match the first element of any parent but, in practice, that doesn’t work at all. This is a real shame as it makes it practically impossible to use the powerful “:nth-child()” pseudo-class to select all the odd children of a group, or every fourth one, for example. A related set of selectors are “:first-of-type”, “:nth-of-type”, plus some other “-of-type” strings. Trying to use these will actually cause Inkscape to crash entirely, so definitely steer clear of them!

La pseudo-classe « :not() » figure également sur la liste des règles CSS utiles qui ne fonctionnent pas. Elle devrait vous permettre de sélectionner les éléments qui ne correspondent pas à une règle particulière. Par exemple, « :not(path) » pour sélectionner tous les éléments qui ne sont pas des chemins. Au lieu de cela, Inkscape se contente d'avaler la nouvelle règle lorsque vous la saisissez - sans qu'elle n'apparaisse du tout dans la boîte de dialogue. Le fait de la préfixer avec un sélecteur de classe (par exemple, « .row-1:not(path) ») lui permet d'apparaître, mais elle ne fonctionne certainement pas comme elle le devrait.

Il existe quelques pseudo-classes qui fonctionnent plus ou moins avec Inkscape, mais pas suffisamment bien pour être véritablement utiles. Les sélecteurs « :first-child », « :last-child » et « :nth-child() » fonctionnent, mais uniquement s'ils sont appliqués à un sélecteur de classe ou d'ID. Par exemple, « .squares:first-child » sélectionnera tout élément de la classe « squares » qui est le premier enfant de son parent. Dans le fichier d'exemple, cela correspondra au carré en haut à gauche, car il est le premier enfant de l'élément de groupe qui contient la ligne. En théorie, vous devriez pouvoir utiliser simplement « :first-child » ou « *:first-child » pour faire correspondre le premier élément de n'importe quel parent mais, en pratique, cela ne fonctionne pas du tout. C'est vraiment dommage car cela rend pratiquement impossible l'utilisation de la puissante pseudo-classe « :nth-child() » pour sélectionner tous les enfants impairs d'un groupe, ou tous les quatrièmes, par exemple.

Un ensemble connexe de sélecteurs est « :first-of-type », « :nth-of-type », plus quelques autres chaînes « -of-type ». Si vous essayez de les utiliser, Inkscape se plantera complètement, alors évitez-les absolument !

To summarise my findings, the Selectors dialog is effective when used with simple class, ID or element selectors, including combining them in a comma-separated list. But most of the more powerful CSS rules either don’t work at all, don’t work as expected, or might even kill the program. It’s probably best to see this dialog for what it is: a replacement for the Selection Sets dialog that also lets you set some CSS rules in a stylesheet, should you wish to. If you’re enough of a developer to specifically need a stylesheet in your document, then you’re probably better off managing it outside of Inkscape for now. If you want to manage and store just some simple selections, on the other hand, then this dialog should serve you well enough. Over time, it’s likely that Inkscape’s CSS capabilities will improve, and perhaps some of the more complex rules will be supported. But, for now, you’re best to either keep it simple, or to do it by hand.

Pour résumer mes conclusions, la boîte de dialogue Sélecteurs est efficace lorsqu'elle est utilisée avec de simples sélecteurs de classe, d'ID ou d'élément, y compris pour les combiner dans une liste séparée par des virgules. Mais la plupart des règles CSS plus puissantes ne fonctionnent pas du tout, ne fonctionnent pas comme prévu, ou peuvent même tuer le programme. Il est probablement préférable de considérer cette boîte de dialogue pour ce qu'elle est : un remplacement de la boîte de dialogue Jeux de sélection qui vous permet également de définir certaines règles CSS dans une feuille de style, si vous le souhaitez. Si vous êtes suffisamment développeur pour avoir spécifiquement besoin d'une feuille de style dans votre document, il est probablement préférable de la gérer en dehors d'Inkscape pour le moment. En revanche, si vous souhaitez gérer et stocker quelques sélections simples, cette boîte de dialogue devrait vous suffire.

Au fil du temps, il est probable que les capacités CSS d'Inkscape s'amélioreront, et peut-être que certaines règles plus complexes seront prises en charge. Mais, pour l'instant, il est préférable de rester simple, ou de le faire à la main.

Links Overview of CSS resources and tutorials on MDN: https://developer.mozilla.org/en-US/docs/Web/CSS Attribute selectors on MDN: https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors The @namespace rule on MDN (works in browsers, not in Inkscape): https://developer.mozilla.org/en-US/docs/Web/CSS/@namespace

Liens

Aperçu des ressources et des didacticiels CSS sur MDN : https://developer.mozilla.org/en-US/docs/Web/CSS

Sélecteurs d'attributs sur MDN : https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors

La règle @namespace sur MDN (fonctionne dans les navigateurs, pas dans Inkscape) : https://developer.mozilla.org/en-US/docs/Web/CSS/@namespace

issue175/inkscape.txt · Dernière modification : 2021/12/03 18:20 de andre_domenech