Outils pour utilisateurs

Outils du site


issue187:inkscape

Over the past two articles, I’ve introduced the Web > Interactive Mockup extension, and gone on to show how it’s possible to create the same effect – and with fewer problems – with just a minimal amount of JavaScript. This time, I’ll be finishing this project by adding a little more code that will demonstrate some additional mock-up capabilities that simply aren’t possible using the extension. As a reminder, so far I’ve created a mock-up design consisting of three layers, each representing a different page in a website (which could equally well have been a design for an app, tutorial or presentation). By stacking the layers on top of each other, the JS code simply has to hide all the layers, then re-show the right one when the mock UI is clicked. This code is stored in the Inkscape document, and accessed via File > Document Properties, then the Scripting tab, the Embedded Scripts tab, and finally clicking on the randomly-generated Script ID in the list. Your code will appear in the Content pane at the bottom of the dialog – which is unfortunately not resizeable (you may wish to copy/paste between Inkscape and a text editor to make it easier to modify the code). After last month’s additions, the code looks like that shown above.

Au cours des deux derniers articles, j'ai présenté l'extension Web > Maquette Web interactive, puis j'ai montré comment il est possible de créer le même effet - et avec moins de problèmes - avec une quantité minimale de JavaScript. Cette fois-ci, je vais terminer ce projet en ajoutant un peu plus de code qui démontrera quelques capacités supplémentaires de maquettage qui ne sont tout simplement pas possibles en utilisant l'extension.

Pour rappel, jusqu'à présent, j'ai créé une maquette composée de trois calques, chacune représentant une page différente d'un site Web (qui aurait tout aussi bien pu être la conception d'une application, d'un tutoriel ou d'une présentation). En empilant les calques les uns sur les autres, le code JS doit simplement masquer tous les calques, puis réafficher le bon lorsque l'on clique sur la maquette de l'interface utilisateur. Ce code est stocké dans le document Inkscape, et on y accède via Fichier > Propriétés du document, puis l'onglet Programmation, l'onglet Programmes incorporés, et enfin en cliquant sur l'ID du script généré aléatoirement dans la liste. Le code apparaîtra dans le volet Contenu au bas de la boîte de dialogue - qui n'est malheureusement pas redimensionnable (vous pouvez copier/coller entre Inkscape et un éditeur de texte pour faciliter la modification du code). Après les ajouts du mois dernier, le code ressemble à celui illustré ci-dessus.

Within each interactive element, a single line of JS triggers the change to a different ‘page’ of the demo. These can be found by right-clicking on one of the elements, selecting Object Properties, then expanding the Interactivity section at the bottom of the dialog. For my examples, I’m just triggering changes on mouse clicks, so the ‘onclick’ field contains something like this: showLayer(“about”) That’s all we needed to do in order to create an interactive mock-up that scales with the size of the web browser, and doesn’t allow any non-active pages to be visible. Now let’s push things a little further with the addition of some new features. When looking at the pages of our mock website, it’s clear that they have some common elements – in this case the whole header section. Wouldn’t it be nice if we could keep those on a separate layer, so that any changes to those elements can be made in a single place, rather than having to apply them to each separate layer in our file? This is the sort of thing for which many applications use a ‘Master’ layer. Although not as politically charged as the use of ‘master-slave’ relationships in the computing world, it’s nonetheless a term that can offend people, and which is tending to be phased out. So rather than propagate a troublesome word for no real gain, I’ll be using the term ‘Main layer’.

Dans chaque élément interactif, une seule ligne de JS déclenche le passage à une autre « page » de la démo. Vous pouvez les trouver en cliquant avec le bouton droit de la souris sur l'un des éléments, en sélectionnant Propriétés de l'objet, puis en développant la section Interactivité en bas de la boîte de dialogue. Pour mes exemples, je ne déclenche des changements qu'en cas de clics de souris, le champ « onclick » contient donc quelque chose comme ceci :

showLayer(“about”)

C'est tout ce dont nous avions besoin pour créer une maquette interactive qui s'adapte à la taille du navigateur Web et empêche les pages non actives d'être visibles. Poussons maintenant les choses un peu plus loin en ajoutant quelques nouvelles fonctionnalités.

Lorsque l'on regarde les pages de notre site Web fictif, il est clair qu'elles ont des éléments communs - dans ce cas, toute la section de l'en-tête. Ne serait-il pas agréable de pouvoir les conserver sur un calque séparé, de sorte que toute modification de ces éléments puisse être effectuée en un seul endroit, plutôt que de devoir l'appliquer à chaque calque distinct de notre fichier ? C'est le genre de choses pour lesquelles de nombreuses applications utilisent un calque « maître ». Bien qu'il ne soit pas aussi politiquement chargé que l'utilisation des relations « maître-esclave » dans le monde de l'informatique, c'est néanmoins un terme qui peut offenser les gens et qui tend à disparaître progressivement. Aussi, plutôt que de propager un mot gênant sans réel intérêt, j'utiliserai le terme « calque principal ».

Our first step, therefore, is to split the file into a single Main layer, plus one additional layer for each page. The Main layer will contain all the common elements, and the others will contain just the page-specific parts. We therefore want our Main layer to be at the bottom of the z-stack, and to remain visible at all times. Here’s how our existing three layers are split into the four we now need: On the left we have the previous three pages. On the right we now have our Main layer at the bottom, with the three content layers above. I’ve added a green border around each of the content layers to indicate their extents: they each now have a transparent background, so without that it wouldn’t be so clear exactly how they relate to the positions in the old pages. These green borders are a temporary addition while developing the mock-up, and are removed before the layers are actually used. Additionally, although I’ve spread the pages out for this image, in practice they’re all stacked on top of each other within the document’s viewBox, as before. By showing the Main layer, plus one of the others at a time, we can therefore reproduce the same appearance as the three layers in the old version. All we need to do now is to modify our code to do the same thing on our behalf. To make the new code a little more readable, we’ll first use the XML editor to change the ID of the new layer to ‘main’, in the same way that we changed the layer IDs previously. When viewed in the XML editor, the top level of our document now looks something like that shown above.

Notre première étape consiste donc à diviser le fichier en un seul calque principal, plus un calque supplémentaire pour chaque page. Le calque principal contiendra tous les éléments communs, et les autres ne contiendront que les parties spécifiques à la page. Nous voulons donc que notre calque principal se trouve en bas de la pile z et qu'il reste visible à tout moment. Voici comment nos trois calques existants sont divisés en quatre calques dont nous avons maintenant besoin (en haut à gauche).

À gauche, nous avons les trois pages précédentes. À droite, nous avons maintenant notre calque principal en bas, avec les trois calques de contenu au-dessus. J'ai ajouté une bordure verte autour de chacune des couches de contenu pour indiquer leur étendue : elles ont toutes un arrière-plan transparent ; sans cela, leur lien avec leurs positions dans les anciennes pages ne serait pas très clair. Ces bordures vertes sont un ajout temporaire pendant le développement de la maquette, et seront retirées avant que les calques ne soient réellement utilisés. En outre, bien que j'aie étalé les pages sur cette image, dans la pratique, elles sont toutes empilées les unes sur les autres dans la fenêtre (viewBox) du document, comme auparavant.

En affichant le calque principal, plus un des autres à la fois, nous pouvons donc reproduire la même apparence que les trois calques de l'ancienne version. Il ne nous reste plus qu'à modifier notre code pour qu'il fasse la même chose à notre place. Pour rendre le nouveau code un peu plus lisible, nous allons d'abord utiliser l'éditeur XML pour changer l'ID du nouveau calque en « main » (principal), de la même manière que nous avons changé les ID des calques précédemment. Dans l'éditeur XML, le niveau supérieur de notre document ressemble maintenant à l'illustration ci-dessus.

Looking back at our JavaScript file from earlier, we still want our function to perform the same basic task: hide all the layers, then show a specific one. Except now we also want it to show a second layer at the same time. It’s these two lines that are responsible for re-showing the specified layer in the existing code: const layerToShow = document.querySelector(“#” + id); layerToShow.style.display = “inline”; We could simply add a similar pair of lines, hard-coding the ID in the querySelector() call as “#main”. That would definitely do the job, but it’s not very flexible. What if we want to show two ‘main’ layers later, perhaps to separate the text from the graphic elements? To give us this extra flexibility let’s create an array of layers that we want to show, then loop over them to turn them all on. If you’re not a programmer you may not be familiar with arrays: for our purposes you can think of them as a special type of variable that can hold a list of things. For this simple mock-up, our list will always contain ‘main’ and the id that was passed into the function, but you should be able to guess how you might extend it to include ‘main-text’ and ‘main-graphics’: const layersToDisplay = [“main”, id];

En regardant notre fichier JavaScript de tout à l'heure, nous voulons toujours que notre fonction effectue la même tâche de base : masquer tous les calques, puis afficher un calque spécifique. Sauf que maintenant, nous voulons aussi qu'elle affiche un deuxième calque en même temps. Ce sont ces deux lignes qui sont responsables du réaffichage du calque spécifié dans le code existant :

const layerToShow = document.querySelector(“#” + id);

layerToShow.style.display = “inline”;

Nous pourrions simplement ajouter une paire de lignes similaires, en codant en dur l'ID dans l'appel querySelector() comme « #main ». Cela ferait l'affaire, mais ce n'est pas très souple. Que faire si nous voulons afficher deux calques « principaux » plus tard, peut-être pour séparer le texte des éléments graphiques ? Pour nous donner cette flexibilité supplémentaire, créons un éventail (« array ») des calques que nous voulons afficher, puis bouclons sur ceux-ci pour les activer tous. Si vous n'êtes pas un programmeur, vous n'êtes peut-être pas familier avec les « arrays » : pour nos besoins, vous pouvez les considérer comme un type spécial de variable qui peut contenir une liste de choses. Pour cette simple maquette, notre liste contiendra toujours « main » et l'id qui a été passé dans la fonction, mais vous devriez être capable de deviner comment vous pourriez l'étendre pour inclure « main-text » et « main-graphics » :

const layersToDisplay = [“main”, id] ;

Now we need to step through the array, pulling out one item at a time to work with. As we pull each of them out (using a forEach() loop), we get to assign the value to a variable. By naming this variable ‘id’, we are able to reuse our existing code for finding and showing the layer. The end result is something very similar to the code that was previously at the end of the showLayer() function, just with a little more wrapped around it (shown above). The last thing we need to do is to make sure that all the clickable elements still call the showLayer() function, passing the correct ID, after the re-working of layers that we did earlier. It’s particularly important to double-check any items that you’ve moved to the Main layer. Once you’re happy, load the page into a web browser and ensure each of the elements functions as you expect it to when you click on it – if any don’t, then double-check the code associated with them. So far, so good. But on trying out your interactive mock-up, you may have noticed that the mouse pointer doesn’t change to indicate that elements are clickable. It’s a minor visual thing, but we can definitely improve it. There are various ways to tackle this, but they all end up with us needing a line of CSS that tells the browser what cursor type to use. We want this to apply to all the elements with an ‘onclick’ handler. In our SVG, these are all implemented using ‘onclick’ attributes directly in the XML content – which means we should be able to add a style rule using an ‘[onclick]’ selector (matches any element with an ‘onclick’ attribute). That sounds like a perfect use for Inkscape’s ‘Selectors and CSS dialog’, right?

Maintenant, nous devons parcourir l'éventail, en extrayant un élément à la fois pour travailler avec. Au fur et à mesure que nous extrayons chacun d'entre eux (en utilisant une boucle forEach()), nous pouvons affecter la valeur à une variable. En nommant cette variable « id », nous pouvons réutiliser notre code existant pour trouver et afficher le calque. Le résultat final est très similaire au code qui se trouvait précédemment à la fin de la fonction showLayer(), mais avec un peu plus d'habillage (voir ci-dessus).

La dernière chose que nous devons faire est de nous assurer que tous les éléments cliquables appellent toujours la fonction showLayer(), en passant l'ID correct, après le remaniement des calques que nous avons fait plus tôt. Il est particulièrement important de revérifier tous les éléments que vous avez déplacés vers le calque principal. Une fois que vous êtes satisfait, chargez la page dans un navigateur Web et vérifiez que chaque élément fonctionne comme prévu lorsque vous cliquez dessus - si ce n'est pas le cas, vérifiez le code qui lui est associé.

Jusqu'ici, tout va bien. Mais en essayant votre maquette interactive, vous avez peut-être remarqué que le pointeur de la souris ne change pas pour indiquer que les éléments sont cliquables. Il s'agit d'un problème visuel mineur, mais nous pouvons certainement l'améliorer. Il existe plusieurs façons d'aborder ce problème, mais elles aboutissent toutes à la nécessité d'une ligne de CSS qui indique au navigateur le type de curseur à utiliser. Nous voulons que cette ligne s'applique à tous les éléments dotés d'un attribut « onclick ». Dans notre SVG, ces éléments sont tous implémentés à l'aide d'attributs « onclick » directement dans le contenu XML, ce qui signifie que nous devrions être en mesure d'ajouter une règle de style à l'aide d'un sélecteur « [onclick] » (correspondant à tout élément avec un attribut « onclick »). Cela semble être une utilisation parfaite de la boîte de dialogue « Sélecteurs et CSS » d'Inkscape, n'est-ce pas ?

Wrong. As I mentioned in part 112 of this series, the dialog doesn’t recognise the attribute selector syntax. An alternative is to create a suitable <style> block directly in the XML, either using Inkscape’s built-in XML editor, or by editing the SVG file in a text editor. Both of these approaches are a little awkward, especially if you’re not already an XML aficionado. Instead, let’s take a similar approach to the one we used for setting the height and width attributes: we’ll write a short bit of JavaScript that will manipulate the document directly when it’s loaded into the browser. We want this code to run once, on page load, so the following snippet should be added to the JS outside the showLayer() function. Just appending it to the bottom of the existing code is probably the easiest option. let css = document.createElementNS(“http://www.w3.org/2000/svg”, “style”); css.textContent = “[onclick] { cursor: pointer; }”; document.documentElement.appendChild(css); The first line of this code creates a new <style> block (in the SVG namespace) and assigns it to the ‘css’ variable. The second line just inserts a single CSS rule into the block: if any element has an ‘onclick’ attribute, the mouse cursor should be set to ‘pointer’ mode when it moves over the element. Finally, the third line inserts our new style block as a child of the <svg> element, after all the other content, where the browser will pick it up and automatically apply the rules.

Malheusement, non. Comme je l'ai mentionné dans la partie 112 de cette série, le dialogue ne reconnaît pas la syntaxe des sélecteurs d'attributs. Une alternative est de créer un bloc <style> approprié directement dans le XML, soit en utilisant l'éditeur XML intégré d'Inkscape, soit en éditant le fichier SVG dans un éditeur de texte. Ces deux approches sont un peu maladroites, surtout si vous n'êtes pas déjà un aficionado du XML. Au lieu de cela, nous allons adopter une approche similaire à celle que nous avons utilisée pour définir les attributs height et width : nous allons écrire un petit bout de JavaScript qui va manipuler le document directement lorsqu'il est chargé dans le navigateur. Nous voulons que ce code ne s'exécute qu'une seule fois, au chargement de la page. Le bout de code suivant doit donc être ajouté au JS en dehors de la fonction showLayer(). L'option la plus simple consiste à l'ajouter au bas du code existant.

let css = document.createElementNS(“http://www.w3.org/2000/svg”, “style”) ;

css.textContent = “[onclick] { cursor : pointer ; }” ;

document.documentElement.appendChild(css) ;

La première ligne de ce code crée un nouveau bloc <style> (dans l'espace de noms SVG) et l'assigne à la variable « css ». La deuxième ligne insère une seule règle CSS dans le bloc : si un élément possède un attribut « onclick », le curseur de la souris doit passer en mode « pointeur » lorsqu'il se déplace sur l'élément. Enfin, la troisième ligne insère notre nouveau bloc de style en tant qu'enfant de l'élément <svg>, après tout le reste du contenu, où le navigateur le détectera et appliquera automatiquement les règles.

There’s one last thing I’d like to do to really make this mock-up work effectively. You may have noticed that each page includes a ‘hamburger menu’ at the top-right. Let’s see if we can make that work, at least to some extent. One approach would be to create six pages instead of three: a second version of each page would simply duplicate the original, but with the addition of the open menu. If you were solely using the Interactive Mockup extension, then that is pretty much your only option. But we’re already up to our elbows in real JavaScript, so we have more subtle tools at our disposal. We’ve already discovered that we can have more than one layer visible at a time, and rely on transparency to ensure that all the right parts are displayed at once. This is, after all, what we did when we added the Main layer. So why not do the same with the menu? In this scenario each ‘page’ consists of the Main layer, the relevant page layer, and an optional menu layer that sits on top of them all. Let’s begin by designing the menu as a new layer at the top of the z-stack (shown above). Each of the first three entries in the menu carries the same onclick handler as the equivalent on the main layer (in fact I copied and pasted the objects from there). We’ll deal with the ‘Sign Out’ option later. Now the question is how to make the menu pop-up when we click on the hamburger button – but that’s really not so tricky. If we use the XML editor to give the ‘Menu’ layer an ID of ‘menu’, then you can probably guess what this function (shown bottom right) will do.

Il reste une dernière chose que j'aimerais faire pour que cette maquette soit vraiment efficace. Vous avez peut-être remarqué que chaque page comporte un « menu hamburger » en haut à droite. Voyons si nous pouvons faire en sorte que cela fonctionne, du moins dans une certaine mesure.

Une approche consisterait à créer six pages au lieu de trois : une deuxième version de chaque page reproduirait simplement l'original, mais avec l'ajout du menu ouvert. Si vous utilisez uniquement l'extension Maquette Web interactive, c'est à peu près votre seule option. Mais comme nous sommes déjà plongés dans le JavaScript réel, nous disposons d'outils plus subtils.

Nous avons déjà découvert que nous pouvons avoir plus d'une couche visible à la fois, et nous appuyer sur la transparence pour nous assurer que toutes les bonnes parties sont affichées en même temps. C'est, après tout, ce que nous avons fait lorsque nous avons ajouté le calque principal. Alors pourquoi ne pas faire de même avec le menu ? Dans ce scénario, chaque « page » se compose de la couche principale, de la couche de page correspondante et d'une couche de menu facultative qui se trouve au-dessus de toutes ces couches. Commençons par concevoir le menu sous la forme d'un nouveau calque en haut de la pile z (voir ci-dessus).

Chacune des trois premières entrées du menu porte le même gestionnaire de clic que son équivalent sur le calque principal (en fait, j'ai copié et collé les objets à partir de là). Nous nous occuperons de l'option « Déconnexion » plus tard. Maintenant, la question est de savoir comment faire apparaître le menu lorsque nous cliquons sur le bouton hamburger - mais ce n'est pas si compliqué. Si nous utilisons l'éditeur XML pour donner à la couche « Menu » l'ID « menu », vous pouvez probablement deviner ce que fera cette fonction (en bas à droite).

All we need to do now is to call the showMenu() function from the onclick handler of the hamburger menu that lives on the Main layer. We’re not calling the existing showLayer() function, so none of the existing layers is hidden. All that happens is that the Menu layer is displayed in addition to the others that were already visible – exactly what we wanted. As it stands, the mock-up is good enough for demo purposes, but perhaps a little clunky in parts. When the menu is ‘opened’, for example, there’s no way to ‘close’ it other than to navigate to one of the pages. One possible enhancement might be to add an almost-transparent rectangle to that layer, behind the main content. A suitable closeMenu() function, and an onclick handler added to the rectangle, would allow you to click outside the menu to close it. I’ll leave that one as an exercise for the reader. And what of that ‘Sign Out’ option? You could create another layer containing a mocked-up sign-out dialog, but do you really need to? Once you’re comfortable with showing and hiding things in JavaScript, it’s always tempting to go a little too far, and turn your ‘interactive mock-up’ into something approaching a full UI demonstration. Sometimes that might be appropriate, but often it’s better to do the bare minimum you can to help people to visualise how the final website or application might work. Too much detail or functionality can actually be a distraction, and can even inhibit further discussions or ideas. In this case, therefore, the ‘Sign Out’ option will simply get an onclick handler containing this: alert(“You are now signed out”);

Tout ce que nous devons faire maintenant, c'est d'appeler la fonction showMenu() à partir l'attribut onclick du menu hamburger qui se trouve sur la couche Main. Nous n'appelons pas la fonction showLayer() existante, de sorte qu'aucun des calques existants n'est caché. Tout ce qui se passe, c'est que le calque Menu est affiché en plus des autres qui étaient déjà visibles, exactement ce que nous voulions.

En l'état actuel des choses, la maquette est suffisamment bonne pour servir de démonstration, mais peut-être un peu maladroite par endroits. Lorsque le menu est « ouvert », par exemple, il n'y a aucun moyen de le « fermer » autrement qu'en naviguant vers l'une des pages. Une amélioration possible serait d'ajouter un rectangle presque transparent à cette couche, derrière le contenu principal. Une fonction closeMenu() appropriée et un gestionnaire de clic ajouté au rectangle, vous permettraient de cliquer en dehors du menu pour le fermer. Je laisse au lecteur le soin de s'exercer sur ce point.

Et qu'en est-il de l'option « Déconnexion » ? Vous pourriez créer un autre calque contenant une maquette de dialogue de déconnexion, mais est-ce vraiment nécessaire ? Une fois que vous êtes à l'aise avec l'affichage et la dissimulation des éléments en JavaScript, il est toujours tentant d'aller un peu trop loin et de transformer votre « maquette interactive » en quelque chose qui s'approche d'une démonstration complète de l'interface utilisateur. Cela peut parfois être approprié, mais il est souvent préférable de faire le strict minimum pour aider les gens à visualiser le fonctionnement du site Web ou de l'application finale. Trop de détails ou de fonctionnalités peuvent en fait constituer une distraction, voire inhiber les discussions ou les idées ultérieures. Dans ce cas, donc, l'option « Déconnexion » sera simplement dotée d'un attribut onclick contenant ceci :

alert(“You are now signed out”) ;

With that addition our simple mock-up is complete. The key thing to take away is that the code for doing something like this probably isn’t as complex as you thought. While the Interactive Mockup extension can definitely be useful, you can easily get more functionality, and certainly a lot more flexibility, by just learning enough JavaScript to be able to target some elements in the page and selectively change their ‘style.display’ properties. If you do want to reproduce something like my mock-up, perhaps as a bit of practice to get a feel for the JS side of things, here’s the complete code we ended up with in the Document Properties dialog for your convenience (see above). In addition to that, each clickable element on the page has a single function call in the ‘onclick’ field of the ‘Interactivity’ section at the bottom of the Object > Object Properties dialog. In most cases, this was just a call to the showLayer() function, passing in the name of the page to display (e.g. showLayer(“contact”) ). In the case of the hamburger menu, it was a call to the showMenu() function. And our final addition was a call to the browser’s built-in alert() function for the ‘Sign Out’ option. When you take a step back and look at it, that’s really quite a lot of functionality in this interactive mock-up, for not a huge amount of code. But we’re done with this now – and with ‘Interactive Mockup’ being the last of the new extensions, we’re done with the features that were added to Inkscape 1.x. Next month, I’ll start what is sure to be a long series on the new features and additions in Inkscape 1.2.x.

Avec cet ajout, notre maquette simple est terminée. Ce qu'il faut retenir, c'est que le code pour faire quelque chose comme ça n'est probablement pas aussi complexe que vous le pensiez. Bien que l'extension Maquette Web interactive puisse être très utile, vous pouvez facilement obtenir plus de fonctionnalités, et certainement beaucoup plus de flexibilité, en apprenant suffisamment de JavaScript pour pouvoir cibler certains éléments de la page et changer sélectivement leurs propriétés « style.display ».

Si vous souhaitez reproduire quelque chose comme ma maquette, peut-être pour vous entraîner et vous familiariser avec l'aspect JS des choses, voici le code complet que nous avons obtenu dans la boîte de dialogue des propriétés du document (voir ci-dessus).

En outre, chaque élément cliquable de la page dispose d'un appel de fonction unique dans le champ « onclick » de la section « Interactivité » au bas de la boîte de dialogue Objet > Propriétés de l'objet. Dans la plupart des cas, il s'agit simplement d'un appel à la fonction showLayer(), avec le nom de la page à afficher (par exemple, showLayer(“contact”)). Dans le cas du menu hamburger, il s'agissait d'un appel à la fonction showMenu(). Enfin, nous avons ajouté un appel à la fonction intégrée alert() du navigateur pour l'option « Déconnexion ».

Lorsque l'on prend un peu de recul et que l'on y regarde de plus près, on s'aperçoit qu'il y a vraiment beaucoup de fonctionnalités dans cette maquette interactive, pour une quantité minime de code. Mais nous en avons terminé avec cela - et avec la « maquette interactive » qui est la dernière des nouvelles extensions - et nous en avons terminé avec les fonctionnalités qui ont été ajoutées à Inkscape 1.x. Le mois prochain, je commencerai ce qui sera certainement une longue série sur les nouvelles fonctionnalités et les ajouts dans Inkscape 1.2.x.

issue187/inkscape.txt · Dernière modification : 2022/11/30 18:37 de andre_domenech