Comment créer un plugin de croquis avec JavaScript, HTML et CSS (Partie 1)

Publié: 2022-03-10
Résumé rapide ↬ Si vous avez déjà travaillé avec Sketch, il y a de fortes chances qu'il y ait eu de nombreux moments où vous avez pensé : "Si seulement Sketch pouvait faire cette chose en particulier, je serais capable d'accomplir la tâche à accomplir beaucoup plus rapide, plus facile et meilleur. Eh bien, ne vous inquiétez plus! Dans cet article en deux parties, vous apprendrez à créer vos propres plugins Sketch à partir de zéro, vous donnant les compétences nécessaires pour résoudre exactement ce type de problèmes.

Ce didacticiel est destiné aux personnes qui connaissent et utilisent l'application Sketch et qui n'ont pas peur de jouer avec le code. Pour en tirer le meilleur parti, vous devrez avoir au moins une expérience de base en écriture JavaScript (et, éventuellement, HTML/CSS).

Le plugin que nous allons créer s'appelle "Mosaic". Dans la première partie, nous découvrirons les fichiers de base qui composent un plugin Sketch ; nous allons écrire du JavaScript et créer une interface utilisateur pour notre plugin à l'aide de HTML et de CSS. Le prochain article portera sur la façon de connecter l'interface utilisateur au code principal du plugin, comment implémenter les principales fonctionnalités du plugin, et à la fin, vous apprendrez également comment optimiser le code et le fonctionnement du plugin.

Je partagerai également le code du plugin (JS, HTML, CSS) et les fichiers que vous pourrez examiner et utiliser à des fins d'apprentissage.

Que sont les plugins Sketch et comment fonctionnent-ils ?

Dans Sketch, les plugins sont un moyen d'ajouter des fonctionnalités et des fonctionnalités qui ne sont pas présentes dans Sketch "prêt à l'emploi". Considérant qu'il y aura presque toujours une fonctionnalité ou une intégration manquante dans un programme donné (en particulier compte tenu du grand nombre de besoins que tout concepteur individuel pourrait avoir !), on peut commencer à imaginer comment les plugins pourraient être particulièrement utiles et puissants. Les plugins d'esquisse sont capables de faire à peu près tout ce que vous attendez, comme manipuler la couleur, la forme, la taille, l'ordre, le style, le regroupement et les effets des calques, mais aussi de faire des choses comme faire des requêtes aux ressources Internet, présenter un utilisateur interface, et bien plus encore !

Côté programmation, tous les plugins Sketch sont écrits en code JavaScript. Eh bien, en fait, ce n'est pas tout à fait vrai. Il est plus exact de dire que la plupart des plugins Sketch sont écrits en JavaScript, car il est également possible d'écrire un plugin Sketch dans l'un des langages de programmation d'Apple, Objective-C et Swift, même s'ils nécessitent une petite quantité de connaissances en JavaScript.

Ne vous inquiétez pas cependant. Dans cet article, nous allons nous concentrer sur la façon de créer des plugins Sketch en utilisant uniquement JavaScript, HTML et CSS. Nous n'aborderons pas les bases de HTML, CSS ou JavaScript - cet article suppose au moins une certaine connaissance et expérience de ces trois éléments. Le site Web des développeurs MDN est un excellent endroit pour en savoir plus sur le développement Web.

Plus après saut! Continuez à lire ci-dessous ↓

Commençons!

Tout d'abord, que fabriquons-nous ?

Dans ce didacticiel, je vais vous apprendre à créer un plugin de base convivial pour les débutants, capable de créer, dupliquer et modifier des calques, ainsi que de présenter à l'utilisateur une interface utilisateur agréable. Ce faisant, mon objectif est d'établir une connaissance fondamentale sur laquelle vous pouvez vous appuyer et l'utiliser pour créer vos propres plugins.

Le plugin que nous allons construire s'appelle Mosaic et est en fait un "générateur de motifs". Nourrissez-le de vos calques, modifiez quelques paramètres et cela créera un motif :

Image montrant l'interface utilisateur du plugin Mosaic et quelques exemples de modèles.
L'interface utilisateur de Mosaic et quelques exemples de motifs créés avec. ( Grand aperçu )

Si vous souhaitez installer et jouer avec Mosaic, vous pouvez télécharger le plugin terminé à partir de GitHub.

Un peu d'histoire : Mosaic s'inspire en grande partie d'un plugin Adobe Fireworks à l'ancienne appelé Twist-and-Fade . Twist-and-Fade était assez puissant, capable de dupliquer un calque un certain nombre de fois tout en ajustant sa teinte, sa position, sa rotation, sa taille et son opacité. Le plugin était même capable de générer des GIF animés, comme celui-ci, où il créait les cadres des deux éléments rotatifs de la cassette :

Image montrant une cassette de musique avec des tambours rotatifs
Cassette animée (source). ( Grand aperçu )

(Voici une vidéo de démonstration de Twist and Fade si vous souhaitez voir exactement comment cela a fonctionné.)

Pour les besoins de ce didacticiel, nous allons créer un plugin quelque peu similaire pour Sketch, bien qu'intentionnellement simplifié afin de garder le didacticiel aussi accessible que possible. Concrètement, notre plugin pourra :

  • Dupliquez n'importe quel calque d'esquisse (bitmap ou vecteur) et modifiez la position, la rotation et l'opacité du calque dupliqué. Cela nous donnera une introduction à la manipulation des calques à l'aide des API JavaScript de Sketch.
  • Affichez une interface utilisateur créée à l'aide de HTML, CSS et JS, qui vous apprendra comment créer facilement une interface pour le plugin, en utilisant des technologies Web que vous connaissez peut-être déjà. L'interface du plug-in est assez importante car c'est ainsi que nous rassemblerons les entrées de l'utilisateur concernant la façon dont l'utilisateur souhaite que l'image de mosaïque résultante ressemble.

Création de notre plugin de base en dix secondes chrono

Tout d'abord, nous allons créer la "base" (ou modèle) du plugin que nous voulons construire. Nous pourrions créer manuellement tous les fichiers et dossiers nécessaires qui composent un plugin, mais heureusement, nous n'avons pas à le faire, car Sketch peut le faire pour nous. Après avoir généré le plugin de modèle, nous pourrons le personnaliser comme bon nous semble.

Il existe une technique très simple et rapide que nous pouvons utiliser pour créer le plug-in de modèle, qui est à peu près ma méthode de prédilection lorsque j'ai besoin de créer un plug-in pour résoudre le problème auquel je suis confronté à un moment donné. Voici comment cela fonctionne:

Avec Sketch ouvert, vérifiez la barre de menu en haut de l'écran et cliquez sur Plugins -> Run Script . Cela ouvrira une boîte de dialogue que nous pourrons utiliser pour tester et exécuter le code. Nous pouvons également enregistrer tout code que nous y saisissons en tant que plugin, qui est la partie qui nous intéresse particulièrement en ce moment.

Effacez tout code déjà présent dans cette boîte de dialogue et remplacez-le par le code de démonstration suivant :

 const UI = require("sketch/ui"); UI.message(" Hey there, you fantastic plugin developer you! This is your plugin! Talking to you from the digital computer screen! In Sketch! Simply stupendous!");

Ensuite, appuyez sur Save Script as Plugin en bas à gauche de la fenêtre, entrez le nom que vous souhaitez donner à ce plugin (dans notre cas, il s'agit de "Mosaic"), puis Save Script as Plugin .

Appuyez sur "Enregistrer" en bas à gauche de la fenêtre et entrez le nom que vous souhaitez pour ce plugin. ( Grand aperçu )

Croyez-le ou non, nous avons déjà terminé - il ne reste plus qu'à manger le gâteau que nous venons de cuisiner. Voici la partie amusante. En ouvrant à nouveau le menu Plugins, vous devriez voir quelque chose comme ceci : votre tout nouveau plugin répertorié comme "Mosaic" ! Clique dessus!

( Grand aperçu )

Félicitations, vous venez d'écrire votre premier plugin Sketch !

Ce que vous devriez voir après avoir cliqué sur "Mosaic" devrait ressembler à la courte vidéo ci-dessus, avec un message d'info-bulle discret apparaissant au bas de l'écran commençant par les mots "Hey there..." - c'est exactement ce que le code que nous avons collé le dit à faire. C'est ce qui rend cette technique si géniale : elle permet de coller, de modifier et de tester facilement du code sans avoir à créer un plugin à partir de zéro. Si vous connaissez ou avez déjà joué avec la console Web de votre navigateur, c'est essentiellement cela. Avoir cet outil dans votre poche arrière lorsque vous créez et testez du code est indispensable.

Faisons un bref aperçu de ce que fait le code que vous avez ajouté :

Tout d'abord, il importe le module sketch/ui de la bibliothèque JS intégrée de Sketch et l'affecte à la variable UI . Ce module contient quelques méthodes utiles liées à l'interface, dont nous utiliserons :

 const UI = require("sketch/ui");

Ensuite, il appelle la méthode message (qui fait partie du module sketch/ui ) avec la chaîne de texte que nous voulons afficher dans l'info-bulle que nous avons vue :

 UI.message(" Hey there, you fantastic plugin developer you! This is your plugin! Talking to you from the digital computer screen! In Sketch! Simply stupendous!");

La méthode message() fournit un excellent moyen de présenter un message discret à l'utilisateur ; c'est idéal pour les cas où vous n'avez pas besoin de voler le focus (non modal) et n'avez pas besoin de boutons ou de champs de texte fantaisistes. Il existe également d'autres moyens de présenter des éléments d'interface utilisateur courants tels que des alertes, des invites, etc., dont certains seront utilisés lors de la création de Mosaic.

Personnaliser les métadonnées de notre plugin

Nous avons maintenant un plugin de base pour commencer, mais nous devons encore le peaufiner davantage et le faire vraiment nôtre. Notre prochaine étape consistera à modifier les métadonnées du plugin.

Pour cette étape, nous devrons jeter un coup d'œil à ce qu'on appelle le plugin bundle . Lorsque vous appuyez sur Enregistrer dans la fenêtre "Exécuter le script", Sketch enregistre votre plugin dans un dossier nommé Mosaic.sketchplugin que vous pouvez trouver dans le répertoire ~/Library/Application Support/com.bohemiancoding.sketch3/Plugins . C'est un peu long et ennuyeux à retenir ; en tant que raccourci, vous pouvez également le récupérer via Plugins -> Manage Plugins -> (right-click your plugin) -> Reveal Plugins Folder . Même s'il apparaît dans le Finder sous la forme d'un fichier unique, il s'agit en fait d'un dossier contenant tout ce dont notre plugin a besoin pour que Sketch l'exécute. La raison pour laquelle il apparaît comme un fichier unique bien qu'il s'agisse d'un dossier est que lorsque vous avez installé Sketch pour la première fois, Sketch a enregistré l'extension .sketchplugin en tant que "bundle" (un type spécial de dossier qui apparaît sous forme de fichier) et a demandé qu'il s'ouvre automatiquement. dans Sketch lorsqu'il est ouvert.

Jetons un coup d'œil à l'intérieur. Faites un clic droit sur Mosaic.sketchplugin , puis cliquez sur "Afficher le contenu du package". À l'intérieur, vous devriez voir la structure de répertoires suivante :

 Contents/ └ Resources/ └ Sketch/ └ manifest.json └ script.cocoascript

Vous vous demandez peut-être pourquoi il y a un fichier avec l'extension .cocoascript . Ne vous inquiétez pas, il s'agit simplement d'un fichier JavaScript normal et ne contient que le code que nous avons entré précédemment. Allez-y et renommez ce fichier en index.js , ce qui modifiera la structure du répertoire pour qu'elle ressemble à celle ci-dessous :

 Contents/ └ Resources/ └ Sketch/ └ manifest.json └ index.js

La manière la plus courante d'organiser les fichiers dans un bundle de plugins est la suivante : votre code (JavaScript) et manifest.json appartiennent à Sketch/ , et les ressources (pensez aux images, fichiers audio, fichiers texte, etc.) appartiennent à Resources/ .

Commençons par peaufiner le fichier nommé manifest.json . Ouvrez-le dans votre éditeur de code préféré, tel que Visual Studio Code ou Atom.

Vous verrez qu'il y a relativement peu de choses à l'intérieur pour le moment, mais nous en ajouterons plus bientôt. Le manifeste du plugin sert principalement à deux fins :

  1. Premièrement, il fournit des métadonnées qui décrivent le plugin à l'utilisateur - des choses comme son nom, sa version, le nom de l'auteur, etc. Sketch utilise ces informations dans la boîte de dialogue Sketch -> Preferences -> Plugins pour créer une liste et une description de votre plugin.
  2. Deuxièmement, il indique également à Sketch comment se lancer dans votre entreprise; c'est-à-dire qu'il indique à Sketch à quoi vous souhaitez que le menu de votre plugin ressemble, quels raccourcis clavier attribuer à votre plugin et où se trouve le code de votre plugin (afin que Sketch puisse l'exécuter).

Considérant l'objectif #1, décrivant le plugin à l'utilisateur, vous remarquerez probablement qu'il n'y a actuellement aucune description ou auteur donné, ce qui serait déroutant pour l'utilisateur et rendrait le plugin difficile à identifier. Corrigeons cela en ajustant les valeurs des clés pertinentes à :

 { "description": "Generate awesome designs and repeating patterns from your layers!", "author": "=> Your name here <=" }

Ensuite, ajustons l'identifiant du plugin. Cet identifiant utilise ce qu'on appelle une "notation de domaine inversée" qui est une façon très concise (ou ennuyeuse, faites votre choix) de dire "prenez le domaine de votre site, inversez l'ordre, puis mettez le nom de votre produit à la fin". Cela donnera quelque chose comme : com.your-company-or-your-name-its-not-that-big-a-deal.yourproduct .

Vous n'êtes pas obligé de vous en tenir à cette convention de nommage - vous pouvez mettre ce que vous voulez ici, tant qu'il est suffisamment unique pour éviter les conflits avec d'autres plugins (bien que ce soit probablement une bonne idée de s'en tenir au format RDN, d'autant plus qu'il fournit un système simple et réutilisable pour vos identifiants de plugin).

Pour cela, changez votre identifiant en com.your-name.mosaic :

 { "identifier": "com.your-name.mosaic" }

Personnellement, j'aime prendre toutes les clés liées aux métadonnées (titre, auteur, identifiant, etc.) et les regrouper en haut du manifeste afin qu'elles ne soient pas dispersées partout et m'aident à préserver ma santé mentale lorsque j'ai besoin de les trouver. .

Ensuite, regardons les touches de menu et de commands . Ces deux sont chargés de dire à Sketch quel code appeler et en réponse à quoi.

Si vous regardez la clé de menu , vous verrez qu'elle contient une clé de title , dont la valeur est le nom avec lequel notre plugin apparaîtra dans le menu Plugins . Il possède également une clé items , qui est une liste d' identifiants de commande :

 { "menu": { "title": "Mosaic", "items": [ "com.bohemiancoding.sketch.runscriptidentifier" ] } }

Pour le moment, il n'y a qu'un seul identifiant de commande dans cette liste, "com.bohemiancoding.sketch.runscriptidentifier" . Les identificateurs de commande pointent toujours vers une commande dans la liste des commands . À l'heure actuelle, notre plugin n'a qu'une seule commande, qui est celle avec cet identifiant :

 { "commands": [ { "script" : "script.cocoascript", "name" : "Mosaic", "handlers" : { "run" : "onRun" }, "identifier" : "com.bohemiancoding.sketch.runscriptidentifier" } ] }

Chaque fois que vous ajoutez un identifiant de commande à une entrée de menu , Sketch recherchera l'entrée de commande qui a cet identifiant et affichera la valeur de sa clé de name (qui dans ce cas est "Mosaic") et l'affichera dans le menu de votre plugin à la place de l'identifiant.

En ce qui concerne le rôle joué par les commandes, nous pouvons considérer une entrée de commande comme un moyen d'indiquer à Sketch quelle fonction dans le code JavaScript de notre plugin nous voulons exécuter lorsque cette commande est invoquée, "l'invocation" étant généralement le clic de l'utilisateur sur le menu associé Objet. L'entrée de commande ne fait rien par elle-même, c'est juste JSON - elle fournit simplement une description à Sketch de l'endroit où rechercher le JavaScript dont il a besoin pour s'exécuter lorsque la commande est invoquée.

Jusqu'à présent, nous avons parlé de ce que font les clés de name et d' identifier d'une commande, mais il y a deux autres clés dans une commande qui doivent être traitées : script et handlers .

La clé de script indique à Sketch où se trouve le fichier JavaScript qu'il doit exécuter. Notez comment Sketch suppose que le fichier de script en question se trouve dans le dossier Sketch/ , c'est pourquoi, par souci de simplicité, vous voudrez vous assurer que tout votre code JavaScript se trouve quelque part sous le dossier Sketch/ . Avant de passer à cette clé , il est important que vous vous assuriez de changer la valeur de cette clé en index.js , tout comme nous avons renommé le fichier plus tôt. Sinon, Sketch ne pourra pas trouver et exécuter votre fichier JavaScript.

La valeur de la clé des handlers est ce que Sketch examine pour déterminer quelle fonction dans votre JavaScript appeler. Ici, nous n'avons qu'un seul ensemble de gestionnaires : run , avec la valeur onRun . run est le nom d'une action Sketch prédéfinie et intégrée . Cette action d' run sera toujours appelée lorsqu'un utilisateur clique sur un élément de menu faisant référence à cette commande. onRun est le nom d'une fonction dans le fichier script.cocoascript généré automatiquement (que nous avons renommé en index.js ), et la fonction que nous voulons appeler lorsque l'événement run se produit, c'est-à-dire lorsque l'utilisateur clique sur l'élément de menu.

Dans l'exemple que nous avons jusqu'à présent, ce processus se déroule comme suit :

  1. L'utilisateur clique sur notre élément de menu.
  2. Sketch trouve la commande associée à cet élément de menu.
  3. Sketch trouve le fichier de script auquel la commande fait référence et l'exécute (ce qui signifie dans ce cas qu'il exécute le JavaScript dans index.js ).
  4. Étant donné que cette commande a été invoquée par un clic sur un élément de menu, elle est considérée comme une action d' run . Cela signifie que Sketch examinera la valeur handlers.run de la commande pour la fonction à appeler ensuite, qui dans ce cas est onRun .
  5. Sketch appelle la fonction onRun .

Les commandes sont le plus souvent appelées en réponse à un utilisateur cliquant sur l'un de vos éléments de menu, mais elles peuvent également être appelées en réponse à d'autres actions de l'utilisateur, telles que l'utilisateur modifiant la sélection ou une propriété sur un calque. Cependant, pour ce plugin, nous n'utiliserons aucune de ces autres actions. (Vous pouvez en savoir plus sur les actions et leur fonctionnement dans la page d'aide de l'API d'action.)

Avant de passer de ce manifeste, nous voudrons faire deux autres ajustements. En ce moment, notre menu a la structure :

 Mosaic └ Mosaic 
Image montrant l'élément de menu Mosaïque imbriqué de manière redondante dans un autre menu nommé Mosaïque
Assez redondant, non ? ( Grand aperçu )

…ce qui est un peu redondant puisque notre plugin n'a qu'un seul élément de menu. Cela ajoute également un peu de friction inutile pour notre utilisateur car notre plugin prend maintenant deux clics pour être invoqué au lieu d'un. Nous pouvons résoudre ce problème en ajoutant isRoot: true à notre menu :

 { "menu": { "title" : "Mosaic", "items" : [ "com.bohemiancoding.sketch.runscriptidentifier" ], "isRoot": true } }

Cela indique à Sketch de placer le premier niveau d'éléments de menu directement sous le menu Plugins , plutôt que de les imbriquer sous le title du menu.

Appuyez sur enregistrer et revenez à Sketch. Vous devriez voir que maintenant Mosaic -> Mosaic a été remplacé par Mosaic — parfait !

Image montrant l'interface utilisateur du plugin Mosaic
Interface utilisateur de Mosaic. ( Grand aperçu )

En ce qui concerne notre deuxième ajustement, allons-y et renommons cet identifiant de commande en quelque chose de moins lourd. Étant donné que les identifiants de commande n'ont besoin d'être uniques que dans le contexte d'un plugin individuel, nous pouvons le renommer en toute sécurité en quelque chose de plus concis et évident, comme "open" :

 { "commands": [ { ... "identifier" : "open" } ], "menu": { ... "items" : [ "open" ] } }

Avant de poursuivre, il est utile de noter que les menus peuvent également contenir d'autres menus. Vous pouvez facilement créer un sous-menu en imbriquant une autre entrée { title: ..., items: ... } dans la liste des items d'un autre menu :

 { "menu": { "title" : "Mosaic", "items" : [ "open", { "title" : "I'm a sub-menu!", "items" : [ "another-command-identifier" ] } ] } }

Construire l'interface utilisateur du plugin

Jusqu'à présent, nous avons écrit du code de démonstration et personnalisé le manifeste de notre plugin. Nous allons maintenant passer à la création de son interface utilisateur, qui est essentiellement une page Web intégrée dans une fenêtre (de la même manière que les navigateurs que vous utilisez couramment) :

La fenêtre du plugin. ( Grand aperçu )
Image montrant les composants composant l'interface de notre plugin : fenêtre et vue web
Les composants composant notre plugin. ( Grand aperçu )

La fenêtre

La conception de l'interface utilisateur de Mosaic a sa propre fenêtre, que nous pouvons considérer comme le composant le plus basique ; on va commencer par ça. Pour créer et afficher une fenêtre, nous devrons utiliser une classe intégrée à macOS par défaut, appelée NSWindow . Au cours du reste de ce didacticiel, nous ferons cela un peu (en utilisant des API intégrées comme NSWindow ) ce qui peut sembler un peu intimidant si vous n'êtes pas familier avec cela, mais ne vous inquiétez pas - je vais vous expliquer tout en chemin !

Remarque : alors que nous parlons d'API intégrées, la raison pour laquelle nous pouvons utiliser cette classe est grâce à un pont présent dans le runtime JavaScript utilisé par les plugins Sketch. Ce pont importe automatiquement ces classes, méthodes et fonctions intégrées qui ne seraient normalement disponibles que pour les applications natives.

Ouvrez Sketch/index.js dans votre éditeur de code, supprimez ce qui s'y trouve déjà et collez ce qui suit :

 function onRun(context){ const window = NSWindow.alloc().initWithContentRect_styleMask_backing_defer_( NSMakeRect(0, 0, 145, 500), NSWindowStyleMaskClosable | NSWindowStyleMaskTitled | NSWindowStyleMaskResizable, NSBackingStoreBuffered, false ); window.releasedWhenClosed = false; window.makeKeyAndOrderFront(nil); };

Voyons ce que fait ce premier morceau de code :

 function onRun(context){

Rappelez-vous plus tôt quand nous avons parlé des commandes et de leur fonctionnement, et nous avons dit à Sketch d'appeler en réponse à un clic de menu s'appelait onRun ? (Si vous avez besoin d'un rappel, revoyez cette partie ci-dessus, puis revenez.) Tout ce bit ne fait que créer cette fonction. Vous remarquerez également que notre fonction onRun prend un argument de context . C'est un argument avec lequel Sketch appellera vos gestionnaires de commandes et qui peut nous fournir certaines informations. Plus tard, nous l'utiliserons pour obtenir l'URL de notre bundle de plugins sur l'ordinateur de l'utilisateur.

 const window = NSWindow.alloc().initWithContentRect_styleMask_backing_defer( NSMakeRect(0, 0, 145, 500), NSWindowStyleMaskClosable | NSWindowStyleMaskTitled | NSWindowStyleMaskResizable, NSBackingStoreBuffered, false );

Ici, nous faisons en fait quelques choses :

  1. Tout d'abord, nous appelons alloc() sur NSWindow ; cela signifie essentiellement "réserver de la mémoire pour une instance de NSWindow". Il suffit de savoir que vous devrez le faire pour chaque instance d'une classe native que vous souhaitez créer. La méthode alloc est disponible dans chaque classe native.
  2. Ensuite, nous appelons la méthode d'initialisation de NSWindow (c'est-à-dire la méthode qui crée réellement une instance de NSWindow ), qui est nommée initWithContentRect:styleMask:backing:defer: . Vous remarquerez que c'est différent de ce que nous appelons dans notre code ci-dessus - il y a un tas de deux-points ( : ) entre chaque argument. Comme nous ne pouvons pas utiliser cette syntaxe en JavaScript, Sketch la renomme commodément en quelque chose que nous pouvons réellement utiliser en remplaçant les deux-points par des traits de soulignement, c'est ainsi que nous obtenons son nom JS : initWithContentRect_styleMask_backing_defer .
  3. Ensuite, nous passons chacun des arguments dont la méthode a besoin. Pour le premier argument, contentRect , nous fournissons un rectangle d'une taille suffisamment grande pour notre interface utilisateur.
  4. Pour styleMask , nous utilisons un masque de bits qui indique que nous voulons que notre fenêtre ait un bouton de fermeture, une barre de titre et qu'elle soit redimensionnable.
  5. Les deux arguments suivants, backing et defer , seront toujours définis sur NSBackingStoreBuffered et false , nous n'avons donc pas vraiment besoin de nous en soucier. (La documentation de cette méthode explique plus en détail pourquoi.)
 window.releasedWhenClosed = false; window.makeKeyAndOrderFront(null);

Ici, nous définissons la propriété releasedWhenClosed de NSWindow sur false , ce qui signifie : « Hey ! ne supprimez pas cette fenêtre de la mémoire simplement parce que l'utilisateur la ferme. Ensuite, nous appelons makeKeyAndOrderFront (null) , ce qui signifie : "Déplacez cette fenêtre au premier plan et donnez-lui le focus clavier."

Affichage Web : l'interface

Pour faciliter les choses, j'ai déjà écrit le code HTML et CSS de l'interface utilisateur Web du plugin que nous allons utiliser ; le seul code restant que nous devrons ajouter consistera à nous assurer que nous sommes en mesure de communiquer entre lui et notre code de plug-in Sketch.

Ensuite, téléchargez le code HTML et CSS. Une fois que vous l'avez téléchargé, extrayez-le, puis déplacez le dossier nommé "web-ui" dans le dossier Resources de notre plugin.

Remarque : L'écriture et l'optimisation du code HTML/CSS réel sortent du cadre de ce didacticiel, car il se concentre sur JavaScript, qui alimente les fonctionnalités principales du plug-in ; mais il existe une tonne de tutoriels sur le Web sur ce sujet, si vous souhaitez en savoir plus.

Si vous lancez notre plugin maintenant, vous verrez qu'il affiche une fenêtre — youpi, progressez ! Mais c'est vide, sans titre, et pas encore particulièrement utile. Nous devons l'obtenir pour afficher notre interface Web. Pour ce faire, nous devrons utiliser une autre classe native, WKWebView , qui est une vue spécialement conçue pour afficher du contenu Web.

Nous ajouterons le code nécessaire pour créer notre WKWebView sous le code que nous avons écrit pour notre fenêtre :

 function onRun(context){ // Create window const window = NSWindow.alloc().initWithContentRect_styleMask_backing_defer( NSMakeRect(0, 0, 145, 500), NSWindowStyleMaskClosable | NSWindowStyleMaskTitled | NSWindowStyleMaskResizable, NSBackingStoreBuffered, false ); window.releasedWhenClosed = false; // Create web view, and set it as the view for our window to display const webView = WKWebView.alloc().init(); window.contentView = webView; // Load our UI into the web view const webUIFolderURL = context.scriptURL .URLByDeletingLastPathComponent() .URLByAppendingPathComponent("../Resources/web-ui/"); const indexURL = webUIFolderURL.URLByAppendingPathComponent("index.html"); webView.loadFileURL_allowingReadAccessToURL(indexURL, webUIFolderURL); // Make window key and move to front window.makeKeyAndOrderFront(nil); };

Si nous exécutons notre plugin maintenant, nous verrons que nous avons maintenant une fenêtre ouverte qui affiche notre interface utilisateur Web. Succès!

Encore une fois, avant de continuer, examinons ce que fait le code que nous avons ajouté :

 const webView = WKWebView.alloc().init();

Cela devrait vous sembler familier - c'est fondamentalement la même chose que ce que nous avons fait lorsque nous avons créé notre NSWindow : allouer de la mémoire pour une vue Web, puis l'initialiser.

 window.contentView = webView;

Cette ligne de code indique à notre fenêtre d'afficher la vue Web que nous venons de créer.

 const webUIFolderURL = context.scriptURL .URLByDeletingLastPathComponent() .URLByAppendingPathComponent("../Resources/web-ui/");

Ici, notre objectif est de créer une URL qui pointe vers le dossier web-ui que nous avons créé précédemment. Afin d'obtenir cette URL, nous avons besoin d'un moyen de déterminer où se trouve le bundle de notre plugin dans le système de fichiers de l'utilisateur. Ici, nous utilisons la propriété context.scriptURL , qui nous donne l'URL du script en cours d'exécution. Cependant, cela ne nous donne pas une String JavaScript comme vous pourriez vous y attendre, mais une instance d'une classe native, NSURL , qui contient quelques méthodes qui facilitent la manipulation des chaînes d'URL.

Nous devons transformer ce que context.scriptURL nous donne -

 file://path-to-your-plugin/Contents/Sketch/index.js

- dans:

 file://path-to-your-plugin/Contents/Resources/web-ui/

Pas à pas:

  1. Appeler URLByDeletingLastPathComponent() la première fois nous donne file://path-to-your-plugin/Contents/Sketch/
  2. Appeler URLByDeletingLastPathComponent() nous donne à nouveau file://path-to-your-plugin/Contents/
  3. Et enfin, ajouter Resources/web-ui/ à la fin en utilisant URLByAppendingPathComponent ("Resources/web-ui/") nous donne file://path-to-your-plugin/Contents/Resources/web-ui/

Nous devons également créer une deuxième URL qui pointe directement vers le fichier index.html :

 const indexURL = webUIFolderURL.URLByAppendingPathComponent("index.html");

Enfin, nous disons à notre vue Web de charger index.html et de lui donner accès au contenu du dossier web-ui :

 webView.loadFileURL_allowingReadAccessToURL(indexURL, webUIFolderURL);

Bien. Jusqu'à présent, nous avons une fenêtre qui affiche notre interface utilisateur Web, comme nous le souhaitions. Cependant, ce n'est pas encore tout à fait terminé - notre conception originale n'a pas de barre de titre (ou "chrome"), mais notre fenêtre actuelle en a une. Il y a aussi le fait que lorsque nous cliquons à l'intérieur d'un document Sketch, ce document se déplace devant notre fenêtre, ce qui n'est pas ce que nous voulons - nous voulons que l'utilisateur puisse interagir avec la fenêtre du plugin et le document Sketch sans avoir à recentrer constamment d'une fenêtre à l'autre.

Pour résoudre ce problème, nous devons d'abord supprimer le chrome de la fenêtre par défaut et ne conserver que les boutons. L'ajout des deux lignes de code ci-dessous supprimera la barre de titre.

Remarque : Comme précédemment, toutes les propriétés et méthodes que nous utilisons ci-dessous sont documentées dans la page de documentation de NSWindow .

 window.titlebarAppearsTransparent = true; window.titleVisibility = NSWindowTitleHidden;

Ces deux prochaines lignes de code supprimeront les boutons de la fenêtre (également appelés "feux de signalisation" dans le jargon MacOS) dont nous n'avons pas besoin - "zoomer" et "réduire" - ne laissant que le bouton "fermer":

 window.standardWindowButton(NSWindowZoomButton).hidden = true; window.standardWindowButton(NSWindowMiniaturizeButton).hidden = true;

Pendant que nous y sommes, continuons également et modifions la couleur d'arrière-plan de la fenêtre pour qu'elle corresponde à celle de notre interface utilisateur Web :

 window.backgroundColor = NSColor.colorWithRed_green_blue_alpha(1, 0.98, 0.98, 1);

Ensuite, nous devons faire quelque chose pour garder notre fenêtre de plug-in flottante au-dessus des autres fenêtres, afin que l'utilisateur puisse interagir avec ses documents Sketch sans avoir à se soucier de la disparition de la fenêtre de Mosaic. Nous pouvons utiliser un type spécial de NSWindow pour cela, appelé NSPanel , qui est capable de "rester au-dessus" des autres fenêtres. Tout ce qui est nécessaire pour cela est de changer NSWindow en NSPanel , qui est un changement de code sur une seule ligne :

 const window = NSPanel.alloc().initWithContentRect_styleMask_backing_defer(

Maintenant, nous disons à notre fenêtre de panneau de flotter (rester au-dessus de toutes les autres) et de ne prendre le focus clavier/souris que lorsque cela est nécessaire :

 window.floatingPanel = true; window.becomesKeyOnlyIfNeeded = true;

Nous pouvons également modifier notre fenêtre pour qu'elle se rouvre automatiquement dans la dernière position où elle se trouvait :

 window.frameAutosaveName = "mosaic-panel-frame";

Cette ligne dit essentiellement "se souvenir de la position de cette fenêtre en l'enregistrant avec les préférences de Sketch sous la clé mosaic-panel-frame ".

Tous ensemble, nous avons maintenant le code suivant :

 function onRun(context){ // Create window const window = NSPanel.alloc().initWithContentRect_styleMask_backing_defer( NSMakeRect(0, 0, 145, 500), NSWindowStyleMaskClosable | NSWindowStyleMaskTitled | NSWindowStyleMaskResizable, NSBackingStoreBuffered, false ); window.becomesKeyOnlyIfNeeded = true; window.floatingPanel = true; window.frameAutosaveName = "mosaic-panel-frame"; window.releasedWhenClosed = false; window.standardWindowButton(NSWindowZoomButton).hidden = true; window.standardWindowButton(NSWindowMiniaturizeButton).hidden = true; window.titlebarAppearsTransparent = true; window.titleVisibility = NSWindowTitleHidden; window.backgroundColor = NSColor.colorWithRed_green_blue_alpha(1, 0.98, 0.98, 1); // Create web view, and set it as the view for our window to display const webView = WKWebView.alloc().init(); window.contentView = webView; // Load our UI into the webview const webUIFolderURL = context.scriptURL .URLByDeletingLastPathComponent() .URLByAppendingPathComponent("../Resources/web-ui/"); const indexURL = webUIFolderURL.URLByAppendingPathComponent("index.html"); webView.loadFileURL_allowingReadAccessToURL(indexURL, webUIFolderURL); // Make window key and move to front window.makeKeyAndOrderFront(nil); };

Organiser le code

Avant de passer à la partie suivante, c'est une bonne idée d'organiser notre code afin qu'il soit plus facile à naviguer et à modifier. Comme nous avons encore beaucoup de code à ajouter et que nous voulons éviter index.js ne devienne un dépotoir désordonné pour tout notre code, divisons un peu les choses et déplaçons notre code spécifique à l'interface utilisateur dans un fichier nommé ui.js , sous le dossier Sketch . Nous allons également extraire certaines des tâches d'interface utilisateur que nous effectuons, comme la création de la vue Web et de la fenêtre, dans leurs propres fonctions.

Créez un nouveau fichier appelé ui.js et insérez-y le code ci-dessous :

 // Private var _window; function createWebView(pageURL){ const webView = WKWebView.alloc().init(); webView.loadFileURL_allowingReadAccessToURL(pageURL, pageURL.URLByDeletingLastPathComponent()); return webView; }; function createWindow(){ const window = NSPanel.alloc().initWithContentRect_styleMask_backing_defer( NSMakeRect(0, 0, 420, 646), NSWindowStyleMaskClosable | NSWindowStyleMaskTitled | NSWindowStyleMaskResizable, NSBackingStoreBuffered, false ); window.becomesKeyOnlyIfNeeded = true; window.floatingPanel = true; window.frameAutosaveName = "mosaic-panel-frame"; window.releasedWhenClosed = false; window.standardWindowButton(NSWindowZoomButton).hidden = true; window.standardWindowButton(NSWindowMiniaturizeButton).hidden = true; window.titlebarAppearsTransparent = true; window.titleVisibility = NSWindowTitleHidden; window.backgroundColor = NSColor.colorWithRed_green_blue_alpha(1, 0.98, 0.98, 1); return window; }; function showWindow(window){ window.makeKeyAndOrderFront(nil); }; // Public function loadAndShow(baseURL){ if(_window){ showWindow(_window); return; } const pageURL = baseURL .URLByDeletingLastPathComponent() .URLByAppendingPathComponent("../Resources/web-ui/index.html"); const window = createWindow(); const webView = createWebView(pageURL); window.contentView = webView; _window = window; showWindow(_window); }; function cleanup(){ if(_window){ _window.orderOut(nil); _window = null; } }; // Export module.exports = { loadAndShow, cleanup };

Il y a quelques changements clés que nous avons apportés ici qu'il est important de noter. Outre le fait que nous avons créé des fonctions spécifiques pour la création, le masquage et l'affichage de notre fenêtre et de sa vue Web, nous avons également modularisé notre code d'interface utilisateur.

Remarquez la module.exports = { loadAndShow, cleanup } en bas ? C'est une façon pour nous de spécifier exactement quels objets et fonctions les scripts qui importent ce code d'interface utilisateur peuvent utiliser (et de cacher ceux dont nous ne voulons pas qu'ils s'inquiètent), ce qui signifie que nous avons maintenant une API plus organisée pour interagir avec, montrant et détruisant notre interface utilisateur.

Lecture recommandée : Libérer le plein potentiel des symboles dans l'esquisse

Voyons à quoi cela ressemble en pratique. De retour dans index.js , supprimez l'ancien code et ajoutez ce qui suit :

 const UI = require("./ui"); function onRun(context){ UI.loadAndShow(context.scriptURL); };

Nous utilisons une fonction spéciale que Sketch met automatiquement à notre disposition, require , pour importer notre code ui.js et affecter le module renvoyé à la variable UI . Cela nous donne accès à une API simplifiée pour déclencher notre interface utilisateur. Les choses sont beaucoup plus ordonnées maintenant et faciles à trouver !

Conclusion

Bravo - vous avez fait du chemin ! In the next part of this tutorial, we'll give our web UI the ability to send us a message when the “Apply” button is clicked, and we'll focus on the main plugin functionality: actually generating layer mosaics!